| use std::cell::LazyCell; | |
| use std::ops::ControlFlow; | |
| use rustc_abi::{ExternAbi, FieldIdx, ScalableElt}; | |
| use rustc_data_structures::unord::{UnordMap, UnordSet}; | |
| use rustc_errors::codes::*; | |
| use rustc_errors::{Diag, DiagCtxtHandle, Diagnostic, EmissionGuarantee, Level, MultiSpan}; | |
| use rustc_hir as hir; | |
| use rustc_hir::attrs::ReprAttr::ReprPacked; | |
| use rustc_hir::def::{CtorKind, DefKind}; | |
| use rustc_hir::{LangItem, Node, find_attr, intravisit}; | |
| use rustc_infer::infer::{RegionVariableOrigin, TyCtxtInferExt}; | |
| use rustc_infer::traits::{Obligation, ObligationCauseCode, WellFormedLoc}; | |
| use rustc_lint_defs::builtin::{REPR_TRANSPARENT_NON_ZST_FIELDS, UNSUPPORTED_CALLING_CONVENTIONS}; | |
| use rustc_macros::Diagnostic; | |
| use rustc_middle::hir::nested_filter; | |
| use rustc_middle::middle::resolve_bound_vars::ResolvedArg; | |
| use rustc_middle::middle::stability::EvalResult; | |
| use rustc_middle::ty::error::TypeErrorToStringExt; | |
| use rustc_middle::ty::layout::{LayoutError, MAX_SIMD_LANES}; | |
| use rustc_middle::ty::util::Discr; | |
| use rustc_middle::ty::{ | |
| AdtDef, BottomUpFolder, FnSig, GenericArgKind, RegionKind, TypeFoldable, TypeSuperVisitable, | |
| TypeVisitable, TypeVisitableExt, fold_regions, | |
| }; | |
| use rustc_session::lint::builtin::UNINHABITED_STATIC; | |
| use rustc_target::spec::{AbiMap, AbiMapping}; | |
| use rustc_trait_selection::error_reporting::InferCtxtErrorExt; | |
| use rustc_trait_selection::traits; | |
| use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; | |
| use tracing::{debug, instrument}; | |
| use ty::TypingMode; | |
| use super::compare_impl_item::check_type_bounds; | |
| use super::*; | |
| use crate::check::wfcheck::{ | |
| check_associated_item, check_trait_item, check_variances_for_type_defn, check_where_clauses, | |
| enter_wf_checking_ctxt, | |
| }; | |
| fn add_abi_diag_help<T: EmissionGuarantee>(abi: ExternAbi, diag: &mut Diag<'_, T>) { | |
| if let ExternAbi::Cdecl { unwind } = abi { | |
| let c_abi = ExternAbi::C { unwind }; | |
| diag.help(format!("use `extern {c_abi}` instead",)); | |
| } else if let ExternAbi::Stdcall { unwind } = abi { | |
| let c_abi = ExternAbi::C { unwind }; | |
| let system_abi = ExternAbi::System { unwind }; | |
| diag.help(format!( | |
| "if you need `extern {abi}` on win32 and `extern {c_abi}` everywhere else, \ | |
| use `extern {system_abi}`" | |
| )); | |
| } | |
| } | |
| pub fn check_abi(tcx: TyCtxt<'_>, hir_id: hir::HirId, span: Span, abi: ExternAbi) { | |
| struct UnsupportedCallingConventions { | |
| abi: ExternAbi, | |
| } | |
| impl<'a> Diagnostic<'a, ()> for UnsupportedCallingConventions { | |
| fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { | |
| let Self { abi } = self; | |
| let mut lint = Diag::new( | |
| dcx, | |
| level, | |
| format!("{abi} is not a supported ABI for the current target"), | |
| ); | |
| add_abi_diag_help(abi, &mut lint); | |
| lint | |
| } | |
| } | |
| // FIXME: This should be checked earlier, e.g. in `rustc_ast_lowering`, as this | |
| // currently only guards function imports, function definitions, and function pointer types. | |
| // Functions in trait declarations can still use "deprecated" ABIs without any warning. | |
| match AbiMap::from_target(&tcx.sess.target).canonize_abi(abi, false) { | |
| AbiMapping::Direct(..) => (), | |
| // already erred in rustc_ast_lowering | |
| AbiMapping::Invalid => { | |
| tcx.dcx().span_delayed_bug(span, format!("{abi} should be rejected in ast_lowering")); | |
| } | |
| AbiMapping::Deprecated(..) => { | |
| tcx.emit_node_span_lint( | |
| UNSUPPORTED_CALLING_CONVENTIONS, | |
| hir_id, | |
| span, | |
| UnsupportedCallingConventions { abi }, | |
| ); | |
| } | |
| } | |
| } | |
| pub fn check_custom_abi(tcx: TyCtxt<'_>, def_id: LocalDefId, fn_sig: FnSig<'_>, fn_sig_span: Span) { | |
| if fn_sig.abi == ExternAbi::Custom { | |
| // Function definitions that use `extern "custom"` must be naked functions. | |
| if !find_attr!(tcx, def_id, Naked(_)) { | |
| tcx.dcx().emit_err(crate::errors::AbiCustomClothedFunction { | |
| span: fn_sig_span, | |
| naked_span: tcx.def_span(def_id).shrink_to_lo(), | |
| }); | |
| } | |
| } | |
| } | |
| fn check_struct(tcx: TyCtxt<'_>, def_id: LocalDefId) { | |
| let def = tcx.adt_def(def_id); | |
| let span = tcx.def_span(def_id); | |
| def.destructor(tcx); // force the destructor to be evaluated | |
| if let Some(scalable) = def.repr().scalable { | |
| check_scalable_vector(tcx, span, def_id, scalable); | |
| } else if def.repr().simd() { | |
| check_simd(tcx, span, def_id); | |
| } | |
| check_transparent(tcx, def); | |
| check_packed(tcx, span, def); | |
| } | |
| fn check_union(tcx: TyCtxt<'_>, def_id: LocalDefId) { | |
| let def = tcx.adt_def(def_id); | |
| let span = tcx.def_span(def_id); | |
| def.destructor(tcx); // force the destructor to be evaluated | |
| check_transparent(tcx, def); | |
| check_union_fields(tcx, span, def_id); | |
| check_packed(tcx, span, def); | |
| } | |
| fn allowed_union_or_unsafe_field<'tcx>( | |
| tcx: TyCtxt<'tcx>, | |
| ty: Ty<'tcx>, | |
| typing_env: ty::TypingEnv<'tcx>, | |
| span: Span, | |
| ) -> bool { | |
| // HACK (not that bad of a hack don't worry): Some codegen tests don't even define proper | |
| // impls for `Copy`. Let's short-circuit here for this validity check, since a lot of them | |
| // use unions. We should eventually fix all the tests to define that lang item or use | |
| // minicore stubs. | |
| if ty.is_trivially_pure_clone_copy() { | |
| return true; | |
| } | |
| // If `BikeshedGuaranteedNoDrop` is not defined in a `#[no_core]` test, fall back to `Copy`. | |
| // This is an underapproximation of `BikeshedGuaranteedNoDrop`, | |
| let def_id = tcx | |
| .lang_items() | |
| .get(LangItem::BikeshedGuaranteedNoDrop) | |
| .unwrap_or_else(|| tcx.require_lang_item(LangItem::Copy, span)); | |
| let Ok(ty) = tcx.try_normalize_erasing_regions(typing_env, ty) else { | |
| tcx.dcx().span_delayed_bug(span, "could not normalize field type"); | |
| return true; | |
| }; | |
| let (infcx, param_env) = tcx.infer_ctxt().build_with_typing_env(typing_env); | |
| infcx.predicate_must_hold_modulo_regions(&Obligation::new( | |
| tcx, | |
| ObligationCause::dummy_with_span(span), | |
| param_env, | |
| ty::TraitRef::new(tcx, def_id, [ty]), | |
| )) | |
| } | |
| /// Check that the fields of the `union` do not need dropping. | |
| fn check_union_fields(tcx: TyCtxt<'_>, span: Span, item_def_id: LocalDefId) -> bool { | |
| let def = tcx.adt_def(item_def_id); | |
| assert!(def.is_union()); | |
| let typing_env = ty::TypingEnv::non_body_analysis(tcx, item_def_id); | |
| let args = ty::GenericArgs::identity_for_item(tcx, item_def_id); | |
| for field in &def.non_enum_variant().fields { | |
| if !allowed_union_or_unsafe_field(tcx, field.ty(tcx, args), typing_env, span) { | |
| let (field_span, ty_span) = match tcx.hir_get_if_local(field.did) { | |
| // We are currently checking the type this field came from, so it must be local. | |
| Some(Node::Field(field)) => (field.span, field.ty.span), | |
| _ => unreachable!("mir field has to correspond to hir field"), | |
| }; | |
| tcx.dcx().emit_err(errors::InvalidUnionField { | |
| field_span, | |
| sugg: errors::InvalidUnionFieldSuggestion { | |
| lo: ty_span.shrink_to_lo(), | |
| hi: ty_span.shrink_to_hi(), | |
| }, | |
| note: (), | |
| }); | |
| return false; | |
| } | |
| } | |
| true | |
| } | |
| /// Check that a `static` is inhabited. | |
| fn check_static_inhabited(tcx: TyCtxt<'_>, def_id: LocalDefId) { | |
| #[derive(Diagnostic)] | |
| #[diag("static of uninhabited type")] | |
| #[note("uninhabited statics cannot be initialized, and any access would be an immediate error")] | |
| struct StaticOfUninhabitedType; | |
| // Make sure statics are inhabited. | |
| // Other parts of the compiler assume that there are no uninhabited places. In principle it | |
| // would be enough to check this for `extern` statics, as statics with an initializer will | |
| // have UB during initialization if they are uninhabited, but there also seems to be no good | |
| // reason to allow any statics to be uninhabited. | |
| let ty = tcx.type_of(def_id).instantiate_identity(); | |
| let span = tcx.def_span(def_id); | |
| let layout = match tcx.layout_of(ty::TypingEnv::fully_monomorphized().as_query_input(ty)) { | |
| Ok(l) => l, | |
| // Foreign statics that overflow their allowed size should emit an error | |
| Err(LayoutError::SizeOverflow(_)) | |
| if matches!(tcx.def_kind(def_id), DefKind::Static{ .. } | |
| if tcx.def_kind(tcx.local_parent(def_id)) == DefKind::ForeignMod) => | |
| { | |
| tcx.dcx().emit_err(errors::TooLargeStatic { span }); | |
| return; | |
| } | |
| // SIMD types with invalid layout (e.g., zero-length) should emit an error | |
| Err(e @ LayoutError::InvalidSimd { .. }) => { | |
| let ty_span = tcx.ty_span(def_id); | |
| tcx.dcx().span_err(ty_span, e.to_string()); | |
| return; | |
| } | |
| // Generic statics are rejected, but we still reach this case. | |
| Err(e) => { | |
| tcx.dcx().span_delayed_bug(span, format!("{e:?}")); | |
| return; | |
| } | |
| }; | |
| if layout.is_uninhabited() { | |
| tcx.emit_node_span_lint( | |
| UNINHABITED_STATIC, | |
| tcx.local_def_id_to_hir_id(def_id), | |
| span, | |
| StaticOfUninhabitedType, | |
| ); | |
| } | |
| } | |
| /// Checks that an opaque type does not contain cycles and does not use `Self` or `T::Foo` | |
| /// projections that would result in "inheriting lifetimes". | |
| fn check_opaque(tcx: TyCtxt<'_>, def_id: LocalDefId) { | |
| let hir::OpaqueTy { origin, .. } = *tcx.hir_expect_opaque_ty(def_id); | |
| // HACK(jynelson): trying to infer the type of `impl trait` breaks documenting | |
| // `async-std` (and `pub async fn` in general). | |
| // Since rustdoc doesn't care about the hidden type behind `impl Trait`, just don't look at it! | |
| // See https://github.com/rust-lang/rust/issues/75100 | |
| if tcx.sess.opts.actually_rustdoc { | |
| return; | |
| } | |
| if tcx.type_of(def_id).instantiate_identity().references_error() { | |
| return; | |
| } | |
| if check_opaque_for_cycles(tcx, def_id).is_err() { | |
| return; | |
| } | |
| let _ = check_opaque_meets_bounds(tcx, def_id, origin); | |
| } | |
| /// Checks that an opaque type does not contain cycles. | |
| pub(super) fn check_opaque_for_cycles<'tcx>( | |
| tcx: TyCtxt<'tcx>, | |
| def_id: LocalDefId, | |
| ) -> Result<(), ErrorGuaranteed> { | |
| let args = GenericArgs::identity_for_item(tcx, def_id); | |
| // First, try to look at any opaque expansion cycles, considering coroutine fields | |
| // (even though these aren't necessarily true errors). | |
| if tcx.try_expand_impl_trait_type(def_id.to_def_id(), args).is_err() { | |
| let reported = opaque_type_cycle_error(tcx, def_id); | |
| return Err(reported); | |
| } | |
| Ok(()) | |
| } | |
| /// Check that the hidden type behind `impl Trait` actually implements `Trait`. | |
| /// | |
| /// This is mostly checked at the places that specify the opaque type, but we | |
| /// check those cases in the `param_env` of that function, which may have | |
| /// bounds not on this opaque type: | |
| /// | |
| /// ```ignore (illustrative) | |
| /// type X<T> = impl Clone; | |
| /// fn f<T: Clone>(t: T) -> X<T> { | |
| /// t | |
| /// } | |
| /// ``` | |
| /// | |
| /// Without this check the above code is incorrectly accepted: we would ICE if | |
| /// some tried, for example, to clone an `Option<X<&mut ()>>`. | |
| #[instrument(level = "debug", skip(tcx))] | |
| fn check_opaque_meets_bounds<'tcx>( | |
| tcx: TyCtxt<'tcx>, | |
| def_id: LocalDefId, | |
| origin: hir::OpaqueTyOrigin<LocalDefId>, | |
| ) -> Result<(), ErrorGuaranteed> { | |
| let (span, definition_def_id) = | |
| if let Some((span, def_id)) = best_definition_site_of_opaque(tcx, def_id, origin) { | |
| (span, Some(def_id)) | |
| } else { | |
| (tcx.def_span(def_id), None) | |
| }; | |
| let defining_use_anchor = match origin { | |
| hir::OpaqueTyOrigin::FnReturn { parent, .. } | |
| | hir::OpaqueTyOrigin::AsyncFn { parent, .. } | |
| | hir::OpaqueTyOrigin::TyAlias { parent, .. } => parent, | |
| }; | |
| let param_env = tcx.param_env(defining_use_anchor); | |
| // FIXME(#132279): Once `PostBorrowckAnalysis` is supported in the old solver, this branch should be removed. | |
| let infcx = tcx.infer_ctxt().build(if tcx.next_trait_solver_globally() { | |
| TypingMode::post_borrowck_analysis(tcx, defining_use_anchor) | |
| } else { | |
| TypingMode::analysis_in_body(tcx, defining_use_anchor) | |
| }); | |
| let ocx = ObligationCtxt::new_with_diagnostics(&infcx); | |
| let args = match origin { | |
| hir::OpaqueTyOrigin::FnReturn { parent, .. } | |
| | hir::OpaqueTyOrigin::AsyncFn { parent, .. } | |
| | hir::OpaqueTyOrigin::TyAlias { parent, .. } => GenericArgs::identity_for_item( | |
| tcx, parent, | |
| ) | |
| .extend_to(tcx, def_id.to_def_id(), |param, _| { | |
| tcx.map_opaque_lifetime_to_parent_lifetime(param.def_id.expect_local()).into() | |
| }), | |
| }; | |
| let opaque_ty = Ty::new_opaque(tcx, def_id.to_def_id(), args); | |
| // `ReErased` regions appear in the "parent_args" of closures/coroutines. | |
| // We're ignoring them here and replacing them with fresh region variables. | |
| // See tests in ui/type-alias-impl-trait/closure_{parent_args,wf_outlives}.rs. | |
| // | |
| // FIXME: Consider wrapping the hidden type in an existential `Binder` and instantiating it | |
| // here rather than using ReErased. | |
| let hidden_ty = tcx.type_of(def_id.to_def_id()).instantiate(tcx, args); | |
| let hidden_ty = fold_regions(tcx, hidden_ty, |re, _dbi| match re.kind() { | |
| ty::ReErased => infcx.next_region_var(RegionVariableOrigin::Misc(span)), | |
| _ => re, | |
| }); | |
| // HACK: We eagerly instantiate some bounds to report better errors for them... | |
| // This isn't necessary for correctness, since we register these bounds when | |
| // equating the opaque below, but we should clean this up in the new solver. | |
| for (predicate, pred_span) in | |
| tcx.explicit_item_bounds(def_id).iter_instantiated_copied(tcx, args) | |
| { | |
| let predicate = predicate.fold_with(&mut BottomUpFolder { | |
| tcx, | |
| ty_op: |ty| if ty == opaque_ty { hidden_ty } else { ty }, | |
| lt_op: |lt| lt, | |
| ct_op: |ct| ct, | |
| }); | |
| ocx.register_obligation(Obligation::new( | |
| tcx, | |
| ObligationCause::new( | |
| span, | |
| def_id, | |
| ObligationCauseCode::OpaqueTypeBound(pred_span, definition_def_id), | |
| ), | |
| param_env, | |
| predicate, | |
| )); | |
| } | |
| let misc_cause = ObligationCause::misc(span, def_id); | |
| // FIXME: We should just register the item bounds here, rather than equating. | |
| // FIXME(const_trait_impl): When we do that, please make sure to also register | |
| // the `[const]` bounds. | |
| match ocx.eq(&misc_cause, param_env, opaque_ty, hidden_ty) { | |
| Ok(()) => {} | |
| Err(ty_err) => { | |
| // Some types may be left "stranded" if they can't be reached | |
| // from a lowered rustc_middle bound but they're mentioned in the HIR. | |
| // This will happen, e.g., when a nested opaque is inside of a non- | |
| // existent associated type, like `impl Trait<Missing = impl Trait>`. | |
| // See <tests/ui/impl-trait/stranded-opaque.rs>. | |
| let ty_err = ty_err.to_string(tcx); | |
| let guar = tcx.dcx().span_delayed_bug( | |
| span, | |
| format!("could not unify `{hidden_ty}` with revealed type:\n{ty_err}"), | |
| ); | |
| return Err(guar); | |
| } | |
| } | |
| // Additionally require the hidden type to be well-formed with only the generics of the opaque type. | |
| // Defining use functions may have more bounds than the opaque type, which is ok, as long as the | |
| // hidden type is well formed even without those bounds. | |
| let predicate = | |
| ty::Binder::dummy(ty::PredicateKind::Clause(ty::ClauseKind::WellFormed(hidden_ty.into()))); | |
| ocx.register_obligation(Obligation::new(tcx, misc_cause.clone(), param_env, predicate)); | |
| // Check that all obligations are satisfied by the implementation's | |
| // version. | |
| let errors = ocx.evaluate_obligations_error_on_ambiguity(); | |
| if !errors.is_empty() { | |
| let guar = infcx.err_ctxt().report_fulfillment_errors(errors); | |
| return Err(guar); | |
| } | |
| let wf_tys = ocx.assumed_wf_types_and_report_errors(param_env, defining_use_anchor)?; | |
| ocx.resolve_regions_and_report_errors(defining_use_anchor, param_env, wf_tys)?; | |
| if infcx.next_trait_solver() { | |
| Ok(()) | |
| } else if let hir::OpaqueTyOrigin::FnReturn { .. } | hir::OpaqueTyOrigin::AsyncFn { .. } = | |
| origin | |
| { | |
| // HACK: this should also fall through to the hidden type check below, but the original | |
| // implementation had a bug where equivalent lifetimes are not identical. This caused us | |
| // to reject existing stable code that is otherwise completely fine. The real fix is to | |
| // compare the hidden types via our type equivalence/relation infra instead of doing an | |
| // identity check. | |
| let _ = infcx.take_opaque_types(); | |
| Ok(()) | |
| } else { | |
| // Check that any hidden types found during wf checking match the hidden types that `type_of` sees. | |
| for (mut key, mut ty) in infcx.take_opaque_types() { | |
| ty.ty = infcx.resolve_vars_if_possible(ty.ty); | |
| key = infcx.resolve_vars_if_possible(key); | |
| sanity_check_found_hidden_type(tcx, key, ty)?; | |
| } | |
| Ok(()) | |
| } | |
| } | |
| fn best_definition_site_of_opaque<'tcx>( | |
| tcx: TyCtxt<'tcx>, | |
| opaque_def_id: LocalDefId, | |
| origin: hir::OpaqueTyOrigin<LocalDefId>, | |
| ) -> Option<(Span, LocalDefId)> { | |
| struct TaitConstraintLocator<'tcx> { | |
| opaque_def_id: LocalDefId, | |
| tcx: TyCtxt<'tcx>, | |
| } | |
| impl<'tcx> TaitConstraintLocator<'tcx> { | |
| fn check(&self, item_def_id: LocalDefId) -> ControlFlow<(Span, LocalDefId)> { | |
| if !self.tcx.has_typeck_results(item_def_id) { | |
| return ControlFlow::Continue(()); | |
| } | |
| let opaque_types_defined_by = self.tcx.opaque_types_defined_by(item_def_id); | |
| // Don't try to check items that cannot possibly constrain the type. | |
| if !opaque_types_defined_by.contains(&self.opaque_def_id) { | |
| return ControlFlow::Continue(()); | |
| } | |
| if let Some(hidden_ty) = self | |
| .tcx | |
| .mir_borrowck(item_def_id) | |
| .ok() | |
| .and_then(|opaque_types| opaque_types.get(&self.opaque_def_id)) | |
| { | |
| ControlFlow::Break((hidden_ty.span, item_def_id)) | |
| } else { | |
| ControlFlow::Continue(()) | |
| } | |
| } | |
| } | |
| impl<'tcx> intravisit::Visitor<'tcx> for TaitConstraintLocator<'tcx> { | |
| type NestedFilter = nested_filter::All; | |
| type Result = ControlFlow<(Span, LocalDefId)>; | |
| fn maybe_tcx(&mut self) -> Self::MaybeTyCtxt { | |
| self.tcx | |
| } | |
| fn visit_expr(&mut self, ex: &'tcx hir::Expr<'tcx>) -> Self::Result { | |
| intravisit::walk_expr(self, ex) | |
| } | |
| fn visit_item(&mut self, it: &'tcx hir::Item<'tcx>) -> Self::Result { | |
| self.check(it.owner_id.def_id)?; | |
| intravisit::walk_item(self, it) | |
| } | |
| fn visit_impl_item(&mut self, it: &'tcx hir::ImplItem<'tcx>) -> Self::Result { | |
| self.check(it.owner_id.def_id)?; | |
| intravisit::walk_impl_item(self, it) | |
| } | |
| fn visit_trait_item(&mut self, it: &'tcx hir::TraitItem<'tcx>) -> Self::Result { | |
| self.check(it.owner_id.def_id)?; | |
| intravisit::walk_trait_item(self, it) | |
| } | |
| fn visit_foreign_item(&mut self, it: &'tcx hir::ForeignItem<'tcx>) -> Self::Result { | |
| intravisit::walk_foreign_item(self, it) | |
| } | |
| } | |
| let mut locator = TaitConstraintLocator { tcx, opaque_def_id }; | |
| match origin { | |
| hir::OpaqueTyOrigin::FnReturn { parent, .. } | |
| | hir::OpaqueTyOrigin::AsyncFn { parent, .. } => locator.check(parent).break_value(), | |
| hir::OpaqueTyOrigin::TyAlias { parent, in_assoc_ty: true } => { | |
| let impl_def_id = tcx.local_parent(parent); | |
| for assoc in tcx.associated_items(impl_def_id).in_definition_order() { | |
| match assoc.kind { | |
| ty::AssocKind::Const { .. } | ty::AssocKind::Fn { .. } => { | |
| if let ControlFlow::Break(span) = locator.check(assoc.def_id.expect_local()) | |
| { | |
| return Some(span); | |
| } | |
| } | |
| ty::AssocKind::Type { .. } => {} | |
| } | |
| } | |
| None | |
| } | |
| hir::OpaqueTyOrigin::TyAlias { in_assoc_ty: false, .. } => { | |
| tcx.hir_walk_toplevel_module(&mut locator).break_value() | |
| } | |
| } | |
| } | |
| fn sanity_check_found_hidden_type<'tcx>( | |
| tcx: TyCtxt<'tcx>, | |
| key: ty::OpaqueTypeKey<'tcx>, | |
| mut ty: ty::ProvisionalHiddenType<'tcx>, | |
| ) -> Result<(), ErrorGuaranteed> { | |
| if ty.ty.is_ty_var() { | |
| // Nothing was actually constrained. | |
| return Ok(()); | |
| } | |
| if let ty::Alias(ty::Opaque, alias) = ty.ty.kind() { | |
| if alias.def_id == key.def_id.to_def_id() && alias.args == key.args { | |
| // Nothing was actually constrained, this is an opaque usage that was | |
| // only discovered to be opaque after inference vars resolved. | |
| return Ok(()); | |
| } | |
| } | |
| let erase_re_vars = |ty: Ty<'tcx>| { | |
| fold_regions(tcx, ty, |r, _| match r.kind() { | |
| RegionKind::ReVar(_) => tcx.lifetimes.re_erased, | |
| _ => r, | |
| }) | |
| }; | |
| // Closures frequently end up containing erased lifetimes in their final representation. | |
| // These correspond to lifetime variables that never got resolved, so we patch this up here. | |
| ty.ty = erase_re_vars(ty.ty); | |
| // Get the hidden type. | |
| let hidden_ty = tcx.type_of(key.def_id).instantiate(tcx, key.args); | |
| let hidden_ty = erase_re_vars(hidden_ty); | |
| // If the hidden types differ, emit a type mismatch diagnostic. | |
| if hidden_ty == ty.ty { | |
| Ok(()) | |
| } else { | |
| let span = tcx.def_span(key.def_id); | |
| let other = ty::ProvisionalHiddenType { ty: hidden_ty, span }; | |
| Err(ty.build_mismatch_error(&other, tcx)?.emit()) | |
| } | |
| } | |
| /// Check that the opaque's precise captures list is valid (if present). | |
| /// We check this for regular `impl Trait`s and also RPITITs, even though the latter | |
| /// are technically GATs. | |
| /// | |
| /// This function is responsible for: | |
| /// 1. Checking that all type/const params are mention in the captures list. | |
| /// 2. Checking that all lifetimes that are implicitly captured are mentioned. | |
| /// 3. Asserting that all parameters mentioned in the captures list are invariant. | |
| fn check_opaque_precise_captures<'tcx>(tcx: TyCtxt<'tcx>, opaque_def_id: LocalDefId) { | |
| let hir::OpaqueTy { bounds, .. } = *tcx.hir_node_by_def_id(opaque_def_id).expect_opaque_ty(); | |
| let Some(precise_capturing_args) = bounds.iter().find_map(|bound| match *bound { | |
| hir::GenericBound::Use(bounds, ..) => Some(bounds), | |
| _ => None, | |
| }) else { | |
| // No precise capturing args; nothing to validate | |
| return; | |
| }; | |
| let mut expected_captures = UnordSet::default(); | |
| let mut shadowed_captures = UnordSet::default(); | |
| let mut seen_params = UnordMap::default(); | |
| let mut prev_non_lifetime_param = None; | |
| for arg in precise_capturing_args { | |
| let (hir_id, ident) = match *arg { | |
| hir::PreciseCapturingArg::Param(hir::PreciseCapturingNonLifetimeArg { | |
| hir_id, | |
| ident, | |
| .. | |
| }) => { | |
| if prev_non_lifetime_param.is_none() { | |
| prev_non_lifetime_param = Some(ident); | |
| } | |
| (hir_id, ident) | |
| } | |
| hir::PreciseCapturingArg::Lifetime(&hir::Lifetime { hir_id, ident, .. }) => { | |
| if let Some(prev_non_lifetime_param) = prev_non_lifetime_param { | |
| tcx.dcx().emit_err(errors::LifetimesMustBeFirst { | |
| lifetime_span: ident.span, | |
| name: ident.name, | |
| other_span: prev_non_lifetime_param.span, | |
| }); | |
| } | |
| (hir_id, ident) | |
| } | |
| }; | |
| let ident = ident.normalize_to_macros_2_0(); | |
| if let Some(span) = seen_params.insert(ident, ident.span) { | |
| tcx.dcx().emit_err(errors::DuplicatePreciseCapture { | |
| name: ident.name, | |
| first_span: span, | |
| second_span: ident.span, | |
| }); | |
| } | |
| match tcx.named_bound_var(hir_id) { | |
| Some(ResolvedArg::EarlyBound(def_id)) => { | |
| expected_captures.insert(def_id.to_def_id()); | |
| // Make sure we allow capturing these lifetimes through `Self` and | |
| // `T::Assoc` projection syntax, too. These will occur when we only | |
| // see lifetimes are captured after hir-lowering -- this aligns with | |
| // the cases that were stabilized with the `impl_trait_projection` | |
| // feature -- see <https://github.com/rust-lang/rust/pull/115659>. | |
| if let DefKind::LifetimeParam = tcx.def_kind(def_id) | |
| && let Some(def_id) = tcx | |
| .map_opaque_lifetime_to_parent_lifetime(def_id) | |
| .opt_param_def_id(tcx, tcx.parent(opaque_def_id.to_def_id())) | |
| { | |
| shadowed_captures.insert(def_id); | |
| } | |
| } | |
| _ => { | |
| tcx.dcx() | |
| .span_delayed_bug(tcx.hir_span(hir_id), "parameter should have been resolved"); | |
| } | |
| } | |
| } | |
| let variances = tcx.variances_of(opaque_def_id); | |
| let mut def_id = Some(opaque_def_id.to_def_id()); | |
| while let Some(generics) = def_id { | |
| let generics = tcx.generics_of(generics); | |
| def_id = generics.parent; | |
| for param in &generics.own_params { | |
| if expected_captures.contains(¶m.def_id) { | |
| assert_eq!( | |
| variances[param.index as usize], | |
| ty::Invariant, | |
| "precise captured param should be invariant" | |
| ); | |
| continue; | |
| } | |
| // If a param is shadowed by a early-bound (duplicated) lifetime, then | |
| // it may or may not be captured as invariant, depending on if it shows | |
| // up through `Self` or `T::Assoc` syntax. | |
| if shadowed_captures.contains(¶m.def_id) { | |
| continue; | |
| } | |
| match param.kind { | |
| ty::GenericParamDefKind::Lifetime => { | |
| let use_span = tcx.def_span(param.def_id); | |
| let opaque_span = tcx.def_span(opaque_def_id); | |
| // Check if the lifetime param was captured but isn't named in the precise captures list. | |
| if variances[param.index as usize] == ty::Invariant { | |
| if let DefKind::OpaqueTy = tcx.def_kind(tcx.parent(param.def_id)) | |
| && let Some(def_id) = tcx | |
| .map_opaque_lifetime_to_parent_lifetime(param.def_id.expect_local()) | |
| .opt_param_def_id(tcx, tcx.parent(opaque_def_id.to_def_id())) | |
| { | |
| tcx.dcx().emit_err(errors::LifetimeNotCaptured { | |
| opaque_span, | |
| use_span, | |
| param_span: tcx.def_span(def_id), | |
| }); | |
| } else { | |
| if tcx.def_kind(tcx.parent(param.def_id)) == DefKind::Trait { | |
| tcx.dcx().emit_err(errors::LifetimeImplicitlyCaptured { | |
| opaque_span, | |
| param_span: tcx.def_span(param.def_id), | |
| }); | |
| } else { | |
| // If the `use_span` is actually just the param itself, then we must | |
| // have not duplicated the lifetime but captured the original. | |
| // The "effective" `use_span` will be the span of the opaque itself, | |
| // and the param span will be the def span of the param. | |
| tcx.dcx().emit_err(errors::LifetimeNotCaptured { | |
| opaque_span, | |
| use_span: opaque_span, | |
| param_span: use_span, | |
| }); | |
| } | |
| } | |
| continue; | |
| } | |
| } | |
| ty::GenericParamDefKind::Type { .. } => { | |
| if matches!(tcx.def_kind(param.def_id), DefKind::Trait | DefKind::TraitAlias) { | |
| // FIXME(precise_capturing): Structured suggestion for this would be useful | |
| tcx.dcx().emit_err(errors::SelfTyNotCaptured { | |
| trait_span: tcx.def_span(param.def_id), | |
| opaque_span: tcx.def_span(opaque_def_id), | |
| }); | |
| } else { | |
| // FIXME(precise_capturing): Structured suggestion for this would be useful | |
| tcx.dcx().emit_err(errors::ParamNotCaptured { | |
| param_span: tcx.def_span(param.def_id), | |
| opaque_span: tcx.def_span(opaque_def_id), | |
| kind: "type", | |
| }); | |
| } | |
| } | |
| ty::GenericParamDefKind::Const { .. } => { | |
| // FIXME(precise_capturing): Structured suggestion for this would be useful | |
| tcx.dcx().emit_err(errors::ParamNotCaptured { | |
| param_span: tcx.def_span(param.def_id), | |
| opaque_span: tcx.def_span(opaque_def_id), | |
| kind: "const", | |
| }); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| fn is_enum_of_nonnullable_ptr<'tcx>( | |
| tcx: TyCtxt<'tcx>, | |
| adt_def: AdtDef<'tcx>, | |
| args: GenericArgsRef<'tcx>, | |
| ) -> bool { | |
| if adt_def.repr().inhibit_enum_layout_opt() { | |
| return false; | |
| } | |
| let [var_one, var_two] = &adt_def.variants().raw[..] else { | |
| return false; | |
| }; | |
| let (([], [field]) | ([field], [])) = (&var_one.fields.raw[..], &var_two.fields.raw[..]) else { | |
| return false; | |
| }; | |
| matches!(field.ty(tcx, args).kind(), ty::FnPtr(..) | ty::Ref(..)) | |
| } | |
| fn check_static_linkage(tcx: TyCtxt<'_>, def_id: LocalDefId) { | |
| if tcx.codegen_fn_attrs(def_id).import_linkage.is_some() { | |
| if match tcx.type_of(def_id).instantiate_identity().kind() { | |
| ty::RawPtr(_, _) => false, | |
| ty::Adt(adt_def, args) => !is_enum_of_nonnullable_ptr(tcx, *adt_def, *args), | |
| _ => true, | |
| } { | |
| tcx.dcx().emit_err(errors::LinkageType { span: tcx.def_span(def_id) }); | |
| } | |
| } | |
| } | |
| pub(crate) fn check_item_type(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), ErrorGuaranteed> { | |
| let mut res = Ok(()); | |
| let generics = tcx.generics_of(def_id); | |
| for param in &generics.own_params { | |
| match param.kind { | |
| ty::GenericParamDefKind::Lifetime { .. } => {} | |
| ty::GenericParamDefKind::Type { has_default, .. } => { | |
| if has_default { | |
| tcx.ensure_ok().type_of(param.def_id); | |
| } | |
| } | |
| ty::GenericParamDefKind::Const { has_default, .. } => { | |
| tcx.ensure_ok().type_of(param.def_id); | |
| if has_default { | |
| // need to store default and type of default | |
| let ct = tcx.const_param_default(param.def_id).skip_binder(); | |
| if let ty::ConstKind::Unevaluated(uv) = ct.kind() { | |
| tcx.ensure_ok().type_of(uv.def); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| match tcx.def_kind(def_id) { | |
| DefKind::Static { .. } => { | |
| tcx.ensure_ok().generics_of(def_id); | |
| tcx.ensure_ok().type_of(def_id); | |
| tcx.ensure_ok().predicates_of(def_id); | |
| check_static_inhabited(tcx, def_id); | |
| check_static_linkage(tcx, def_id); | |
| let ty = tcx.type_of(def_id).instantiate_identity(); | |
| res = res.and(wfcheck::check_static_item( | |
| tcx, def_id, ty, /* should_check_for_sync */ true, | |
| )); | |
| // Only `Node::Item` and `Node::ForeignItem` still have HIR based | |
| // checks. Returning early here does not miss any checks and | |
| // avoids this query from having a direct dependency edge on the HIR | |
| return res; | |
| } | |
| DefKind::Enum => { | |
| tcx.ensure_ok().generics_of(def_id); | |
| tcx.ensure_ok().type_of(def_id); | |
| tcx.ensure_ok().predicates_of(def_id); | |
| crate::collect::lower_enum_variant_types(tcx, def_id); | |
| check_enum(tcx, def_id); | |
| check_variances_for_type_defn(tcx, def_id); | |
| } | |
| DefKind::Fn => { | |
| tcx.ensure_ok().generics_of(def_id); | |
| tcx.ensure_ok().type_of(def_id); | |
| tcx.ensure_ok().predicates_of(def_id); | |
| tcx.ensure_ok().fn_sig(def_id); | |
| tcx.ensure_ok().codegen_fn_attrs(def_id); | |
| if let Some(i) = tcx.intrinsic(def_id) { | |
| intrinsic::check_intrinsic_type( | |
| tcx, | |
| def_id, | |
| tcx.def_ident_span(def_id).unwrap(), | |
| i.name, | |
| ) | |
| } | |
| } | |
| DefKind::Impl { of_trait } => { | |
| tcx.ensure_ok().generics_of(def_id); | |
| tcx.ensure_ok().type_of(def_id); | |
| tcx.ensure_ok().predicates_of(def_id); | |
| tcx.ensure_ok().associated_items(def_id); | |
| if of_trait { | |
| let impl_trait_header = tcx.impl_trait_header(def_id); | |
| res = res.and( | |
| tcx.ensure_result() | |
| .coherent_trait(impl_trait_header.trait_ref.instantiate_identity().def_id), | |
| ); | |
| if res.is_ok() { | |
| // Checking this only makes sense if the all trait impls satisfy basic | |
| // requirements (see `coherent_trait` query), otherwise | |
| // we run into infinite recursions a lot. | |
| check_impl_items_against_trait(tcx, def_id, impl_trait_header); | |
| } | |
| } | |
| } | |
| DefKind::Trait => { | |
| tcx.ensure_ok().generics_of(def_id); | |
| tcx.ensure_ok().trait_def(def_id); | |
| tcx.ensure_ok().explicit_super_predicates_of(def_id); | |
| tcx.ensure_ok().predicates_of(def_id); | |
| tcx.ensure_ok().associated_items(def_id); | |
| let assoc_items = tcx.associated_items(def_id); | |
| for &assoc_item in assoc_items.in_definition_order() { | |
| match assoc_item.kind { | |
| ty::AssocKind::Type { .. } if assoc_item.defaultness(tcx).has_value() => { | |
| let trait_args = GenericArgs::identity_for_item(tcx, def_id); | |
| let _: Result<_, rustc_errors::ErrorGuaranteed> = check_type_bounds( | |
| tcx, | |
| assoc_item, | |
| assoc_item, | |
| ty::TraitRef::new_from_args(tcx, def_id.to_def_id(), trait_args), | |
| ); | |
| } | |
| _ => {} | |
| } | |
| } | |
| } | |
| DefKind::TraitAlias => { | |
| tcx.ensure_ok().generics_of(def_id); | |
| tcx.ensure_ok().explicit_implied_predicates_of(def_id); | |
| tcx.ensure_ok().explicit_super_predicates_of(def_id); | |
| tcx.ensure_ok().predicates_of(def_id); | |
| } | |
| def_kind @ (DefKind::Struct | DefKind::Union) => { | |
| tcx.ensure_ok().generics_of(def_id); | |
| tcx.ensure_ok().type_of(def_id); | |
| tcx.ensure_ok().predicates_of(def_id); | |
| let adt = tcx.adt_def(def_id).non_enum_variant(); | |
| for f in adt.fields.iter() { | |
| tcx.ensure_ok().generics_of(f.did); | |
| tcx.ensure_ok().type_of(f.did); | |
| tcx.ensure_ok().predicates_of(f.did); | |
| } | |
| if let Some((_, ctor_def_id)) = adt.ctor { | |
| crate::collect::lower_variant_ctor(tcx, ctor_def_id.expect_local()); | |
| } | |
| match def_kind { | |
| DefKind::Struct => check_struct(tcx, def_id), | |
| DefKind::Union => check_union(tcx, def_id), | |
| _ => unreachable!(), | |
| } | |
| check_variances_for_type_defn(tcx, def_id); | |
| } | |
| DefKind::OpaqueTy => { | |
| check_opaque_precise_captures(tcx, def_id); | |
| let origin = tcx.local_opaque_ty_origin(def_id); | |
| if let hir::OpaqueTyOrigin::FnReturn { parent: fn_def_id, .. } | |
| | hir::OpaqueTyOrigin::AsyncFn { parent: fn_def_id, .. } = origin | |
| && let hir::Node::TraitItem(trait_item) = tcx.hir_node_by_def_id(fn_def_id) | |
| && let (_, hir::TraitFn::Required(..)) = trait_item.expect_fn() | |
| { | |
| // Skip opaques from RPIT in traits with no default body. | |
| } else { | |
| check_opaque(tcx, def_id); | |
| } | |
| tcx.ensure_ok().predicates_of(def_id); | |
| tcx.ensure_ok().explicit_item_bounds(def_id); | |
| tcx.ensure_ok().explicit_item_self_bounds(def_id); | |
| if tcx.is_conditionally_const(def_id) { | |
| tcx.ensure_ok().explicit_implied_const_bounds(def_id); | |
| tcx.ensure_ok().const_conditions(def_id); | |
| } | |
| // Only `Node::Item` and `Node::ForeignItem` still have HIR based | |
| // checks. Returning early here does not miss any checks and | |
| // avoids this query from having a direct dependency edge on the HIR | |
| return res; | |
| } | |
| DefKind::Const { .. } => { | |
| tcx.ensure_ok().generics_of(def_id); | |
| tcx.ensure_ok().type_of(def_id); | |
| tcx.ensure_ok().predicates_of(def_id); | |
| res = res.and(enter_wf_checking_ctxt(tcx, def_id, |wfcx| { | |
| let ty = tcx.type_of(def_id).instantiate_identity(); | |
| let ty_span = tcx.ty_span(def_id); | |
| let ty = wfcx.deeply_normalize(ty_span, Some(WellFormedLoc::Ty(def_id)), ty); | |
| wfcx.register_wf_obligation(ty_span, Some(WellFormedLoc::Ty(def_id)), ty.into()); | |
| wfcx.register_bound( | |
| traits::ObligationCause::new( | |
| ty_span, | |
| def_id, | |
| ObligationCauseCode::SizedConstOrStatic, | |
| ), | |
| tcx.param_env(def_id), | |
| ty, | |
| tcx.require_lang_item(LangItem::Sized, ty_span), | |
| ); | |
| check_where_clauses(wfcx, def_id); | |
| if tcx.is_type_const(def_id) { | |
| wfcheck::check_type_const(wfcx, def_id, ty, true)?; | |
| } | |
| Ok(()) | |
| })); | |
| // Only `Node::Item` and `Node::ForeignItem` still have HIR based | |
| // checks. Returning early here does not miss any checks and | |
| // avoids this query from having a direct dependency edge on the HIR | |
| return res; | |
| } | |
| DefKind::TyAlias => { | |
| tcx.ensure_ok().generics_of(def_id); | |
| tcx.ensure_ok().type_of(def_id); | |
| tcx.ensure_ok().predicates_of(def_id); | |
| check_type_alias_type_params_are_used(tcx, def_id); | |
| let ty = tcx.type_of(def_id).instantiate_identity(); | |
| let span = tcx.def_span(def_id); | |
| if tcx.type_alias_is_lazy(def_id) { | |
| res = res.and(enter_wf_checking_ctxt(tcx, def_id, |wfcx| { | |
| let item_ty = wfcx.deeply_normalize(span, Some(WellFormedLoc::Ty(def_id)), ty); | |
| wfcx.register_wf_obligation( | |
| span, | |
| Some(WellFormedLoc::Ty(def_id)), | |
| item_ty.into(), | |
| ); | |
| check_where_clauses(wfcx, def_id); | |
| Ok(()) | |
| })); | |
| check_variances_for_type_defn(tcx, def_id); | |
| } else { | |
| res = res.and(enter_wf_checking_ctxt(tcx, def_id, |wfcx| { | |
| // HACK: We sometimes incidentally check that const arguments have the correct | |
| // type as a side effect of the anon const desugaring. To make this "consistent" | |
| // for users we explicitly check `ConstArgHasType` clauses so that const args | |
| // that don't go through an anon const still have their types checked. | |
| // | |
| // We use the unnormalized type as this mirrors the behaviour that we previously | |
| // would have had when all const arguments were anon consts. | |
| // | |
| // Changing this to normalized obligations is a breaking change: | |
| // `type Bar = [(); panic!()];` would become an error | |
| if let Some(unnormalized_obligations) = wfcx.unnormalized_obligations(span, ty) | |
| { | |
| let filtered_obligations = | |
| unnormalized_obligations.into_iter().filter(|o| { | |
| matches!(o.predicate.kind().skip_binder(), | |
| ty::PredicateKind::Clause(ty::ClauseKind::ConstArgHasType(ct, _)) | |
| if matches!(ct.kind(), ty::ConstKind::Param(..))) | |
| }); | |
| wfcx.ocx.register_obligations(filtered_obligations) | |
| } | |
| Ok(()) | |
| })); | |
| } | |
| // Only `Node::Item` and `Node::ForeignItem` still have HIR based | |
| // checks. Returning early here does not miss any checks and | |
| // avoids this query from having a direct dependency edge on the HIR | |
| return res; | |
| } | |
| DefKind::ForeignMod => { | |
| let it = tcx.hir_expect_item(def_id); | |
| let hir::ItemKind::ForeignMod { abi, items } = it.kind else { | |
| return Ok(()); | |
| }; | |
| check_abi(tcx, it.hir_id(), it.span, abi); | |
| for &item in items { | |
| let def_id = item.owner_id.def_id; | |
| let generics = tcx.generics_of(def_id); | |
| let own_counts = generics.own_counts(); | |
| if generics.own_params.len() - own_counts.lifetimes != 0 { | |
| let (kinds, kinds_pl, egs) = match (own_counts.types, own_counts.consts) { | |
| (_, 0) => ("type", "types", Some("u32")), | |
| // We don't specify an example value, because we can't generate | |
| // a valid value for any type. | |
| (0, _) => ("const", "consts", None), | |
| _ => ("type or const", "types or consts", None), | |
| }; | |
| let name = if find_attr!(tcx, def_id, RustcEiiForeignItem) { | |
| "externally implementable items" | |
| } else { | |
| "foreign items" | |
| }; | |
| let span = tcx.def_span(def_id); | |
| struct_span_code_err!( | |
| tcx.dcx(), | |
| span, | |
| E0044, | |
| "{name} may not have {kinds} parameters", | |
| ) | |
| .with_span_label(span, format!("can't have {kinds} parameters")) | |
| .with_help( | |
| // FIXME: once we start storing spans for type arguments, turn this | |
| // into a suggestion. | |
| format!( | |
| "replace the {} parameters with concrete {}{}", | |
| kinds, | |
| kinds_pl, | |
| egs.map(|egs| format!(" like `{egs}`")).unwrap_or_default(), | |
| ), | |
| ) | |
| .emit(); | |
| } | |
| tcx.ensure_ok().generics_of(def_id); | |
| tcx.ensure_ok().type_of(def_id); | |
| tcx.ensure_ok().predicates_of(def_id); | |
| if tcx.is_conditionally_const(def_id) { | |
| tcx.ensure_ok().explicit_implied_const_bounds(def_id); | |
| tcx.ensure_ok().const_conditions(def_id); | |
| } | |
| match tcx.def_kind(def_id) { | |
| DefKind::Fn => { | |
| tcx.ensure_ok().codegen_fn_attrs(def_id); | |
| tcx.ensure_ok().fn_sig(def_id); | |
| let item = tcx.hir_foreign_item(item); | |
| let hir::ForeignItemKind::Fn(sig, ..) = item.kind else { bug!() }; | |
| check_c_variadic_abi(tcx, sig.decl, abi, item.span); | |
| } | |
| DefKind::Static { .. } => { | |
| tcx.ensure_ok().codegen_fn_attrs(def_id); | |
| } | |
| _ => (), | |
| } | |
| } | |
| } | |
| DefKind::Closure => { | |
| // This is guaranteed to be called by metadata encoding, | |
| // we still call it in wfcheck eagerly to ensure errors in codegen | |
| // attrs prevent lints from spamming the output. | |
| tcx.ensure_ok().codegen_fn_attrs(def_id); | |
| // We do not call `type_of` for closures here as that | |
| // depends on typecheck and would therefore hide | |
| // any further errors in case one typeck fails. | |
| // Only `Node::Item` and `Node::ForeignItem` still have HIR based | |
| // checks. Returning early here does not miss any checks and | |
| // avoids this query from having a direct dependency edge on the HIR | |
| return res; | |
| } | |
| DefKind::AssocFn => { | |
| tcx.ensure_ok().codegen_fn_attrs(def_id); | |
| tcx.ensure_ok().type_of(def_id); | |
| tcx.ensure_ok().fn_sig(def_id); | |
| tcx.ensure_ok().predicates_of(def_id); | |
| res = res.and(check_associated_item(tcx, def_id)); | |
| let assoc_item = tcx.associated_item(def_id); | |
| match assoc_item.container { | |
| ty::AssocContainer::InherentImpl | ty::AssocContainer::TraitImpl(_) => {} | |
| ty::AssocContainer::Trait => { | |
| res = res.and(check_trait_item(tcx, def_id)); | |
| } | |
| } | |
| // Only `Node::Item` and `Node::ForeignItem` still have HIR based | |
| // checks. Returning early here does not miss any checks and | |
| // avoids this query from having a direct dependency edge on the HIR | |
| return res; | |
| } | |
| DefKind::AssocConst { .. } => { | |
| tcx.ensure_ok().type_of(def_id); | |
| tcx.ensure_ok().predicates_of(def_id); | |
| res = res.and(check_associated_item(tcx, def_id)); | |
| let assoc_item = tcx.associated_item(def_id); | |
| match assoc_item.container { | |
| ty::AssocContainer::InherentImpl | ty::AssocContainer::TraitImpl(_) => {} | |
| ty::AssocContainer::Trait => { | |
| res = res.and(check_trait_item(tcx, def_id)); | |
| } | |
| } | |
| // Only `Node::Item` and `Node::ForeignItem` still have HIR based | |
| // checks. Returning early here does not miss any checks and | |
| // avoids this query from having a direct dependency edge on the HIR | |
| return res; | |
| } | |
| DefKind::AssocTy => { | |
| tcx.ensure_ok().predicates_of(def_id); | |
| res = res.and(check_associated_item(tcx, def_id)); | |
| let assoc_item = tcx.associated_item(def_id); | |
| let has_type = match assoc_item.container { | |
| ty::AssocContainer::InherentImpl | ty::AssocContainer::TraitImpl(_) => true, | |
| ty::AssocContainer::Trait => { | |
| tcx.ensure_ok().explicit_item_bounds(def_id); | |
| tcx.ensure_ok().explicit_item_self_bounds(def_id); | |
| if tcx.is_conditionally_const(def_id) { | |
| tcx.ensure_ok().explicit_implied_const_bounds(def_id); | |
| tcx.ensure_ok().const_conditions(def_id); | |
| } | |
| res = res.and(check_trait_item(tcx, def_id)); | |
| assoc_item.defaultness(tcx).has_value() | |
| } | |
| }; | |
| if has_type { | |
| tcx.ensure_ok().type_of(def_id); | |
| } | |
| // Only `Node::Item` and `Node::ForeignItem` still have HIR based | |
| // checks. Returning early here does not miss any checks and | |
| // avoids this query from having a direct dependency edge on the HIR | |
| return res; | |
| } | |
| // Only `Node::Item` and `Node::ForeignItem` still have HIR based | |
| // checks. Returning early here does not miss any checks and | |
| // avoids this query from having a direct dependency edge on the HIR | |
| DefKind::AnonConst | DefKind::InlineConst => return res, | |
| _ => {} | |
| } | |
| let node = tcx.hir_node_by_def_id(def_id); | |
| res.and(match node { | |
| hir::Node::Crate(_) => bug!("check_well_formed cannot be applied to the crate root"), | |
| hir::Node::Item(item) => wfcheck::check_item(tcx, item), | |
| hir::Node::ForeignItem(item) => wfcheck::check_foreign_item(tcx, item), | |
| _ => unreachable!("{node:?}"), | |
| }) | |
| } | |
| pub(super) fn check_specialization_validity<'tcx>( | |
| tcx: TyCtxt<'tcx>, | |
| trait_def: &ty::TraitDef, | |
| trait_item: ty::AssocItem, | |
| impl_id: DefId, | |
| impl_item: DefId, | |
| ) { | |
| let Ok(ancestors) = trait_def.ancestors(tcx, impl_id) else { return }; | |
| let mut ancestor_impls = ancestors.skip(1).filter_map(|parent| { | |
| if parent.is_from_trait() { | |
| None | |
| } else { | |
| Some((parent, parent.item(tcx, trait_item.def_id))) | |
| } | |
| }); | |
| let opt_result = ancestor_impls.find_map(|(parent_impl, parent_item)| { | |
| match parent_item { | |
| // Parent impl exists, and contains the parent item we're trying to specialize, but | |
| // doesn't mark it `default`. | |
| Some(parent_item) if traits::impl_item_is_final(tcx, &parent_item) => { | |
| Some(Err(parent_impl.def_id())) | |
| } | |
| // Parent impl contains item and makes it specializable. | |
| Some(_) => Some(Ok(())), | |
| // Parent impl doesn't mention the item. This means it's inherited from the | |
| // grandparent. In that case, if parent is a `default impl`, inherited items use the | |
| // "defaultness" from the grandparent, else they are final. | |
| None => { | |
| if tcx.defaultness(parent_impl.def_id()).is_default() { | |
| None | |
| } else { | |
| Some(Err(parent_impl.def_id())) | |
| } | |
| } | |
| } | |
| }); | |
| // If `opt_result` is `None`, we have only encountered `default impl`s that don't contain the | |
| // item. This is allowed, the item isn't actually getting specialized here. | |
| let result = opt_result.unwrap_or(Ok(())); | |
| if let Err(parent_impl) = result { | |
| if !tcx.is_impl_trait_in_trait(impl_item) { | |
| let span = tcx.def_span(impl_item); | |
| let ident = tcx.item_ident(impl_item); | |
| let err = match tcx.span_of_impl(parent_impl) { | |
| Ok(sp) => errors::ImplNotMarkedDefault::Ok { span, ident, ok_label: sp }, | |
| Err(cname) => errors::ImplNotMarkedDefault::Err { span, ident, cname }, | |
| }; | |
| tcx.dcx().emit_err(err); | |
| } else { | |
| tcx.dcx().delayed_bug(format!("parent item: {parent_impl:?} not marked as default")); | |
| } | |
| } | |
| } | |
| fn check_overriding_final_trait_item<'tcx>( | |
| tcx: TyCtxt<'tcx>, | |
| trait_item: ty::AssocItem, | |
| impl_item: ty::AssocItem, | |
| ) { | |
| if trait_item.defaultness(tcx).is_final() { | |
| tcx.dcx().emit_err(errors::OverridingFinalTraitFunction { | |
| impl_span: tcx.def_span(impl_item.def_id), | |
| trait_span: tcx.def_span(trait_item.def_id), | |
| ident: tcx.item_ident(impl_item.def_id), | |
| }); | |
| } | |
| } | |
| fn check_impl_items_against_trait<'tcx>( | |
| tcx: TyCtxt<'tcx>, | |
| impl_id: LocalDefId, | |
| impl_trait_header: ty::ImplTraitHeader<'tcx>, | |
| ) { | |
| let trait_ref = impl_trait_header.trait_ref.instantiate_identity(); | |
| // If the trait reference itself is erroneous (so the compilation is going | |
| // to fail), skip checking the items here -- the `impl_item` table in `tcx` | |
| // isn't populated for such impls. | |
| if trait_ref.references_error() { | |
| return; | |
| } | |
| let impl_item_refs = tcx.associated_item_def_ids(impl_id); | |
| // Negative impls are not expected to have any items | |
| match impl_trait_header.polarity { | |
| ty::ImplPolarity::Reservation | ty::ImplPolarity::Positive => {} | |
| ty::ImplPolarity::Negative => { | |
| if let [first_item_ref, ..] = *impl_item_refs { | |
| let first_item_span = tcx.def_span(first_item_ref); | |
| struct_span_code_err!( | |
| tcx.dcx(), | |
| first_item_span, | |
| E0749, | |
| "negative impls cannot have any items" | |
| ) | |
| .emit(); | |
| } | |
| return; | |
| } | |
| } | |
| let trait_def = tcx.trait_def(trait_ref.def_id); | |
| let self_is_guaranteed_unsize_self = tcx.impl_self_is_guaranteed_unsized(impl_id); | |
| for &impl_item in impl_item_refs { | |
| let ty_impl_item = tcx.associated_item(impl_item); | |
| let ty_trait_item = match ty_impl_item.expect_trait_impl() { | |
| Ok(trait_item_id) => tcx.associated_item(trait_item_id), | |
| Err(ErrorGuaranteed { .. }) => continue, | |
| }; | |
| let res = tcx.ensure_result().compare_impl_item(impl_item.expect_local()); | |
| if res.is_ok() { | |
| match ty_impl_item.kind { | |
| ty::AssocKind::Fn { .. } => { | |
| compare_impl_item::refine::check_refining_return_position_impl_trait_in_trait( | |
| tcx, | |
| ty_impl_item, | |
| ty_trait_item, | |
| tcx.impl_trait_ref(ty_impl_item.container_id(tcx)).instantiate_identity(), | |
| ); | |
| } | |
| ty::AssocKind::Const { .. } => {} | |
| ty::AssocKind::Type { .. } => {} | |
| } | |
| } | |
| if self_is_guaranteed_unsize_self && tcx.generics_require_sized_self(ty_trait_item.def_id) { | |
| tcx.emit_node_span_lint( | |
| rustc_lint_defs::builtin::DEAD_CODE, | |
| tcx.local_def_id_to_hir_id(ty_impl_item.def_id.expect_local()), | |
| tcx.def_span(ty_impl_item.def_id), | |
| errors::UselessImplItem, | |
| ) | |
| } | |
| check_specialization_validity( | |
| tcx, | |
| trait_def, | |
| ty_trait_item, | |
| impl_id.to_def_id(), | |
| impl_item, | |
| ); | |
| check_overriding_final_trait_item(tcx, ty_trait_item, ty_impl_item); | |
| } | |
| if let Ok(ancestors) = trait_def.ancestors(tcx, impl_id.to_def_id()) { | |
| // Check for missing items from trait | |
| let mut missing_items = Vec::new(); | |
| let mut must_implement_one_of: Option<&[Ident]> = | |
| trait_def.must_implement_one_of.as_deref(); | |
| for &trait_item_id in tcx.associated_item_def_ids(trait_ref.def_id) { | |
| let leaf_def = ancestors.leaf_def(tcx, trait_item_id); | |
| let is_implemented = leaf_def | |
| .as_ref() | |
| .is_some_and(|node_item| node_item.item.defaultness(tcx).has_value()); | |
| if !is_implemented | |
| && tcx.defaultness(impl_id).is_final() | |
| // unsized types don't need to implement methods that have `Self: Sized` bounds. | |
| && !(self_is_guaranteed_unsize_self && tcx.generics_require_sized_self(trait_item_id)) | |
| { | |
| missing_items.push(tcx.associated_item(trait_item_id)); | |
| } | |
| // true if this item is specifically implemented in this impl | |
| let is_implemented_here = | |
| leaf_def.as_ref().is_some_and(|node_item| !node_item.defining_node.is_from_trait()); | |
| if !is_implemented_here { | |
| let full_impl_span = tcx.hir_span_with_body(tcx.local_def_id_to_hir_id(impl_id)); | |
| match tcx.eval_default_body_stability(trait_item_id, full_impl_span) { | |
| EvalResult::Deny { feature, reason, issue, .. } => default_body_is_unstable( | |
| tcx, | |
| full_impl_span, | |
| trait_item_id, | |
| feature, | |
| reason, | |
| issue, | |
| ), | |
| // Unmarked default bodies are considered stable (at least for now). | |
| EvalResult::Allow | EvalResult::Unmarked => {} | |
| } | |
| } | |
| if let Some(required_items) = &must_implement_one_of { | |
| if is_implemented_here { | |
| let trait_item = tcx.associated_item(trait_item_id); | |
| if required_items.contains(&trait_item.ident(tcx)) { | |
| must_implement_one_of = None; | |
| } | |
| } | |
| } | |
| if let Some(leaf_def) = &leaf_def | |
| && !leaf_def.is_final() | |
| && let def_id = leaf_def.item.def_id | |
| && tcx.impl_method_has_trait_impl_trait_tys(def_id) | |
| { | |
| let def_kind = tcx.def_kind(def_id); | |
| let descr = tcx.def_kind_descr(def_kind, def_id); | |
| let (msg, feature) = if tcx.asyncness(def_id).is_async() { | |
| ( | |
| format!("async {descr} in trait cannot be specialized"), | |
| "async functions in traits", | |
| ) | |
| } else { | |
| ( | |
| format!( | |
| "{descr} with return-position `impl Trait` in trait cannot be specialized" | |
| ), | |
| "return position `impl Trait` in traits", | |
| ) | |
| }; | |
| tcx.dcx() | |
| .struct_span_err(tcx.def_span(def_id), msg) | |
| .with_note(format!( | |
| "specialization behaves in inconsistent and surprising ways with \ | |
| {feature}, and for now is disallowed" | |
| )) | |
| .emit(); | |
| } | |
| } | |
| if !missing_items.is_empty() { | |
| let full_impl_span = tcx.hir_span_with_body(tcx.local_def_id_to_hir_id(impl_id)); | |
| missing_items_err(tcx, impl_id, &missing_items, full_impl_span); | |
| } | |
| if let Some(missing_items) = must_implement_one_of { | |
| let attr_span = find_attr!(tcx, trait_ref.def_id, RustcMustImplementOneOf {attr_span, ..} => *attr_span); | |
| missing_items_must_implement_one_of_err( | |
| tcx, | |
| tcx.def_span(impl_id), | |
| missing_items, | |
| attr_span, | |
| ); | |
| } | |
| } | |
| } | |
| fn check_simd(tcx: TyCtxt<'_>, sp: Span, def_id: LocalDefId) { | |
| let t = tcx.type_of(def_id).instantiate_identity(); | |
| if let ty::Adt(def, args) = t.kind() | |
| && def.is_struct() | |
| { | |
| let fields = &def.non_enum_variant().fields; | |
| if fields.is_empty() { | |
| struct_span_code_err!(tcx.dcx(), sp, E0075, "SIMD vector cannot be empty").emit(); | |
| return; | |
| } | |
| let array_field = &fields[FieldIdx::ZERO]; | |
| let array_ty = array_field.ty(tcx, args); | |
| let ty::Array(element_ty, len_const) = array_ty.kind() else { | |
| struct_span_code_err!( | |
| tcx.dcx(), | |
| sp, | |
| E0076, | |
| "SIMD vector's only field must be an array" | |
| ) | |
| .with_span_label(tcx.def_span(array_field.did), "not an array") | |
| .emit(); | |
| return; | |
| }; | |
| if let Some(second_field) = fields.get(FieldIdx::ONE) { | |
| struct_span_code_err!(tcx.dcx(), sp, E0075, "SIMD vector cannot have multiple fields") | |
| .with_span_label(tcx.def_span(second_field.did), "excess field") | |
| .emit(); | |
| return; | |
| } | |
| // FIXME(repr_simd): This check is nice, but perhaps unnecessary due to the fact | |
| // we do not expect users to implement their own `repr(simd)` types. If they could, | |
| // this check is easily side-steppable by hiding the const behind normalization. | |
| // The consequence is that the error is, in general, only observable post-mono. | |
| if let Some(len) = len_const.try_to_target_usize(tcx) { | |
| if len == 0 { | |
| struct_span_code_err!(tcx.dcx(), sp, E0075, "SIMD vector cannot be empty").emit(); | |
| return; | |
| } else if len > MAX_SIMD_LANES { | |
| struct_span_code_err!( | |
| tcx.dcx(), | |
| sp, | |
| E0075, | |
| "SIMD vector cannot have more than {MAX_SIMD_LANES} elements", | |
| ) | |
| .emit(); | |
| return; | |
| } | |
| } | |
| // Check that we use types valid for use in the lanes of a SIMD "vector register" | |
| // These are scalar types which directly match a "machine" type | |
| // Yes: Integers, floats, "thin" pointers | |
| // No: char, "wide" pointers, compound types | |
| match element_ty.kind() { | |
| ty::Param(_) => (), // pass struct<T>([T; 4]) through, let monomorphization catch errors | |
| ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::RawPtr(_, _) => (), // struct([u8; 4]) is ok | |
| _ => { | |
| struct_span_code_err!( | |
| tcx.dcx(), | |
| sp, | |
| E0077, | |
| "SIMD vector element type should be a \ | |
| primitive scalar (integer/float/pointer) type" | |
| ) | |
| .emit(); | |
| return; | |
| } | |
| } | |
| } | |
| } | |
| #[tracing::instrument(skip(tcx), level = "debug")] | |
| fn check_scalable_vector(tcx: TyCtxt<'_>, span: Span, def_id: LocalDefId, scalable: ScalableElt) { | |
| let ty = tcx.type_of(def_id).instantiate_identity(); | |
| let ty::Adt(def, args) = ty.kind() else { return }; | |
| if !def.is_struct() { | |
| tcx.dcx().delayed_bug("`rustc_scalable_vector` applied to non-struct"); | |
| return; | |
| } | |
| let fields = &def.non_enum_variant().fields; | |
| match scalable { | |
| ScalableElt::ElementCount(..) if fields.is_empty() => { | |
| let mut err = | |
| tcx.dcx().struct_span_err(span, "scalable vectors must have a single field"); | |
| err.help("scalable vector types' only field must be a primitive scalar type"); | |
| err.emit(); | |
| return; | |
| } | |
| ScalableElt::ElementCount(..) if fields.len() >= 2 => { | |
| tcx.dcx().struct_span_err(span, "scalable vectors cannot have multiple fields").emit(); | |
| return; | |
| } | |
| ScalableElt::Container if fields.is_empty() => { | |
| let mut err = tcx | |
| .dcx() | |
| .struct_span_err(span, "scalable vector tuples must have at least one field"); | |
| err.help("tuples of scalable vectors can only contain multiple of the same scalable vector type"); | |
| err.emit(); | |
| return; | |
| } | |
| ScalableElt::Container if fields.len() > 8 => { | |
| let mut err = tcx | |
| .dcx() | |
| .struct_span_err(span, "scalable vector tuples can have at most eight fields"); | |
| err.help("tuples of scalable vectors can only contain multiple of the same scalable vector type"); | |
| err.emit(); | |
| return; | |
| } | |
| _ => {} | |
| } | |
| match scalable { | |
| ScalableElt::ElementCount(..) => { | |
| let element_ty = &fields[FieldIdx::ZERO].ty(tcx, args); | |
| // Check that `element_ty` only uses types valid in the lanes of a scalable vector | |
| // register: scalar types which directly match a "machine" type - integers, floats and | |
| // bools | |
| match element_ty.kind() { | |
| ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Bool => (), | |
| _ => { | |
| let mut err = tcx.dcx().struct_span_err( | |
| span, | |
| "element type of a scalable vector must be a primitive scalar", | |
| ); | |
| err.help("only `u*`, `i*`, `f*` and `bool` types are accepted"); | |
| err.emit(); | |
| } | |
| } | |
| } | |
| ScalableElt::Container => { | |
| let mut prev_field_ty = None; | |
| for field in fields.iter() { | |
| let element_ty = field.ty(tcx, args); | |
| if let ty::Adt(def, _) = element_ty.kind() | |
| && def.repr().scalable() | |
| { | |
| match def | |
| .repr() | |
| .scalable | |
| .expect("`repr().scalable.is_some()` != `repr().scalable()`") | |
| { | |
| ScalableElt::ElementCount(_) => { /* expected field */ } | |
| ScalableElt::Container => { | |
| tcx.dcx().span_err( | |
| tcx.def_span(field.did), | |
| "scalable vector structs cannot contain other scalable vector structs", | |
| ); | |
| break; | |
| } | |
| } | |
| } else { | |
| tcx.dcx().span_err( | |
| tcx.def_span(field.did), | |
| "scalable vector structs can only have scalable vector fields", | |
| ); | |
| break; | |
| } | |
| if let Some(prev_ty) = prev_field_ty.replace(element_ty) | |
| && prev_ty != element_ty | |
| { | |
| tcx.dcx().span_err( | |
| tcx.def_span(field.did), | |
| "all fields in a scalable vector struct must be the same type", | |
| ); | |
| break; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| pub(super) fn check_packed(tcx: TyCtxt<'_>, sp: Span, def: ty::AdtDef<'_>) { | |
| let repr = def.repr(); | |
| if repr.packed() { | |
| if let Some(reprs) = find_attr!(tcx, def.did(), Repr { reprs, .. } => reprs) { | |
| for (r, _) in reprs { | |
| if let ReprPacked(pack) = r | |
| && let Some(repr_pack) = repr.pack | |
| && pack != &repr_pack | |
| { | |
| struct_span_code_err!( | |
| tcx.dcx(), | |
| sp, | |
| E0634, | |
| "type has conflicting packed representation hints" | |
| ) | |
| .emit(); | |
| } | |
| } | |
| } | |
| if repr.align.is_some() { | |
| struct_span_code_err!( | |
| tcx.dcx(), | |
| sp, | |
| E0587, | |
| "type has conflicting packed and align representation hints" | |
| ) | |
| .emit(); | |
| } else if let Some(def_spans) = check_packed_inner(tcx, def.did(), &mut vec![]) { | |
| let mut err = struct_span_code_err!( | |
| tcx.dcx(), | |
| sp, | |
| E0588, | |
| "packed type cannot transitively contain a `#[repr(align)]` type" | |
| ); | |
| err.span_note( | |
| tcx.def_span(def_spans[0].0), | |
| format!("`{}` has a `#[repr(align)]` attribute", tcx.item_name(def_spans[0].0)), | |
| ); | |
| if def_spans.len() > 2 { | |
| let mut first = true; | |
| for (adt_def, span) in def_spans.iter().skip(1).rev() { | |
| let ident = tcx.item_name(*adt_def); | |
| err.span_note( | |
| *span, | |
| if first { | |
| format!( | |
| "`{}` contains a field of type `{}`", | |
| tcx.type_of(def.did()).instantiate_identity(), | |
| ident | |
| ) | |
| } else { | |
| format!("...which contains a field of type `{ident}`") | |
| }, | |
| ); | |
| first = false; | |
| } | |
| } | |
| err.emit(); | |
| } | |
| } | |
| } | |
| pub(super) fn check_packed_inner( | |
| tcx: TyCtxt<'_>, | |
| def_id: DefId, | |
| stack: &mut Vec<DefId>, | |
| ) -> Option<Vec<(DefId, Span)>> { | |
| if let ty::Adt(def, args) = tcx.type_of(def_id).instantiate_identity().kind() { | |
| if def.is_struct() || def.is_union() { | |
| if def.repr().align.is_some() { | |
| return Some(vec![(def.did(), DUMMY_SP)]); | |
| } | |
| stack.push(def_id); | |
| for field in &def.non_enum_variant().fields { | |
| if let ty::Adt(def, _) = field.ty(tcx, args).kind() | |
| && !stack.contains(&def.did()) | |
| && let Some(mut defs) = check_packed_inner(tcx, def.did(), stack) | |
| { | |
| defs.push((def.did(), field.ident(tcx).span)); | |
| return Some(defs); | |
| } | |
| } | |
| stack.pop(); | |
| } | |
| } | |
| None | |
| } | |
| pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>) { | |
| struct ZeroSizedFieldReprTransparentIncompatibility<'tcx> { | |
| unsuited: UnsuitedInfo<'tcx>, | |
| } | |
| impl<'a, 'tcx> Diagnostic<'a, ()> for ZeroSizedFieldReprTransparentIncompatibility<'tcx> { | |
| fn into_diag(self, dcx: DiagCtxtHandle<'a>, level: Level) -> Diag<'a, ()> { | |
| let Self { unsuited } = self; | |
| let (title, note) = match unsuited.reason { | |
| UnsuitedReason::NonExhaustive => ( | |
| "external non-exhaustive types", | |
| "is marked with `#[non_exhaustive]`, so it could become non-zero-sized in the future.", | |
| ), | |
| UnsuitedReason::PrivateField => ( | |
| "external types with private fields", | |
| "contains private fields, so it could become non-zero-sized in the future.", | |
| ), | |
| UnsuitedReason::ReprC => ( | |
| "`repr(C)` types", | |
| "is a `#[repr(C)]` type, so it is not guaranteed to be zero-sized on all targets.", | |
| ), | |
| }; | |
| Diag::new( | |
| dcx, | |
| level, | |
| format!("zero-sized fields in `repr(transparent)` cannot contain {title}"), | |
| ) | |
| .with_note(format!( | |
| "this field contains `{field_ty}`, which {note}", | |
| field_ty = unsuited.ty, | |
| )) | |
| } | |
| } | |
| if !adt.repr().transparent() { | |
| return; | |
| } | |
| if adt.is_union() && !tcx.features().transparent_unions() { | |
| feature_err( | |
| &tcx.sess, | |
| sym::transparent_unions, | |
| tcx.def_span(adt.did()), | |
| "transparent unions are unstable", | |
| ) | |
| .emit(); | |
| } | |
| if adt.variants().len() != 1 { | |
| bad_variant_count(tcx, adt, tcx.def_span(adt.did()), adt.did()); | |
| // Don't bother checking the fields. | |
| return; | |
| } | |
| let typing_env = ty::TypingEnv::non_body_analysis(tcx, adt.did()); | |
| // For each field, figure out if it has "trivial" layout (i.e., is a 1-ZST). | |
| struct FieldInfo<'tcx> { | |
| span: Span, | |
| trivial: bool, | |
| ty: Ty<'tcx>, | |
| } | |
| let field_infos = adt.all_fields().map(|field| { | |
| let ty = field.ty(tcx, GenericArgs::identity_for_item(tcx, field.did)); | |
| let layout = tcx.layout_of(typing_env.as_query_input(ty)); | |
| // We are currently checking the type this field came from, so it must be local | |
| let span = tcx.hir_span_if_local(field.did).unwrap(); | |
| let trivial = layout.is_ok_and(|layout| layout.is_1zst()); | |
| FieldInfo { span, trivial, ty } | |
| }); | |
| let non_trivial_fields = field_infos | |
| .clone() | |
| .filter_map(|field| if !field.trivial { Some(field.span) } else { None }); | |
| let non_trivial_count = non_trivial_fields.clone().count(); | |
| if non_trivial_count >= 2 { | |
| bad_non_zero_sized_fields( | |
| tcx, | |
| adt, | |
| non_trivial_count, | |
| non_trivial_fields, | |
| tcx.def_span(adt.did()), | |
| ); | |
| return; | |
| } | |
| // Even some 1-ZST fields are not allowed though, if they have `non_exhaustive` or private | |
| // fields or `repr(C)`. We call those fields "unsuited". | |
| struct UnsuitedInfo<'tcx> { | |
| /// The source of the problem, a type that is found somewhere within the field type. | |
| ty: Ty<'tcx>, | |
| reason: UnsuitedReason, | |
| } | |
| enum UnsuitedReason { | |
| NonExhaustive, | |
| PrivateField, | |
| ReprC, | |
| } | |
| fn check_unsuited<'tcx>( | |
| tcx: TyCtxt<'tcx>, | |
| typing_env: ty::TypingEnv<'tcx>, | |
| ty: Ty<'tcx>, | |
| ) -> ControlFlow<UnsuitedInfo<'tcx>> { | |
| // We can encounter projections during traversal, so ensure the type is normalized. | |
| let ty = tcx.try_normalize_erasing_regions(typing_env, ty).unwrap_or(ty); | |
| match ty.kind() { | |
| ty::Tuple(list) => list.iter().try_for_each(|t| check_unsuited(tcx, typing_env, t)), | |
| ty::Array(ty, _) => check_unsuited(tcx, typing_env, *ty), | |
| ty::Adt(def, args) => { | |
| if !def.did().is_local() && !find_attr!(tcx, def.did(), RustcPubTransparent(_)) { | |
| let non_exhaustive = def.is_variant_list_non_exhaustive() | |
| || def.variants().iter().any(ty::VariantDef::is_field_list_non_exhaustive); | |
| let has_priv = def.all_fields().any(|f| !f.vis.is_public()); | |
| if non_exhaustive || has_priv { | |
| return ControlFlow::Break(UnsuitedInfo { | |
| ty, | |
| reason: if non_exhaustive { | |
| UnsuitedReason::NonExhaustive | |
| } else { | |
| UnsuitedReason::PrivateField | |
| }, | |
| }); | |
| } | |
| } | |
| if def.repr().c() { | |
| return ControlFlow::Break(UnsuitedInfo { ty, reason: UnsuitedReason::ReprC }); | |
| } | |
| def.all_fields() | |
| .map(|field| field.ty(tcx, args)) | |
| .try_for_each(|t| check_unsuited(tcx, typing_env, t)) | |
| } | |
| _ => ControlFlow::Continue(()), | |
| } | |
| } | |
| let mut prev_unsuited_1zst = false; | |
| for field in field_infos { | |
| if field.trivial | |
| && let Some(unsuited) = check_unsuited(tcx, typing_env, field.ty).break_value() | |
| { | |
| // If there are any non-trivial fields, then there can be no non-exhaustive 1-zsts. | |
| // Otherwise, it's only an issue if there's >1 non-exhaustive 1-zst. | |
| if non_trivial_count > 0 || prev_unsuited_1zst { | |
| tcx.emit_node_span_lint( | |
| REPR_TRANSPARENT_NON_ZST_FIELDS, | |
| tcx.local_def_id_to_hir_id(adt.did().expect_local()), | |
| field.span, | |
| ZeroSizedFieldReprTransparentIncompatibility { unsuited }, | |
| ); | |
| } else { | |
| prev_unsuited_1zst = true; | |
| } | |
| } | |
| } | |
| } | |
| #[allow(trivial_numeric_casts)] | |
| fn check_enum(tcx: TyCtxt<'_>, def_id: LocalDefId) { | |
| let def = tcx.adt_def(def_id); | |
| def.destructor(tcx); // force the destructor to be evaluated | |
| if def.variants().is_empty() { | |
| find_attr!(tcx, def_id, Repr { reprs, first_span } => { | |
| struct_span_code_err!( | |
| tcx.dcx(), | |
| reprs.first().map(|repr| repr.1).unwrap_or(*first_span), | |
| E0084, | |
| "unsupported representation for zero-variant enum" | |
| ) | |
| .with_span_label(tcx.def_span(def_id), "zero-variant enum") | |
| .emit(); | |
| }); | |
| } | |
| for v in def.variants() { | |
| if let ty::VariantDiscr::Explicit(discr_def_id) = v.discr { | |
| tcx.ensure_ok().typeck(discr_def_id.expect_local()); | |
| } | |
| } | |
| if def.repr().int.is_none() { | |
| let is_unit = |var: &ty::VariantDef| matches!(var.ctor_kind(), Some(CtorKind::Const)); | |
| let get_disr = |var: &ty::VariantDef| match var.discr { | |
| ty::VariantDiscr::Explicit(disr) => Some(disr), | |
| ty::VariantDiscr::Relative(_) => None, | |
| }; | |
| let non_unit = def.variants().iter().find(|var| !is_unit(var)); | |
| let disr_unit = | |
| def.variants().iter().filter(|var| is_unit(var)).find_map(|var| get_disr(var)); | |
| let disr_non_unit = | |
| def.variants().iter().filter(|var| !is_unit(var)).find_map(|var| get_disr(var)); | |
| if disr_non_unit.is_some() || (disr_unit.is_some() && non_unit.is_some()) { | |
| let mut err = struct_span_code_err!( | |
| tcx.dcx(), | |
| tcx.def_span(def_id), | |
| E0732, | |
| "`#[repr(inttype)]` must be specified for enums with explicit discriminants and non-unit variants" | |
| ); | |
| if let Some(disr_non_unit) = disr_non_unit { | |
| err.span_label( | |
| tcx.def_span(disr_non_unit), | |
| "explicit discriminant on non-unit variant specified here", | |
| ); | |
| } else { | |
| err.span_label( | |
| tcx.def_span(disr_unit.unwrap()), | |
| "explicit discriminant specified here", | |
| ); | |
| err.span_label( | |
| tcx.def_span(non_unit.unwrap().def_id), | |
| "non-unit discriminant declared here", | |
| ); | |
| } | |
| err.emit(); | |
| } | |
| } | |
| detect_discriminant_duplicate(tcx, def); | |
| check_transparent(tcx, def); | |
| } | |
| /// Part of enum check. Given the discriminants of an enum, errors if two or more discriminants are equal | |
| fn detect_discriminant_duplicate<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>) { | |
| // Helper closure to reduce duplicate code. This gets called everytime we detect a duplicate. | |
| // Here `idx` refers to the order of which the discriminant appears, and its index in `vs` | |
| let report = |dis: Discr<'tcx>, idx, err: &mut Diag<'_>| { | |
| let var = adt.variant(idx); // HIR for the duplicate discriminant | |
| let (span, display_discr) = match var.discr { | |
| ty::VariantDiscr::Explicit(discr_def_id) => { | |
| // In the case the discriminant is both a duplicate and overflowed, let the user know | |
| if let hir::Node::AnonConst(expr) = | |
| tcx.hir_node_by_def_id(discr_def_id.expect_local()) | |
| && let hir::ExprKind::Lit(lit) = &tcx.hir_body(expr.body).value.kind | |
| && let rustc_ast::LitKind::Int(lit_value, _int_kind) = &lit.node | |
| && *lit_value != dis.val | |
| { | |
| (tcx.def_span(discr_def_id), format!("`{dis}` (overflowed from `{lit_value}`)")) | |
| } else { | |
| // Otherwise, format the value as-is | |
| (tcx.def_span(discr_def_id), format!("`{dis}`")) | |
| } | |
| } | |
| // This should not happen. | |
| ty::VariantDiscr::Relative(0) => (tcx.def_span(var.def_id), format!("`{dis}`")), | |
| ty::VariantDiscr::Relative(distance_to_explicit) => { | |
| // At this point we know this discriminant is a duplicate, and was not explicitly | |
| // assigned by the user. Here we iterate backwards to fetch the HIR for the last | |
| // explicitly assigned discriminant, and letting the user know that this was the | |
| // increment startpoint, and how many steps from there leading to the duplicate | |
| if let Some(explicit_idx) = | |
| idx.as_u32().checked_sub(distance_to_explicit).map(VariantIdx::from_u32) | |
| { | |
| let explicit_variant = adt.variant(explicit_idx); | |
| let ve_ident = var.name; | |
| let ex_ident = explicit_variant.name; | |
| let sp = if distance_to_explicit > 1 { "variants" } else { "variant" }; | |
| err.span_label( | |
| tcx.def_span(explicit_variant.def_id), | |
| format!( | |
| "discriminant for `{ve_ident}` incremented from this startpoint \ | |
| (`{ex_ident}` + {distance_to_explicit} {sp} later \ | |
| => `{ve_ident}` = {dis})" | |
| ), | |
| ); | |
| } | |
| (tcx.def_span(var.def_id), format!("`{dis}`")) | |
| } | |
| }; | |
| err.span_label(span, format!("{display_discr} assigned here")); | |
| }; | |
| let mut discrs = adt.discriminants(tcx).collect::<Vec<_>>(); | |
| // Here we loop through the discriminants, comparing each discriminant to another. | |
| // When a duplicate is detected, we instantiate an error and point to both | |
| // initial and duplicate value. The duplicate discriminant is then discarded by swapping | |
| // it with the last element and decrementing the `vec.len` (which is why we have to evaluate | |
| // `discrs.len()` anew every iteration, and why this could be tricky to do in a functional | |
| // style as we are mutating `discrs` on the fly). | |
| let mut i = 0; | |
| while i < discrs.len() { | |
| let var_i_idx = discrs[i].0; | |
| let mut error: Option<Diag<'_, _>> = None; | |
| let mut o = i + 1; | |
| while o < discrs.len() { | |
| let var_o_idx = discrs[o].0; | |
| if discrs[i].1.val == discrs[o].1.val { | |
| let err = error.get_or_insert_with(|| { | |
| let mut ret = struct_span_code_err!( | |
| tcx.dcx(), | |
| tcx.def_span(adt.did()), | |
| E0081, | |
| "discriminant value `{}` assigned more than once", | |
| discrs[i].1, | |
| ); | |
| report(discrs[i].1, var_i_idx, &mut ret); | |
| ret | |
| }); | |
| report(discrs[o].1, var_o_idx, err); | |
| // Safe to unwrap here, as we wouldn't reach this point if `discrs` was empty | |
| discrs[o] = *discrs.last().unwrap(); | |
| discrs.pop(); | |
| } else { | |
| o += 1; | |
| } | |
| } | |
| if let Some(e) = error { | |
| e.emit(); | |
| } | |
| i += 1; | |
| } | |
| } | |
| fn check_type_alias_type_params_are_used<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) { | |
| if tcx.type_alias_is_lazy(def_id) { | |
| // Since we compute the variances for lazy type aliases and already reject bivariant | |
| // parameters as unused, we can and should skip this check for lazy type aliases. | |
| return; | |
| } | |
| let generics = tcx.generics_of(def_id); | |
| if generics.own_counts().types == 0 { | |
| return; | |
| } | |
| let ty = tcx.type_of(def_id).instantiate_identity(); | |
| if ty.references_error() { | |
| // If there is already another error, do not emit an error for not using a type parameter. | |
| return; | |
| } | |
| // Lazily calculated because it is only needed in case of an error. | |
| let bounded_params = LazyCell::new(|| { | |
| tcx.explicit_predicates_of(def_id) | |
| .predicates | |
| .iter() | |
| .filter_map(|(predicate, span)| { | |
| let bounded_ty = match predicate.kind().skip_binder() { | |
| ty::ClauseKind::Trait(pred) => pred.trait_ref.self_ty(), | |
| ty::ClauseKind::TypeOutlives(pred) => pred.0, | |
| _ => return None, | |
| }; | |
| if let ty::Param(param) = bounded_ty.kind() { | |
| Some((param.index, span)) | |
| } else { | |
| None | |
| } | |
| }) | |
| // FIXME: This assumes that elaborated `Sized` bounds come first (which does hold at the | |
| // time of writing). This is a bit fragile since we later use the span to detect elaborated | |
| // `Sized` bounds. If they came last for example, this would break `Trait + /*elab*/Sized` | |
| // since it would overwrite the span of the user-written bound. This could be fixed by | |
| // folding the spans with `Span::to` which requires a bit of effort I think. | |
| .collect::<FxIndexMap<_, _>>() | |
| }); | |
| let mut params_used = DenseBitSet::new_empty(generics.own_params.len()); | |
| for leaf in ty.walk() { | |
| if let GenericArgKind::Type(leaf_ty) = leaf.kind() | |
| && let ty::Param(param) = leaf_ty.kind() | |
| { | |
| debug!("found use of ty param {:?}", param); | |
| params_used.insert(param.index); | |
| } | |
| } | |
| for param in &generics.own_params { | |
| if !params_used.contains(param.index) | |
| && let ty::GenericParamDefKind::Type { .. } = param.kind | |
| { | |
| let span = tcx.def_span(param.def_id); | |
| let param_name = Ident::new(param.name, span); | |
| // The corresponding predicates are post-`Sized`-elaboration. Therefore we | |
| // * check for emptiness to detect lone user-written `?Sized` bounds | |
| // * compare the param span to the pred span to detect lone user-written `Sized` bounds | |
| let has_explicit_bounds = bounded_params.is_empty() | |
| || (*bounded_params).get(¶m.index).is_some_and(|&&pred_sp| pred_sp != span); | |
| let const_param_help = !has_explicit_bounds; | |
| let mut diag = tcx.dcx().create_err(errors::UnusedGenericParameter { | |
| span, | |
| param_name, | |
| param_def_kind: tcx.def_descr(param.def_id), | |
| help: errors::UnusedGenericParameterHelp::TyAlias { param_name }, | |
| usage_spans: vec![], | |
| const_param_help, | |
| }); | |
| diag.code(E0091); | |
| diag.emit(); | |
| } | |
| } | |
| } | |
| /// Emit an error for recursive opaque types. | |
| /// | |
| /// If this is a return `impl Trait`, find the item's return expressions and point at them. For | |
| /// direct recursion this is enough, but for indirect recursion also point at the last intermediary | |
| /// `impl Trait`. | |
| /// | |
| /// If all the return expressions evaluate to `!`, then we explain that the error will go away | |
| /// after changing it. This can happen when a user uses `panic!()` or similar as a placeholder. | |
| fn opaque_type_cycle_error(tcx: TyCtxt<'_>, opaque_def_id: LocalDefId) -> ErrorGuaranteed { | |
| let span = tcx.def_span(opaque_def_id); | |
| let mut err = struct_span_code_err!(tcx.dcx(), span, E0720, "cannot resolve opaque type"); | |
| let mut label = false; | |
| if let Some((def_id, visitor)) = get_owner_return_paths(tcx, opaque_def_id) { | |
| let typeck_results = tcx.typeck(def_id); | |
| if visitor | |
| .returns | |
| .iter() | |
| .filter_map(|expr| typeck_results.node_type_opt(expr.hir_id)) | |
| .all(|ty| matches!(ty.kind(), ty::Never)) | |
| { | |
| let spans = visitor | |
| .returns | |
| .iter() | |
| .filter(|expr| typeck_results.node_type_opt(expr.hir_id).is_some()) | |
| .map(|expr| expr.span) | |
| .collect::<Vec<Span>>(); | |
| let span_len = spans.len(); | |
| if span_len == 1 { | |
| err.span_label(spans[0], "this returned value is of `!` type"); | |
| } else { | |
| let mut multispan: MultiSpan = spans.clone().into(); | |
| for span in spans { | |
| multispan.push_span_label(span, "this returned value is of `!` type"); | |
| } | |
| err.span_note(multispan, "these returned values have a concrete \"never\" type"); | |
| } | |
| err.help("this error will resolve once the item's body returns a concrete type"); | |
| } else { | |
| let mut seen = FxHashSet::default(); | |
| seen.insert(span); | |
| err.span_label(span, "recursive opaque type"); | |
| label = true; | |
| for (sp, ty) in visitor | |
| .returns | |
| .iter() | |
| .filter_map(|e| typeck_results.node_type_opt(e.hir_id).map(|t| (e.span, t))) | |
| .filter(|(_, ty)| !matches!(ty.kind(), ty::Never)) | |
| { | |
| #[derive(Default)] | |
| struct OpaqueTypeCollector { | |
| opaques: Vec<DefId>, | |
| closures: Vec<DefId>, | |
| } | |
| impl<'tcx> ty::TypeVisitor<TyCtxt<'tcx>> for OpaqueTypeCollector { | |
| fn visit_ty(&mut self, t: Ty<'tcx>) { | |
| match *t.kind() { | |
| ty::Alias(ty::Opaque, ty::AliasTy { def_id: def, .. }) => { | |
| self.opaques.push(def); | |
| } | |
| ty::Closure(def_id, ..) | ty::Coroutine(def_id, ..) => { | |
| self.closures.push(def_id); | |
| t.super_visit_with(self); | |
| } | |
| _ => t.super_visit_with(self), | |
| } | |
| } | |
| } | |
| let mut visitor = OpaqueTypeCollector::default(); | |
| ty.visit_with(&mut visitor); | |
| for def_id in visitor.opaques { | |
| let ty_span = tcx.def_span(def_id); | |
| if !seen.contains(&ty_span) { | |
| let descr = if ty.is_impl_trait() { "opaque " } else { "" }; | |
| err.span_label(ty_span, format!("returning this {descr}type `{ty}`")); | |
| seen.insert(ty_span); | |
| } | |
| err.span_label(sp, format!("returning here with type `{ty}`")); | |
| } | |
| for closure_def_id in visitor.closures { | |
| let Some(closure_local_did) = closure_def_id.as_local() else { | |
| continue; | |
| }; | |
| let typeck_results = tcx.typeck(closure_local_did); | |
| let mut label_match = |ty: Ty<'_>, span| { | |
| for arg in ty.walk() { | |
| if let ty::GenericArgKind::Type(ty) = arg.kind() | |
| && let ty::Alias( | |
| ty::Opaque, | |
| ty::AliasTy { def_id: captured_def_id, .. }, | |
| ) = *ty.kind() | |
| && captured_def_id == opaque_def_id.to_def_id() | |
| { | |
| err.span_label( | |
| span, | |
| format!( | |
| "{} captures itself here", | |
| tcx.def_descr(closure_def_id) | |
| ), | |
| ); | |
| } | |
| } | |
| }; | |
| // Label any closure upvars that capture the opaque | |
| for capture in typeck_results.closure_min_captures_flattened(closure_local_did) | |
| { | |
| label_match(capture.place.ty(), capture.get_path_span(tcx)); | |
| } | |
| // Label any coroutine locals that capture the opaque | |
| if tcx.is_coroutine(closure_def_id) | |
| && let Some(coroutine_layout) = tcx.mir_coroutine_witnesses(closure_def_id) | |
| { | |
| for interior_ty in &coroutine_layout.field_tys { | |
| label_match(interior_ty.ty, interior_ty.source_info.span); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| if !label { | |
| err.span_label(span, "cannot resolve opaque type"); | |
| } | |
| err.emit() | |
| } | |
| pub(super) fn check_coroutine_obligations( | |
| tcx: TyCtxt<'_>, | |
| def_id: LocalDefId, | |
| ) -> Result<(), ErrorGuaranteed> { | |
| debug_assert!(!tcx.is_typeck_child(def_id.to_def_id())); | |
| let typeck_results = tcx.typeck(def_id); | |
| let param_env = tcx.param_env(def_id); | |
| debug!(?typeck_results.coroutine_stalled_predicates); | |
| let mode = if tcx.next_trait_solver_globally() { | |
| // This query is conceptually between HIR typeck and | |
| // MIR borrowck. We use the opaque types defined by HIR | |
| // and ignore region constraints. | |
| TypingMode::borrowck(tcx, def_id) | |
| } else { | |
| TypingMode::analysis_in_body(tcx, def_id) | |
| }; | |
| // Typeck writeback gives us predicates with their regions erased. | |
| // We only need to check the goals while ignoring lifetimes to give good | |
| // error message and to avoid breaking the assumption of `mir_borrowck` | |
| // that all obligations already hold modulo regions. | |
| let infcx = tcx.infer_ctxt().ignoring_regions().build(mode); | |
| let ocx = ObligationCtxt::new_with_diagnostics(&infcx); | |
| for (predicate, cause) in &typeck_results.coroutine_stalled_predicates { | |
| ocx.register_obligation(Obligation::new(tcx, cause.clone(), param_env, *predicate)); | |
| } | |
| let errors = ocx.evaluate_obligations_error_on_ambiguity(); | |
| debug!(?errors); | |
| if !errors.is_empty() { | |
| return Err(infcx.err_ctxt().report_fulfillment_errors(errors)); | |
| } | |
| if !tcx.next_trait_solver_globally() { | |
| // Check that any hidden types found when checking these stalled coroutine obligations | |
| // are valid. | |
| for (key, ty) in infcx.take_opaque_types() { | |
| let hidden_type = infcx.resolve_vars_if_possible(ty); | |
| let key = infcx.resolve_vars_if_possible(key); | |
| sanity_check_found_hidden_type(tcx, key, hidden_type)?; | |
| } | |
| } else { | |
| // We're not checking region constraints here, so we can simply drop the | |
| // added opaque type uses in `TypingMode::Borrowck`. | |
| let _ = infcx.take_opaque_types(); | |
| } | |
| Ok(()) | |
| } | |
| pub(super) fn check_potentially_region_dependent_goals<'tcx>( | |
| tcx: TyCtxt<'tcx>, | |
| def_id: LocalDefId, | |
| ) -> Result<(), ErrorGuaranteed> { | |
| if !tcx.next_trait_solver_globally() { | |
| return Ok(()); | |
| } | |
| let typeck_results = tcx.typeck(def_id); | |
| let param_env = tcx.param_env(def_id); | |
| // We use `TypingMode::Borrowck` as we want to use the opaque types computed by HIR typeck. | |
| let typing_mode = TypingMode::borrowck(tcx, def_id); | |
| let infcx = tcx.infer_ctxt().ignoring_regions().build(typing_mode); | |
| let ocx = ObligationCtxt::new_with_diagnostics(&infcx); | |
| for (predicate, cause) in &typeck_results.potentially_region_dependent_goals { | |
| let predicate = fold_regions(tcx, *predicate, |_, _| { | |
| infcx.next_region_var(RegionVariableOrigin::Misc(cause.span)) | |
| }); | |
| ocx.register_obligation(Obligation::new(tcx, cause.clone(), param_env, predicate)); | |
| } | |
| let errors = ocx.evaluate_obligations_error_on_ambiguity(); | |
| debug!(?errors); | |
| if errors.is_empty() { Ok(()) } else { Err(infcx.err_ctxt().report_fulfillment_errors(errors)) } | |
| } |