summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_hir_analysis/src/check
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--compiler/rustc_hir_analysis/src/check/check.rs1443
-rw-r--r--compiler/rustc_hir_analysis/src/check/compare_method.rs1825
-rw-r--r--compiler/rustc_hir_analysis/src/check/dropck.rs (renamed from compiler/rustc_typeck/src/check/dropck.rs)14
-rw-r--r--compiler/rustc_hir_analysis/src/check/intrinsic.rs (renamed from compiler/rustc_typeck/src/check/intrinsic.rs)60
-rw-r--r--compiler/rustc_hir_analysis/src/check/intrinsicck.rs437
-rw-r--r--compiler/rustc_hir_analysis/src/check/mod.rs515
-rw-r--r--compiler/rustc_hir_analysis/src/check/region.rs (renamed from compiler/rustc_typeck/src/check/region.rs)51
-rw-r--r--compiler/rustc_hir_analysis/src/check/wfcheck.rs (renamed from compiler/rustc_typeck/src/check/wfcheck.rs)367
-rw-r--r--compiler/rustc_hir_analysis/src/check_unused.rs192
9 files changed, 4690 insertions, 214 deletions
diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs
new file mode 100644
index 000000000..b70ac0205
--- /dev/null
+++ b/compiler/rustc_hir_analysis/src/check/check.rs
@@ -0,0 +1,1443 @@
+use crate::check::intrinsicck::InlineAsmCtxt;
+
+use super::compare_method::check_type_bounds;
+use super::compare_method::{compare_impl_method, compare_ty_impl};
+use super::*;
+use rustc_attr as attr;
+use rustc_errors::{Applicability, ErrorGuaranteed, MultiSpan};
+use rustc_hir as hir;
+use rustc_hir::def::{DefKind, Res};
+use rustc_hir::def_id::{DefId, LocalDefId};
+use rustc_hir::intravisit::Visitor;
+use rustc_hir::{ItemKind, Node, PathSegment};
+use rustc_infer::infer::outlives::env::OutlivesEnvironment;
+use rustc_infer::infer::{DefiningAnchor, RegionVariableOrigin, TyCtxtInferExt};
+use rustc_infer::traits::Obligation;
+use rustc_lint::builtin::REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS;
+use rustc_middle::hir::nested_filter;
+use rustc_middle::middle::stability::EvalResult;
+use rustc_middle::ty::layout::{LayoutError, MAX_SIMD_LANES};
+use rustc_middle::ty::subst::GenericArgKind;
+use rustc_middle::ty::util::{Discr, IntTypeExt};
+use rustc_middle::ty::{
+ self, ParamEnv, ToPredicate, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable,
+};
+use rustc_session::lint::builtin::{UNINHABITED_STATIC, UNSUPPORTED_CALLING_CONVENTIONS};
+use rustc_span::symbol::sym;
+use rustc_span::{self, Span};
+use rustc_target::spec::abi::Abi;
+use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _;
+use rustc_trait_selection::traits::{self, ObligationCtxt};
+
+use std::ops::ControlFlow;
+
+pub fn check_abi(tcx: TyCtxt<'_>, hir_id: hir::HirId, span: Span, abi: Abi) {
+ match tcx.sess.target.is_abi_supported(abi) {
+ Some(true) => (),
+ Some(false) => {
+ struct_span_err!(
+ tcx.sess,
+ span,
+ E0570,
+ "`{abi}` is not a supported ABI for the current target",
+ )
+ .emit();
+ }
+ None => {
+ tcx.struct_span_lint_hir(
+ UNSUPPORTED_CALLING_CONVENTIONS,
+ hir_id,
+ span,
+ "use of calling convention not supported on this target",
+ |lint| lint,
+ );
+ }
+ }
+
+ // This ABI is only allowed on function pointers
+ if abi == Abi::CCmseNonSecureCall {
+ struct_span_err!(
+ tcx.sess,
+ span,
+ E0781,
+ "the `\"C-cmse-nonsecure-call\"` ABI is only allowed on function pointers"
+ )
+ .emit();
+ }
+}
+
+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 def.repr().simd() {
+ check_simd(tcx, span, def_id);
+ }
+
+ check_transparent(tcx, span, 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, span, def);
+ check_union_fields(tcx, span, def_id);
+ check_packed(tcx, span, def);
+}
+
+/// 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 item_type = tcx.type_of(item_def_id);
+ if let ty::Adt(def, substs) = item_type.kind() {
+ assert!(def.is_union());
+
+ fn allowed_union_field<'tcx>(
+ ty: Ty<'tcx>,
+ tcx: TyCtxt<'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+ span: Span,
+ ) -> bool {
+ // We don't just accept all !needs_drop fields, due to semver concerns.
+ match ty.kind() {
+ ty::Ref(..) => true, // references never drop (even mutable refs, which are non-Copy and hence fail the later check)
+ ty::Tuple(tys) => {
+ // allow tuples of allowed types
+ tys.iter().all(|ty| allowed_union_field(ty, tcx, param_env, span))
+ }
+ ty::Array(elem, _len) => {
+ // Like `Copy`, we do *not* special-case length 0.
+ allowed_union_field(*elem, tcx, param_env, span)
+ }
+ _ => {
+ // Fallback case: allow `ManuallyDrop` and things that are `Copy`.
+ ty.ty_adt_def().is_some_and(|adt_def| adt_def.is_manually_drop())
+ || ty.is_copy_modulo_regions(tcx, param_env)
+ }
+ }
+ }
+
+ let param_env = tcx.param_env(item_def_id);
+ for field in &def.non_enum_variant().fields {
+ let field_ty = field.ty(tcx, substs);
+
+ if !allowed_union_field(field_ty, tcx, param_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"),
+ };
+ struct_span_err!(
+ tcx.sess,
+ field_span,
+ E0740,
+ "unions cannot contain fields that may need dropping"
+ )
+ .note(
+ "a type is guaranteed not to need dropping \
+ when it implements `Copy`, or when it is the special `ManuallyDrop<_>` type",
+ )
+ .multipart_suggestion_verbose(
+ "when the type does not implement `Copy`, \
+ wrap it inside a `ManuallyDrop<_>` and ensure it is manually dropped",
+ vec![
+ (ty_span.shrink_to_lo(), "std::mem::ManuallyDrop<".into()),
+ (ty_span.shrink_to_hi(), ">".into()),
+ ],
+ Applicability::MaybeIncorrect,
+ )
+ .emit();
+ return false;
+ } else if field_ty.needs_drop(tcx, param_env) {
+ // This should never happen. But we can get here e.g. in case of name resolution errors.
+ tcx.sess.delay_span_bug(span, "we should never accept maybe-dropping union fields");
+ }
+ }
+ } else {
+ span_bug!(span, "unions must be ty::Adt, but got {:?}", item_type.kind());
+ }
+ true
+}
+
+/// Check that a `static` is inhabited.
+fn check_static_inhabited<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) {
+ // 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);
+ let span = tcx.def_span(def_id);
+ let layout = match tcx.layout_of(ParamEnv::reveal_all().and(ty)) {
+ Ok(l) => l,
+ // Foreign statics that overflow their allowed size should emit an error
+ Err(LayoutError::SizeOverflow(_))
+ if {
+ let node = tcx.hir().get_by_def_id(def_id);
+ matches!(
+ node,
+ hir::Node::ForeignItem(hir::ForeignItem {
+ kind: hir::ForeignItemKind::Static(..),
+ ..
+ })
+ )
+ } =>
+ {
+ tcx.sess
+ .struct_span_err(span, "extern static is too large for the current architecture")
+ .emit();
+ return;
+ }
+ // Generic statics are rejected, but we still reach this case.
+ Err(e) => {
+ tcx.sess.delay_span_bug(span, &e.to_string());
+ return;
+ }
+ };
+ if layout.abi.is_uninhabited() {
+ tcx.struct_span_lint_hir(
+ UNINHABITED_STATIC,
+ tcx.hir().local_def_id_to_hir_id(def_id),
+ span,
+ "static of uninhabited type",
+ |lint| {
+ lint
+ .note("uninhabited statics cannot be initialized, and any access would be an immediate error")
+ },
+ );
+ }
+}
+
+/// 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>(tcx: TyCtxt<'tcx>, id: hir::ItemId) {
+ let item = tcx.hir().item(id);
+ let hir::ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) = item.kind else {
+ tcx.sess.delay_span_bug(tcx.hir().span(id.hir_id()), "expected opaque item");
+ return;
+ };
+
+ // 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 concrete 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;
+ }
+
+ let substs = InternalSubsts::identity_for_item(tcx, item.owner_id.to_def_id());
+ let span = tcx.def_span(item.owner_id.def_id);
+
+ check_opaque_for_inheriting_lifetimes(tcx, item.owner_id.def_id, span);
+ if tcx.type_of(item.owner_id.def_id).references_error() {
+ return;
+ }
+ if check_opaque_for_cycles(tcx, item.owner_id.def_id, substs, span, &origin).is_err() {
+ return;
+ }
+ check_opaque_meets_bounds(tcx, item.owner_id.def_id, substs, span, &origin);
+}
+/// Checks that an opaque type does not use `Self` or `T::Foo` projections that would result
+/// in "inheriting lifetimes".
+#[instrument(level = "debug", skip(tcx, span))]
+pub(super) fn check_opaque_for_inheriting_lifetimes<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ def_id: LocalDefId,
+ span: Span,
+) {
+ let item = tcx.hir().expect_item(def_id);
+ debug!(?item, ?span);
+
+ struct FoundParentLifetime;
+ struct FindParentLifetimeVisitor<'tcx>(&'tcx ty::Generics);
+ impl<'tcx> ty::visit::TypeVisitor<'tcx> for FindParentLifetimeVisitor<'tcx> {
+ type BreakTy = FoundParentLifetime;
+
+ fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
+ debug!("FindParentLifetimeVisitor: r={:?}", r);
+ if let ty::ReEarlyBound(ty::EarlyBoundRegion { index, .. }) = *r {
+ if index < self.0.parent_count as u32 {
+ return ControlFlow::Break(FoundParentLifetime);
+ } else {
+ return ControlFlow::CONTINUE;
+ }
+ }
+
+ r.super_visit_with(self)
+ }
+
+ fn visit_const(&mut self, c: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
+ if let ty::ConstKind::Unevaluated(..) = c.kind() {
+ // FIXME(#72219) We currently don't detect lifetimes within substs
+ // which would violate this check. Even though the particular substitution is not used
+ // within the const, this should still be fixed.
+ return ControlFlow::CONTINUE;
+ }
+ c.super_visit_with(self)
+ }
+ }
+
+ struct ProhibitOpaqueVisitor<'tcx> {
+ tcx: TyCtxt<'tcx>,
+ opaque_identity_ty: Ty<'tcx>,
+ generics: &'tcx ty::Generics,
+ selftys: Vec<(Span, Option<String>)>,
+ }
+
+ impl<'tcx> ty::visit::TypeVisitor<'tcx> for ProhibitOpaqueVisitor<'tcx> {
+ type BreakTy = Ty<'tcx>;
+
+ fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
+ debug!("check_opaque_for_inheriting_lifetimes: (visit_ty) t={:?}", t);
+ if t == self.opaque_identity_ty {
+ ControlFlow::CONTINUE
+ } else {
+ t.super_visit_with(&mut FindParentLifetimeVisitor(self.generics))
+ .map_break(|FoundParentLifetime| t)
+ }
+ }
+ }
+
+ impl<'tcx> Visitor<'tcx> for ProhibitOpaqueVisitor<'tcx> {
+ type NestedFilter = nested_filter::OnlyBodies;
+
+ fn nested_visit_map(&mut self) -> Self::Map {
+ self.tcx.hir()
+ }
+
+ fn visit_ty(&mut self, arg: &'tcx hir::Ty<'tcx>) {
+ match arg.kind {
+ hir::TyKind::Path(hir::QPath::Resolved(None, path)) => match &path.segments {
+ [PathSegment { res: Res::SelfTyParam { .. }, .. }] => {
+ let impl_ty_name = None;
+ self.selftys.push((path.span, impl_ty_name));
+ }
+ [PathSegment { res: Res::SelfTyAlias { alias_to: def_id, .. }, .. }] => {
+ let impl_ty_name = Some(self.tcx.def_path_str(*def_id));
+ self.selftys.push((path.span, impl_ty_name));
+ }
+ _ => {}
+ },
+ _ => {}
+ }
+ hir::intravisit::walk_ty(self, arg);
+ }
+ }
+
+ if let ItemKind::OpaqueTy(hir::OpaqueTy {
+ origin: hir::OpaqueTyOrigin::AsyncFn(..) | hir::OpaqueTyOrigin::FnReturn(..),
+ ..
+ }) = item.kind
+ {
+ let mut visitor = ProhibitOpaqueVisitor {
+ opaque_identity_ty: tcx.mk_opaque(
+ def_id.to_def_id(),
+ InternalSubsts::identity_for_item(tcx, def_id.to_def_id()),
+ ),
+ generics: tcx.generics_of(def_id),
+ tcx,
+ selftys: vec![],
+ };
+ let prohibit_opaque = tcx
+ .explicit_item_bounds(def_id)
+ .iter()
+ .try_for_each(|(predicate, _)| predicate.visit_with(&mut visitor));
+ debug!(
+ "check_opaque_for_inheriting_lifetimes: prohibit_opaque={:?}, visitor.opaque_identity_ty={:?}, visitor.generics={:?}",
+ prohibit_opaque, visitor.opaque_identity_ty, visitor.generics
+ );
+
+ if let Some(ty) = prohibit_opaque.break_value() {
+ visitor.visit_item(&item);
+ let is_async = match item.kind {
+ ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) => {
+ matches!(origin, hir::OpaqueTyOrigin::AsyncFn(..))
+ }
+ _ => unreachable!(),
+ };
+
+ let mut err = struct_span_err!(
+ tcx.sess,
+ span,
+ E0760,
+ "`{}` return type cannot contain a projection or `Self` that references lifetimes from \
+ a parent scope",
+ if is_async { "async fn" } else { "impl Trait" },
+ );
+
+ for (span, name) in visitor.selftys {
+ err.span_suggestion(
+ span,
+ "consider spelling out the type instead",
+ name.unwrap_or_else(|| format!("{:?}", ty)),
+ Applicability::MaybeIncorrect,
+ );
+ }
+ err.emit();
+ }
+ }
+}
+
+/// Checks that an opaque type does not contain cycles.
+pub(super) fn check_opaque_for_cycles<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ def_id: LocalDefId,
+ substs: SubstsRef<'tcx>,
+ span: Span,
+ origin: &hir::OpaqueTyOrigin,
+) -> Result<(), ErrorGuaranteed> {
+ if tcx.try_expand_impl_trait_type(def_id.to_def_id(), substs).is_err() {
+ let reported = match origin {
+ hir::OpaqueTyOrigin::AsyncFn(..) => async_opaque_type_cycle_error(tcx, span),
+ _ => opaque_type_cycle_error(tcx, def_id, span),
+ };
+ Err(reported)
+ } else {
+ Ok(())
+ }
+}
+
+/// Check that the concrete 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,
+ substs: SubstsRef<'tcx>,
+ span: Span,
+ origin: &hir::OpaqueTyOrigin,
+) {
+ let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
+ let defining_use_anchor = match *origin {
+ hir::OpaqueTyOrigin::FnReturn(did) | hir::OpaqueTyOrigin::AsyncFn(did) => did,
+ hir::OpaqueTyOrigin::TyAlias => def_id,
+ };
+ let param_env = tcx.param_env(defining_use_anchor);
+
+ let infcx = tcx
+ .infer_ctxt()
+ .with_opaque_type_inference(DefiningAnchor::Bind(defining_use_anchor))
+ .build();
+ let ocx = ObligationCtxt::new(&infcx);
+ let opaque_ty = tcx.mk_opaque(def_id.to_def_id(), substs);
+
+ // `ReErased` regions appear in the "parent_substs" of closures/generators.
+ // We're ignoring them here and replacing them with fresh region variables.
+ // See tests in ui/type-alias-impl-trait/closure_{parent_substs,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.bound_type_of(def_id.to_def_id()).subst(tcx, substs);
+ let hidden_ty = tcx.fold_regions(hidden_ty, |re, _dbi| match re.kind() {
+ ty::ReErased => infcx.next_region_var(RegionVariableOrigin::MiscVariable(span)),
+ _ => re,
+ });
+
+ let misc_cause = traits::ObligationCause::misc(span, hir_id);
+
+ match infcx.at(&misc_cause, param_env).eq(opaque_ty, hidden_ty) {
+ Ok(infer_ok) => ocx.register_infer_ok_obligations(infer_ok),
+ Err(ty_err) => {
+ tcx.sess.delay_span_bug(
+ span,
+ &format!("could not unify `{hidden_ty}` with revealed type:\n{ty_err}"),
+ );
+ }
+ }
+
+ // 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::WellFormed(hidden_ty.into())).to_predicate(tcx);
+ ocx.register_obligation(Obligation::new(misc_cause, param_env, predicate));
+
+ // Check that all obligations are satisfied by the implementation's
+ // version.
+ let errors = ocx.select_all_or_error();
+ if !errors.is_empty() {
+ infcx.err_ctxt().report_fulfillment_errors(&errors, None, false);
+ }
+ match origin {
+ // Checked when type checking the function containing them.
+ hir::OpaqueTyOrigin::FnReturn(..) | hir::OpaqueTyOrigin::AsyncFn(..) => {}
+ // Can have different predicates to their defining use
+ hir::OpaqueTyOrigin::TyAlias => {
+ let outlives_environment = OutlivesEnvironment::new(param_env);
+ infcx.check_region_obligations_and_report_errors(
+ defining_use_anchor,
+ &outlives_environment,
+ );
+ }
+ }
+ // Clean up after ourselves
+ let _ = infcx.inner.borrow_mut().opaque_type_storage.take_opaque_types();
+}
+
+fn check_item_type<'tcx>(tcx: TyCtxt<'tcx>, id: hir::ItemId) {
+ debug!(
+ "check_item_type(it.def_id={:?}, it.name={})",
+ id.owner_id,
+ tcx.def_path_str(id.owner_id.to_def_id())
+ );
+ let _indenter = indenter();
+ match tcx.def_kind(id.owner_id) {
+ DefKind::Static(..) => {
+ tcx.ensure().typeck(id.owner_id.def_id);
+ maybe_check_static_with_link_section(tcx, id.owner_id.def_id);
+ check_static_inhabited(tcx, id.owner_id.def_id);
+ }
+ DefKind::Const => {
+ tcx.ensure().typeck(id.owner_id.def_id);
+ }
+ DefKind::Enum => {
+ let item = tcx.hir().item(id);
+ let hir::ItemKind::Enum(ref enum_definition, _) = item.kind else {
+ return;
+ };
+ check_enum(tcx, &enum_definition.variants, item.owner_id.def_id);
+ }
+ DefKind::Fn => {} // entirely within check_item_body
+ DefKind::Impl => {
+ let it = tcx.hir().item(id);
+ let hir::ItemKind::Impl(ref impl_) = it.kind else {
+ return;
+ };
+ debug!("ItemKind::Impl {} with id {:?}", it.ident, it.owner_id);
+ if let Some(impl_trait_ref) = tcx.impl_trait_ref(it.owner_id) {
+ check_impl_items_against_trait(
+ tcx,
+ it.span,
+ it.owner_id.def_id,
+ impl_trait_ref,
+ &impl_.items,
+ );
+ check_on_unimplemented(tcx, it);
+ }
+ }
+ DefKind::Trait => {
+ let it = tcx.hir().item(id);
+ let hir::ItemKind::Trait(_, _, _, _, ref items) = it.kind else {
+ return;
+ };
+ check_on_unimplemented(tcx, it);
+
+ for item in items.iter() {
+ let item = tcx.hir().trait_item(item.id);
+ match item.kind {
+ hir::TraitItemKind::Fn(ref sig, _) => {
+ let abi = sig.header.abi;
+ fn_maybe_err(tcx, item.ident.span, abi);
+ }
+ hir::TraitItemKind::Type(.., Some(default)) => {
+ let assoc_item = tcx.associated_item(item.owner_id);
+ let trait_substs =
+ InternalSubsts::identity_for_item(tcx, it.owner_id.to_def_id());
+ let _: Result<_, rustc_errors::ErrorGuaranteed> = check_type_bounds(
+ tcx,
+ assoc_item,
+ assoc_item,
+ default.span,
+ ty::TraitRef { def_id: it.owner_id.to_def_id(), substs: trait_substs },
+ );
+ }
+ _ => {}
+ }
+ }
+ }
+ DefKind::Struct => {
+ check_struct(tcx, id.owner_id.def_id);
+ }
+ DefKind::Union => {
+ check_union(tcx, id.owner_id.def_id);
+ }
+ DefKind::OpaqueTy => {
+ check_opaque(tcx, id);
+ }
+ DefKind::ImplTraitPlaceholder => {
+ let parent = tcx.impl_trait_in_trait_parent(id.owner_id.to_def_id());
+ // Only check the validity of this opaque type if the function has a default body
+ if let hir::Node::TraitItem(hir::TraitItem {
+ kind: hir::TraitItemKind::Fn(_, hir::TraitFn::Provided(_)),
+ ..
+ }) = tcx.hir().get_by_def_id(parent.expect_local())
+ {
+ check_opaque(tcx, id);
+ }
+ }
+ DefKind::TyAlias => {
+ let pty_ty = tcx.type_of(id.owner_id);
+ let generics = tcx.generics_of(id.owner_id);
+ check_type_params_are_used(tcx, &generics, pty_ty);
+ }
+ DefKind::ForeignMod => {
+ let it = tcx.hir().item(id);
+ let hir::ItemKind::ForeignMod { abi, items } = it.kind else {
+ return;
+ };
+ check_abi(tcx, it.hir_id(), it.span, abi);
+
+ if abi == Abi::RustIntrinsic {
+ for item in items {
+ let item = tcx.hir().foreign_item(item.id);
+ intrinsic::check_intrinsic_type(tcx, item);
+ }
+ } else if abi == Abi::PlatformIntrinsic {
+ for item in items {
+ let item = tcx.hir().foreign_item(item.id);
+ intrinsic::check_platform_intrinsic_type(tcx, item);
+ }
+ } else {
+ for item in items {
+ let def_id = item.id.owner_id.def_id;
+ let generics = tcx.generics_of(def_id);
+ let own_counts = generics.own_counts();
+ if generics.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),
+ };
+ struct_span_err!(
+ tcx.sess,
+ item.span,
+ E0044,
+ "foreign items may not have {kinds} parameters",
+ )
+ .span_label(item.span, &format!("can't have {kinds} parameters"))
+ .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();
+ }
+
+ let item = tcx.hir().foreign_item(item.id);
+ match item.kind {
+ hir::ForeignItemKind::Fn(ref fn_decl, _, _) => {
+ require_c_abi_if_c_variadic(tcx, fn_decl, abi, item.span);
+ }
+ hir::ForeignItemKind::Static(..) => {
+ check_static_inhabited(tcx, def_id);
+ }
+ _ => {}
+ }
+ }
+ }
+ }
+ DefKind::GlobalAsm => {
+ let it = tcx.hir().item(id);
+ let hir::ItemKind::GlobalAsm(asm) = it.kind else { span_bug!(it.span, "DefKind::GlobalAsm but got {:#?}", it) };
+ InlineAsmCtxt::new_global_asm(tcx).check_asm(asm, id.hir_id());
+ }
+ _ => {}
+ }
+}
+
+pub(super) fn check_on_unimplemented(tcx: TyCtxt<'_>, item: &hir::Item<'_>) {
+ // an error would be reported if this fails.
+ let _ = traits::OnUnimplementedDirective::of_item(tcx, item.owner_id.to_def_id());
+}
+
+pub(super) fn check_specialization_validity<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ trait_def: &ty::TraitDef,
+ trait_item: &ty::AssocItem,
+ impl_id: DefId,
+ impl_item: &hir::ImplItemRef,
+) {
+ 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.impl_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 {
+ report_forbidden_specialization(tcx, impl_item, parent_impl);
+ }
+}
+
+fn check_impl_items_against_trait<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ full_impl_span: Span,
+ impl_id: LocalDefId,
+ impl_trait_ref: ty::TraitRef<'tcx>,
+ impl_item_refs: &[hir::ImplItemRef],
+) {
+ // 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 impl_trait_ref.references_error() {
+ return;
+ }
+
+ // Negative impls are not expected to have any items
+ match tcx.impl_polarity(impl_id) {
+ ty::ImplPolarity::Reservation | ty::ImplPolarity::Positive => {}
+ ty::ImplPolarity::Negative => {
+ if let [first_item_ref, ..] = impl_item_refs {
+ let first_item_span = tcx.hir().impl_item(first_item_ref.id).span;
+ struct_span_err!(
+ tcx.sess,
+ first_item_span,
+ E0749,
+ "negative impls cannot have any items"
+ )
+ .emit();
+ }
+ return;
+ }
+ }
+
+ let trait_def = tcx.trait_def(impl_trait_ref.def_id);
+
+ for impl_item in impl_item_refs {
+ let ty_impl_item = tcx.associated_item(impl_item.id.owner_id);
+ let ty_trait_item = if let Some(trait_item_id) = ty_impl_item.trait_item_def_id {
+ tcx.associated_item(trait_item_id)
+ } else {
+ // Checked in `associated_item`.
+ tcx.sess.delay_span_bug(impl_item.span, "missing associated item in trait");
+ continue;
+ };
+ let impl_item_full = tcx.hir().impl_item(impl_item.id);
+ match impl_item_full.kind {
+ hir::ImplItemKind::Const(..) => {
+ let _ = tcx.compare_assoc_const_impl_item_with_trait_item((
+ impl_item.id.owner_id.def_id,
+ ty_impl_item.trait_item_def_id.unwrap(),
+ ));
+ }
+ hir::ImplItemKind::Fn(..) => {
+ let opt_trait_span = tcx.hir().span_if_local(ty_trait_item.def_id);
+ compare_impl_method(
+ tcx,
+ &ty_impl_item,
+ &ty_trait_item,
+ impl_trait_ref,
+ opt_trait_span,
+ );
+ }
+ hir::ImplItemKind::Type(impl_ty) => {
+ let opt_trait_span = tcx.hir().span_if_local(ty_trait_item.def_id);
+ compare_ty_impl(
+ tcx,
+ &ty_impl_item,
+ impl_ty.span,
+ &ty_trait_item,
+ impl_trait_ref,
+ opt_trait_span,
+ );
+ }
+ }
+
+ check_specialization_validity(
+ tcx,
+ trait_def,
+ &ty_trait_item,
+ impl_id.to_def_id(),
+ 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(impl_trait_ref.def_id) {
+ let is_implemented = ancestors
+ .leaf_def(tcx, trait_item_id)
+ .map_or(false, |node_item| node_item.item.defaultness(tcx).has_value());
+
+ if !is_implemented && tcx.impl_defaultness(impl_id).is_final() {
+ missing_items.push(tcx.associated_item(trait_item_id));
+ }
+
+ // true if this item is specifically implemented in this impl
+ let is_implemented_here = ancestors
+ .leaf_def(tcx, trait_item_id)
+ .map_or(false, |node_item| !node_item.defining_node.is_from_trait());
+
+ if !is_implemented_here {
+ 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 !missing_items.is_empty() {
+ missing_items_err(tcx, tcx.def_span(impl_id), &missing_items, full_impl_span);
+ }
+
+ if let Some(missing_items) = must_implement_one_of {
+ let attr_span = tcx
+ .get_attr(impl_trait_ref.def_id, sym::rustc_must_implement_one_of)
+ .map(|attr| attr.span);
+
+ missing_items_must_implement_one_of_err(
+ tcx,
+ tcx.def_span(impl_id),
+ missing_items,
+ attr_span,
+ );
+ }
+ }
+}
+
+pub fn check_simd(tcx: TyCtxt<'_>, sp: Span, def_id: LocalDefId) {
+ let t = tcx.type_of(def_id);
+ if let ty::Adt(def, substs) = t.kind()
+ && def.is_struct()
+ {
+ let fields = &def.non_enum_variant().fields;
+ if fields.is_empty() {
+ struct_span_err!(tcx.sess, sp, E0075, "SIMD vector cannot be empty").emit();
+ return;
+ }
+ let e = fields[0].ty(tcx, substs);
+ if !fields.iter().all(|f| f.ty(tcx, substs) == e) {
+ struct_span_err!(tcx.sess, sp, E0076, "SIMD vector should be homogeneous")
+ .span_label(sp, "SIMD elements must have the same type")
+ .emit();
+ return;
+ }
+
+ let len = if let ty::Array(_ty, c) = e.kind() {
+ c.try_eval_usize(tcx, tcx.param_env(def.did()))
+ } else {
+ Some(fields.len() as u64)
+ };
+ if let Some(len) = len {
+ if len == 0 {
+ struct_span_err!(tcx.sess, sp, E0075, "SIMD vector cannot be empty").emit();
+ return;
+ } else if len > MAX_SIMD_LANES {
+ struct_span_err!(
+ tcx.sess,
+ 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, "fat" pointers, compound types
+ match e.kind() {
+ ty::Param(_) => (), // pass struct<T>(T, T, T, T) through, let monomorphization catch errors
+ ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::RawPtr(_) => (), // struct(u8, u8, u8, u8) is ok
+ ty::Array(t, _) if matches!(t.kind(), ty::Param(_)) => (), // pass struct<T>([T; N]) through, let monomorphization catch errors
+ ty::Array(t, _clen)
+ if matches!(
+ t.kind(),
+ ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::RawPtr(_)
+ ) =>
+ { /* struct([f32; 4]) is ok */ }
+ _ => {
+ struct_span_err!(
+ tcx.sess,
+ sp,
+ E0077,
+ "SIMD vector element type should be a \
+ primitive scalar (integer/float/pointer) type"
+ )
+ .emit();
+ return;
+ }
+ }
+ }
+}
+
+pub(super) fn check_packed(tcx: TyCtxt<'_>, sp: Span, def: ty::AdtDef<'_>) {
+ let repr = def.repr();
+ if repr.packed() {
+ for attr in tcx.get_attrs(def.did(), sym::repr) {
+ for r in attr::parse_repr_attr(&tcx.sess, attr) {
+ if let attr::ReprPacked(pack) = r
+ && let Some(repr_pack) = repr.pack
+ && pack as u64 != repr_pack.bytes()
+ {
+ struct_span_err!(
+ tcx.sess,
+ sp,
+ E0634,
+ "type has conflicting packed representation hints"
+ )
+ .emit();
+ }
+ }
+ }
+ if repr.align.is_some() {
+ struct_span_err!(
+ tcx.sess,
+ 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_err!(
+ tcx.sess,
+ 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()),
+ 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, substs) = tcx.type_of(def_id).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, substs).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>, sp: Span, adt: ty::AdtDef<'tcx>) {
+ if !adt.repr().transparent() {
+ return;
+ }
+
+ if adt.is_union() && !tcx.features().transparent_unions {
+ feature_err(
+ &tcx.sess.parse_sess,
+ sym::transparent_unions,
+ sp,
+ "transparent unions are unstable",
+ )
+ .emit();
+ }
+
+ if adt.variants().len() != 1 {
+ bad_variant_count(tcx, adt, sp, adt.did());
+ if adt.variants().is_empty() {
+ // Don't bother checking the fields. No variants (and thus no fields) exist.
+ return;
+ }
+ }
+
+ // For each field, figure out if it's known to be a ZST and align(1), with "known"
+ // respecting #[non_exhaustive] attributes.
+ let field_infos = adt.all_fields().map(|field| {
+ let ty = field.ty(tcx, InternalSubsts::identity_for_item(tcx, field.did));
+ let param_env = tcx.param_env(field.did);
+ let layout = tcx.layout_of(param_env.and(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 zst = layout.map_or(false, |layout| layout.is_zst());
+ let align1 = layout.map_or(false, |layout| layout.align.abi.bytes() == 1);
+ if !zst {
+ return (span, zst, align1, None);
+ }
+
+ fn check_non_exhaustive<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ t: Ty<'tcx>,
+ ) -> ControlFlow<(&'static str, DefId, SubstsRef<'tcx>, bool)> {
+ match t.kind() {
+ ty::Tuple(list) => list.iter().try_for_each(|t| check_non_exhaustive(tcx, t)),
+ ty::Array(ty, _) => check_non_exhaustive(tcx, *ty),
+ ty::Adt(def, subst) => {
+ if !def.did().is_local() {
+ 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((
+ def.descr(),
+ def.did(),
+ subst,
+ non_exhaustive,
+ ));
+ }
+ }
+ def.all_fields()
+ .map(|field| field.ty(tcx, subst))
+ .try_for_each(|t| check_non_exhaustive(tcx, t))
+ }
+ _ => ControlFlow::Continue(()),
+ }
+ }
+
+ (span, zst, align1, check_non_exhaustive(tcx, ty).break_value())
+ });
+
+ let non_zst_fields = field_infos
+ .clone()
+ .filter_map(|(span, zst, _align1, _non_exhaustive)| if !zst { Some(span) } else { None });
+ let non_zst_count = non_zst_fields.clone().count();
+ if non_zst_count >= 2 {
+ bad_non_zero_sized_fields(tcx, adt, non_zst_count, non_zst_fields, sp);
+ }
+ let incompatible_zst_fields =
+ field_infos.clone().filter(|(_, _, _, opt)| opt.is_some()).count();
+ let incompat = incompatible_zst_fields + non_zst_count >= 2 && non_zst_count < 2;
+ for (span, zst, align1, non_exhaustive) in field_infos {
+ if zst && !align1 {
+ struct_span_err!(
+ tcx.sess,
+ span,
+ E0691,
+ "zero-sized field in transparent {} has alignment larger than 1",
+ adt.descr(),
+ )
+ .span_label(span, "has alignment larger than 1")
+ .emit();
+ }
+ if incompat && let Some((descr, def_id, substs, non_exhaustive)) = non_exhaustive {
+ tcx.struct_span_lint_hir(
+ REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS,
+ tcx.hir().local_def_id_to_hir_id(adt.did().expect_local()),
+ span,
+ "zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types",
+ |lint| {
+ let note = if non_exhaustive {
+ "is marked with `#[non_exhaustive]`"
+ } else {
+ "contains private fields"
+ };
+ let field_ty = tcx.def_path_str_with_substs(def_id, substs);
+ lint
+ .note(format!("this {descr} contains `{field_ty}`, which {note}, \
+ and makes it not a breaking change to become non-zero-sized in the future."))
+ },
+ )
+ }
+ }
+}
+
+#[allow(trivial_numeric_casts)]
+fn check_enum<'tcx>(tcx: TyCtxt<'tcx>, vs: &'tcx [hir::Variant<'tcx>], def_id: LocalDefId) {
+ let def = tcx.adt_def(def_id);
+ let sp = tcx.def_span(def_id);
+ def.destructor(tcx); // force the destructor to be evaluated
+
+ if vs.is_empty() {
+ if let Some(attr) = tcx.get_attrs(def_id.to_def_id(), sym::repr).next() {
+ struct_span_err!(
+ tcx.sess,
+ attr.span,
+ E0084,
+ "unsupported representation for zero-variant enum"
+ )
+ .span_label(sp, "zero-variant enum")
+ .emit();
+ }
+ }
+
+ let repr_type_ty = def.repr().discr_type().to_ty(tcx);
+ if repr_type_ty == tcx.types.i128 || repr_type_ty == tcx.types.u128 {
+ if !tcx.features().repr128 {
+ feature_err(
+ &tcx.sess.parse_sess,
+ sym::repr128,
+ sp,
+ "repr with 128-bit type is unstable",
+ )
+ .emit();
+ }
+ }
+
+ for v in vs {
+ if let Some(ref e) = v.disr_expr {
+ tcx.ensure().typeck(tcx.hir().local_def_id(e.hir_id));
+ }
+ }
+
+ if tcx.adt_def(def_id).repr().int.is_none() {
+ let is_unit = |var: &hir::Variant<'_>| matches!(var.data, hir::VariantData::Unit(..));
+
+ let has_disr = |var: &hir::Variant<'_>| var.disr_expr.is_some();
+ let has_non_units = vs.iter().any(|var| !is_unit(var));
+ let disr_units = vs.iter().any(|var| is_unit(&var) && has_disr(&var));
+ let disr_non_unit = vs.iter().any(|var| !is_unit(&var) && has_disr(&var));
+
+ if disr_non_unit || (disr_units && has_non_units) {
+ let mut err =
+ struct_span_err!(tcx.sess, sp, E0732, "`#[repr(inttype)]` must be specified");
+ err.emit();
+ }
+ }
+
+ detect_discriminant_duplicate(tcx, def.discriminants(tcx).collect(), vs, sp);
+
+ check_transparent(tcx, sp, 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>,
+ mut discrs: Vec<(VariantIdx, Discr<'tcx>)>,
+ vs: &'tcx [hir::Variant<'tcx>],
+ self_span: Span,
+) {
+ // 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: usize, err: &mut Diagnostic| {
+ let var = &vs[idx]; // HIR for the duplicate discriminant
+ let (span, display_discr) = match var.disr_expr {
+ Some(ref expr) => {
+ // In the case the discriminant is both a duplicate and overflowed, let the user know
+ if 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.hir().span(expr.hir_id), format!("`{dis}` (overflowed from `{lit_value}`)"))
+ // Otherwise, format the value as-is
+ } else {
+ (tcx.hir().span(expr.hir_id), format!("`{dis}`"))
+ }
+ }
+ None => {
+ // 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((n, hir::Variant { span, ident, .. })) =
+ vs[..idx].iter().rev().enumerate().find(|v| v.1.disr_expr.is_some())
+ {
+ let ve_ident = var.ident;
+ let n = n + 1;
+ let sp = if n > 1 { "variants" } else { "variant" };
+
+ err.span_label(
+ *span,
+ format!("discriminant for `{ve_ident}` incremented from this startpoint (`{ident}` + {n} {sp} later => `{ve_ident}` = {dis})"),
+ );
+ }
+
+ (vs[idx].span, format!("`{dis}`"))
+ }
+ };
+
+ err.span_label(span, format!("{display_discr} assigned here"));
+ };
+
+ // 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 hir_var_i_idx = discrs[i].0.index();
+ let mut error: Option<DiagnosticBuilder<'_, _>> = None;
+
+ let mut o = i + 1;
+ while o < discrs.len() {
+ let hir_var_o_idx = discrs[o].0.index();
+
+ if discrs[i].1.val == discrs[o].1.val {
+ let err = error.get_or_insert_with(|| {
+ let mut ret = struct_span_err!(
+ tcx.sess,
+ self_span,
+ E0081,
+ "discriminant value `{}` assigned more than once",
+ discrs[i].1,
+ );
+
+ report(discrs[i].1, hir_var_i_idx, &mut ret);
+
+ ret
+ });
+
+ report(discrs[o].1, hir_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(mut e) = error {
+ e.emit();
+ }
+
+ i += 1;
+ }
+}
+
+pub(super) fn check_type_params_are_used<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ generics: &ty::Generics,
+ ty: Ty<'tcx>,
+) {
+ debug!("check_type_params_are_used(generics={:?}, ty={:?})", generics, ty);
+
+ assert_eq!(generics.parent, None);
+
+ if generics.own_counts().types == 0 {
+ return;
+ }
+
+ let mut params_used = BitSet::new_empty(generics.params.len());
+
+ if ty.references_error() {
+ // If there is already another error, do not emit
+ // an error for not using a type parameter.
+ assert!(tcx.sess.has_errors().is_some());
+ return;
+ }
+
+ for leaf in ty.walk() {
+ if let GenericArgKind::Type(leaf_ty) = leaf.unpack()
+ && let ty::Param(param) = leaf_ty.kind()
+ {
+ debug!("found use of ty param {:?}", param);
+ params_used.insert(param.index);
+ }
+ }
+
+ for param in &generics.params {
+ if !params_used.contains(param.index)
+ && let ty::GenericParamDefKind::Type { .. } = param.kind
+ {
+ let span = tcx.def_span(param.def_id);
+ struct_span_err!(
+ tcx.sess,
+ span,
+ E0091,
+ "type parameter `{}` is unused",
+ param.name,
+ )
+ .span_label(span, "unused type parameter")
+ .emit();
+ }
+ }
+}
+
+pub(super) fn check_mod_item_types(tcx: TyCtxt<'_>, module_def_id: LocalDefId) {
+ let module = tcx.hir_module_items(module_def_id);
+ for id in module.items() {
+ check_item_type(tcx, id);
+ }
+}
+
+fn async_opaque_type_cycle_error(tcx: TyCtxt<'_>, span: Span) -> ErrorGuaranteed {
+ struct_span_err!(tcx.sess, span, E0733, "recursion in an `async fn` requires boxing")
+ .span_label(span, "recursive `async fn`")
+ .note("a recursive `async fn` must be rewritten to return a boxed `dyn Future`")
+ .note(
+ "consider using the `async_recursion` crate: https://crates.io/crates/async_recursion",
+ )
+ .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<'_>, def_id: LocalDefId, span: Span) -> ErrorGuaranteed {
+ let mut err = struct_span_err!(tcx.sess, span, E0720, "cannot resolve opaque type");
+
+ let mut label = false;
+ if let Some((def_id, visitor)) = get_owner_return_paths(tcx, 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))
+ {
+ struct OpaqueTypeCollector(Vec<DefId>);
+ impl<'tcx> ty::visit::TypeVisitor<'tcx> for OpaqueTypeCollector {
+ fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
+ match *t.kind() {
+ ty::Opaque(def, _) => {
+ self.0.push(def);
+ ControlFlow::CONTINUE
+ }
+ _ => t.super_visit_with(self),
+ }
+ }
+ }
+ let mut visitor = OpaqueTypeCollector(vec![]);
+ ty.visit_with(&mut visitor);
+ for def_id in visitor.0 {
+ let ty_span = tcx.def_span(def_id);
+ if !seen.contains(&ty_span) {
+ err.span_label(ty_span, &format!("returning this opaque type `{ty}`"));
+ seen.insert(ty_span);
+ }
+ err.span_label(sp, &format!("returning here with type `{ty}`"));
+ }
+ }
+ }
+ }
+ if !label {
+ err.span_label(span, "cannot resolve opaque type");
+ }
+ err.emit()
+}
diff --git a/compiler/rustc_hir_analysis/src/check/compare_method.rs b/compiler/rustc_hir_analysis/src/check/compare_method.rs
new file mode 100644
index 000000000..32f66b06f
--- /dev/null
+++ b/compiler/rustc_hir_analysis/src/check/compare_method.rs
@@ -0,0 +1,1825 @@
+use super::potentially_plural_count;
+use crate::errors::LifetimesOrBoundsMismatchOnTrait;
+use hir::def_id::{DefId, LocalDefId};
+use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticId, ErrorGuaranteed};
+use rustc_hir as hir;
+use rustc_hir::def::{DefKind, Res};
+use rustc_hir::intravisit;
+use rustc_hir::{GenericParamKind, ImplItemKind, TraitItemKind};
+use rustc_infer::infer::outlives::env::OutlivesEnvironment;
+use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
+use rustc_infer::infer::{self, TyCtxtInferExt};
+use rustc_infer::traits::util;
+use rustc_middle::ty::error::{ExpectedFound, TypeError};
+use rustc_middle::ty::util::ExplicitSelf;
+use rustc_middle::ty::InternalSubsts;
+use rustc_middle::ty::{
+ self, AssocItem, DefIdTree, Ty, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitable,
+};
+use rustc_middle::ty::{GenericParamDefKind, ToPredicate, TyCtxt};
+use rustc_span::Span;
+use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt;
+use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _;
+use rustc_trait_selection::traits::{
+ self, ObligationCause, ObligationCauseCode, ObligationCtxt, Reveal,
+};
+use std::iter;
+
+/// Checks that a method from an impl conforms to the signature of
+/// the same method as declared in the trait.
+///
+/// # Parameters
+///
+/// - `impl_m`: type of the method we are checking
+/// - `impl_m_span`: span to use for reporting errors
+/// - `trait_m`: the method in the trait
+/// - `impl_trait_ref`: the TraitRef corresponding to the trait implementation
+pub(crate) fn compare_impl_method<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ impl_m: &ty::AssocItem,
+ trait_m: &ty::AssocItem,
+ impl_trait_ref: ty::TraitRef<'tcx>,
+ trait_item_span: Option<Span>,
+) {
+ debug!("compare_impl_method(impl_trait_ref={:?})", impl_trait_ref);
+
+ let impl_m_span = tcx.def_span(impl_m.def_id);
+
+ if let Err(_) = compare_self_type(tcx, impl_m, impl_m_span, trait_m, impl_trait_ref) {
+ return;
+ }
+
+ if let Err(_) = compare_number_of_generics(tcx, impl_m, impl_m_span, trait_m, trait_item_span) {
+ return;
+ }
+
+ if let Err(_) = compare_generic_param_kinds(tcx, impl_m, trait_m) {
+ return;
+ }
+
+ if let Err(_) =
+ compare_number_of_method_arguments(tcx, impl_m, impl_m_span, trait_m, trait_item_span)
+ {
+ return;
+ }
+
+ if let Err(_) = compare_synthetic_generics(tcx, impl_m, trait_m) {
+ return;
+ }
+
+ if let Err(_) = compare_predicate_entailment(tcx, impl_m, impl_m_span, trait_m, impl_trait_ref)
+ {
+ return;
+ }
+}
+
+/// This function is best explained by example. Consider a trait:
+///
+/// trait Trait<'t, T> {
+/// // `trait_m`
+/// fn method<'a, M>(t: &'t T, m: &'a M) -> Self;
+/// }
+///
+/// And an impl:
+///
+/// impl<'i, 'j, U> Trait<'j, &'i U> for Foo {
+/// // `impl_m`
+/// fn method<'b, N>(t: &'j &'i U, m: &'b N) -> Foo;
+/// }
+///
+/// We wish to decide if those two method types are compatible.
+/// For this we have to show that, assuming the bounds of the impl hold, the
+/// bounds of `trait_m` imply the bounds of `impl_m`.
+///
+/// We start out with `trait_to_impl_substs`, that maps the trait
+/// type parameters to impl type parameters. This is taken from the
+/// impl trait reference:
+///
+/// trait_to_impl_substs = {'t => 'j, T => &'i U, Self => Foo}
+///
+/// We create a mapping `dummy_substs` that maps from the impl type
+/// parameters to fresh types and regions. For type parameters,
+/// this is the identity transform, but we could as well use any
+/// placeholder types. For regions, we convert from bound to free
+/// regions (Note: but only early-bound regions, i.e., those
+/// declared on the impl or used in type parameter bounds).
+///
+/// impl_to_placeholder_substs = {'i => 'i0, U => U0, N => N0 }
+///
+/// Now we can apply `placeholder_substs` to the type of the impl method
+/// to yield a new function type in terms of our fresh, placeholder
+/// types:
+///
+/// <'b> fn(t: &'i0 U0, m: &'b) -> Foo
+///
+/// We now want to extract and substitute the type of the *trait*
+/// method and compare it. To do so, we must create a compound
+/// substitution by combining `trait_to_impl_substs` and
+/// `impl_to_placeholder_substs`, and also adding a mapping for the method
+/// type parameters. We extend the mapping to also include
+/// the method parameters.
+///
+/// trait_to_placeholder_substs = { T => &'i0 U0, Self => Foo, M => N0 }
+///
+/// Applying this to the trait method type yields:
+///
+/// <'a> fn(t: &'i0 U0, m: &'a) -> Foo
+///
+/// This type is also the same but the name of the bound region (`'a`
+/// vs `'b`). However, the normal subtyping rules on fn types handle
+/// this kind of equivalency just fine.
+///
+/// We now use these substitutions to ensure that all declared bounds are
+/// satisfied by the implementation's method.
+///
+/// We do this by creating a parameter environment which contains a
+/// substitution corresponding to `impl_to_placeholder_substs`. We then build
+/// `trait_to_placeholder_substs` and use it to convert the predicates contained
+/// in the `trait_m` generics to the placeholder form.
+///
+/// Finally we register each of these predicates as an obligation and check that
+/// they hold.
+#[instrument(level = "debug", skip(tcx, impl_m_span, impl_trait_ref))]
+fn compare_predicate_entailment<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ impl_m: &AssocItem,
+ impl_m_span: Span,
+ trait_m: &AssocItem,
+ impl_trait_ref: ty::TraitRef<'tcx>,
+) -> Result<(), ErrorGuaranteed> {
+ let trait_to_impl_substs = impl_trait_ref.substs;
+
+ // This node-id should be used for the `body_id` field on each
+ // `ObligationCause` (and the `FnCtxt`).
+ //
+ // FIXME(@lcnr): remove that after removing `cause.body_id` from
+ // obligations.
+ let impl_m_hir_id = tcx.hir().local_def_id_to_hir_id(impl_m.def_id.expect_local());
+ // We sometimes modify the span further down.
+ let mut cause = ObligationCause::new(
+ impl_m_span,
+ impl_m_hir_id,
+ ObligationCauseCode::CompareImplItemObligation {
+ impl_item_def_id: impl_m.def_id.expect_local(),
+ trait_item_def_id: trait_m.def_id,
+ kind: impl_m.kind,
+ },
+ );
+
+ // Create mapping from impl to placeholder.
+ let impl_to_placeholder_substs = InternalSubsts::identity_for_item(tcx, impl_m.def_id);
+
+ // Create mapping from trait to placeholder.
+ let trait_to_placeholder_substs =
+ impl_to_placeholder_substs.rebase_onto(tcx, impl_m.container_id(tcx), trait_to_impl_substs);
+ debug!("compare_impl_method: trait_to_placeholder_substs={:?}", trait_to_placeholder_substs);
+
+ let impl_m_generics = tcx.generics_of(impl_m.def_id);
+ let trait_m_generics = tcx.generics_of(trait_m.def_id);
+ let impl_m_predicates = tcx.predicates_of(impl_m.def_id);
+ let trait_m_predicates = tcx.predicates_of(trait_m.def_id);
+
+ // Check region bounds.
+ check_region_bounds_on_impl_item(tcx, impl_m, trait_m, &trait_m_generics, &impl_m_generics)?;
+
+ // Create obligations for each predicate declared by the impl
+ // definition in the context of the trait's parameter
+ // environment. We can't just use `impl_env.caller_bounds`,
+ // however, because we want to replace all late-bound regions with
+ // region variables.
+ let impl_predicates = tcx.predicates_of(impl_m_predicates.parent.unwrap());
+ let mut hybrid_preds = impl_predicates.instantiate_identity(tcx);
+
+ debug!("compare_impl_method: impl_bounds={:?}", hybrid_preds);
+
+ // This is the only tricky bit of the new way we check implementation methods
+ // We need to build a set of predicates where only the method-level bounds
+ // are from the trait and we assume all other bounds from the implementation
+ // to be previously satisfied.
+ //
+ // We then register the obligations from the impl_m and check to see
+ // if all constraints hold.
+ hybrid_preds
+ .predicates
+ .extend(trait_m_predicates.instantiate_own(tcx, trait_to_placeholder_substs).predicates);
+
+ // Construct trait parameter environment and then shift it into the placeholder viewpoint.
+ // The key step here is to update the caller_bounds's predicates to be
+ // the new hybrid bounds we computed.
+ let normalize_cause = traits::ObligationCause::misc(impl_m_span, impl_m_hir_id);
+ let param_env = ty::ParamEnv::new(
+ tcx.intern_predicates(&hybrid_preds.predicates),
+ Reveal::UserFacing,
+ hir::Constness::NotConst,
+ );
+ let param_env = traits::normalize_param_env_or_error(tcx, param_env, normalize_cause);
+
+ let infcx = &tcx.infer_ctxt().build();
+ let ocx = ObligationCtxt::new(infcx);
+
+ debug!("compare_impl_method: caller_bounds={:?}", param_env.caller_bounds());
+
+ let mut selcx = traits::SelectionContext::new(&infcx);
+ let impl_m_own_bounds = impl_m_predicates.instantiate_own(tcx, impl_to_placeholder_substs);
+ for (predicate, span) in iter::zip(impl_m_own_bounds.predicates, impl_m_own_bounds.spans) {
+ let normalize_cause = traits::ObligationCause::misc(span, impl_m_hir_id);
+ let traits::Normalized { value: predicate, obligations } =
+ traits::normalize(&mut selcx, param_env, normalize_cause, predicate);
+
+ ocx.register_obligations(obligations);
+ let cause = ObligationCause::new(
+ span,
+ impl_m_hir_id,
+ ObligationCauseCode::CompareImplItemObligation {
+ impl_item_def_id: impl_m.def_id.expect_local(),
+ trait_item_def_id: trait_m.def_id,
+ kind: impl_m.kind,
+ },
+ );
+ ocx.register_obligation(traits::Obligation::new(cause, param_env, predicate));
+ }
+
+ // We now need to check that the signature of the impl method is
+ // compatible with that of the trait method. We do this by
+ // checking that `impl_fty <: trait_fty`.
+ //
+ // FIXME. Unfortunately, this doesn't quite work right now because
+ // associated type normalization is not integrated into subtype
+ // checks. For the comparison to be valid, we need to
+ // normalize the associated types in the impl/trait methods
+ // first. However, because function types bind regions, just
+ // calling `normalize_associated_types_in` would have no effect on
+ // any associated types appearing in the fn arguments or return
+ // type.
+
+ // Compute placeholder form of impl and trait method tys.
+ let tcx = infcx.tcx;
+
+ let mut wf_tys = FxHashSet::default();
+
+ let impl_sig = infcx.replace_bound_vars_with_fresh_vars(
+ impl_m_span,
+ infer::HigherRankedType,
+ tcx.fn_sig(impl_m.def_id),
+ );
+
+ let norm_cause = ObligationCause::misc(impl_m_span, impl_m_hir_id);
+ let impl_sig = ocx.normalize(norm_cause.clone(), param_env, impl_sig);
+ let impl_fty = tcx.mk_fn_ptr(ty::Binder::dummy(impl_sig));
+ debug!("compare_impl_method: impl_fty={:?}", impl_fty);
+
+ let trait_sig = tcx.bound_fn_sig(trait_m.def_id).subst(tcx, trait_to_placeholder_substs);
+ let trait_sig = tcx.liberate_late_bound_regions(impl_m.def_id, trait_sig);
+
+ // Next, add all inputs and output as well-formed tys. Importantly,
+ // we have to do this before normalization, since the normalized ty may
+ // not contain the input parameters. See issue #87748.
+ wf_tys.extend(trait_sig.inputs_and_output.iter());
+ let trait_sig = ocx.normalize(norm_cause, param_env, trait_sig);
+ // We also have to add the normalized trait signature
+ // as we don't normalize during implied bounds computation.
+ wf_tys.extend(trait_sig.inputs_and_output.iter());
+ let trait_fty = tcx.mk_fn_ptr(ty::Binder::dummy(trait_sig));
+
+ debug!("compare_impl_method: trait_fty={:?}", trait_fty);
+
+ // FIXME: We'd want to keep more accurate spans than "the method signature" when
+ // processing the comparison between the trait and impl fn, but we sadly lose them
+ // and point at the whole signature when a trait bound or specific input or output
+ // type would be more appropriate. In other places we have a `Vec<Span>`
+ // corresponding to their `Vec<Predicate>`, but we don't have that here.
+ // Fixing this would improve the output of test `issue-83765.rs`.
+ let mut result = infcx
+ .at(&cause, param_env)
+ .sup(trait_fty, impl_fty)
+ .map(|infer_ok| ocx.register_infer_ok_obligations(infer_ok));
+
+ // HACK(RPITIT): #101614. When we are trying to infer the hidden types for
+ // RPITITs, we need to equate the output tys instead of just subtyping. If
+ // we just use `sup` above, we'll end up `&'static str <: _#1t`, which causes
+ // us to infer `_#1t = #'_#2r str`, where `'_#2r` is unconstrained, which gets
+ // fixed up to `ReEmpty`, and which is certainly not what we want.
+ if trait_fty.has_infer_types() {
+ result = result.and_then(|()| {
+ infcx
+ .at(&cause, param_env)
+ .eq(trait_sig.output(), impl_sig.output())
+ .map(|infer_ok| ocx.register_infer_ok_obligations(infer_ok))
+ });
+ }
+
+ if let Err(terr) = result {
+ debug!("sub_types failed: impl ty {:?}, trait ty {:?}", impl_fty, trait_fty);
+
+ let (impl_err_span, trait_err_span) =
+ extract_spans_for_error_reporting(&infcx, terr, &cause, impl_m, trait_m);
+
+ cause.span = impl_err_span;
+
+ let mut diag = struct_span_err!(
+ tcx.sess,
+ cause.span(),
+ E0053,
+ "method `{}` has an incompatible type for trait",
+ trait_m.name
+ );
+ match &terr {
+ TypeError::ArgumentMutability(0) | TypeError::ArgumentSorts(_, 0)
+ if trait_m.fn_has_self_parameter =>
+ {
+ let ty = trait_sig.inputs()[0];
+ let sugg = match ExplicitSelf::determine(ty, |_| ty == impl_trait_ref.self_ty()) {
+ ExplicitSelf::ByValue => "self".to_owned(),
+ ExplicitSelf::ByReference(_, hir::Mutability::Not) => "&self".to_owned(),
+ ExplicitSelf::ByReference(_, hir::Mutability::Mut) => "&mut self".to_owned(),
+ _ => format!("self: {ty}"),
+ };
+
+ // When the `impl` receiver is an arbitrary self type, like `self: Box<Self>`, the
+ // span points only at the type `Box<Self`>, but we want to cover the whole
+ // argument pattern and type.
+ let span = match tcx.hir().expect_impl_item(impl_m.def_id.expect_local()).kind {
+ ImplItemKind::Fn(ref sig, body) => tcx
+ .hir()
+ .body_param_names(body)
+ .zip(sig.decl.inputs.iter())
+ .map(|(param, ty)| param.span.to(ty.span))
+ .next()
+ .unwrap_or(impl_err_span),
+ _ => bug!("{:?} is not a method", impl_m),
+ };
+
+ diag.span_suggestion(
+ span,
+ "change the self-receiver type to match the trait",
+ sugg,
+ Applicability::MachineApplicable,
+ );
+ }
+ TypeError::ArgumentMutability(i) | TypeError::ArgumentSorts(_, i) => {
+ if trait_sig.inputs().len() == *i {
+ // Suggestion to change output type. We do not suggest in `async` functions
+ // to avoid complex logic or incorrect output.
+ match tcx.hir().expect_impl_item(impl_m.def_id.expect_local()).kind {
+ ImplItemKind::Fn(ref sig, _)
+ if sig.header.asyncness == hir::IsAsync::NotAsync =>
+ {
+ let msg = "change the output type to match the trait";
+ let ap = Applicability::MachineApplicable;
+ match sig.decl.output {
+ hir::FnRetTy::DefaultReturn(sp) => {
+ let sugg = format!("-> {} ", trait_sig.output());
+ diag.span_suggestion_verbose(sp, msg, sugg, ap);
+ }
+ hir::FnRetTy::Return(hir_ty) => {
+ let sugg = trait_sig.output();
+ diag.span_suggestion(hir_ty.span, msg, sugg, ap);
+ }
+ };
+ }
+ _ => {}
+ };
+ } else if let Some(trait_ty) = trait_sig.inputs().get(*i) {
+ diag.span_suggestion(
+ impl_err_span,
+ "change the parameter type to match the trait",
+ trait_ty,
+ Applicability::MachineApplicable,
+ );
+ }
+ }
+ _ => {}
+ }
+
+ infcx.err_ctxt().note_type_err(
+ &mut diag,
+ &cause,
+ trait_err_span.map(|sp| (sp, "type in trait".to_owned())),
+ Some(infer::ValuePairs::Terms(ExpectedFound {
+ expected: trait_fty.into(),
+ found: impl_fty.into(),
+ })),
+ terr,
+ false,
+ false,
+ );
+
+ return Err(diag.emit());
+ }
+
+ // Check that all obligations are satisfied by the implementation's
+ // version.
+ let errors = ocx.select_all_or_error();
+ if !errors.is_empty() {
+ let reported = infcx.err_ctxt().report_fulfillment_errors(&errors, None, false);
+ return Err(reported);
+ }
+
+ // Finally, resolve all regions. This catches wily misuses of
+ // lifetime parameters.
+ let outlives_environment = OutlivesEnvironment::with_bounds(
+ param_env,
+ Some(infcx),
+ infcx.implied_bounds_tys(param_env, impl_m_hir_id, wf_tys),
+ );
+ infcx.check_region_obligations_and_report_errors(
+ impl_m.def_id.expect_local(),
+ &outlives_environment,
+ );
+
+ Ok(())
+}
+
+pub fn collect_trait_impl_trait_tys<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ def_id: DefId,
+) -> Result<&'tcx FxHashMap<DefId, Ty<'tcx>>, ErrorGuaranteed> {
+ let impl_m = tcx.opt_associated_item(def_id).unwrap();
+ let trait_m = tcx.opt_associated_item(impl_m.trait_item_def_id.unwrap()).unwrap();
+ let impl_trait_ref = tcx.impl_trait_ref(impl_m.impl_container(tcx).unwrap()).unwrap();
+ let param_env = tcx.param_env(def_id);
+
+ let trait_to_impl_substs = impl_trait_ref.substs;
+
+ let impl_m_hir_id = tcx.hir().local_def_id_to_hir_id(impl_m.def_id.expect_local());
+ let return_span = tcx.hir().fn_decl_by_hir_id(impl_m_hir_id).unwrap().output.span();
+ let cause = ObligationCause::new(
+ return_span,
+ impl_m_hir_id,
+ ObligationCauseCode::CompareImplItemObligation {
+ impl_item_def_id: impl_m.def_id.expect_local(),
+ trait_item_def_id: trait_m.def_id,
+ kind: impl_m.kind,
+ },
+ );
+
+ // Create mapping from impl to placeholder.
+ let impl_to_placeholder_substs = InternalSubsts::identity_for_item(tcx, impl_m.def_id);
+
+ // Create mapping from trait to placeholder.
+ let trait_to_placeholder_substs =
+ impl_to_placeholder_substs.rebase_onto(tcx, impl_m.container_id(tcx), trait_to_impl_substs);
+
+ let infcx = &tcx.infer_ctxt().build();
+ let ocx = ObligationCtxt::new(infcx);
+
+ let norm_cause = ObligationCause::misc(return_span, impl_m_hir_id);
+ let impl_sig = ocx.normalize(
+ norm_cause.clone(),
+ param_env,
+ infcx.replace_bound_vars_with_fresh_vars(
+ return_span,
+ infer::HigherRankedType,
+ tcx.fn_sig(impl_m.def_id),
+ ),
+ );
+ let impl_return_ty = impl_sig.output();
+
+ let mut collector = ImplTraitInTraitCollector::new(&ocx, return_span, param_env, impl_m_hir_id);
+ let unnormalized_trait_sig = tcx
+ .liberate_late_bound_regions(
+ impl_m.def_id,
+ tcx.bound_fn_sig(trait_m.def_id).subst(tcx, trait_to_placeholder_substs),
+ )
+ .fold_with(&mut collector);
+ let trait_sig = ocx.normalize(norm_cause.clone(), param_env, unnormalized_trait_sig);
+ let trait_return_ty = trait_sig.output();
+
+ let wf_tys = FxHashSet::from_iter(
+ unnormalized_trait_sig.inputs_and_output.iter().chain(trait_sig.inputs_and_output.iter()),
+ );
+
+ match infcx.at(&cause, param_env).eq(trait_return_ty, impl_return_ty) {
+ Ok(infer::InferOk { value: (), obligations }) => {
+ ocx.register_obligations(obligations);
+ }
+ Err(terr) => {
+ let mut diag = struct_span_err!(
+ tcx.sess,
+ cause.span(),
+ E0053,
+ "method `{}` has an incompatible return type for trait",
+ trait_m.name
+ );
+ let hir = tcx.hir();
+ infcx.err_ctxt().note_type_err(
+ &mut diag,
+ &cause,
+ hir.get_if_local(impl_m.def_id)
+ .and_then(|node| node.fn_decl())
+ .map(|decl| (decl.output.span(), "return type in trait".to_owned())),
+ Some(infer::ValuePairs::Terms(ExpectedFound {
+ expected: trait_return_ty.into(),
+ found: impl_return_ty.into(),
+ })),
+ terr,
+ false,
+ false,
+ );
+ return Err(diag.emit());
+ }
+ }
+
+ // Unify the whole function signature. We need to do this to fully infer
+ // the lifetimes of the return type, but do this after unifying just the
+ // return types, since we want to avoid duplicating errors from
+ // `compare_predicate_entailment`.
+ match infcx
+ .at(&cause, param_env)
+ .eq(tcx.mk_fn_ptr(ty::Binder::dummy(trait_sig)), tcx.mk_fn_ptr(ty::Binder::dummy(impl_sig)))
+ {
+ Ok(infer::InferOk { value: (), obligations }) => {
+ ocx.register_obligations(obligations);
+ }
+ Err(terr) => {
+ let guar = tcx.sess.delay_span_bug(
+ return_span,
+ format!("could not unify `{trait_sig}` and `{impl_sig}`: {terr:?}"),
+ );
+ return Err(guar);
+ }
+ }
+
+ // Check that all obligations are satisfied by the implementation's
+ // RPITs.
+ let errors = ocx.select_all_or_error();
+ if !errors.is_empty() {
+ let reported = infcx.err_ctxt().report_fulfillment_errors(&errors, None, false);
+ return Err(reported);
+ }
+
+ // Finally, resolve all regions. This catches wily misuses of
+ // lifetime parameters.
+ let outlives_environment = OutlivesEnvironment::with_bounds(
+ param_env,
+ Some(infcx),
+ infcx.implied_bounds_tys(param_env, impl_m_hir_id, wf_tys),
+ );
+ infcx.check_region_obligations_and_report_errors(
+ impl_m.def_id.expect_local(),
+ &outlives_environment,
+ );
+
+ let mut collected_tys = FxHashMap::default();
+ for (def_id, (ty, substs)) in collector.types {
+ match infcx.fully_resolve(ty) {
+ Ok(ty) => {
+ // `ty` contains free regions that we created earlier while liberating the
+ // trait fn signature. However, projection normalization expects `ty` to
+ // contains `def_id`'s early-bound regions.
+ let id_substs = InternalSubsts::identity_for_item(tcx, def_id);
+ debug!(?id_substs, ?substs);
+ let map: FxHashMap<ty::GenericArg<'tcx>, ty::GenericArg<'tcx>> =
+ std::iter::zip(substs, id_substs).collect();
+ debug!(?map);
+
+ // NOTE(compiler-errors): RPITITs, like all other RPITs, have early-bound
+ // region substs that are synthesized during AST lowering. These are substs
+ // that are appended to the parent substs (trait and trait method). However,
+ // we're trying to infer the unsubstituted type value of the RPITIT inside
+ // the *impl*, so we can later use the impl's method substs to normalize
+ // an RPITIT to a concrete type (`confirm_impl_trait_in_trait_candidate`).
+ //
+ // Due to the design of RPITITs, during AST lowering, we have no idea that
+ // an impl method corresponds to a trait method with RPITITs in it. Therefore,
+ // we don't have a list of early-bound region substs for the RPITIT in the impl.
+ // Since early region parameters are index-based, we can't just rebase these
+ // (trait method) early-bound region substs onto the impl, and there's no
+ // guarantee that the indices from the trait substs and impl substs line up.
+ // So to fix this, we subtract the number of trait substs and add the number of
+ // impl substs to *renumber* these early-bound regions to their corresponding
+ // indices in the impl's substitutions list.
+ //
+ // Also, we only need to account for a difference in trait and impl substs,
+ // since we previously enforce that the trait method and impl method have the
+ // same generics.
+ let num_trait_substs = trait_to_impl_substs.len();
+ let num_impl_substs = tcx.generics_of(impl_m.container_id(tcx)).params.len();
+ let ty = tcx.fold_regions(ty, |region, _| {
+ let (ty::ReFree(_) | ty::ReEarlyBound(_)) = region.kind() else { return region; };
+ let Some(ty::ReEarlyBound(e)) = map.get(&region.into()).map(|r| r.expect_region().kind())
+ else {
+ tcx
+ .sess
+ .delay_span_bug(
+ return_span,
+ "expected ReFree to map to ReEarlyBound"
+ );
+ return tcx.lifetimes.re_static;
+ };
+ tcx.mk_region(ty::ReEarlyBound(ty::EarlyBoundRegion {
+ def_id: e.def_id,
+ name: e.name,
+ index: (e.index as usize - num_trait_substs + num_impl_substs) as u32,
+ }))
+ });
+ debug!(%ty);
+ collected_tys.insert(def_id, ty);
+ }
+ Err(err) => {
+ tcx.sess.delay_span_bug(
+ return_span,
+ format!("could not fully resolve: {ty} => {err:?}"),
+ );
+ collected_tys.insert(def_id, tcx.ty_error());
+ }
+ }
+ }
+
+ Ok(&*tcx.arena.alloc(collected_tys))
+}
+
+struct ImplTraitInTraitCollector<'a, 'tcx> {
+ ocx: &'a ObligationCtxt<'a, 'tcx>,
+ types: FxHashMap<DefId, (Ty<'tcx>, ty::SubstsRef<'tcx>)>,
+ span: Span,
+ param_env: ty::ParamEnv<'tcx>,
+ body_id: hir::HirId,
+}
+
+impl<'a, 'tcx> ImplTraitInTraitCollector<'a, 'tcx> {
+ fn new(
+ ocx: &'a ObligationCtxt<'a, 'tcx>,
+ span: Span,
+ param_env: ty::ParamEnv<'tcx>,
+ body_id: hir::HirId,
+ ) -> Self {
+ ImplTraitInTraitCollector { ocx, types: FxHashMap::default(), span, param_env, body_id }
+ }
+}
+
+impl<'tcx> TypeFolder<'tcx> for ImplTraitInTraitCollector<'_, 'tcx> {
+ fn tcx<'a>(&'a self) -> TyCtxt<'tcx> {
+ self.ocx.infcx.tcx
+ }
+
+ fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
+ if let ty::Projection(proj) = ty.kind()
+ && self.tcx().def_kind(proj.item_def_id) == DefKind::ImplTraitPlaceholder
+ {
+ if let Some((ty, _)) = self.types.get(&proj.item_def_id) {
+ return *ty;
+ }
+ //FIXME(RPITIT): Deny nested RPITIT in substs too
+ if proj.substs.has_escaping_bound_vars() {
+ bug!("FIXME(RPITIT): error here");
+ }
+ // Replace with infer var
+ let infer_ty = self.ocx.infcx.next_ty_var(TypeVariableOrigin {
+ span: self.span,
+ kind: TypeVariableOriginKind::MiscVariable,
+ });
+ self.types.insert(proj.item_def_id, (infer_ty, proj.substs));
+ // Recurse into bounds
+ for (pred, pred_span) in self.tcx().bound_explicit_item_bounds(proj.item_def_id).subst_iter_copied(self.tcx(), proj.substs) {
+ let pred = pred.fold_with(self);
+ let pred = self.ocx.normalize(
+ ObligationCause::misc(self.span, self.body_id),
+ self.param_env,
+ pred,
+ );
+
+ self.ocx.register_obligation(traits::Obligation::new(
+ ObligationCause::new(
+ self.span,
+ self.body_id,
+ ObligationCauseCode::BindingObligation(proj.item_def_id, pred_span),
+ ),
+ self.param_env,
+ pred,
+ ));
+ }
+ infer_ty
+ } else {
+ ty.super_fold_with(self)
+ }
+ }
+}
+
+fn check_region_bounds_on_impl_item<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ impl_m: &ty::AssocItem,
+ trait_m: &ty::AssocItem,
+ trait_generics: &ty::Generics,
+ impl_generics: &ty::Generics,
+) -> Result<(), ErrorGuaranteed> {
+ let trait_params = trait_generics.own_counts().lifetimes;
+ let impl_params = impl_generics.own_counts().lifetimes;
+
+ debug!(
+ "check_region_bounds_on_impl_item: \
+ trait_generics={:?} \
+ impl_generics={:?}",
+ trait_generics, impl_generics
+ );
+
+ // Must have same number of early-bound lifetime parameters.
+ // Unfortunately, if the user screws up the bounds, then this
+ // will change classification between early and late. E.g.,
+ // if in trait we have `<'a,'b:'a>`, and in impl we just have
+ // `<'a,'b>`, then we have 2 early-bound lifetime parameters
+ // in trait but 0 in the impl. But if we report "expected 2
+ // but found 0" it's confusing, because it looks like there
+ // are zero. Since I don't quite know how to phrase things at
+ // the moment, give a kind of vague error message.
+ if trait_params != impl_params {
+ let span = tcx
+ .hir()
+ .get_generics(impl_m.def_id.expect_local())
+ .expect("expected impl item to have generics or else we can't compare them")
+ .span;
+ let generics_span = if let Some(local_def_id) = trait_m.def_id.as_local() {
+ Some(
+ tcx.hir()
+ .get_generics(local_def_id)
+ .expect("expected trait item to have generics or else we can't compare them")
+ .span,
+ )
+ } else {
+ None
+ };
+
+ let reported = tcx.sess.emit_err(LifetimesOrBoundsMismatchOnTrait {
+ span,
+ item_kind: assoc_item_kind_str(impl_m),
+ ident: impl_m.ident(tcx),
+ generics_span,
+ });
+ return Err(reported);
+ }
+
+ Ok(())
+}
+
+#[instrument(level = "debug", skip(infcx))]
+fn extract_spans_for_error_reporting<'tcx>(
+ infcx: &infer::InferCtxt<'tcx>,
+ terr: TypeError<'_>,
+ cause: &ObligationCause<'tcx>,
+ impl_m: &ty::AssocItem,
+ trait_m: &ty::AssocItem,
+) -> (Span, Option<Span>) {
+ let tcx = infcx.tcx;
+ let mut impl_args = match tcx.hir().expect_impl_item(impl_m.def_id.expect_local()).kind {
+ ImplItemKind::Fn(ref sig, _) => {
+ sig.decl.inputs.iter().map(|t| t.span).chain(iter::once(sig.decl.output.span()))
+ }
+ _ => bug!("{:?} is not a method", impl_m),
+ };
+ let trait_args =
+ trait_m.def_id.as_local().map(|def_id| match tcx.hir().expect_trait_item(def_id).kind {
+ TraitItemKind::Fn(ref sig, _) => {
+ sig.decl.inputs.iter().map(|t| t.span).chain(iter::once(sig.decl.output.span()))
+ }
+ _ => bug!("{:?} is not a TraitItemKind::Fn", trait_m),
+ });
+
+ match terr {
+ TypeError::ArgumentMutability(i) => {
+ (impl_args.nth(i).unwrap(), trait_args.and_then(|mut args| args.nth(i)))
+ }
+ TypeError::ArgumentSorts(ExpectedFound { .. }, i) => {
+ (impl_args.nth(i).unwrap(), trait_args.and_then(|mut args| args.nth(i)))
+ }
+ _ => (cause.span(), tcx.hir().span_if_local(trait_m.def_id)),
+ }
+}
+
+fn compare_self_type<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ impl_m: &ty::AssocItem,
+ impl_m_span: Span,
+ trait_m: &ty::AssocItem,
+ impl_trait_ref: ty::TraitRef<'tcx>,
+) -> Result<(), ErrorGuaranteed> {
+ // Try to give more informative error messages about self typing
+ // mismatches. Note that any mismatch will also be detected
+ // below, where we construct a canonical function type that
+ // includes the self parameter as a normal parameter. It's just
+ // that the error messages you get out of this code are a bit more
+ // inscrutable, particularly for cases where one method has no
+ // self.
+
+ let self_string = |method: &ty::AssocItem| {
+ let untransformed_self_ty = match method.container {
+ ty::ImplContainer => impl_trait_ref.self_ty(),
+ ty::TraitContainer => tcx.types.self_param,
+ };
+ let self_arg_ty = tcx.fn_sig(method.def_id).input(0);
+ let param_env = ty::ParamEnv::reveal_all();
+
+ let infcx = tcx.infer_ctxt().build();
+ let self_arg_ty = tcx.liberate_late_bound_regions(method.def_id, self_arg_ty);
+ let can_eq_self = |ty| infcx.can_eq(param_env, untransformed_self_ty, ty).is_ok();
+ match ExplicitSelf::determine(self_arg_ty, can_eq_self) {
+ ExplicitSelf::ByValue => "self".to_owned(),
+ ExplicitSelf::ByReference(_, hir::Mutability::Not) => "&self".to_owned(),
+ ExplicitSelf::ByReference(_, hir::Mutability::Mut) => "&mut self".to_owned(),
+ _ => format!("self: {self_arg_ty}"),
+ }
+ };
+
+ match (trait_m.fn_has_self_parameter, impl_m.fn_has_self_parameter) {
+ (false, false) | (true, true) => {}
+
+ (false, true) => {
+ let self_descr = self_string(impl_m);
+ let mut err = struct_span_err!(
+ tcx.sess,
+ impl_m_span,
+ E0185,
+ "method `{}` has a `{}` declaration in the impl, but not in the trait",
+ trait_m.name,
+ self_descr
+ );
+ err.span_label(impl_m_span, format!("`{self_descr}` used in impl"));
+ if let Some(span) = tcx.hir().span_if_local(trait_m.def_id) {
+ err.span_label(span, format!("trait method declared without `{self_descr}`"));
+ } else {
+ err.note_trait_signature(trait_m.name, trait_m.signature(tcx));
+ }
+ let reported = err.emit();
+ return Err(reported);
+ }
+
+ (true, false) => {
+ let self_descr = self_string(trait_m);
+ let mut err = struct_span_err!(
+ tcx.sess,
+ impl_m_span,
+ E0186,
+ "method `{}` has a `{}` declaration in the trait, but not in the impl",
+ trait_m.name,
+ self_descr
+ );
+ err.span_label(impl_m_span, format!("expected `{self_descr}` in impl"));
+ if let Some(span) = tcx.hir().span_if_local(trait_m.def_id) {
+ err.span_label(span, format!("`{self_descr}` used in trait"));
+ } else {
+ err.note_trait_signature(trait_m.name, trait_m.signature(tcx));
+ }
+ let reported = err.emit();
+ return Err(reported);
+ }
+ }
+
+ Ok(())
+}
+
+/// Checks that the number of generics on a given assoc item in a trait impl is the same
+/// as the number of generics on the respective assoc item in the trait definition.
+///
+/// For example this code emits the errors in the following code:
+/// ```
+/// trait Trait {
+/// fn foo();
+/// type Assoc<T>;
+/// }
+///
+/// impl Trait for () {
+/// fn foo<T>() {}
+/// //~^ error
+/// type Assoc = u32;
+/// //~^ error
+/// }
+/// ```
+///
+/// Notably this does not error on `foo<T>` implemented as `foo<const N: u8>` or
+/// `foo<const N: u8>` implemented as `foo<const N: u32>`. This is handled in
+/// [`compare_generic_param_kinds`]. This function also does not handle lifetime parameters
+fn compare_number_of_generics<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ impl_: &ty::AssocItem,
+ _impl_span: Span,
+ trait_: &ty::AssocItem,
+ trait_span: Option<Span>,
+) -> Result<(), ErrorGuaranteed> {
+ let trait_own_counts = tcx.generics_of(trait_.def_id).own_counts();
+ let impl_own_counts = tcx.generics_of(impl_.def_id).own_counts();
+
+ // This avoids us erroring on `foo<T>` implemented as `foo<const N: u8>` as this is implemented
+ // in `compare_generic_param_kinds` which will give a nicer error message than something like:
+ // "expected 1 type parameter, found 0 type parameters"
+ if (trait_own_counts.types + trait_own_counts.consts)
+ == (impl_own_counts.types + impl_own_counts.consts)
+ {
+ return Ok(());
+ }
+
+ let matchings = [
+ ("type", trait_own_counts.types, impl_own_counts.types),
+ ("const", trait_own_counts.consts, impl_own_counts.consts),
+ ];
+
+ let item_kind = assoc_item_kind_str(impl_);
+
+ let mut err_occurred = None;
+ for (kind, trait_count, impl_count) in matchings {
+ if impl_count != trait_count {
+ let arg_spans = |kind: ty::AssocKind, generics: &hir::Generics<'_>| {
+ let mut spans = generics
+ .params
+ .iter()
+ .filter(|p| match p.kind {
+ hir::GenericParamKind::Lifetime {
+ kind: hir::LifetimeParamKind::Elided,
+ } => {
+ // A fn can have an arbitrary number of extra elided lifetimes for the
+ // same signature.
+ !matches!(kind, ty::AssocKind::Fn)
+ }
+ _ => true,
+ })
+ .map(|p| p.span)
+ .collect::<Vec<Span>>();
+ if spans.is_empty() {
+ spans = vec![generics.span]
+ }
+ spans
+ };
+ let (trait_spans, impl_trait_spans) = if let Some(def_id) = trait_.def_id.as_local() {
+ let trait_item = tcx.hir().expect_trait_item(def_id);
+ let arg_spans: Vec<Span> = arg_spans(trait_.kind, trait_item.generics);
+ let impl_trait_spans: Vec<Span> = trait_item
+ .generics
+ .params
+ .iter()
+ .filter_map(|p| match p.kind {
+ GenericParamKind::Type { synthetic: true, .. } => Some(p.span),
+ _ => None,
+ })
+ .collect();
+ (Some(arg_spans), impl_trait_spans)
+ } else {
+ (trait_span.map(|s| vec![s]), vec![])
+ };
+
+ let impl_item = tcx.hir().expect_impl_item(impl_.def_id.expect_local());
+ let impl_item_impl_trait_spans: Vec<Span> = impl_item
+ .generics
+ .params
+ .iter()
+ .filter_map(|p| match p.kind {
+ GenericParamKind::Type { synthetic: true, .. } => Some(p.span),
+ _ => None,
+ })
+ .collect();
+ let spans = arg_spans(impl_.kind, impl_item.generics);
+ let span = spans.first().copied();
+
+ let mut err = tcx.sess.struct_span_err_with_code(
+ spans,
+ &format!(
+ "{} `{}` has {} {kind} parameter{} but its trait \
+ declaration has {} {kind} parameter{}",
+ item_kind,
+ trait_.name,
+ impl_count,
+ pluralize!(impl_count),
+ trait_count,
+ pluralize!(trait_count),
+ kind = kind,
+ ),
+ DiagnosticId::Error("E0049".into()),
+ );
+
+ let mut suffix = None;
+
+ if let Some(spans) = trait_spans {
+ let mut spans = spans.iter();
+ if let Some(span) = spans.next() {
+ err.span_label(
+ *span,
+ format!(
+ "expected {} {} parameter{}",
+ trait_count,
+ kind,
+ pluralize!(trait_count),
+ ),
+ );
+ }
+ for span in spans {
+ err.span_label(*span, "");
+ }
+ } else {
+ suffix = Some(format!(", expected {trait_count}"));
+ }
+
+ if let Some(span) = span {
+ err.span_label(
+ span,
+ format!(
+ "found {} {} parameter{}{}",
+ impl_count,
+ kind,
+ pluralize!(impl_count),
+ suffix.unwrap_or_else(String::new),
+ ),
+ );
+ }
+
+ for span in impl_trait_spans.iter().chain(impl_item_impl_trait_spans.iter()) {
+ err.span_label(*span, "`impl Trait` introduces an implicit type parameter");
+ }
+
+ let reported = err.emit();
+ err_occurred = Some(reported);
+ }
+ }
+
+ if let Some(reported) = err_occurred { Err(reported) } else { Ok(()) }
+}
+
+fn compare_number_of_method_arguments<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ impl_m: &ty::AssocItem,
+ impl_m_span: Span,
+ trait_m: &ty::AssocItem,
+ trait_item_span: Option<Span>,
+) -> Result<(), ErrorGuaranteed> {
+ let impl_m_fty = tcx.fn_sig(impl_m.def_id);
+ let trait_m_fty = tcx.fn_sig(trait_m.def_id);
+ let trait_number_args = trait_m_fty.inputs().skip_binder().len();
+ let impl_number_args = impl_m_fty.inputs().skip_binder().len();
+ if trait_number_args != impl_number_args {
+ let trait_span = if let Some(def_id) = trait_m.def_id.as_local() {
+ match tcx.hir().expect_trait_item(def_id).kind {
+ TraitItemKind::Fn(ref trait_m_sig, _) => {
+ let pos = if trait_number_args > 0 { trait_number_args - 1 } else { 0 };
+ if let Some(arg) = trait_m_sig.decl.inputs.get(pos) {
+ Some(if pos == 0 {
+ arg.span
+ } else {
+ arg.span.with_lo(trait_m_sig.decl.inputs[0].span.lo())
+ })
+ } else {
+ trait_item_span
+ }
+ }
+ _ => bug!("{:?} is not a method", impl_m),
+ }
+ } else {
+ trait_item_span
+ };
+ let impl_span = match tcx.hir().expect_impl_item(impl_m.def_id.expect_local()).kind {
+ ImplItemKind::Fn(ref impl_m_sig, _) => {
+ let pos = if impl_number_args > 0 { impl_number_args - 1 } else { 0 };
+ if let Some(arg) = impl_m_sig.decl.inputs.get(pos) {
+ if pos == 0 {
+ arg.span
+ } else {
+ arg.span.with_lo(impl_m_sig.decl.inputs[0].span.lo())
+ }
+ } else {
+ impl_m_span
+ }
+ }
+ _ => bug!("{:?} is not a method", impl_m),
+ };
+ let mut err = struct_span_err!(
+ tcx.sess,
+ impl_span,
+ E0050,
+ "method `{}` has {} but the declaration in trait `{}` has {}",
+ trait_m.name,
+ potentially_plural_count(impl_number_args, "parameter"),
+ tcx.def_path_str(trait_m.def_id),
+ trait_number_args
+ );
+ if let Some(trait_span) = trait_span {
+ err.span_label(
+ trait_span,
+ format!(
+ "trait requires {}",
+ potentially_plural_count(trait_number_args, "parameter")
+ ),
+ );
+ } else {
+ err.note_trait_signature(trait_m.name, trait_m.signature(tcx));
+ }
+ err.span_label(
+ impl_span,
+ format!(
+ "expected {}, found {}",
+ potentially_plural_count(trait_number_args, "parameter"),
+ impl_number_args
+ ),
+ );
+ let reported = err.emit();
+ return Err(reported);
+ }
+
+ Ok(())
+}
+
+fn compare_synthetic_generics<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ impl_m: &ty::AssocItem,
+ trait_m: &ty::AssocItem,
+) -> Result<(), ErrorGuaranteed> {
+ // FIXME(chrisvittal) Clean up this function, list of FIXME items:
+ // 1. Better messages for the span labels
+ // 2. Explanation as to what is going on
+ // If we get here, we already have the same number of generics, so the zip will
+ // be okay.
+ let mut error_found = None;
+ let impl_m_generics = tcx.generics_of(impl_m.def_id);
+ let trait_m_generics = tcx.generics_of(trait_m.def_id);
+ let impl_m_type_params = impl_m_generics.params.iter().filter_map(|param| match param.kind {
+ GenericParamDefKind::Type { synthetic, .. } => Some((param.def_id, synthetic)),
+ GenericParamDefKind::Lifetime | GenericParamDefKind::Const { .. } => None,
+ });
+ let trait_m_type_params = trait_m_generics.params.iter().filter_map(|param| match param.kind {
+ GenericParamDefKind::Type { synthetic, .. } => Some((param.def_id, synthetic)),
+ GenericParamDefKind::Lifetime | GenericParamDefKind::Const { .. } => None,
+ });
+ for ((impl_def_id, impl_synthetic), (trait_def_id, trait_synthetic)) in
+ iter::zip(impl_m_type_params, trait_m_type_params)
+ {
+ if impl_synthetic != trait_synthetic {
+ let impl_def_id = impl_def_id.expect_local();
+ let impl_span = tcx.def_span(impl_def_id);
+ let trait_span = tcx.def_span(trait_def_id);
+ let mut err = struct_span_err!(
+ tcx.sess,
+ impl_span,
+ E0643,
+ "method `{}` has incompatible signature for trait",
+ trait_m.name
+ );
+ err.span_label(trait_span, "declaration in trait here");
+ match (impl_synthetic, trait_synthetic) {
+ // The case where the impl method uses `impl Trait` but the trait method uses
+ // explicit generics
+ (true, false) => {
+ err.span_label(impl_span, "expected generic parameter, found `impl Trait`");
+ (|| {
+ // try taking the name from the trait impl
+ // FIXME: this is obviously suboptimal since the name can already be used
+ // as another generic argument
+ let new_name = tcx.opt_item_name(trait_def_id)?;
+ let trait_m = trait_m.def_id.as_local()?;
+ let trait_m = tcx.hir().expect_trait_item(trait_m);
+
+ let impl_m = impl_m.def_id.as_local()?;
+ let impl_m = tcx.hir().expect_impl_item(impl_m);
+
+ // in case there are no generics, take the spot between the function name
+ // and the opening paren of the argument list
+ let new_generics_span = tcx.def_ident_span(impl_def_id)?.shrink_to_hi();
+ // in case there are generics, just replace them
+ let generics_span =
+ impl_m.generics.span.substitute_dummy(new_generics_span);
+ // replace with the generics from the trait
+ let new_generics =
+ tcx.sess.source_map().span_to_snippet(trait_m.generics.span).ok()?;
+
+ err.multipart_suggestion(
+ "try changing the `impl Trait` argument to a generic parameter",
+ vec![
+ // replace `impl Trait` with `T`
+ (impl_span, new_name.to_string()),
+ // replace impl method generics with trait method generics
+ // This isn't quite right, as users might have changed the names
+ // of the generics, but it works for the common case
+ (generics_span, new_generics),
+ ],
+ Applicability::MaybeIncorrect,
+ );
+ Some(())
+ })();
+ }
+ // The case where the trait method uses `impl Trait`, but the impl method uses
+ // explicit generics.
+ (false, true) => {
+ err.span_label(impl_span, "expected `impl Trait`, found generic parameter");
+ (|| {
+ let impl_m = impl_m.def_id.as_local()?;
+ let impl_m = tcx.hir().expect_impl_item(impl_m);
+ let input_tys = match impl_m.kind {
+ hir::ImplItemKind::Fn(ref sig, _) => sig.decl.inputs,
+ _ => unreachable!(),
+ };
+ struct Visitor(Option<Span>, hir::def_id::LocalDefId);
+ impl<'v> intravisit::Visitor<'v> for Visitor {
+ fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) {
+ intravisit::walk_ty(self, ty);
+ if let hir::TyKind::Path(hir::QPath::Resolved(None, ref path)) =
+ ty.kind
+ && let Res::Def(DefKind::TyParam, def_id) = path.res
+ && def_id == self.1.to_def_id()
+ {
+ self.0 = Some(ty.span);
+ }
+ }
+ }
+ let mut visitor = Visitor(None, impl_def_id);
+ for ty in input_tys {
+ intravisit::Visitor::visit_ty(&mut visitor, ty);
+ }
+ let span = visitor.0?;
+
+ let bounds = impl_m.generics.bounds_for_param(impl_def_id).next()?.bounds;
+ let bounds = bounds.first()?.span().to(bounds.last()?.span());
+ let bounds = tcx.sess.source_map().span_to_snippet(bounds).ok()?;
+
+ err.multipart_suggestion(
+ "try removing the generic parameter and using `impl Trait` instead",
+ vec![
+ // delete generic parameters
+ (impl_m.generics.span, String::new()),
+ // replace param usage with `impl Trait`
+ (span, format!("impl {bounds}")),
+ ],
+ Applicability::MaybeIncorrect,
+ );
+ Some(())
+ })();
+ }
+ _ => unreachable!(),
+ }
+ let reported = err.emit();
+ error_found = Some(reported);
+ }
+ }
+ if let Some(reported) = error_found { Err(reported) } else { Ok(()) }
+}
+
+/// Checks that all parameters in the generics of a given assoc item in a trait impl have
+/// the same kind as the respective generic parameter in the trait def.
+///
+/// For example all 4 errors in the following code are emitted here:
+/// ```
+/// trait Foo {
+/// fn foo<const N: u8>();
+/// type bar<const N: u8>;
+/// fn baz<const N: u32>();
+/// type blah<T>;
+/// }
+///
+/// impl Foo for () {
+/// fn foo<const N: u64>() {}
+/// //~^ error
+/// type bar<const N: u64> {}
+/// //~^ error
+/// fn baz<T>() {}
+/// //~^ error
+/// type blah<const N: i64> = u32;
+/// //~^ error
+/// }
+/// ```
+///
+/// This function does not handle lifetime parameters
+fn compare_generic_param_kinds<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ impl_item: &ty::AssocItem,
+ trait_item: &ty::AssocItem,
+) -> Result<(), ErrorGuaranteed> {
+ assert_eq!(impl_item.kind, trait_item.kind);
+
+ let ty_const_params_of = |def_id| {
+ tcx.generics_of(def_id).params.iter().filter(|param| {
+ matches!(
+ param.kind,
+ GenericParamDefKind::Const { .. } | GenericParamDefKind::Type { .. }
+ )
+ })
+ };
+
+ for (param_impl, param_trait) in
+ iter::zip(ty_const_params_of(impl_item.def_id), ty_const_params_of(trait_item.def_id))
+ {
+ use GenericParamDefKind::*;
+ if match (&param_impl.kind, &param_trait.kind) {
+ (Const { .. }, Const { .. })
+ if tcx.type_of(param_impl.def_id) != tcx.type_of(param_trait.def_id) =>
+ {
+ true
+ }
+ (Const { .. }, Type { .. }) | (Type { .. }, Const { .. }) => true,
+ // this is exhaustive so that anyone adding new generic param kinds knows
+ // to make sure this error is reported for them.
+ (Const { .. }, Const { .. }) | (Type { .. }, Type { .. }) => false,
+ (Lifetime { .. }, _) | (_, Lifetime { .. }) => unreachable!(),
+ } {
+ let param_impl_span = tcx.def_span(param_impl.def_id);
+ let param_trait_span = tcx.def_span(param_trait.def_id);
+
+ let mut err = struct_span_err!(
+ tcx.sess,
+ param_impl_span,
+ E0053,
+ "{} `{}` has an incompatible generic parameter for trait `{}`",
+ assoc_item_kind_str(&impl_item),
+ trait_item.name,
+ &tcx.def_path_str(tcx.parent(trait_item.def_id))
+ );
+
+ let make_param_message = |prefix: &str, param: &ty::GenericParamDef| match param.kind {
+ Const { .. } => {
+ format!("{} const parameter of type `{}`", prefix, tcx.type_of(param.def_id))
+ }
+ Type { .. } => format!("{} type parameter", prefix),
+ Lifetime { .. } => unreachable!(),
+ };
+
+ let trait_header_span = tcx.def_ident_span(tcx.parent(trait_item.def_id)).unwrap();
+ err.span_label(trait_header_span, "");
+ err.span_label(param_trait_span, make_param_message("expected", param_trait));
+
+ let impl_header_span = tcx.def_span(tcx.parent(impl_item.def_id));
+ err.span_label(impl_header_span, "");
+ err.span_label(param_impl_span, make_param_message("found", param_impl));
+
+ let reported = err.emit();
+ return Err(reported);
+ }
+ }
+
+ Ok(())
+}
+
+/// Use `tcx.compare_assoc_const_impl_item_with_trait_item` instead
+pub(crate) fn raw_compare_const_impl<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ (impl_const_item_def, trait_const_item_def): (LocalDefId, DefId),
+) -> Result<(), ErrorGuaranteed> {
+ let impl_const_item = tcx.associated_item(impl_const_item_def);
+ let trait_const_item = tcx.associated_item(trait_const_item_def);
+ let impl_trait_ref = tcx.impl_trait_ref(impl_const_item.container_id(tcx)).unwrap();
+ debug!("compare_const_impl(impl_trait_ref={:?})", impl_trait_ref);
+
+ let impl_c_span = tcx.def_span(impl_const_item_def.to_def_id());
+
+ let infcx = tcx.infer_ctxt().build();
+ let param_env = tcx.param_env(impl_const_item_def.to_def_id());
+ let ocx = ObligationCtxt::new(&infcx);
+
+ // The below is for the most part highly similar to the procedure
+ // for methods above. It is simpler in many respects, especially
+ // because we shouldn't really have to deal with lifetimes or
+ // predicates. In fact some of this should probably be put into
+ // shared functions because of DRY violations...
+ let trait_to_impl_substs = impl_trait_ref.substs;
+
+ // Create a parameter environment that represents the implementation's
+ // method.
+ let impl_c_hir_id = tcx.hir().local_def_id_to_hir_id(impl_const_item_def);
+
+ // Compute placeholder form of impl and trait const tys.
+ let impl_ty = tcx.type_of(impl_const_item_def.to_def_id());
+ let trait_ty = tcx.bound_type_of(trait_const_item_def).subst(tcx, trait_to_impl_substs);
+ let mut cause = ObligationCause::new(
+ impl_c_span,
+ impl_c_hir_id,
+ ObligationCauseCode::CompareImplItemObligation {
+ impl_item_def_id: impl_const_item_def,
+ trait_item_def_id: trait_const_item_def,
+ kind: impl_const_item.kind,
+ },
+ );
+
+ // There is no "body" here, so just pass dummy id.
+ let impl_ty = ocx.normalize(cause.clone(), param_env, impl_ty);
+
+ debug!("compare_const_impl: impl_ty={:?}", impl_ty);
+
+ let trait_ty = ocx.normalize(cause.clone(), param_env, trait_ty);
+
+ debug!("compare_const_impl: trait_ty={:?}", trait_ty);
+
+ let err = infcx
+ .at(&cause, param_env)
+ .sup(trait_ty, impl_ty)
+ .map(|ok| ocx.register_infer_ok_obligations(ok));
+
+ if let Err(terr) = err {
+ debug!(
+ "checking associated const for compatibility: impl ty {:?}, trait ty {:?}",
+ impl_ty, trait_ty
+ );
+
+ // Locate the Span containing just the type of the offending impl
+ match tcx.hir().expect_impl_item(impl_const_item_def).kind {
+ ImplItemKind::Const(ref ty, _) => cause.span = ty.span,
+ _ => bug!("{:?} is not a impl const", impl_const_item),
+ }
+
+ let mut diag = struct_span_err!(
+ tcx.sess,
+ cause.span,
+ E0326,
+ "implemented const `{}` has an incompatible type for trait",
+ trait_const_item.name
+ );
+
+ let trait_c_span = trait_const_item_def.as_local().map(|trait_c_def_id| {
+ // Add a label to the Span containing just the type of the const
+ match tcx.hir().expect_trait_item(trait_c_def_id).kind {
+ TraitItemKind::Const(ref ty, _) => ty.span,
+ _ => bug!("{:?} is not a trait const", trait_const_item),
+ }
+ });
+
+ infcx.err_ctxt().note_type_err(
+ &mut diag,
+ &cause,
+ trait_c_span.map(|span| (span, "type in trait".to_owned())),
+ Some(infer::ValuePairs::Terms(ExpectedFound {
+ expected: trait_ty.into(),
+ found: impl_ty.into(),
+ })),
+ terr,
+ false,
+ false,
+ );
+ return Err(diag.emit());
+ };
+
+ // Check that all obligations are satisfied by the implementation's
+ // version.
+ let errors = ocx.select_all_or_error();
+ if !errors.is_empty() {
+ return Err(infcx.err_ctxt().report_fulfillment_errors(&errors, None, false));
+ }
+
+ // FIXME return `ErrorReported` if region obligations error?
+ let outlives_environment = OutlivesEnvironment::new(param_env);
+ infcx.check_region_obligations_and_report_errors(impl_const_item_def, &outlives_environment);
+ Ok(())
+}
+
+pub(crate) fn compare_ty_impl<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ impl_ty: &ty::AssocItem,
+ impl_ty_span: Span,
+ trait_ty: &ty::AssocItem,
+ impl_trait_ref: ty::TraitRef<'tcx>,
+ trait_item_span: Option<Span>,
+) {
+ debug!("compare_impl_type(impl_trait_ref={:?})", impl_trait_ref);
+
+ let _: Result<(), ErrorGuaranteed> = (|| {
+ compare_number_of_generics(tcx, impl_ty, impl_ty_span, trait_ty, trait_item_span)?;
+
+ compare_generic_param_kinds(tcx, impl_ty, trait_ty)?;
+
+ let sp = tcx.def_span(impl_ty.def_id);
+ compare_type_predicate_entailment(tcx, impl_ty, sp, trait_ty, impl_trait_ref)?;
+
+ check_type_bounds(tcx, trait_ty, impl_ty, impl_ty_span, impl_trait_ref)
+ })();
+}
+
+/// The equivalent of [compare_predicate_entailment], but for associated types
+/// instead of associated functions.
+fn compare_type_predicate_entailment<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ impl_ty: &ty::AssocItem,
+ impl_ty_span: Span,
+ trait_ty: &ty::AssocItem,
+ impl_trait_ref: ty::TraitRef<'tcx>,
+) -> Result<(), ErrorGuaranteed> {
+ let impl_substs = InternalSubsts::identity_for_item(tcx, impl_ty.def_id);
+ let trait_to_impl_substs =
+ impl_substs.rebase_onto(tcx, impl_ty.container_id(tcx), impl_trait_ref.substs);
+
+ let impl_ty_generics = tcx.generics_of(impl_ty.def_id);
+ let trait_ty_generics = tcx.generics_of(trait_ty.def_id);
+ let impl_ty_predicates = tcx.predicates_of(impl_ty.def_id);
+ let trait_ty_predicates = tcx.predicates_of(trait_ty.def_id);
+
+ check_region_bounds_on_impl_item(
+ tcx,
+ impl_ty,
+ trait_ty,
+ &trait_ty_generics,
+ &impl_ty_generics,
+ )?;
+
+ let impl_ty_own_bounds = impl_ty_predicates.instantiate_own(tcx, impl_substs);
+
+ if impl_ty_own_bounds.is_empty() {
+ // Nothing to check.
+ return Ok(());
+ }
+
+ // This `HirId` should be used for the `body_id` field on each
+ // `ObligationCause` (and the `FnCtxt`). This is what
+ // `regionck_item` expects.
+ let impl_ty_hir_id = tcx.hir().local_def_id_to_hir_id(impl_ty.def_id.expect_local());
+ debug!("compare_type_predicate_entailment: trait_to_impl_substs={:?}", trait_to_impl_substs);
+
+ // The predicates declared by the impl definition, the trait and the
+ // associated type in the trait are assumed.
+ let impl_predicates = tcx.predicates_of(impl_ty_predicates.parent.unwrap());
+ let mut hybrid_preds = impl_predicates.instantiate_identity(tcx);
+ hybrid_preds
+ .predicates
+ .extend(trait_ty_predicates.instantiate_own(tcx, trait_to_impl_substs).predicates);
+
+ debug!("compare_type_predicate_entailment: bounds={:?}", hybrid_preds);
+
+ let normalize_cause = traits::ObligationCause::misc(impl_ty_span, impl_ty_hir_id);
+ let param_env = ty::ParamEnv::new(
+ tcx.intern_predicates(&hybrid_preds.predicates),
+ Reveal::UserFacing,
+ hir::Constness::NotConst,
+ );
+ let param_env = traits::normalize_param_env_or_error(tcx, param_env, normalize_cause);
+ let infcx = tcx.infer_ctxt().build();
+ let ocx = ObligationCtxt::new(&infcx);
+
+ debug!("compare_type_predicate_entailment: caller_bounds={:?}", param_env.caller_bounds());
+
+ let mut selcx = traits::SelectionContext::new(&infcx);
+
+ assert_eq!(impl_ty_own_bounds.predicates.len(), impl_ty_own_bounds.spans.len());
+ for (span, predicate) in std::iter::zip(impl_ty_own_bounds.spans, impl_ty_own_bounds.predicates)
+ {
+ let cause = ObligationCause::misc(span, impl_ty_hir_id);
+ let traits::Normalized { value: predicate, obligations } =
+ traits::normalize(&mut selcx, param_env, cause, predicate);
+
+ let cause = ObligationCause::new(
+ span,
+ impl_ty_hir_id,
+ ObligationCauseCode::CompareImplItemObligation {
+ impl_item_def_id: impl_ty.def_id.expect_local(),
+ trait_item_def_id: trait_ty.def_id,
+ kind: impl_ty.kind,
+ },
+ );
+ ocx.register_obligations(obligations);
+ ocx.register_obligation(traits::Obligation::new(cause, param_env, predicate));
+ }
+
+ // Check that all obligations are satisfied by the implementation's
+ // version.
+ let errors = ocx.select_all_or_error();
+ if !errors.is_empty() {
+ let reported = infcx.err_ctxt().report_fulfillment_errors(&errors, None, false);
+ return Err(reported);
+ }
+
+ // Finally, resolve all regions. This catches wily misuses of
+ // lifetime parameters.
+ let outlives_environment = OutlivesEnvironment::new(param_env);
+ infcx.check_region_obligations_and_report_errors(
+ impl_ty.def_id.expect_local(),
+ &outlives_environment,
+ );
+
+ Ok(())
+}
+
+/// Validate that `ProjectionCandidate`s created for this associated type will
+/// be valid.
+///
+/// Usually given
+///
+/// trait X { type Y: Copy } impl X for T { type Y = S; }
+///
+/// We are able to normalize `<T as X>::U` to `S`, and so when we check the
+/// impl is well-formed we have to prove `S: Copy`.
+///
+/// For default associated types the normalization is not possible (the value
+/// from the impl could be overridden). We also can't normalize generic
+/// associated types (yet) because they contain bound parameters.
+#[instrument(level = "debug", skip(tcx))]
+pub fn check_type_bounds<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ trait_ty: &ty::AssocItem,
+ impl_ty: &ty::AssocItem,
+ impl_ty_span: Span,
+ impl_trait_ref: ty::TraitRef<'tcx>,
+) -> Result<(), ErrorGuaranteed> {
+ // Given
+ //
+ // impl<A, B> Foo<u32> for (A, B) {
+ // type Bar<C> =...
+ // }
+ //
+ // - `impl_trait_ref` would be `<(A, B) as Foo<u32>>
+ // - `impl_ty_substs` would be `[A, B, ^0.0]` (`^0.0` here is the bound var with db 0 and index 0)
+ // - `rebased_substs` would be `[(A, B), u32, ^0.0]`, combining the substs from
+ // the *trait* with the generic associated type parameters (as bound vars).
+ //
+ // A note regarding the use of bound vars here:
+ // Imagine as an example
+ // ```
+ // trait Family {
+ // type Member<C: Eq>;
+ // }
+ //
+ // impl Family for VecFamily {
+ // type Member<C: Eq> = i32;
+ // }
+ // ```
+ // Here, we would generate
+ // ```notrust
+ // forall<C> { Normalize(<VecFamily as Family>::Member<C> => i32) }
+ // ```
+ // when we really would like to generate
+ // ```notrust
+ // forall<C> { Normalize(<VecFamily as Family>::Member<C> => i32) :- Implemented(C: Eq) }
+ // ```
+ // But, this is probably fine, because although the first clause can be used with types C that
+ // do not implement Eq, for it to cause some kind of problem, there would have to be a
+ // VecFamily::Member<X> for some type X where !(X: Eq), that appears in the value of type
+ // Member<C: Eq> = .... That type would fail a well-formedness check that we ought to be doing
+ // elsewhere, which would check that any <T as Family>::Member<X> meets the bounds declared in
+ // the trait (notably, that X: Eq and T: Family).
+ let defs: &ty::Generics = tcx.generics_of(impl_ty.def_id);
+ let mut substs = smallvec::SmallVec::with_capacity(defs.count());
+ if let Some(def_id) = defs.parent {
+ let parent_defs = tcx.generics_of(def_id);
+ InternalSubsts::fill_item(&mut substs, tcx, parent_defs, &mut |param, _| {
+ tcx.mk_param_from_def(param)
+ });
+ }
+ let mut bound_vars: smallvec::SmallVec<[ty::BoundVariableKind; 8]> =
+ smallvec::SmallVec::with_capacity(defs.count());
+ InternalSubsts::fill_single(&mut substs, defs, &mut |param, _| match param.kind {
+ GenericParamDefKind::Type { .. } => {
+ let kind = ty::BoundTyKind::Param(param.name);
+ let bound_var = ty::BoundVariableKind::Ty(kind);
+ bound_vars.push(bound_var);
+ tcx.mk_ty(ty::Bound(
+ ty::INNERMOST,
+ ty::BoundTy { var: ty::BoundVar::from_usize(bound_vars.len() - 1), kind },
+ ))
+ .into()
+ }
+ GenericParamDefKind::Lifetime => {
+ let kind = ty::BoundRegionKind::BrNamed(param.def_id, param.name);
+ let bound_var = ty::BoundVariableKind::Region(kind);
+ bound_vars.push(bound_var);
+ tcx.mk_region(ty::ReLateBound(
+ ty::INNERMOST,
+ ty::BoundRegion { var: ty::BoundVar::from_usize(bound_vars.len() - 1), kind },
+ ))
+ .into()
+ }
+ GenericParamDefKind::Const { .. } => {
+ let bound_var = ty::BoundVariableKind::Const;
+ bound_vars.push(bound_var);
+ tcx.mk_const(ty::ConstS {
+ ty: tcx.type_of(param.def_id),
+ kind: ty::ConstKind::Bound(
+ ty::INNERMOST,
+ ty::BoundVar::from_usize(bound_vars.len() - 1),
+ ),
+ })
+ .into()
+ }
+ });
+ let bound_vars = tcx.mk_bound_variable_kinds(bound_vars.into_iter());
+ let impl_ty_substs = tcx.intern_substs(&substs);
+ let container_id = impl_ty.container_id(tcx);
+
+ let rebased_substs = impl_ty_substs.rebase_onto(tcx, container_id, impl_trait_ref.substs);
+ let impl_ty_value = tcx.type_of(impl_ty.def_id);
+
+ let param_env = tcx.param_env(impl_ty.def_id);
+
+ // When checking something like
+ //
+ // trait X { type Y: PartialEq<<Self as X>::Y> }
+ // impl X for T { default type Y = S; }
+ //
+ // We will have to prove the bound S: PartialEq<<T as X>::Y>. In this case
+ // we want <T as X>::Y to normalize to S. This is valid because we are
+ // checking the default value specifically here. Add this equality to the
+ // ParamEnv for normalization specifically.
+ let normalize_param_env = {
+ let mut predicates = param_env.caller_bounds().iter().collect::<Vec<_>>();
+ match impl_ty_value.kind() {
+ ty::Projection(proj)
+ if proj.item_def_id == trait_ty.def_id && proj.substs == rebased_substs =>
+ {
+ // Don't include this predicate if the projected type is
+ // exactly the same as the projection. This can occur in
+ // (somewhat dubious) code like this:
+ //
+ // impl<T> X for T where T: X { type Y = <T as X>::Y; }
+ }
+ _ => predicates.push(
+ ty::Binder::bind_with_vars(
+ ty::ProjectionPredicate {
+ projection_ty: ty::ProjectionTy {
+ item_def_id: trait_ty.def_id,
+ substs: rebased_substs,
+ },
+ term: impl_ty_value.into(),
+ },
+ bound_vars,
+ )
+ .to_predicate(tcx),
+ ),
+ };
+ ty::ParamEnv::new(
+ tcx.intern_predicates(&predicates),
+ Reveal::UserFacing,
+ param_env.constness(),
+ )
+ };
+ debug!(?normalize_param_env);
+
+ let impl_ty_hir_id = tcx.hir().local_def_id_to_hir_id(impl_ty.def_id.expect_local());
+ let impl_ty_substs = InternalSubsts::identity_for_item(tcx, impl_ty.def_id);
+ let rebased_substs = impl_ty_substs.rebase_onto(tcx, container_id, impl_trait_ref.substs);
+
+ let infcx = tcx.infer_ctxt().build();
+ let ocx = ObligationCtxt::new(&infcx);
+
+ let assumed_wf_types =
+ ocx.assumed_wf_types(param_env, impl_ty_span, impl_ty.def_id.expect_local());
+
+ let mut selcx = traits::SelectionContext::new(&infcx);
+ let normalize_cause = ObligationCause::new(
+ impl_ty_span,
+ impl_ty_hir_id,
+ ObligationCauseCode::CheckAssociatedTypeBounds {
+ impl_item_def_id: impl_ty.def_id.expect_local(),
+ trait_item_def_id: trait_ty.def_id,
+ },
+ );
+ let mk_cause = |span: Span| {
+ let code = if span.is_dummy() {
+ traits::ItemObligation(trait_ty.def_id)
+ } else {
+ traits::BindingObligation(trait_ty.def_id, span)
+ };
+ ObligationCause::new(impl_ty_span, impl_ty_hir_id, code)
+ };
+
+ let obligations = tcx
+ .bound_explicit_item_bounds(trait_ty.def_id)
+ .subst_iter_copied(tcx, rebased_substs)
+ .map(|(concrete_ty_bound, span)| {
+ debug!("check_type_bounds: concrete_ty_bound = {:?}", concrete_ty_bound);
+ traits::Obligation::new(mk_cause(span), param_env, concrete_ty_bound)
+ })
+ .collect();
+ debug!("check_type_bounds: item_bounds={:?}", obligations);
+
+ for mut obligation in util::elaborate_obligations(tcx, obligations) {
+ let traits::Normalized { value: normalized_predicate, obligations } = traits::normalize(
+ &mut selcx,
+ normalize_param_env,
+ normalize_cause.clone(),
+ obligation.predicate,
+ );
+ debug!("compare_projection_bounds: normalized predicate = {:?}", normalized_predicate);
+ obligation.predicate = normalized_predicate;
+
+ ocx.register_obligations(obligations);
+ ocx.register_obligation(obligation);
+ }
+ // Check that all obligations are satisfied by the implementation's
+ // version.
+ let errors = ocx.select_all_or_error();
+ if !errors.is_empty() {
+ let reported = infcx.err_ctxt().report_fulfillment_errors(&errors, None, false);
+ return Err(reported);
+ }
+
+ // Finally, resolve all regions. This catches wily misuses of
+ // lifetime parameters.
+ let implied_bounds = infcx.implied_bounds_tys(param_env, impl_ty_hir_id, assumed_wf_types);
+ let outlives_environment =
+ OutlivesEnvironment::with_bounds(param_env, Some(&infcx), implied_bounds);
+
+ infcx.check_region_obligations_and_report_errors(
+ impl_ty.def_id.expect_local(),
+ &outlives_environment,
+ );
+
+ let constraints = infcx.inner.borrow_mut().opaque_type_storage.take_opaque_types();
+ for (key, value) in constraints {
+ infcx
+ .err_ctxt()
+ .report_mismatched_types(
+ &ObligationCause::misc(
+ value.hidden_type.span,
+ tcx.hir().local_def_id_to_hir_id(impl_ty.def_id.expect_local()),
+ ),
+ tcx.mk_opaque(key.def_id.to_def_id(), key.substs),
+ value.hidden_type.ty,
+ TypeError::Mismatch,
+ )
+ .emit();
+ }
+
+ Ok(())
+}
+
+fn assoc_item_kind_str(impl_item: &ty::AssocItem) -> &'static str {
+ match impl_item.kind {
+ ty::AssocKind::Const => "const",
+ ty::AssocKind::Fn => "method",
+ ty::AssocKind::Type => "type",
+ }
+}
diff --git a/compiler/rustc_typeck/src/check/dropck.rs b/compiler/rustc_hir_analysis/src/check/dropck.rs
index 321064ec0..a74016e22 100644
--- a/compiler/rustc_typeck/src/check/dropck.rs
+++ b/compiler/rustc_hir_analysis/src/check/dropck.rs
@@ -1,4 +1,4 @@
-// FIXME(@lcnr): Move this module out of `rustc_typeck`.
+// FIXME(@lcnr): Move this module out of `rustc_hir_analysis`.
//
// We don't do any drop checking during hir typeck.
use crate::hir::def_id::{DefId, LocalDefId};
@@ -144,6 +144,8 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>(
let assumptions_in_impl_context = generic_assumptions.instantiate(tcx, &self_to_impl_substs);
let assumptions_in_impl_context = assumptions_in_impl_context.predicates;
+ debug!(?assumptions_in_impl_context, ?dtor_predicates.predicates);
+
let self_param_env = tcx.param_env(self_type_did);
// An earlier version of this code attempted to do this checking
@@ -182,13 +184,7 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>(
let p = p.kind();
match (predicate.skip_binder(), p.skip_binder()) {
(ty::PredicateKind::Trait(a), ty::PredicateKind::Trait(b)) => {
- // Since struct predicates cannot have ~const, project the impl predicate
- // onto one that ignores the constness. This is equivalent to saying that
- // we match a `Trait` bound on the struct with a `Trait` or `~const Trait`
- // in the impl.
- let non_const_a =
- ty::TraitPredicate { constness: ty::BoundConstness::NotConst, ..a };
- relator.relate(predicate.rebind(non_const_a), p.rebind(b)).is_ok()
+ relator.relate(predicate.rebind(a), p.rebind(b)).is_ok()
}
(ty::PredicateKind::Projection(a), ty::PredicateKind::Projection(b)) => {
relator.relate(predicate.rebind(a), p.rebind(b)).is_ok()
@@ -196,7 +192,7 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>(
(
ty::PredicateKind::ConstEvaluatable(a),
ty::PredicateKind::ConstEvaluatable(b),
- ) => tcx.try_unify_abstract_consts(self_param_env.and((a, b))),
+ ) => relator.relate(predicate.rebind(a), predicate.rebind(b)).is_ok(),
(
ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(ty_a, lt_a)),
ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(ty_b, lt_b)),
diff --git a/compiler/rustc_typeck/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs
index 3f2a0da8d..609095c9c 100644
--- a/compiler/rustc_typeck/src/check/intrinsic.rs
+++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs
@@ -7,10 +7,10 @@ use crate::errors::{
};
use crate::require_same_types;
-use rustc_errors::struct_span_err;
+use hir::def_id::DefId;
+use rustc_errors::{struct_span_err, DiagnosticMessage};
use rustc_hir as hir;
use rustc_middle::traits::{ObligationCause, ObligationCauseCode};
-use rustc_middle::ty::subst::Subst;
use rustc_middle::ty::{self, TyCtxt};
use rustc_span::symbol::{kw, sym, Symbol};
use rustc_target::spec::abi::Abi;
@@ -26,7 +26,7 @@ fn equate_intrinsic_type<'tcx>(
) {
let (own_counts, span) = match &it.kind {
hir::ForeignItemKind::Fn(.., generics) => {
- let own_counts = tcx.generics_of(it.def_id.to_def_id()).own_counts();
+ let own_counts = tcx.generics_of(it.owner_id.to_def_id()).own_counts();
(own_counts, generics.span)
}
_ => {
@@ -57,18 +57,25 @@ fn equate_intrinsic_type<'tcx>(
{
let fty = tcx.mk_fn_ptr(sig);
let cause = ObligationCause::new(it.span, it.hir_id(), ObligationCauseCode::IntrinsicType);
- require_same_types(tcx, &cause, tcx.mk_fn_ptr(tcx.fn_sig(it.def_id)), fty);
+ require_same_types(tcx, &cause, tcx.mk_fn_ptr(tcx.fn_sig(it.owner_id)), fty);
}
}
/// Returns the unsafety of the given intrinsic.
-pub fn intrinsic_operation_unsafety(intrinsic: Symbol) -> hir::Unsafety {
- match intrinsic {
+pub fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: DefId) -> hir::Unsafety {
+ let has_safe_attr = match tcx.has_attr(intrinsic_id, sym::rustc_safe_intrinsic) {
+ true => hir::Unsafety::Normal,
+ false => hir::Unsafety::Unsafe,
+ };
+ let is_in_list = match tcx.item_name(intrinsic_id) {
// When adding a new intrinsic to this list,
// it's usually worth updating that intrinsic's documentation
// to note that it's safe to call, since
// safe extern fns are otherwise unprecedented.
sym::abort
+ | sym::assert_inhabited
+ | sym::assert_zero_valid
+ | sym::assert_uninit_valid
| sym::size_of
| sym::min_align_of
| sym::needs_drop
@@ -92,8 +99,7 @@ pub fn intrinsic_operation_unsafety(intrinsic: Symbol) -> hir::Unsafety {
| sym::type_id
| sym::likely
| sym::unlikely
- | sym::ptr_guaranteed_eq
- | sym::ptr_guaranteed_ne
+ | sym::ptr_guaranteed_cmp
| sym::minnumf32
| sym::minnumf64
| sym::maxnumf32
@@ -102,16 +108,29 @@ pub fn intrinsic_operation_unsafety(intrinsic: Symbol) -> hir::Unsafety {
| sym::type_name
| sym::forget
| sym::black_box
- | sym::variant_count => hir::Unsafety::Normal,
+ | sym::variant_count
+ | sym::ptr_mask => hir::Unsafety::Normal,
_ => hir::Unsafety::Unsafe,
+ };
+
+ if has_safe_attr != is_in_list {
+ tcx.sess.struct_span_err(
+ tcx.def_span(intrinsic_id),
+ DiagnosticMessage::Str(format!(
+ "intrinsic safety mismatch between list of intrinsics within the compiler and core library intrinsics for intrinsic `{}`",
+ tcx.item_name(intrinsic_id)
+ ))).emit();
}
+
+ is_in_list
}
/// Remember to add all intrinsics here, in `compiler/rustc_codegen_llvm/src/intrinsic.rs`,
/// and in `library/core/src/intrinsics.rs`.
pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) {
let param = |n| tcx.mk_ty_param(n, Symbol::intern(&format!("P{}", n)));
- let intrinsic_name = tcx.item_name(it.def_id.to_def_id());
+ let intrinsic_id = it.owner_id.to_def_id();
+ let intrinsic_name = tcx.item_name(intrinsic_id);
let name_str = intrinsic_name.as_str();
let bound_vars = tcx.mk_bound_variable_kinds(
@@ -158,7 +177,7 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) {
};
(n_tps, 0, inputs, output, hir::Unsafety::Unsafe)
} else {
- let unsafety = intrinsic_operation_unsafety(intrinsic_name);
+ let unsafety = intrinsic_operation_unsafety(tcx, intrinsic_id);
let (n_tps, inputs, output) = match intrinsic_name {
sym::abort => (0, Vec::new(), tcx.types.never),
sym::unreachable => (0, Vec::new(), tcx.types.never),
@@ -200,6 +219,15 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) {
],
tcx.mk_ptr(ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Not }),
),
+ sym::ptr_mask => (
+ 1,
+ vec![
+ tcx.mk_ptr(ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Not }),
+ tcx.types.usize,
+ ],
+ tcx.mk_ptr(ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Not }),
+ ),
+
sym::copy | sym::copy_nonoverlapping => (
1,
vec![
@@ -289,8 +317,8 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) {
(1, vec![param(0), param(0)], tcx.intern_tup(&[param(0), tcx.types.bool]))
}
- sym::ptr_guaranteed_eq | sym::ptr_guaranteed_ne => {
- (1, vec![tcx.mk_imm_ptr(param(0)), tcx.mk_imm_ptr(param(0))], tcx.types.bool)
+ sym::ptr_guaranteed_cmp => {
+ (1, vec![tcx.mk_imm_ptr(param(0)), tcx.mk_imm_ptr(param(0))], tcx.types.u8)
}
sym::const_allocate => {
@@ -465,7 +493,11 @@ pub fn check_platform_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>)
sym::simd_scatter => (3, vec![param(0), param(1), param(2)], tcx.mk_unit()),
sym::simd_insert => (2, vec![param(0), tcx.types.u32, param(1)], param(0)),
sym::simd_extract => (2, vec![param(0), tcx.types.u32], param(1)),
- sym::simd_cast | sym::simd_as => (2, vec![param(0)], param(1)),
+ sym::simd_cast
+ | sym::simd_as
+ | sym::simd_cast_ptr
+ | sym::simd_expose_addr
+ | sym::simd_from_exposed_addr => (2, vec![param(0)], param(1)),
sym::simd_bitmask => (2, vec![param(0)], param(1)),
sym::simd_select | sym::simd_select_bitmask => {
(2, vec![param(0), param(1), param(1)], param(1))
diff --git a/compiler/rustc_hir_analysis/src/check/intrinsicck.rs b/compiler/rustc_hir_analysis/src/check/intrinsicck.rs
new file mode 100644
index 000000000..17c4d0d48
--- /dev/null
+++ b/compiler/rustc_hir_analysis/src/check/intrinsicck.rs
@@ -0,0 +1,437 @@
+use rustc_ast::InlineAsmTemplatePiece;
+use rustc_data_structures::fx::FxHashSet;
+use rustc_hir as hir;
+use rustc_middle::ty::{self, Article, FloatTy, IntTy, Ty, TyCtxt, TypeVisitable, UintTy};
+use rustc_session::lint;
+use rustc_span::{Symbol, DUMMY_SP};
+use rustc_target::asm::{InlineAsmReg, InlineAsmRegClass, InlineAsmRegOrRegClass, InlineAsmType};
+
+pub struct InlineAsmCtxt<'a, 'tcx> {
+ tcx: TyCtxt<'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+ get_operand_ty: Box<dyn Fn(&'tcx hir::Expr<'tcx>) -> Ty<'tcx> + 'a>,
+}
+
+impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
+ pub fn new_global_asm(tcx: TyCtxt<'tcx>) -> Self {
+ InlineAsmCtxt {
+ tcx,
+ param_env: ty::ParamEnv::empty(),
+ get_operand_ty: Box::new(|e| bug!("asm operand in global asm: {e:?}")),
+ }
+ }
+
+ pub fn new_in_fn(
+ tcx: TyCtxt<'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+ get_operand_ty: impl Fn(&'tcx hir::Expr<'tcx>) -> Ty<'tcx> + 'a,
+ ) -> Self {
+ InlineAsmCtxt { tcx, param_env, get_operand_ty: Box::new(get_operand_ty) }
+ }
+
+ // FIXME(compiler-errors): This could use `<$ty as Pointee>::Metadata == ()`
+ fn is_thin_ptr_ty(&self, ty: Ty<'tcx>) -> bool {
+ // Type still may have region variables, but `Sized` does not depend
+ // on those, so just erase them before querying.
+ if ty.is_sized(self.tcx, self.param_env) {
+ return true;
+ }
+ if let ty::Foreign(..) = ty.kind() {
+ return true;
+ }
+ false
+ }
+
+ fn check_asm_operand_type(
+ &self,
+ idx: usize,
+ reg: InlineAsmRegOrRegClass,
+ expr: &'tcx hir::Expr<'tcx>,
+ template: &[InlineAsmTemplatePiece],
+ is_input: bool,
+ tied_input: Option<(&'tcx hir::Expr<'tcx>, Option<InlineAsmType>)>,
+ target_features: &FxHashSet<Symbol>,
+ ) -> Option<InlineAsmType> {
+ let ty = (self.get_operand_ty)(expr);
+ if ty.has_non_region_infer() {
+ bug!("inference variable in asm operand ty: {:?} {:?}", expr, ty);
+ }
+ let asm_ty_isize = match self.tcx.sess.target.pointer_width {
+ 16 => InlineAsmType::I16,
+ 32 => InlineAsmType::I32,
+ 64 => InlineAsmType::I64,
+ _ => unreachable!(),
+ };
+
+ let asm_ty = match *ty.kind() {
+ // `!` is allowed for input but not for output (issue #87802)
+ ty::Never if is_input => return None,
+ ty::Error(_) => return None,
+ ty::Int(IntTy::I8) | ty::Uint(UintTy::U8) => Some(InlineAsmType::I8),
+ ty::Int(IntTy::I16) | ty::Uint(UintTy::U16) => Some(InlineAsmType::I16),
+ ty::Int(IntTy::I32) | ty::Uint(UintTy::U32) => Some(InlineAsmType::I32),
+ ty::Int(IntTy::I64) | ty::Uint(UintTy::U64) => Some(InlineAsmType::I64),
+ ty::Int(IntTy::I128) | ty::Uint(UintTy::U128) => Some(InlineAsmType::I128),
+ ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize) => Some(asm_ty_isize),
+ ty::Float(FloatTy::F32) => Some(InlineAsmType::F32),
+ ty::Float(FloatTy::F64) => Some(InlineAsmType::F64),
+ ty::FnPtr(_) => Some(asm_ty_isize),
+ ty::RawPtr(ty::TypeAndMut { ty, mutbl: _ }) if self.is_thin_ptr_ty(ty) => {
+ Some(asm_ty_isize)
+ }
+ ty::Adt(adt, substs) if adt.repr().simd() => {
+ let fields = &adt.non_enum_variant().fields;
+ let elem_ty = fields[0].ty(self.tcx, substs);
+ match elem_ty.kind() {
+ ty::Never | ty::Error(_) => return None,
+ ty::Int(IntTy::I8) | ty::Uint(UintTy::U8) => {
+ Some(InlineAsmType::VecI8(fields.len() as u64))
+ }
+ ty::Int(IntTy::I16) | ty::Uint(UintTy::U16) => {
+ Some(InlineAsmType::VecI16(fields.len() as u64))
+ }
+ ty::Int(IntTy::I32) | ty::Uint(UintTy::U32) => {
+ Some(InlineAsmType::VecI32(fields.len() as u64))
+ }
+ ty::Int(IntTy::I64) | ty::Uint(UintTy::U64) => {
+ Some(InlineAsmType::VecI64(fields.len() as u64))
+ }
+ ty::Int(IntTy::I128) | ty::Uint(UintTy::U128) => {
+ Some(InlineAsmType::VecI128(fields.len() as u64))
+ }
+ ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize) => {
+ Some(match self.tcx.sess.target.pointer_width {
+ 16 => InlineAsmType::VecI16(fields.len() as u64),
+ 32 => InlineAsmType::VecI32(fields.len() as u64),
+ 64 => InlineAsmType::VecI64(fields.len() as u64),
+ _ => unreachable!(),
+ })
+ }
+ ty::Float(FloatTy::F32) => Some(InlineAsmType::VecF32(fields.len() as u64)),
+ ty::Float(FloatTy::F64) => Some(InlineAsmType::VecF64(fields.len() as u64)),
+ _ => None,
+ }
+ }
+ ty::Infer(_) => unreachable!(),
+ _ => None,
+ };
+ let Some(asm_ty) = asm_ty else {
+ let msg = &format!("cannot use value of type `{ty}` for inline assembly");
+ let mut err = self.tcx.sess.struct_span_err(expr.span, msg);
+ err.note(
+ "only integers, floats, SIMD vectors, pointers and function pointers \
+ can be used as arguments for inline assembly",
+ );
+ err.emit();
+ return None;
+ };
+
+ // Check that the type implements Copy. The only case where this can
+ // possibly fail is for SIMD types which don't #[derive(Copy)].
+ if !ty.is_copy_modulo_regions(self.tcx, self.param_env) {
+ let msg = "arguments for inline assembly must be copyable";
+ let mut err = self.tcx.sess.struct_span_err(expr.span, msg);
+ err.note(&format!("`{ty}` does not implement the Copy trait"));
+ err.emit();
+ }
+
+ // Ideally we wouldn't need to do this, but LLVM's register allocator
+ // really doesn't like it when tied operands have different types.
+ //
+ // This is purely an LLVM limitation, but we have to live with it since
+ // there is no way to hide this with implicit conversions.
+ //
+ // For the purposes of this check we only look at the `InlineAsmType`,
+ // which means that pointers and integers are treated as identical (modulo
+ // size).
+ if let Some((in_expr, Some(in_asm_ty))) = tied_input {
+ if in_asm_ty != asm_ty {
+ let msg = "incompatible types for asm inout argument";
+ let mut err = self.tcx.sess.struct_span_err(vec![in_expr.span, expr.span], msg);
+
+ let in_expr_ty = (self.get_operand_ty)(in_expr);
+ err.span_label(in_expr.span, &format!("type `{in_expr_ty}`"));
+ err.span_label(expr.span, &format!("type `{ty}`"));
+ err.note(
+ "asm inout arguments must have the same type, \
+ unless they are both pointers or integers of the same size",
+ );
+ err.emit();
+ }
+
+ // All of the later checks have already been done on the input, so
+ // let's not emit errors and warnings twice.
+ return Some(asm_ty);
+ }
+
+ // Check the type against the list of types supported by the selected
+ // register class.
+ let asm_arch = self.tcx.sess.asm_arch.unwrap();
+ let reg_class = reg.reg_class();
+ let supported_tys = reg_class.supported_types(asm_arch);
+ let Some((_, feature)) = supported_tys.iter().find(|&&(t, _)| t == asm_ty) else {
+ let msg = &format!("type `{ty}` cannot be used with this register class");
+ let mut err = self.tcx.sess.struct_span_err(expr.span, msg);
+ let supported_tys: Vec<_> =
+ supported_tys.iter().map(|(t, _)| t.to_string()).collect();
+ err.note(&format!(
+ "register class `{}` supports these types: {}",
+ reg_class.name(),
+ supported_tys.join(", "),
+ ));
+ if let Some(suggest) = reg_class.suggest_class(asm_arch, asm_ty) {
+ err.help(&format!(
+ "consider using the `{}` register class instead",
+ suggest.name()
+ ));
+ }
+ err.emit();
+ return Some(asm_ty);
+ };
+
+ // Check whether the selected type requires a target feature. Note that
+ // this is different from the feature check we did earlier. While the
+ // previous check checked that this register class is usable at all
+ // with the currently enabled features, some types may only be usable
+ // with a register class when a certain feature is enabled. We check
+ // this here since it depends on the results of typeck.
+ //
+ // Also note that this check isn't run when the operand type is never
+ // (!). In that case we still need the earlier check to verify that the
+ // register class is usable at all.
+ if let Some(feature) = feature {
+ if !target_features.contains(&feature) {
+ let msg = &format!("`{}` target feature is not enabled", feature);
+ let mut err = self.tcx.sess.struct_span_err(expr.span, msg);
+ err.note(&format!(
+ "this is required to use type `{}` with register class `{}`",
+ ty,
+ reg_class.name(),
+ ));
+ err.emit();
+ return Some(asm_ty);
+ }
+ }
+
+ // Check whether a modifier is suggested for using this type.
+ if let Some((suggested_modifier, suggested_result)) =
+ reg_class.suggest_modifier(asm_arch, asm_ty)
+ {
+ // Search for any use of this operand without a modifier and emit
+ // the suggestion for them.
+ let mut spans = vec![];
+ for piece in template {
+ if let &InlineAsmTemplatePiece::Placeholder { operand_idx, modifier, span } = piece
+ {
+ if operand_idx == idx && modifier.is_none() {
+ spans.push(span);
+ }
+ }
+ }
+ if !spans.is_empty() {
+ let (default_modifier, default_result) =
+ reg_class.default_modifier(asm_arch).unwrap();
+ self.tcx.struct_span_lint_hir(
+ lint::builtin::ASM_SUB_REGISTER,
+ expr.hir_id,
+ spans,
+ "formatting may not be suitable for sub-register argument",
+ |lint| {
+ lint.span_label(expr.span, "for this argument");
+ lint.help(&format!(
+ "use `{{{idx}:{suggested_modifier}}}` to have the register formatted as `{suggested_result}`",
+ ));
+ lint.help(&format!(
+ "or use `{{{idx}:{default_modifier}}}` to keep the default formatting of `{default_result}`",
+ ));
+ lint
+ },
+ );
+ }
+ }
+
+ Some(asm_ty)
+ }
+
+ pub fn check_asm(&self, asm: &hir::InlineAsm<'tcx>, enclosing_id: hir::HirId) {
+ let hir = self.tcx.hir();
+ let enclosing_def_id = hir.local_def_id(enclosing_id).to_def_id();
+ let target_features = self.tcx.asm_target_features(enclosing_def_id);
+ let Some(asm_arch) = self.tcx.sess.asm_arch else {
+ self.tcx.sess.delay_span_bug(DUMMY_SP, "target architecture does not support asm");
+ return;
+ };
+ for (idx, (op, op_sp)) in asm.operands.iter().enumerate() {
+ // Validate register classes against currently enabled target
+ // features. We check that at least one type is available for
+ // the enabled features.
+ //
+ // We ignore target feature requirements for clobbers: if the
+ // feature is disabled then the compiler doesn't care what we
+ // do with the registers.
+ //
+ // Note that this is only possible for explicit register
+ // operands, which cannot be used in the asm string.
+ if let Some(reg) = op.reg() {
+ // Some explicit registers cannot be used depending on the
+ // target. Reject those here.
+ if let InlineAsmRegOrRegClass::Reg(reg) = reg {
+ if let InlineAsmReg::Err = reg {
+ // `validate` will panic on `Err`, as an error must
+ // already have been reported.
+ continue;
+ }
+ if let Err(msg) = reg.validate(
+ asm_arch,
+ self.tcx.sess.relocation_model(),
+ &target_features,
+ &self.tcx.sess.target,
+ op.is_clobber(),
+ ) {
+ let msg = format!("cannot use register `{}`: {}", reg.name(), msg);
+ self.tcx.sess.struct_span_err(*op_sp, &msg).emit();
+ continue;
+ }
+ }
+
+ if !op.is_clobber() {
+ let mut missing_required_features = vec![];
+ let reg_class = reg.reg_class();
+ if let InlineAsmRegClass::Err = reg_class {
+ continue;
+ }
+ for &(_, feature) in reg_class.supported_types(asm_arch) {
+ match feature {
+ Some(feature) => {
+ if target_features.contains(&feature) {
+ missing_required_features.clear();
+ break;
+ } else {
+ missing_required_features.push(feature);
+ }
+ }
+ None => {
+ missing_required_features.clear();
+ break;
+ }
+ }
+ }
+
+ // We are sorting primitive strs here and can use unstable sort here
+ missing_required_features.sort_unstable();
+ missing_required_features.dedup();
+ match &missing_required_features[..] {
+ [] => {}
+ [feature] => {
+ let msg = format!(
+ "register class `{}` requires the `{}` target feature",
+ reg_class.name(),
+ feature
+ );
+ self.tcx.sess.struct_span_err(*op_sp, &msg).emit();
+ // register isn't enabled, don't do more checks
+ continue;
+ }
+ features => {
+ let msg = format!(
+ "register class `{}` requires at least one of the following target features: {}",
+ reg_class.name(),
+ features
+ .iter()
+ .map(|f| f.as_str())
+ .intersperse(", ")
+ .collect::<String>(),
+ );
+ self.tcx.sess.struct_span_err(*op_sp, &msg).emit();
+ // register isn't enabled, don't do more checks
+ continue;
+ }
+ }
+ }
+ }
+
+ match *op {
+ hir::InlineAsmOperand::In { reg, ref expr } => {
+ self.check_asm_operand_type(
+ idx,
+ reg,
+ expr,
+ asm.template,
+ true,
+ None,
+ &target_features,
+ );
+ }
+ hir::InlineAsmOperand::Out { reg, late: _, ref expr } => {
+ if let Some(expr) = expr {
+ self.check_asm_operand_type(
+ idx,
+ reg,
+ expr,
+ asm.template,
+ false,
+ None,
+ &target_features,
+ );
+ }
+ }
+ hir::InlineAsmOperand::InOut { reg, late: _, ref expr } => {
+ self.check_asm_operand_type(
+ idx,
+ reg,
+ expr,
+ asm.template,
+ false,
+ None,
+ &target_features,
+ );
+ }
+ hir::InlineAsmOperand::SplitInOut { reg, late: _, ref in_expr, ref out_expr } => {
+ let in_ty = self.check_asm_operand_type(
+ idx,
+ reg,
+ in_expr,
+ asm.template,
+ true,
+ None,
+ &target_features,
+ );
+ if let Some(out_expr) = out_expr {
+ self.check_asm_operand_type(
+ idx,
+ reg,
+ out_expr,
+ asm.template,
+ false,
+ Some((in_expr, in_ty)),
+ &target_features,
+ );
+ }
+ }
+ // No special checking is needed for these:
+ // - Typeck has checked that Const operands are integers.
+ // - AST lowering guarantees that SymStatic points to a static.
+ hir::InlineAsmOperand::Const { .. } | hir::InlineAsmOperand::SymStatic { .. } => {}
+ // Check that sym actually points to a function. Later passes
+ // depend on this.
+ hir::InlineAsmOperand::SymFn { anon_const } => {
+ let ty = self.tcx.typeck_body(anon_const.body).node_type(anon_const.hir_id);
+ match ty.kind() {
+ ty::Never | ty::Error(_) => {}
+ ty::FnDef(..) => {}
+ _ => {
+ let mut err =
+ self.tcx.sess.struct_span_err(*op_sp, "invalid `sym` operand");
+ err.span_label(
+ self.tcx.hir().span(anon_const.body.hir_id),
+ &format!("is {} `{}`", ty.kind().article(), ty),
+ );
+ err.help("`sym` operands must refer to either a function or a static");
+ err.emit();
+ }
+ };
+ }
+ }
+ }
+ }
+}
diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs
new file mode 100644
index 000000000..2e7b10257
--- /dev/null
+++ b/compiler/rustc_hir_analysis/src/check/mod.rs
@@ -0,0 +1,515 @@
+/*!
+
+# typeck: check phase
+
+Within the check phase of type check, we check each item one at a time
+(bodies of function expressions are checked as part of the containing
+function). Inference is used to supply types wherever they are unknown.
+
+By far the most complex case is checking the body of a function. This
+can be broken down into several distinct phases:
+
+- gather: creates type variables to represent the type of each local
+ variable and pattern binding.
+
+- main: the main pass does the lion's share of the work: it
+ determines the types of all expressions, resolves
+ methods, checks for most invalid conditions, and so forth. In
+ some cases, where a type is unknown, it may create a type or region
+ variable and use that as the type of an expression.
+
+ In the process of checking, various constraints will be placed on
+ these type variables through the subtyping relationships requested
+ through the `demand` module. The `infer` module is in charge
+ of resolving those constraints.
+
+- regionck: after main is complete, the regionck pass goes over all
+ types looking for regions and making sure that they did not escape
+ into places where they are not in scope. This may also influence the
+ final assignments of the various region variables if there is some
+ flexibility.
+
+- writeback: writes the final types within a function body, replacing
+ type variables with their final inferred types. These final types
+ are written into the `tcx.node_types` table, which should *never* contain
+ any reference to a type variable.
+
+## Intermediate types
+
+While type checking a function, the intermediate types for the
+expressions, blocks, and so forth contained within the function are
+stored in `fcx.node_types` and `fcx.node_substs`. These types
+may contain unresolved type variables. After type checking is
+complete, the functions in the writeback module are used to take the
+types from this table, resolve them, and then write them into their
+permanent home in the type context `tcx`.
+
+This means that during inferencing you should use `fcx.write_ty()`
+and `fcx.expr_ty()` / `fcx.node_ty()` to write/obtain the types of
+nodes within the function.
+
+The types of top-level items, which never contain unbound type
+variables, are stored directly into the `tcx` typeck_results.
+
+N.B., a type variable is not the same thing as a type parameter. A
+type variable is an instance of a type parameter. That is,
+given a generic function `fn foo<T>(t: T)`, while checking the
+function `foo`, the type `ty_param(0)` refers to the type `T`, which
+is treated in abstract. However, when `foo()` is called, `T` will be
+substituted for a fresh type variable `N`. This variable will
+eventually be resolved to some concrete type (which might itself be
+a type parameter).
+
+*/
+
+mod check;
+mod compare_method;
+pub mod dropck;
+pub mod intrinsic;
+pub mod intrinsicck;
+mod region;
+pub mod wfcheck;
+
+pub use check::check_abi;
+
+use check::check_mod_item_types;
+use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_errors::{pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder};
+use rustc_hir as hir;
+use rustc_hir::def_id::{DefId, LocalDefId};
+use rustc_hir::intravisit::Visitor;
+use rustc_index::bit_set::BitSet;
+use rustc_middle::ty::query::Providers;
+use rustc_middle::ty::{self, Ty, TyCtxt};
+use rustc_middle::ty::{InternalSubsts, SubstsRef};
+use rustc_session::parse::feature_err;
+use rustc_span::source_map::DUMMY_SP;
+use rustc_span::symbol::{kw, Ident};
+use rustc_span::{self, BytePos, Span, Symbol};
+use rustc_target::abi::VariantIdx;
+use rustc_target::spec::abi::Abi;
+use rustc_trait_selection::traits::error_reporting::suggestions::ReturnsVisitor;
+use std::num::NonZeroU32;
+
+use crate::require_c_abi_if_c_variadic;
+use crate::util::common::indenter;
+
+use self::compare_method::collect_trait_impl_trait_tys;
+use self::region::region_scope_tree;
+
+pub fn provide(providers: &mut Providers) {
+ wfcheck::provide(providers);
+ *providers = Providers {
+ adt_destructor,
+ check_mod_item_types,
+ region_scope_tree,
+ collect_trait_impl_trait_tys,
+ compare_assoc_const_impl_item_with_trait_item: compare_method::raw_compare_const_impl,
+ ..*providers
+ };
+}
+
+fn adt_destructor(tcx: TyCtxt<'_>, def_id: DefId) -> Option<ty::Destructor> {
+ tcx.calculate_dtor(def_id, dropck::check_drop_impl)
+}
+
+/// Given a `DefId` for an opaque type in return position, find its parent item's return
+/// expressions.
+fn get_owner_return_paths<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ def_id: LocalDefId,
+) -> Option<(LocalDefId, ReturnsVisitor<'tcx>)> {
+ let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
+ let parent_id = tcx.hir().get_parent_item(hir_id).def_id;
+ tcx.hir().find_by_def_id(parent_id).and_then(|node| node.body_id()).map(|body_id| {
+ let body = tcx.hir().body(body_id);
+ let mut visitor = ReturnsVisitor::default();
+ visitor.visit_body(body);
+ (parent_id, visitor)
+ })
+}
+
+/// Forbid defining intrinsics in Rust code,
+/// as they must always be defined by the compiler.
+// FIXME: Move this to a more appropriate place.
+pub fn fn_maybe_err(tcx: TyCtxt<'_>, sp: Span, abi: Abi) {
+ if let Abi::RustIntrinsic | Abi::PlatformIntrinsic = abi {
+ tcx.sess.span_err(sp, "intrinsic must be in `extern \"rust-intrinsic\" { ... }` block");
+ }
+}
+
+fn maybe_check_static_with_link_section(tcx: TyCtxt<'_>, id: LocalDefId) {
+ // Only restricted on wasm target for now
+ if !tcx.sess.target.is_like_wasm {
+ return;
+ }
+
+ // If `#[link_section]` is missing, then nothing to verify
+ let attrs = tcx.codegen_fn_attrs(id);
+ if attrs.link_section.is_none() {
+ return;
+ }
+
+ // For the wasm32 target statics with `#[link_section]` are placed into custom
+ // sections of the final output file, but this isn't link custom sections of
+ // other executable formats. Namely we can only embed a list of bytes,
+ // nothing with provenance (pointers to anything else). If any provenance
+ // show up, reject it here.
+ // `#[link_section]` may contain arbitrary, or even undefined bytes, but it is
+ // the consumer's responsibility to ensure all bytes that have been read
+ // have defined values.
+ if let Ok(alloc) = tcx.eval_static_initializer(id.to_def_id())
+ && alloc.inner().provenance().len() != 0
+ {
+ let msg = "statics with a custom `#[link_section]` must be a \
+ simple list of bytes on the wasm target with no \
+ extra levels of indirection such as references";
+ tcx.sess.span_err(tcx.def_span(id), msg);
+ }
+}
+
+fn report_forbidden_specialization(
+ tcx: TyCtxt<'_>,
+ impl_item: &hir::ImplItemRef,
+ parent_impl: DefId,
+) {
+ let mut err = struct_span_err!(
+ tcx.sess,
+ impl_item.span,
+ E0520,
+ "`{}` specializes an item from a parent `impl`, but \
+ that item is not marked `default`",
+ impl_item.ident
+ );
+ err.span_label(impl_item.span, format!("cannot specialize default item `{}`", impl_item.ident));
+
+ match tcx.span_of_impl(parent_impl) {
+ Ok(span) => {
+ err.span_label(span, "parent `impl` is here");
+ err.note(&format!(
+ "to specialize, `{}` in the parent `impl` must be marked `default`",
+ impl_item.ident
+ ));
+ }
+ Err(cname) => {
+ err.note(&format!("parent implementation is in crate `{cname}`"));
+ }
+ }
+
+ err.emit();
+}
+
+fn missing_items_err(
+ tcx: TyCtxt<'_>,
+ impl_span: Span,
+ missing_items: &[&ty::AssocItem],
+ full_impl_span: Span,
+) {
+ let missing_items_msg = missing_items
+ .iter()
+ .map(|trait_item| trait_item.name.to_string())
+ .collect::<Vec<_>>()
+ .join("`, `");
+
+ let mut err = struct_span_err!(
+ tcx.sess,
+ impl_span,
+ E0046,
+ "not all trait items implemented, missing: `{missing_items_msg}`",
+ );
+ err.span_label(impl_span, format!("missing `{missing_items_msg}` in implementation"));
+
+ // `Span` before impl block closing brace.
+ let hi = full_impl_span.hi() - BytePos(1);
+ // Point at the place right before the closing brace of the relevant `impl` to suggest
+ // adding the associated item at the end of its body.
+ let sugg_sp = full_impl_span.with_lo(hi).with_hi(hi);
+ // Obtain the level of indentation ending in `sugg_sp`.
+ let padding =
+ tcx.sess.source_map().indentation_before(sugg_sp).unwrap_or_else(|| String::new());
+
+ for trait_item in missing_items {
+ let snippet = suggestion_signature(trait_item, tcx);
+ let code = format!("{}{}\n{}", padding, snippet, padding);
+ let msg = format!("implement the missing item: `{snippet}`");
+ let appl = Applicability::HasPlaceholders;
+ if let Some(span) = tcx.hir().span_if_local(trait_item.def_id) {
+ err.span_label(span, format!("`{}` from trait", trait_item.name));
+ err.tool_only_span_suggestion(sugg_sp, &msg, code, appl);
+ } else {
+ err.span_suggestion_hidden(sugg_sp, &msg, code, appl);
+ }
+ }
+ err.emit();
+}
+
+fn missing_items_must_implement_one_of_err(
+ tcx: TyCtxt<'_>,
+ impl_span: Span,
+ missing_items: &[Ident],
+ annotation_span: Option<Span>,
+) {
+ let missing_items_msg =
+ missing_items.iter().map(Ident::to_string).collect::<Vec<_>>().join("`, `");
+
+ let mut err = struct_span_err!(
+ tcx.sess,
+ impl_span,
+ E0046,
+ "not all trait items implemented, missing one of: `{missing_items_msg}`",
+ );
+ err.span_label(impl_span, format!("missing one of `{missing_items_msg}` in implementation"));
+
+ if let Some(annotation_span) = annotation_span {
+ err.span_note(annotation_span, "required because of this annotation");
+ }
+
+ err.emit();
+}
+
+fn default_body_is_unstable(
+ tcx: TyCtxt<'_>,
+ impl_span: Span,
+ item_did: DefId,
+ feature: Symbol,
+ reason: Option<Symbol>,
+ issue: Option<NonZeroU32>,
+) {
+ let missing_item_name = &tcx.associated_item(item_did).name;
+ let use_of_unstable_library_feature_note = match reason {
+ Some(r) => format!("use of unstable library feature '{feature}': {r}"),
+ None => format!("use of unstable library feature '{feature}'"),
+ };
+
+ let mut err = struct_span_err!(
+ tcx.sess,
+ impl_span,
+ E0046,
+ "not all trait items implemented, missing: `{missing_item_name}`",
+ );
+ err.note(format!("default implementation of `{missing_item_name}` is unstable"));
+ err.note(use_of_unstable_library_feature_note);
+ rustc_session::parse::add_feature_diagnostics_for_issue(
+ &mut err,
+ &tcx.sess.parse_sess,
+ feature,
+ rustc_feature::GateIssue::Library(issue),
+ );
+ err.emit();
+}
+
+/// Re-sugar `ty::GenericPredicates` in a way suitable to be used in structured suggestions.
+fn bounds_from_generic_predicates<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ predicates: ty::GenericPredicates<'tcx>,
+) -> (String, String) {
+ let mut types: FxHashMap<Ty<'tcx>, Vec<DefId>> = FxHashMap::default();
+ let mut projections = vec![];
+ for (predicate, _) in predicates.predicates {
+ debug!("predicate {:?}", predicate);
+ let bound_predicate = predicate.kind();
+ match bound_predicate.skip_binder() {
+ ty::PredicateKind::Trait(trait_predicate) => {
+ let entry = types.entry(trait_predicate.self_ty()).or_default();
+ let def_id = trait_predicate.def_id();
+ if Some(def_id) != tcx.lang_items().sized_trait() {
+ // Type params are `Sized` by default, do not add that restriction to the list
+ // if it is a positive requirement.
+ entry.push(trait_predicate.def_id());
+ }
+ }
+ ty::PredicateKind::Projection(projection_pred) => {
+ projections.push(bound_predicate.rebind(projection_pred));
+ }
+ _ => {}
+ }
+ }
+ let generics = if types.is_empty() {
+ "".to_string()
+ } else {
+ format!(
+ "<{}>",
+ types
+ .keys()
+ .filter_map(|t| match t.kind() {
+ ty::Param(_) => Some(t.to_string()),
+ // Avoid suggesting the following:
+ // fn foo<T, <T as Trait>::Bar>(_: T) where T: Trait, <T as Trait>::Bar: Other {}
+ _ => None,
+ })
+ .collect::<Vec<_>>()
+ .join(", ")
+ )
+ };
+ let mut where_clauses = vec![];
+ for (ty, bounds) in types {
+ where_clauses
+ .extend(bounds.into_iter().map(|bound| format!("{}: {}", ty, tcx.def_path_str(bound))));
+ }
+ for projection in &projections {
+ let p = projection.skip_binder();
+ // FIXME: this is not currently supported syntax, we should be looking at the `types` and
+ // insert the associated types where they correspond, but for now let's be "lazy" and
+ // propose this instead of the following valid resugaring:
+ // `T: Trait, Trait::Assoc = K` → `T: Trait<Assoc = K>`
+ where_clauses.push(format!(
+ "{} = {}",
+ tcx.def_path_str(p.projection_ty.item_def_id),
+ p.term,
+ ));
+ }
+ let where_clauses = if where_clauses.is_empty() {
+ String::new()
+ } else {
+ format!(" where {}", where_clauses.join(", "))
+ };
+ (generics, where_clauses)
+}
+
+/// Return placeholder code for the given function.
+fn fn_sig_suggestion<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ sig: ty::FnSig<'tcx>,
+ ident: Ident,
+ predicates: ty::GenericPredicates<'tcx>,
+ assoc: &ty::AssocItem,
+) -> String {
+ let args = sig
+ .inputs()
+ .iter()
+ .enumerate()
+ .map(|(i, ty)| {
+ Some(match ty.kind() {
+ ty::Param(_) if assoc.fn_has_self_parameter && i == 0 => "self".to_string(),
+ ty::Ref(reg, ref_ty, mutability) if i == 0 => {
+ let reg = format!("{reg} ");
+ let reg = match &reg[..] {
+ "'_ " | " " => "",
+ reg => reg,
+ };
+ if assoc.fn_has_self_parameter {
+ match ref_ty.kind() {
+ ty::Param(param) if param.name == kw::SelfUpper => {
+ format!("&{}{}self", reg, mutability.prefix_str())
+ }
+
+ _ => format!("self: {ty}"),
+ }
+ } else {
+ format!("_: {ty}")
+ }
+ }
+ _ => {
+ if assoc.fn_has_self_parameter && i == 0 {
+ format!("self: {ty}")
+ } else {
+ format!("_: {ty}")
+ }
+ }
+ })
+ })
+ .chain(std::iter::once(if sig.c_variadic { Some("...".to_string()) } else { None }))
+ .flatten()
+ .collect::<Vec<String>>()
+ .join(", ");
+ let output = sig.output();
+ let output = if !output.is_unit() { format!(" -> {output}") } else { String::new() };
+
+ let unsafety = sig.unsafety.prefix_str();
+ let (generics, where_clauses) = bounds_from_generic_predicates(tcx, predicates);
+
+ // FIXME: this is not entirely correct, as the lifetimes from borrowed params will
+ // not be present in the `fn` definition, not will we account for renamed
+ // lifetimes between the `impl` and the `trait`, but this should be good enough to
+ // fill in a significant portion of the missing code, and other subsequent
+ // suggestions can help the user fix the code.
+ format!("{unsafety}fn {ident}{generics}({args}){output}{where_clauses} {{ todo!() }}")
+}
+
+pub fn ty_kind_suggestion(ty: Ty<'_>) -> Option<&'static str> {
+ Some(match ty.kind() {
+ ty::Bool => "true",
+ ty::Char => "'a'",
+ ty::Int(_) | ty::Uint(_) => "42",
+ ty::Float(_) => "3.14159",
+ ty::Error(_) | ty::Never => return None,
+ _ => "value",
+ })
+}
+
+/// Return placeholder code for the given associated item.
+/// Similar to `ty::AssocItem::suggestion`, but appropriate for use as the code snippet of a
+/// structured suggestion.
+fn suggestion_signature(assoc: &ty::AssocItem, tcx: TyCtxt<'_>) -> String {
+ match assoc.kind {
+ ty::AssocKind::Fn => {
+ // We skip the binder here because the binder would deanonymize all
+ // late-bound regions, and we don't want method signatures to show up
+ // `as for<'r> fn(&'r MyType)`. Pretty-printing handles late-bound
+ // regions just fine, showing `fn(&MyType)`.
+ fn_sig_suggestion(
+ tcx,
+ tcx.fn_sig(assoc.def_id).skip_binder(),
+ assoc.ident(tcx),
+ tcx.predicates_of(assoc.def_id),
+ assoc,
+ )
+ }
+ ty::AssocKind::Type => format!("type {} = Type;", assoc.name),
+ ty::AssocKind::Const => {
+ let ty = tcx.type_of(assoc.def_id);
+ let val = ty_kind_suggestion(ty).unwrap_or("value");
+ format!("const {}: {} = {};", assoc.name, ty, val)
+ }
+ }
+}
+
+/// Emit an error when encountering two or more variants in a transparent enum.
+fn bad_variant_count<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>, sp: Span, did: DefId) {
+ let variant_spans: Vec<_> = adt
+ .variants()
+ .iter()
+ .map(|variant| tcx.hir().span_if_local(variant.def_id).unwrap())
+ .collect();
+ let msg = format!("needs exactly one variant, but has {}", adt.variants().len(),);
+ let mut err = struct_span_err!(tcx.sess, sp, E0731, "transparent enum {msg}");
+ err.span_label(sp, &msg);
+ if let [start @ .., end] = &*variant_spans {
+ for variant_span in start {
+ err.span_label(*variant_span, "");
+ }
+ err.span_label(*end, &format!("too many variants in `{}`", tcx.def_path_str(did)));
+ }
+ err.emit();
+}
+
+/// Emit an error when encountering two or more non-zero-sized fields in a transparent
+/// enum.
+fn bad_non_zero_sized_fields<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ adt: ty::AdtDef<'tcx>,
+ field_count: usize,
+ field_spans: impl Iterator<Item = Span>,
+ sp: Span,
+) {
+ let msg = format!("needs at most one non-zero-sized field, but has {field_count}");
+ let mut err = struct_span_err!(
+ tcx.sess,
+ sp,
+ E0690,
+ "{}transparent {} {}",
+ if adt.is_enum() { "the variant of a " } else { "" },
+ adt.descr(),
+ msg,
+ );
+ err.span_label(sp, &msg);
+ for sp in field_spans {
+ err.span_label(sp, "this field is non-zero-sized");
+ }
+ err.emit();
+}
+
+// FIXME: Consider moving this method to a more fitting place.
+pub fn potentially_plural_count(count: usize, word: &str) -> String {
+ format!("{} {}{}", count, word, pluralize!(count))
+}
diff --git a/compiler/rustc_typeck/src/check/region.rs b/compiler/rustc_hir_analysis/src/check/region.rs
index 0081e9049..ff32329e4 100644
--- a/compiler/rustc_typeck/src/check/region.rs
+++ b/compiler/rustc_hir_analysis/src/check/region.rs
@@ -126,6 +126,29 @@ fn resolve_block<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, blk: &'tcx h
for (i, statement) in blk.stmts.iter().enumerate() {
match statement.kind {
+ hir::StmtKind::Local(hir::Local { els: Some(els), .. }) => {
+ // Let-else has a special lexical structure for variables.
+ // First we take a checkpoint of the current scope context here.
+ let mut prev_cx = visitor.cx;
+
+ visitor.enter_scope(Scope {
+ id: blk.hir_id.local_id,
+ data: ScopeData::Remainder(FirstStatementIndex::new(i)),
+ });
+ visitor.cx.var_parent = visitor.cx.parent;
+ visitor.visit_stmt(statement);
+ // We need to back out temporarily to the last enclosing scope
+ // for the `else` block, so that even the temporaries receiving
+ // extended lifetime will be dropped inside this block.
+ // We are visiting the `else` block in this order so that
+ // the sequence of visits agree with the order in the default
+ // `hir::intravisit` visitor.
+ mem::swap(&mut prev_cx, &mut visitor.cx);
+ visitor.terminating_scopes.insert(els.hir_id.local_id);
+ visitor.visit_block(els);
+ // From now on, we continue normally.
+ visitor.cx = prev_cx;
+ }
hir::StmtKind::Local(..) | hir::StmtKind::Item(..) => {
// Each declaration introduces a subscope for bindings
// introduced by the declaration; this subscope covers a
@@ -138,10 +161,10 @@ fn resolve_block<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, blk: &'tcx h
data: ScopeData::Remainder(FirstStatementIndex::new(i)),
});
visitor.cx.var_parent = visitor.cx.parent;
+ visitor.visit_stmt(statement)
}
- hir::StmtKind::Expr(..) | hir::StmtKind::Semi(..) => {}
+ hir::StmtKind::Expr(..) | hir::StmtKind::Semi(..) => visitor.visit_stmt(statement),
}
- visitor.visit_stmt(statement)
}
walk_list!(visitor, visit_expr, &blk.expr);
}
@@ -229,9 +252,13 @@ fn resolve_expr<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, expr: &'tcx h
) => {
// For shortcircuiting operators, mark the RHS as a terminating
// scope since it only executes conditionally.
- terminating(r.hir_id.local_id);
- }
+ // `Let` expressions (in a let-chain) shouldn't be terminating, as their temporaries
+ // should live beyond the immediate expression
+ if !matches!(r.kind, hir::ExprKind::Let(_)) {
+ terminating(r.hir_id.local_id);
+ }
+ }
hir::ExprKind::If(_, ref then, Some(ref otherwise)) => {
terminating(then.hir_id.local_id);
terminating(otherwise.hir_id.local_id);
@@ -460,7 +487,6 @@ fn resolve_local<'tcx>(
visitor: &mut RegionResolutionVisitor<'tcx>,
pat: Option<&'tcx hir::Pat<'tcx>>,
init: Option<&'tcx hir::Expr<'tcx>>,
- els: Option<&'tcx hir::Block<'tcx>>,
) {
debug!("resolve_local(pat={:?}, init={:?})", pat, init);
@@ -547,9 +573,6 @@ fn resolve_local<'tcx>(
if let Some(pat) = pat {
visitor.visit_pat(pat);
}
- if let Some(els) = els {
- visitor.visit_block(els);
- }
/// Returns `true` if `pat` match the `P&` non-terminal.
///
@@ -587,8 +610,7 @@ fn resolve_local<'tcx>(
// & expression, and its lifetime would be extended to the end of the block (due
// to a different rule, not the below code).
match pat.kind {
- PatKind::Binding(hir::BindingAnnotation::Ref, ..)
- | PatKind::Binding(hir::BindingAnnotation::RefMut, ..) => true,
+ PatKind::Binding(hir::BindingAnnotation(hir::ByRef::Yes, _), ..) => true,
PatKind::Struct(_, ref field_pats, _) => {
field_pats.iter().any(|fp| is_binding_pat(&fp.pat))
@@ -607,10 +629,7 @@ fn resolve_local<'tcx>(
PatKind::Box(ref subpat) => is_binding_pat(&subpat),
PatKind::Ref(_, _)
- | PatKind::Binding(
- hir::BindingAnnotation::Unannotated | hir::BindingAnnotation::Mutable,
- ..,
- )
+ | PatKind::Binding(hir::BindingAnnotation(hir::ByRef::No, _), ..)
| PatKind::Wild
| PatKind::Path(_)
| PatKind::Lit(_)
@@ -770,7 +789,7 @@ impl<'tcx> Visitor<'tcx> for RegionResolutionVisitor<'tcx> {
// (i.e., `'static`), which means that after `g` returns, it drops,
// and all the associated destruction scope rules apply.
self.cx.var_parent = None;
- resolve_local(self, None, Some(&body.value), None);
+ resolve_local(self, None, Some(&body.value));
}
if body.generator_kind.is_some() {
@@ -797,7 +816,7 @@ impl<'tcx> Visitor<'tcx> for RegionResolutionVisitor<'tcx> {
resolve_expr(self, ex);
}
fn visit_local(&mut self, l: &'tcx Local<'tcx>) {
- resolve_local(self, Some(&l.pat), l.init, l.els)
+ resolve_local(self, Some(&l.pat), l.init)
}
}
diff --git a/compiler/rustc_typeck/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
index d0334cd0d..a23575004 100644
--- a/compiler/rustc_typeck/src/check/wfcheck.rs
+++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
@@ -1,5 +1,5 @@
-use crate::check::regionck::OutlivesEnvironmentExt;
use crate::constrained_generic_params::{identify_constrained_generic_params, Parameter};
+use hir::def::DefKind;
use rustc_ast as ast;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed};
@@ -10,22 +10,21 @@ use rustc_hir::ItemKind;
use rustc_infer::infer::outlives::env::{OutlivesEnvironment, RegionBoundPairs};
use rustc_infer::infer::outlives::obligations::TypeOutlives;
use rustc_infer::infer::{self, InferCtxt, TyCtxtInferExt};
-use rustc_infer::traits::Normalized;
+use rustc_middle::mir::ConstraintCategory;
use rustc_middle::ty::query::Providers;
-use rustc_middle::ty::subst::{GenericArgKind, InternalSubsts, Subst};
use rustc_middle::ty::trait_def::TraitSpecializationKind;
use rustc_middle::ty::{
self, AdtKind, DefIdTree, GenericParamDefKind, ToPredicate, Ty, TyCtxt, TypeFoldable,
TypeSuperVisitable, TypeVisitable, TypeVisitor,
};
+use rustc_middle::ty::{GenericArgKind, InternalSubsts};
use rustc_session::parse::feature_err;
use rustc_span::symbol::{sym, Ident, Symbol};
use rustc_span::{Span, DUMMY_SP};
use rustc_trait_selection::autoderef::Autoderef;
-use rustc_trait_selection::traits::error_reporting::InferCtxtExt;
+use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt;
+use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _;
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
-use rustc_trait_selection::traits::query::normalize::AtExt;
-use rustc_trait_selection::traits::query::NoSolution;
use rustc_trait_selection::traits::{
self, ObligationCause, ObligationCauseCode, ObligationCtxt, WellFormedLoc,
};
@@ -72,9 +71,11 @@ impl<'tcx> WfCheckingCtxt<'_, 'tcx> {
) {
let cause =
traits::ObligationCause::new(span, self.body_id, ObligationCauseCode::WellFormed(loc));
+ // for a type to be WF, we do not need to check if const trait predicates satisfy.
+ let param_env = self.param_env.without_const();
self.ocx.register_obligation(traits::Obligation::new(
cause,
- self.param_env,
+ param_env,
ty::Binder::dummy(ty::PredicateKind::WellFormed(arg)).to_predicate(self.tcx()),
));
}
@@ -86,31 +87,35 @@ pub(super) fn enter_wf_checking_ctxt<'tcx, F>(
body_def_id: LocalDefId,
f: F,
) where
- F: for<'a> FnOnce(&WfCheckingCtxt<'a, 'tcx>) -> FxHashSet<Ty<'tcx>>,
+ F: for<'a> FnOnce(&WfCheckingCtxt<'a, 'tcx>),
{
let param_env = tcx.param_env(body_def_id);
let body_id = tcx.hir().local_def_id_to_hir_id(body_def_id);
- tcx.infer_ctxt().enter(|ref infcx| {
- let ocx = ObligationCtxt::new(infcx);
- let mut wfcx = WfCheckingCtxt { ocx, span, body_id, param_env };
+ let infcx = &tcx.infer_ctxt().build();
+ let ocx = ObligationCtxt::new(infcx);
- if !tcx.features().trivial_bounds {
- wfcx.check_false_global_bounds()
- }
- let wf_tys = f(&mut wfcx);
- let errors = wfcx.select_all_or_error();
- if !errors.is_empty() {
- infcx.report_fulfillment_errors(&errors, None, false);
- return;
- }
+ let assumed_wf_types = ocx.assumed_wf_types(param_env, span, body_def_id);
- let mut outlives_environment = OutlivesEnvironment::new(param_env);
- outlives_environment.add_implied_bounds(infcx, wf_tys, body_id);
- infcx.check_region_obligations_and_report_errors(body_def_id, &outlives_environment);
- })
+ let mut wfcx = WfCheckingCtxt { ocx, span, body_id, param_env };
+
+ if !tcx.features().trivial_bounds {
+ wfcx.check_false_global_bounds()
+ }
+ f(&mut wfcx);
+ let errors = wfcx.select_all_or_error();
+ if !errors.is_empty() {
+ infcx.err_ctxt().report_fulfillment_errors(&errors, None, false);
+ return;
+ }
+
+ let implied_bounds = infcx.implied_bounds_tys(param_env, body_id, assumed_wf_types);
+ let outlives_environment =
+ OutlivesEnvironment::with_bounds(param_env, Some(infcx), implied_bounds);
+
+ infcx.check_region_obligations_and_report_errors(body_def_id, &outlives_environment);
}
-fn check_well_formed(tcx: TyCtxt<'_>, def_id: LocalDefId) {
+fn check_well_formed(tcx: TyCtxt<'_>, def_id: hir::OwnerId) {
let node = tcx.hir().expect_owner(def_id);
match node {
hir::OwnerNode::Crate(_) => {}
@@ -142,10 +147,10 @@ fn check_well_formed(tcx: TyCtxt<'_>, def_id: LocalDefId) {
/// the types first.
#[instrument(skip(tcx), level = "debug")]
fn check_item<'tcx>(tcx: TyCtxt<'tcx>, item: &'tcx hir::Item<'tcx>) {
- let def_id = item.def_id;
+ let def_id = item.owner_id.def_id;
debug!(
- ?item.def_id,
+ ?item.owner_id,
item.name = ? tcx.def_path_str(def_id.to_def_id())
);
@@ -169,7 +174,7 @@ fn check_item<'tcx>(tcx: TyCtxt<'tcx>, item: &'tcx hir::Item<'tcx>) {
// for `T`
hir::ItemKind::Impl(ref impl_) => {
let is_auto = tcx
- .impl_trait_ref(item.def_id)
+ .impl_trait_ref(def_id)
.map_or(false, |trait_ref| tcx.trait_is_auto(trait_ref.def_id));
if let (hir::Defaultness::Default { .. }, true) = (impl_.defaultness, is_auto) {
let sp = impl_.of_trait.as_ref().map_or(item.span, |t| t.path.span);
@@ -205,13 +210,13 @@ fn check_item<'tcx>(tcx: TyCtxt<'tcx>, item: &'tcx hir::Item<'tcx>) {
}
}
hir::ItemKind::Fn(ref sig, ..) => {
- check_item_fn(tcx, item.def_id, item.ident, item.span, sig.decl);
+ check_item_fn(tcx, def_id, item.ident, item.span, sig.decl);
}
hir::ItemKind::Static(ty, ..) => {
- check_item_type(tcx, item.def_id, ty.span, false);
+ check_item_type(tcx, def_id, ty.span, false);
}
hir::ItemKind::Const(ty, ..) => {
- check_item_type(tcx, item.def_id, ty.span, false);
+ check_item_type(tcx, def_id, ty.span, false);
}
hir::ItemKind::Struct(ref struct_def, ref ast_generics) => {
check_type_defn(tcx, item, false, |wfcx| vec![wfcx.non_enum_variant(struct_def)]);
@@ -241,24 +246,24 @@ fn check_item<'tcx>(tcx: TyCtxt<'tcx>, item: &'tcx hir::Item<'tcx>) {
}
fn check_foreign_item(tcx: TyCtxt<'_>, item: &hir::ForeignItem<'_>) {
- let def_id = item.def_id;
+ let def_id = item.owner_id.def_id;
debug!(
- ?item.def_id,
+ ?item.owner_id,
item.name = ? tcx.def_path_str(def_id.to_def_id())
);
match item.kind {
hir::ForeignItemKind::Fn(decl, ..) => {
- check_item_fn(tcx, item.def_id, item.ident, item.span, decl)
+ check_item_fn(tcx, def_id, item.ident, item.span, decl)
}
- hir::ForeignItemKind::Static(ty, ..) => check_item_type(tcx, item.def_id, ty.span, true),
+ hir::ForeignItemKind::Static(ty, ..) => check_item_type(tcx, def_id, ty.span, true),
hir::ForeignItemKind::Type => (),
}
}
fn check_trait_item(tcx: TyCtxt<'_>, trait_item: &hir::TraitItem<'_>) {
- let def_id = trait_item.def_id;
+ let def_id = trait_item.owner_id.def_id;
let (method_sig, span) = match trait_item.kind {
hir::TraitItemKind::Fn(ref sig, _) => (Some(sig), trait_item.span),
@@ -266,11 +271,11 @@ fn check_trait_item(tcx: TyCtxt<'_>, trait_item: &hir::TraitItem<'_>) {
_ => (None, trait_item.span),
};
check_object_unsafe_self_trait_by_name(tcx, trait_item);
- check_associated_item(tcx, trait_item.def_id, span, method_sig);
+ check_associated_item(tcx, def_id, span, method_sig);
let encl_trait_def_id = tcx.local_parent(def_id);
let encl_trait = tcx.hir().expect_item(encl_trait_def_id);
- let encl_trait_def_id = encl_trait.def_id.to_def_id();
+ let encl_trait_def_id = encl_trait.owner_id.to_def_id();
let fn_lang_item_name = if Some(encl_trait_def_id) == tcx.lang_items().fn_trait() {
Some("fn")
} else if Some(encl_trait_def_id) == tcx.lang_items().fn_mut_trait() {
@@ -343,7 +348,7 @@ fn check_gat_where_clauses(tcx: TyCtxt<'_>, associated_items: &[hir::TraitItemRe
loop {
let mut should_continue = false;
for gat_item in associated_items {
- let gat_def_id = gat_item.id.def_id;
+ let gat_def_id = gat_item.id.owner_id;
let gat_item = tcx.associated_item(gat_def_id);
// If this item is not an assoc ty, or has no substs, then it's not a GAT
if gat_item.kind != ty::AssocKind::Type {
@@ -360,7 +365,7 @@ fn check_gat_where_clauses(tcx: TyCtxt<'_>, associated_items: &[hir::TraitItemRe
// constrains the GAT with individually.
let mut new_required_bounds: Option<FxHashSet<ty::Predicate<'_>>> = None;
for item in associated_items {
- let item_def_id = item.id.def_id;
+ let item_def_id = item.id.owner_id;
// Skip our own GAT, since it does not constrain itself at all.
if item_def_id == gat_def_id {
continue;
@@ -383,11 +388,11 @@ fn check_gat_where_clauses(tcx: TyCtxt<'_>, associated_items: &[hir::TraitItemRe
tcx,
param_env,
item_hir_id,
- sig.output(),
+ sig.inputs_and_output,
// We also assume that all of the function signature's parameter types
// are well formed.
&sig.inputs().iter().copied().collect(),
- gat_def_id,
+ gat_def_id.def_id,
gat_generics,
)
}
@@ -410,7 +415,7 @@ fn check_gat_where_clauses(tcx: TyCtxt<'_>, associated_items: &[hir::TraitItemRe
.copied()
.collect::<Vec<_>>(),
&FxHashSet::default(),
- gat_def_id,
+ gat_def_id.def_id,
gat_generics,
)
}
@@ -450,7 +455,7 @@ fn check_gat_where_clauses(tcx: TyCtxt<'_>, associated_items: &[hir::TraitItemRe
}
for (gat_def_id, required_bounds) in required_bounds_by_item {
- let gat_item_hir = tcx.hir().expect_trait_item(gat_def_id);
+ let gat_item_hir = tcx.hir().expect_trait_item(gat_def_id.def_id);
debug!(?required_bounds);
let param_env = tcx.param_env(gat_def_id);
let gat_hir = gat_item_hir.hir_id();
@@ -658,7 +663,7 @@ fn ty_known_to_outlive<'tcx>(
resolve_regions_with_wf_tys(tcx, id, param_env, &wf_tys, |infcx, region_bound_pairs| {
let origin = infer::RelateParamBound(DUMMY_SP, ty, None);
let outlives = &mut TypeOutlives::new(infcx, tcx, region_bound_pairs, None, param_env);
- outlives.type_must_outlive(origin, ty, region);
+ outlives.type_must_outlive(origin, ty, region, ConstraintCategory::BoringNoLocation);
})
}
@@ -676,7 +681,12 @@ fn region_known_to_outlive<'tcx>(
use rustc_infer::infer::outlives::obligations::TypeOutlivesDelegate;
let origin = infer::RelateRegionParamBound(DUMMY_SP);
// `region_a: region_b` -> `region_b <= region_a`
- infcx.push_sub_region_constraint(origin, region_b, region_a);
+ infcx.push_sub_region_constraint(
+ origin,
+ region_b,
+ region_a,
+ ConstraintCategory::BoringNoLocation,
+ );
})
}
@@ -688,26 +698,32 @@ fn resolve_regions_with_wf_tys<'tcx>(
id: hir::HirId,
param_env: ty::ParamEnv<'tcx>,
wf_tys: &FxHashSet<Ty<'tcx>>,
- add_constraints: impl for<'a> FnOnce(&'a InferCtxt<'a, 'tcx>, &'a RegionBoundPairs<'tcx>),
+ add_constraints: impl for<'a> FnOnce(&'a InferCtxt<'tcx>, &'a RegionBoundPairs<'tcx>),
) -> bool {
// Unfortunately, we have to use a new `InferCtxt` each call, because
// region constraints get added and solved there and we need to test each
// call individually.
- tcx.infer_ctxt().enter(|infcx| {
- let mut outlives_environment = OutlivesEnvironment::new(param_env);
- outlives_environment.add_implied_bounds(&infcx, wf_tys.clone(), id);
- let region_bound_pairs = outlives_environment.region_bound_pairs();
+ let infcx = tcx.infer_ctxt().build();
+ let outlives_environment = OutlivesEnvironment::with_bounds(
+ param_env,
+ Some(&infcx),
+ infcx.implied_bounds_tys(param_env, id, wf_tys.clone()),
+ );
+ let region_bound_pairs = outlives_environment.region_bound_pairs();
- add_constraints(&infcx, region_bound_pairs);
+ add_constraints(&infcx, region_bound_pairs);
- let errors = infcx.resolve_regions(&outlives_environment);
+ infcx.process_registered_region_obligations(
+ outlives_environment.region_bound_pairs(),
+ param_env,
+ );
+ let errors = infcx.resolve_regions(&outlives_environment);
- debug!(?errors, "errors");
+ debug!(?errors, "errors");
- // If we were able to prove that the type outlives the region without
- // an error, it must be because of the implied or explicit bounds...
- errors.is_empty()
- })
+ // If we were able to prove that the type outlives the region without
+ // an error, it must be because of the implied or explicit bounds...
+ errors.is_empty()
}
/// TypeVisitor that looks for uses of GATs like
@@ -761,7 +777,7 @@ impl<'tcx> TypeVisitor<'tcx> for GATSubstCollector<'tcx> {
fn could_be_self(trait_def_id: LocalDefId, ty: &hir::Ty<'_>) -> bool {
match ty.kind {
hir::TyKind::TraitObject([trait_ref], ..) => match trait_ref.trait_ref.path.segments {
- [s] => s.res.and_then(|r| r.opt_def_id()) == Some(trait_def_id.to_def_id()),
+ [s] => s.res.opt_def_id() == Some(trait_def_id.to_def_id()),
_ => false,
},
_ => false,
@@ -772,9 +788,9 @@ fn could_be_self(trait_def_id: LocalDefId, ty: &hir::Ty<'_>) -> bool {
/// When this is done, suggest using `Self` instead.
fn check_object_unsafe_self_trait_by_name(tcx: TyCtxt<'_>, item: &hir::TraitItem<'_>) {
let (trait_name, trait_def_id) =
- match tcx.hir().get_by_def_id(tcx.hir().get_parent_item(item.hir_id())) {
+ match tcx.hir().get_by_def_id(tcx.hir().get_parent_item(item.hir_id()).def_id) {
hir::Node::Item(item) => match item.kind {
- hir::ItemKind::Trait(..) => (item.ident, item.def_id),
+ hir::ItemKind::Trait(..) => (item.ident, item.owner_id),
_ => return,
},
_ => return,
@@ -782,18 +798,18 @@ fn check_object_unsafe_self_trait_by_name(tcx: TyCtxt<'_>, item: &hir::TraitItem
let mut trait_should_be_self = vec![];
match &item.kind {
hir::TraitItemKind::Const(ty, _) | hir::TraitItemKind::Type(_, Some(ty))
- if could_be_self(trait_def_id, ty) =>
+ if could_be_self(trait_def_id.def_id, ty) =>
{
trait_should_be_self.push(ty.span)
}
hir::TraitItemKind::Fn(sig, _) => {
for ty in sig.decl.inputs {
- if could_be_self(trait_def_id, ty) {
+ if could_be_self(trait_def_id.def_id, ty) {
trait_should_be_self.push(ty.span);
}
}
match sig.decl.output {
- hir::FnRetTy::Return(ty) if could_be_self(trait_def_id, ty) => {
+ hir::FnRetTy::Return(ty) if could_be_self(trait_def_id.def_id, ty) => {
trait_should_be_self.push(ty.span);
}
_ => {}
@@ -822,16 +838,14 @@ fn check_object_unsafe_self_trait_by_name(tcx: TyCtxt<'_>, item: &hir::TraitItem
}
fn check_impl_item(tcx: TyCtxt<'_>, impl_item: &hir::ImplItem<'_>) {
- let def_id = impl_item.def_id;
-
let (method_sig, span) = match impl_item.kind {
hir::ImplItemKind::Fn(ref sig, _) => (Some(sig), impl_item.span),
// Constrain binding and overflow error spans to `<Ty>` in `type foo = <Ty>`.
- hir::ImplItemKind::TyAlias(ty) if ty.span != DUMMY_SP => (None, ty.span),
+ hir::ImplItemKind::Type(ty) if ty.span != DUMMY_SP => (None, ty.span),
_ => (None, impl_item.span),
};
- check_associated_item(tcx, def_id, span, method_sig);
+ check_associated_item(tcx, impl_item.owner_id.def_id, span, method_sig);
}
fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) {
@@ -965,7 +979,7 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) {
}
}
-#[tracing::instrument(level = "debug", skip(tcx, span, sig_if_method))]
+#[instrument(level = "debug", skip(tcx, span, sig_if_method))]
fn check_associated_item(
tcx: TyCtxt<'_>,
item_id: LocalDefId,
@@ -976,15 +990,9 @@ fn check_associated_item(
enter_wf_checking_ctxt(tcx, span, item_id, |wfcx| {
let item = tcx.associated_item(item_id);
- let (mut implied_bounds, self_ty) = match item.container {
- ty::TraitContainer => (FxHashSet::default(), tcx.types.self_param),
- ty::ImplContainer => {
- let def_id = item.container_id(tcx);
- (
- impl_implied_bounds(tcx, wfcx.param_env, def_id.expect_local(), span),
- tcx.type_of(def_id),
- )
- }
+ let self_ty = match item.container {
+ ty::TraitContainer => tcx.types.self_param,
+ ty::ImplContainer => tcx.type_of(item.container_id(tcx)),
};
match item.kind {
@@ -1002,7 +1010,6 @@ fn check_associated_item(
sig,
hir_sig.decl,
item.def_id.expect_local(),
- &mut implied_bounds,
);
check_method_receiver(wfcx, hir_sig, item, self_ty);
}
@@ -1017,8 +1024,6 @@ fn check_associated_item(
}
}
}
-
- implied_bounds
})
}
@@ -1040,9 +1045,11 @@ fn check_type_defn<'tcx, F>(
) where
F: FnMut(&WfCheckingCtxt<'_, 'tcx>) -> Vec<AdtVariant<'tcx>>,
{
- enter_wf_checking_ctxt(tcx, item.span, item.def_id, |wfcx| {
+ let _ = tcx.representability(item.owner_id.def_id);
+
+ enter_wf_checking_ctxt(tcx, item.span, item.owner_id.def_id, |wfcx| {
let variants = lookup_fields(wfcx);
- let packed = tcx.adt_def(item.def_id).repr().packed();
+ let packed = tcx.adt_def(item.owner_id).repr().packed();
for variant in &variants {
// All field types must be well-formed.
@@ -1066,7 +1073,7 @@ fn check_type_defn<'tcx, F>(
// Just treat unresolved type expression as if it needs drop.
true
} else {
- ty.needs_drop(tcx, tcx.param_env(item.def_id))
+ ty.needs_drop(tcx, tcx.param_env(item.owner_id))
}
}
};
@@ -1098,8 +1105,6 @@ fn check_type_defn<'tcx, F>(
// Explicit `enum` discriminant values must const-evaluate successfully.
if let Some(discr_def_id) = variant.explicit_discr {
- let discr_substs = InternalSubsts::identity_for_item(tcx, discr_def_id.to_def_id());
-
let cause = traits::ObligationCause::new(
tcx.def_span(discr_def_id),
wfcx.body_id,
@@ -1108,31 +1113,28 @@ fn check_type_defn<'tcx, F>(
wfcx.register_obligation(traits::Obligation::new(
cause,
wfcx.param_env,
- ty::Binder::dummy(ty::PredicateKind::ConstEvaluatable(ty::Unevaluated::new(
- ty::WithOptConstParam::unknown(discr_def_id.to_def_id()),
- discr_substs,
- )))
+ ty::Binder::dummy(ty::PredicateKind::ConstEvaluatable(
+ ty::Const::from_anon_const(tcx, discr_def_id),
+ ))
.to_predicate(tcx),
));
}
}
- check_where_clauses(wfcx, item.span, item.def_id);
-
- // No implied bounds in a struct definition.
- FxHashSet::default()
+ check_where_clauses(wfcx, item.span, item.owner_id.def_id);
});
}
#[instrument(skip(tcx, item))]
fn check_trait(tcx: TyCtxt<'_>, item: &hir::Item<'_>) {
- debug!(?item.def_id);
+ debug!(?item.owner_id);
- let trait_def = tcx.trait_def(item.def_id);
+ let def_id = item.owner_id.def_id;
+ let trait_def = tcx.trait_def(def_id);
if trait_def.is_marker
|| matches!(trait_def.specialization_kind, TraitSpecializationKind::Marker)
{
- for associated_def_id in &*tcx.associated_item_def_ids(item.def_id) {
+ for associated_def_id in &*tcx.associated_item_def_ids(def_id) {
struct_span_err!(
tcx.sess,
tcx.def_span(*associated_def_id),
@@ -1143,10 +1145,8 @@ fn check_trait(tcx: TyCtxt<'_>, item: &hir::Item<'_>) {
}
}
- enter_wf_checking_ctxt(tcx, item.span, item.def_id, |wfcx| {
- check_where_clauses(wfcx, item.span, item.def_id);
-
- FxHashSet::default()
+ enter_wf_checking_ctxt(tcx, item.span, def_id, |wfcx| {
+ check_where_clauses(wfcx, item.span, def_id)
});
// Only check traits, don't check trait aliases
@@ -1186,9 +1186,7 @@ fn check_item_fn(
) {
enter_wf_checking_ctxt(tcx, span, def_id, |wfcx| {
let sig = tcx.fn_sig(def_id);
- let mut implied_bounds = FxHashSet::default();
- check_fn_or_method(wfcx, ident.span, sig, decl, def_id, &mut implied_bounds);
- implied_bounds
+ check_fn_or_method(wfcx, ident.span, sig, decl, def_id);
})
}
@@ -1231,13 +1229,10 @@ fn check_item_type(tcx: TyCtxt<'_>, item_id: LocalDefId, ty_span: Span, allow_fo
tcx.require_lang_item(LangItem::Sync, Some(ty_span)),
);
}
-
- // No implied bounds in a const, etc.
- FxHashSet::default()
});
}
-#[tracing::instrument(level = "debug", skip(tcx, ast_self_ty, ast_trait_ref))]
+#[instrument(level = "debug", skip(tcx, ast_self_ty, ast_trait_ref))]
fn check_impl<'tcx>(
tcx: TyCtxt<'tcx>,
item: &'tcx hir::Item<'tcx>,
@@ -1245,13 +1240,13 @@ fn check_impl<'tcx>(
ast_trait_ref: &Option<hir::TraitRef<'_>>,
constness: hir::Constness,
) {
- enter_wf_checking_ctxt(tcx, item.span, item.def_id, |wfcx| {
+ enter_wf_checking_ctxt(tcx, item.span, item.owner_id.def_id, |wfcx| {
match *ast_trait_ref {
Some(ref ast_trait_ref) => {
// `#[rustc_reservation_impl]` impls are not real impls and
// therefore don't need to be WF (the trait's `Self: Trait` predicate
// won't hold).
- let trait_ref = tcx.impl_trait_ref(item.def_id).unwrap();
+ let trait_ref = tcx.impl_trait_ref(item.owner_id).unwrap();
let trait_ref = wfcx.normalize(ast_trait_ref.path.span, None, trait_ref);
let trait_pred = ty::TraitPredicate {
trait_ref,
@@ -1273,19 +1268,21 @@ fn check_impl<'tcx>(
wfcx.register_obligations(obligations);
}
None => {
- let self_ty = tcx.type_of(item.def_id);
- let self_ty = wfcx.normalize(item.span, None, self_ty);
+ let self_ty = tcx.type_of(item.owner_id);
+ let self_ty = wfcx.normalize(
+ item.span,
+ Some(WellFormedLoc::Ty(item.hir_id().expect_owner().def_id)),
+ self_ty,
+ );
wfcx.register_wf_obligation(
ast_self_ty.span,
- Some(WellFormedLoc::Ty(item.hir_id().expect_owner())),
+ Some(WellFormedLoc::Ty(item.hir_id().expect_owner().def_id)),
self_ty.into(),
);
}
}
- check_where_clauses(wfcx, item.span, item.def_id);
-
- impl_implied_bounds(tcx, wfcx.param_env, item.def_id, item.span)
+ check_where_clauses(wfcx, item.span, item.owner_id.def_id);
});
}
@@ -1321,7 +1318,11 @@ fn check_where_clauses<'tcx>(wfcx: &WfCheckingCtxt<'_, 'tcx>, span: Span, def_id
// parameter includes another (e.g., `<T, U = T>`). In those cases, we can't
// be sure if it will error or not as user might always specify the other.
if !ty.needs_subst() {
- wfcx.register_wf_obligation(tcx.def_span(param.def_id), None, ty.into());
+ wfcx.register_wf_obligation(
+ tcx.def_span(param.def_id),
+ Some(WellFormedLoc::Ty(param.def_id.expect_local())),
+ ty.into(),
+ );
}
}
}
@@ -1426,9 +1427,7 @@ fn check_where_clauses<'tcx>(wfcx: &WfCheckingCtxt<'_, 'tcx>, span: Span, def_id
let substituted_pred = predicates.rebind(pred).subst(tcx, substs);
// Don't check non-defaulted params, dependent defaults (including lifetimes)
// or preds with multiple params.
- if substituted_pred.has_param_types_or_consts()
- || param_count.params.len() > 1
- || has_region
+ if substituted_pred.has_non_region_param() || param_count.params.len() > 1 || has_region
{
None
} else if predicates.0.predicates.iter().any(|&(p, _)| p == substituted_pred) {
@@ -1465,21 +1464,26 @@ fn check_where_clauses<'tcx>(wfcx: &WfCheckingCtxt<'_, 'tcx>, span: Span, def_id
assert_eq!(predicates.predicates.len(), predicates.spans.len());
let wf_obligations =
iter::zip(&predicates.predicates, &predicates.spans).flat_map(|(&p, &sp)| {
- traits::wf::predicate_obligations(infcx, wfcx.param_env, wfcx.body_id, p, sp)
+ traits::wf::predicate_obligations(
+ infcx,
+ wfcx.param_env.without_const(),
+ wfcx.body_id,
+ p,
+ sp,
+ )
});
let obligations: Vec<_> = wf_obligations.chain(default_obligations).collect();
wfcx.register_obligations(obligations);
}
-#[tracing::instrument(level = "debug", skip(wfcx, span, hir_decl))]
+#[instrument(level = "debug", skip(wfcx, span, hir_decl))]
fn check_fn_or_method<'tcx>(
wfcx: &WfCheckingCtxt<'_, 'tcx>,
span: Span,
sig: ty::PolyFnSig<'tcx>,
hir_decl: &hir::FnDecl<'_>,
def_id: LocalDefId,
- implied_bounds: &mut FxHashSet<Ty<'tcx>>,
) {
let tcx = wfcx.tcx();
let sig = tcx.liberate_late_bound_regions(def_id.to_def_id(), sig);
@@ -1521,23 +1525,66 @@ fn check_fn_or_method<'tcx>(
);
}
- implied_bounds.extend(sig.inputs());
-
- wfcx.register_wf_obligation(hir_decl.output.span(), None, sig.output().into());
+ wfcx.register_wf_obligation(
+ hir_decl.output.span(),
+ Some(WellFormedLoc::Param {
+ function: def_id,
+ param_idx: sig.inputs().len().try_into().unwrap(),
+ }),
+ sig.output().into(),
+ );
- // FIXME(#27579) return types should not be implied bounds
- implied_bounds.insert(sig.output());
+ check_where_clauses(wfcx, span, def_id);
- debug!(?implied_bounds);
+ check_return_position_impl_trait_in_trait_bounds(
+ tcx,
+ wfcx,
+ def_id,
+ sig.output(),
+ hir_decl.output.span(),
+ );
+}
- check_where_clauses(wfcx, span, def_id);
+/// Basically `check_associated_type_bounds`, but separated for now and should be
+/// deduplicated when RPITITs get lowered into real associated items.
+fn check_return_position_impl_trait_in_trait_bounds<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ wfcx: &WfCheckingCtxt<'_, 'tcx>,
+ fn_def_id: LocalDefId,
+ fn_output: Ty<'tcx>,
+ span: Span,
+) {
+ if let Some(assoc_item) = tcx.opt_associated_item(fn_def_id.to_def_id())
+ && assoc_item.container == ty::AssocItemContainer::TraitContainer
+ {
+ for arg in fn_output.walk() {
+ if let ty::GenericArgKind::Type(ty) = arg.unpack()
+ && let ty::Projection(proj) = ty.kind()
+ && tcx.def_kind(proj.item_def_id) == DefKind::ImplTraitPlaceholder
+ && tcx.impl_trait_in_trait_parent(proj.item_def_id) == fn_def_id.to_def_id()
+ {
+ let bounds = wfcx.tcx().explicit_item_bounds(proj.item_def_id);
+ let wf_obligations = bounds.iter().flat_map(|&(bound, bound_span)| {
+ let normalized_bound = wfcx.normalize(span, None, bound);
+ traits::wf::predicate_obligations(
+ wfcx.infcx,
+ wfcx.param_env,
+ wfcx.body_id,
+ normalized_bound,
+ bound_span,
+ )
+ });
+ wfcx.register_obligations(wf_obligations);
+ }
+ }
+ }
}
const HELP_FOR_SELF_TYPE: &str = "consider changing to `self`, `&self`, `&mut self`, `self: Box<Self>`, \
`self: Rc<Self>`, `self: Arc<Self>`, or `self: Pin<P>` (where P is one \
of the previous types except `Self`)";
-#[tracing::instrument(level = "debug", skip(wfcx))]
+#[instrument(level = "debug", skip(wfcx))]
fn check_method_receiver<'tcx>(
wfcx: &WfCheckingCtxt<'_, 'tcx>,
fn_sig: &hir::FnSig<'_>,
@@ -1629,7 +1676,7 @@ fn receiver_is_valid<'tcx>(
// `self: Self` is always valid.
if can_eq_self(receiver_ty) {
if let Err(err) = wfcx.equate_types(&cause, wfcx.param_env, self_ty, receiver_ty) {
- infcx.report_mismatched_types(&cause, self_ty, receiver_ty, err).emit();
+ infcx.err_ctxt().report_mismatched_types(&cause, self_ty, receiver_ty, err).emit();
}
return true;
}
@@ -1661,7 +1708,10 @@ fn receiver_is_valid<'tcx>(
if let Err(err) =
wfcx.equate_types(&cause, wfcx.param_env, self_ty, potential_self_ty)
{
- infcx.report_mismatched_types(&cause, self_ty, potential_self_ty, err).emit();
+ infcx
+ .err_ctxt()
+ .report_mismatched_types(&cause, self_ty, potential_self_ty, err)
+ .emit();
}
break;
@@ -1728,14 +1778,14 @@ fn check_variances_for_type_defn<'tcx>(
item: &hir::Item<'tcx>,
hir_generics: &hir::Generics<'_>,
) {
- let ty = tcx.type_of(item.def_id);
+ let ty = tcx.type_of(item.owner_id);
if tcx.has_error_field(ty) {
return;
}
- let ty_predicates = tcx.predicates_of(item.def_id);
+ let ty_predicates = tcx.predicates_of(item.owner_id);
assert_eq!(ty_predicates.parent, None);
- let variances = tcx.variances_of(item.def_id);
+ let variances = tcx.variances_of(item.owner_id);
let mut constrained_parameters: FxHashSet<_> = variances
.iter()
@@ -1748,7 +1798,7 @@ fn check_variances_for_type_defn<'tcx>(
// Lazily calculated because it is only needed in case of an error.
let explicitly_bounded_params = LazyCell::new(|| {
- let icx = crate::collect::ItemCtxt::new(tcx, item.def_id.to_def_id());
+ let icx = crate::collect::ItemCtxt::new(tcx, item.owner_id.to_def_id());
hir_generics
.predicates
.iter()
@@ -1817,6 +1867,7 @@ fn report_bivariance(
impl<'tcx> WfCheckingCtxt<'_, 'tcx> {
/// Feature gates RFC 2056 -- trivial bounds, checking for global bounds that
/// aren't true.
+ #[instrument(level = "debug", skip(self))]
fn check_false_global_bounds(&mut self) {
let tcx = self.ocx.infcx.tcx;
let mut span = self.span;
@@ -1868,10 +1919,10 @@ impl<'tcx> WfCheckingCtxt<'_, 'tcx> {
fn check_mod_type_wf(tcx: TyCtxt<'_>, module: LocalDefId) {
let items = tcx.hir_module_items(module);
- items.par_items(|item| tcx.ensure().check_well_formed(item.def_id));
- items.par_impl_items(|item| tcx.ensure().check_well_formed(item.def_id));
- items.par_trait_items(|item| tcx.ensure().check_well_formed(item.def_id));
- items.par_foreign_items(|item| tcx.ensure().check_well_formed(item.def_id));
+ items.par_items(|item| tcx.ensure().check_well_formed(item.owner_id));
+ items.par_impl_items(|item| tcx.ensure().check_well_formed(item.owner_id));
+ items.par_trait_items(|item| tcx.ensure().check_well_formed(item.owner_id));
+ items.par_foreign_items(|item| tcx.ensure().check_well_formed(item.owner_id));
}
///////////////////////////////////////////////////////////////////////////
@@ -1924,40 +1975,6 @@ impl<'a, 'tcx> WfCheckingCtxt<'a, 'tcx> {
}
}
-pub fn impl_implied_bounds<'tcx>(
- tcx: TyCtxt<'tcx>,
- param_env: ty::ParamEnv<'tcx>,
- impl_def_id: LocalDefId,
- span: Span,
-) -> FxHashSet<Ty<'tcx>> {
- // We completely ignore any obligations caused by normalizing the types
- // we assume to be well formed. Considering that the user of the implied
- // bounds will also normalize them, we leave it to them to emit errors
- // which should result in better causes and spans.
- tcx.infer_ctxt().enter(|infcx| {
- let cause = ObligationCause::misc(span, tcx.hir().local_def_id_to_hir_id(impl_def_id));
- match tcx.impl_trait_ref(impl_def_id) {
- Some(trait_ref) => {
- // Trait impl: take implied bounds from all types that
- // appear in the trait reference.
- match infcx.at(&cause, param_env).normalize(trait_ref) {
- Ok(Normalized { value, obligations: _ }) => value.substs.types().collect(),
- Err(NoSolution) => FxHashSet::default(),
- }
- }
-
- None => {
- // Inherent impl: take implied bounds from the `self` type.
- let self_ty = tcx.type_of(impl_def_id);
- match infcx.at(&cause, param_env).normalize(self_ty) {
- Ok(Normalized { value, obligations: _ }) => FxHashSet::from_iter([value]),
- Err(NoSolution) => FxHashSet::default(),
- }
- }
- }
- })
-}
-
fn error_392(
tcx: TyCtxt<'_>,
span: Span,
diff --git a/compiler/rustc_hir_analysis/src/check_unused.rs b/compiler/rustc_hir_analysis/src/check_unused.rs
new file mode 100644
index 000000000..d0c317334
--- /dev/null
+++ b/compiler/rustc_hir_analysis/src/check_unused.rs
@@ -0,0 +1,192 @@
+use crate::errors::{ExternCrateNotIdiomatic, UnusedExternCrate};
+use rustc_data_structures::fx::FxHashMap;
+use rustc_data_structures::unord::UnordSet;
+use rustc_hir as hir;
+use rustc_hir::def::DefKind;
+use rustc_hir::def_id::{DefId, LocalDefId};
+use rustc_middle::ty::TyCtxt;
+use rustc_session::lint;
+use rustc_span::{Span, Symbol};
+
+pub fn check_crate(tcx: TyCtxt<'_>) {
+ let mut used_trait_imports: UnordSet<LocalDefId> = Default::default();
+
+ for item_def_id in tcx.hir().body_owners() {
+ let imports = tcx.used_trait_imports(item_def_id);
+ debug!("GatherVisitor: item_def_id={:?} with imports {:#?}", item_def_id, imports);
+ used_trait_imports.extend(imports.items().copied());
+ }
+
+ for &id in tcx.maybe_unused_trait_imports(()) {
+ debug_assert_eq!(tcx.def_kind(id), DefKind::Use);
+ if tcx.visibility(id).is_public() {
+ continue;
+ }
+ if used_trait_imports.contains(&id) {
+ continue;
+ }
+ let item = tcx.hir().expect_item(id);
+ if item.span.is_dummy() {
+ continue;
+ }
+ let hir::ItemKind::Use(path, _) = item.kind else { unreachable!() };
+ let msg = if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(path.span) {
+ format!("unused import: `{}`", snippet)
+ } else {
+ "unused import".to_owned()
+ };
+ tcx.struct_span_lint_hir(
+ lint::builtin::UNUSED_IMPORTS,
+ item.hir_id(),
+ path.span,
+ msg,
+ |lint| lint,
+ );
+ }
+
+ unused_crates_lint(tcx);
+}
+
+fn unused_crates_lint(tcx: TyCtxt<'_>) {
+ let lint = lint::builtin::UNUSED_EXTERN_CRATES;
+
+ // Collect first the crates that are completely unused. These we
+ // can always suggest removing (no matter which edition we are
+ // in).
+ let unused_extern_crates: FxHashMap<LocalDefId, Span> = tcx
+ .maybe_unused_extern_crates(())
+ .iter()
+ .filter(|&&(def_id, _)| {
+ // The `def_id` here actually was calculated during resolution (at least
+ // at the time of this writing) and is being shipped to us via a side
+ // channel of the tcx. There may have been extra expansion phases,
+ // however, which ended up removing the `def_id` *after* expansion.
+ //
+ // As a result we need to verify that `def_id` is indeed still valid for
+ // our AST and actually present in the HIR map. If it's not there then
+ // there's safely nothing to warn about, and otherwise we carry on with
+ // our execution.
+ //
+ // Note that if we carry through to the `extern_mod_stmt_cnum` query
+ // below it'll cause a panic because `def_id` is actually bogus at this
+ // point in time otherwise.
+ if tcx.hir().find(tcx.hir().local_def_id_to_hir_id(def_id)).is_none() {
+ return false;
+ }
+ true
+ })
+ .filter(|&&(def_id, _)| {
+ tcx.extern_mod_stmt_cnum(def_id).map_or(true, |cnum| {
+ !tcx.is_compiler_builtins(cnum)
+ && !tcx.is_panic_runtime(cnum)
+ && !tcx.has_global_allocator(cnum)
+ && !tcx.has_panic_handler(cnum)
+ })
+ })
+ .cloned()
+ .collect();
+
+ // Collect all the extern crates (in a reliable order).
+ let mut crates_to_lint = vec![];
+
+ for id in tcx.hir().items() {
+ if matches!(tcx.def_kind(id.owner_id), DefKind::ExternCrate) {
+ let item = tcx.hir().item(id);
+ if let hir::ItemKind::ExternCrate(orig_name) = item.kind {
+ crates_to_lint.push(ExternCrateToLint {
+ def_id: item.owner_id.to_def_id(),
+ span: item.span,
+ orig_name,
+ warn_if_unused: !item.ident.as_str().starts_with('_'),
+ });
+ }
+ }
+ }
+
+ let extern_prelude = &tcx.resolutions(()).extern_prelude;
+
+ for extern_crate in &crates_to_lint {
+ let def_id = extern_crate.def_id.expect_local();
+ let item = tcx.hir().expect_item(def_id);
+
+ // If the crate is fully unused, we suggest removing it altogether.
+ // We do this in any edition.
+ if extern_crate.warn_if_unused {
+ if let Some(&span) = unused_extern_crates.get(&def_id) {
+ // Removal suggestion span needs to include attributes (Issue #54400)
+ let id = tcx.hir().local_def_id_to_hir_id(def_id);
+ let span_with_attrs = tcx
+ .hir()
+ .attrs(id)
+ .iter()
+ .map(|attr| attr.span)
+ .fold(span, |acc, attr_span| acc.to(attr_span));
+
+ tcx.emit_spanned_lint(lint, id, span, UnusedExternCrate { span: span_with_attrs });
+ continue;
+ }
+ }
+
+ // If we are not in Rust 2018 edition, then we don't make any further
+ // suggestions.
+ if !tcx.sess.rust_2018() {
+ continue;
+ }
+
+ // If the extern crate isn't in the extern prelude,
+ // there is no way it can be written as a `use`.
+ let orig_name = extern_crate.orig_name.unwrap_or(item.ident.name);
+ if !extern_prelude.get(&orig_name).map_or(false, |from_item| !from_item) {
+ continue;
+ }
+
+ // If the extern crate is renamed, then we cannot suggest replacing it with a use as this
+ // would not insert the new name into the prelude, where other imports in the crate may be
+ // expecting it.
+ if extern_crate.orig_name.is_some() {
+ continue;
+ }
+
+ let id = tcx.hir().local_def_id_to_hir_id(def_id);
+ // If the extern crate has any attributes, they may have funky
+ // semantics we can't faithfully represent using `use` (most
+ // notably `#[macro_use]`). Ignore it.
+ if !tcx.hir().attrs(id).is_empty() {
+ continue;
+ }
+
+ let base_replacement = match extern_crate.orig_name {
+ Some(orig_name) => format!("use {} as {};", orig_name, item.ident.name),
+ None => format!("use {};", item.ident.name),
+ };
+ let vis = tcx.sess.source_map().span_to_snippet(item.vis_span).unwrap_or_default();
+ let add_vis = |to| if vis.is_empty() { to } else { format!("{} {}", vis, to) };
+ tcx.emit_spanned_lint(
+ lint,
+ id,
+ extern_crate.span,
+ ExternCrateNotIdiomatic {
+ span: extern_crate.span,
+ msg_code: add_vis("use".to_string()),
+ suggestion_code: add_vis(base_replacement),
+ },
+ );
+ }
+}
+
+struct ExternCrateToLint {
+ /// `DefId` of the extern crate
+ def_id: DefId,
+
+ /// span from the item
+ span: Span,
+
+ /// if `Some`, then this is renamed (`extern crate orig_name as
+ /// crate_name`), and -- perhaps surprisingly -- this stores the
+ /// *original* name (`item.name` will contain the new name)
+ orig_name: Option<Symbol>,
+
+ /// if `false`, the original name started with `_`, so we shouldn't lint
+ /// about it going unused (but we should still emit idiom lints).
+ warn_if_unused: bool,
+}