about summary refs log tree commit diff
path: root/compiler/rustc_hir_analysis/src/check/mod.rs
diff options
context:
space:
mode:
Diffstat (limited to 'compiler/rustc_hir_analysis/src/check/mod.rs')
-rw-r--r--compiler/rustc_hir_analysis/src/check/mod.rs971
1 files changed, 971 insertions, 0 deletions
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 00000000000..593a9776bde
--- /dev/null
+++ b/compiler/rustc_hir_analysis/src/check/mod.rs
@@ -0,0 +1,971 @@
+/*!
+
+# 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).
+
+*/
+
+pub mod _match;
+mod autoderef;
+mod callee;
+pub mod cast;
+mod check;
+mod closure;
+pub mod coercion;
+mod compare_method;
+pub mod demand;
+mod diverges;
+pub mod dropck;
+mod expectation;
+mod expr;
+mod fallback;
+mod fn_ctxt;
+mod gather_locals;
+mod generator_interior;
+mod inherited;
+pub mod intrinsic;
+mod intrinsicck;
+pub mod method;
+mod op;
+mod pat;
+mod place_op;
+mod region;
+pub mod rvalue_scopes;
+mod upvar;
+pub mod wfcheck;
+pub mod writeback;
+
+use check::{check_abi, check_fn, check_mod_item_types};
+pub use diverges::Diverges;
+pub use expectation::Expectation;
+pub use fn_ctxt::*;
+pub use inherited::{Inherited, InheritedBuilder};
+
+use crate::astconv::AstConv;
+use crate::check::gather_locals::GatherLocalsVisitor;
+use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_errors::{
+    pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, MultiSpan,
+};
+use rustc_hir as hir;
+use rustc_hir::def::Res;
+use rustc_hir::def_id::{DefId, LocalDefId};
+use rustc_hir::intravisit::Visitor;
+use rustc_hir::{HirIdMap, ImplicitSelfKind, Node};
+use rustc_index::bit_set::BitSet;
+use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
+use rustc_middle::ty::query::Providers;
+use rustc_middle::ty::{self, Ty, TyCtxt, UserType};
+use rustc_middle::ty::{InternalSubsts, SubstsRef};
+use rustc_session::config;
+use rustc_session::parse::feature_err;
+use rustc_session::Session;
+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;
+use rustc_trait_selection::traits::error_reporting::recursive_type_with_infinite_size_error;
+use rustc_trait_selection::traits::error_reporting::suggestions::ReturnsVisitor;
+use std::cell::RefCell;
+use std::num::NonZeroU32;
+
+use crate::require_c_abi_if_c_variadic;
+use crate::util::common::indenter;
+
+use self::coercion::DynamicCoerceMany;
+use self::compare_method::collect_trait_impl_trait_tys;
+use self::region::region_scope_tree;
+pub use self::Expectation::*;
+
+#[macro_export]
+macro_rules! type_error_struct {
+    ($session:expr, $span:expr, $typ:expr, $code:ident, $($message:tt)*) => ({
+        let mut err = rustc_errors::struct_span_err!($session, $span, $code, $($message)*);
+
+        if $typ.references_error() {
+            err.downgrade_to_delayed_bug();
+        }
+
+        err
+    })
+}
+
+/// The type of a local binding, including the revealed type for anon types.
+#[derive(Copy, Clone, Debug)]
+pub struct LocalTy<'tcx> {
+    decl_ty: Ty<'tcx>,
+    revealed_ty: Ty<'tcx>,
+}
+
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+pub enum Needs {
+    MutPlace,
+    None,
+}
+
+impl Needs {
+    fn maybe_mut_place(m: hir::Mutability) -> Self {
+        match m {
+            hir::Mutability::Mut => Needs::MutPlace,
+            hir::Mutability::Not => Needs::None,
+        }
+    }
+}
+
+#[derive(Copy, Clone)]
+pub struct UnsafetyState {
+    pub def: hir::HirId,
+    pub unsafety: hir::Unsafety,
+    from_fn: bool,
+}
+
+impl UnsafetyState {
+    pub fn function(unsafety: hir::Unsafety, def: hir::HirId) -> UnsafetyState {
+        UnsafetyState { def, unsafety, from_fn: true }
+    }
+
+    pub fn recurse(self, blk: &hir::Block<'_>) -> UnsafetyState {
+        use hir::BlockCheckMode;
+        match self.unsafety {
+            // If this unsafe, then if the outer function was already marked as
+            // unsafe we shouldn't attribute the unsafe'ness to the block. This
+            // way the block can be warned about instead of ignoring this
+            // extraneous block (functions are never warned about).
+            hir::Unsafety::Unsafe if self.from_fn => self,
+
+            unsafety => {
+                let (unsafety, def) = match blk.rules {
+                    BlockCheckMode::UnsafeBlock(..) => (hir::Unsafety::Unsafe, blk.hir_id),
+                    BlockCheckMode::DefaultBlock => (unsafety, self.def),
+                };
+                UnsafetyState { def, unsafety, from_fn: false }
+            }
+        }
+    }
+}
+
+#[derive(Debug, Copy, Clone)]
+pub enum PlaceOp {
+    Deref,
+    Index,
+}
+
+pub struct BreakableCtxt<'tcx> {
+    may_break: bool,
+
+    // this is `null` for loops where break with a value is illegal,
+    // such as `while`, `for`, and `while let`
+    coerce: Option<DynamicCoerceMany<'tcx>>,
+}
+
+pub struct EnclosingBreakables<'tcx> {
+    stack: Vec<BreakableCtxt<'tcx>>,
+    by_id: HirIdMap<usize>,
+}
+
+impl<'tcx> EnclosingBreakables<'tcx> {
+    fn find_breakable(&mut self, target_id: hir::HirId) -> &mut BreakableCtxt<'tcx> {
+        self.opt_find_breakable(target_id).unwrap_or_else(|| {
+            bug!("could not find enclosing breakable with id {}", target_id);
+        })
+    }
+
+    fn opt_find_breakable(&mut self, target_id: hir::HirId) -> Option<&mut BreakableCtxt<'tcx>> {
+        match self.by_id.get(&target_id) {
+            Some(ix) => Some(&mut self.stack[*ix]),
+            None => None,
+        }
+    }
+}
+
+pub fn provide(providers: &mut Providers) {
+    method::provide(providers);
+    wfcheck::provide(providers);
+    *providers = Providers {
+        typeck_item_bodies,
+        typeck_const_arg,
+        typeck,
+        diagnostic_only_typeck,
+        has_typeck_results,
+        adt_destructor,
+        used_trait_imports,
+        check_mod_item_types,
+        region_scope_tree,
+        collect_trait_impl_trait_tys,
+        ..*providers
+    };
+}
+
+fn adt_destructor(tcx: TyCtxt<'_>, def_id: DefId) -> Option<ty::Destructor> {
+    tcx.calculate_dtor(def_id, dropck::check_drop_impl)
+}
+
+/// If this `DefId` is a "primary tables entry", returns
+/// `Some((body_id, body_ty, fn_sig))`. Otherwise, returns `None`.
+///
+/// If this function returns `Some`, then `typeck_results(def_id)` will
+/// succeed; if it returns `None`, then `typeck_results(def_id)` may or
+/// may not succeed. In some cases where this function returns `None`
+/// (notably closures), `typeck_results(def_id)` would wind up
+/// redirecting to the owning function.
+fn primary_body_of(
+    tcx: TyCtxt<'_>,
+    id: hir::HirId,
+) -> Option<(hir::BodyId, Option<&hir::Ty<'_>>, Option<&hir::FnSig<'_>>)> {
+    match tcx.hir().get(id) {
+        Node::Item(item) => match item.kind {
+            hir::ItemKind::Const(ty, body) | hir::ItemKind::Static(ty, _, body) => {
+                Some((body, Some(ty), None))
+            }
+            hir::ItemKind::Fn(ref sig, .., body) => Some((body, None, Some(sig))),
+            _ => None,
+        },
+        Node::TraitItem(item) => match item.kind {
+            hir::TraitItemKind::Const(ty, Some(body)) => Some((body, Some(ty), None)),
+            hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Provided(body)) => {
+                Some((body, None, Some(sig)))
+            }
+            _ => None,
+        },
+        Node::ImplItem(item) => match item.kind {
+            hir::ImplItemKind::Const(ty, body) => Some((body, Some(ty), None)),
+            hir::ImplItemKind::Fn(ref sig, body) => Some((body, None, Some(sig))),
+            _ => None,
+        },
+        Node::AnonConst(constant) => Some((constant.body, None, None)),
+        _ => None,
+    }
+}
+
+fn has_typeck_results(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
+    // Closures' typeck results come from their outermost function,
+    // as they are part of the same "inference environment".
+    let typeck_root_def_id = tcx.typeck_root_def_id(def_id);
+    if typeck_root_def_id != def_id {
+        return tcx.has_typeck_results(typeck_root_def_id);
+    }
+
+    if let Some(def_id) = def_id.as_local() {
+        let id = tcx.hir().local_def_id_to_hir_id(def_id);
+        primary_body_of(tcx, id).is_some()
+    } else {
+        false
+    }
+}
+
+fn used_trait_imports(tcx: TyCtxt<'_>, def_id: LocalDefId) -> &FxHashSet<LocalDefId> {
+    &*tcx.typeck(def_id).used_trait_imports
+}
+
+fn typeck_const_arg<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    (did, param_did): (LocalDefId, DefId),
+) -> &ty::TypeckResults<'tcx> {
+    let fallback = move || tcx.type_of(param_did);
+    typeck_with_fallback(tcx, did, fallback)
+}
+
+fn typeck<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &ty::TypeckResults<'tcx> {
+    if let Some(param_did) = tcx.opt_const_param_of(def_id) {
+        tcx.typeck_const_arg((def_id, param_did))
+    } else {
+        let fallback = move || tcx.type_of(def_id.to_def_id());
+        typeck_with_fallback(tcx, def_id, fallback)
+    }
+}
+
+/// Used only to get `TypeckResults` for type inference during error recovery.
+/// Currently only used for type inference of `static`s and `const`s to avoid type cycle errors.
+fn diagnostic_only_typeck<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &ty::TypeckResults<'tcx> {
+    let fallback = move || {
+        let span = tcx.hir().span(tcx.hir().local_def_id_to_hir_id(def_id));
+        tcx.ty_error_with_message(span, "diagnostic only typeck table used")
+    };
+    typeck_with_fallback(tcx, def_id, fallback)
+}
+
+fn typeck_with_fallback<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    def_id: LocalDefId,
+    fallback: impl Fn() -> Ty<'tcx> + 'tcx,
+) -> &'tcx ty::TypeckResults<'tcx> {
+    // Closures' typeck results come from their outermost function,
+    // as they are part of the same "inference environment".
+    let typeck_root_def_id = tcx.typeck_root_def_id(def_id.to_def_id()).expect_local();
+    if typeck_root_def_id != def_id {
+        return tcx.typeck(typeck_root_def_id);
+    }
+
+    let id = tcx.hir().local_def_id_to_hir_id(def_id);
+    let span = tcx.hir().span(id);
+
+    // Figure out what primary body this item has.
+    let (body_id, body_ty, fn_sig) = primary_body_of(tcx, id).unwrap_or_else(|| {
+        span_bug!(span, "can't type-check body of {:?}", def_id);
+    });
+    let body = tcx.hir().body(body_id);
+
+    let typeck_results = Inherited::build(tcx, def_id).enter(|inh| {
+        let param_env = tcx.param_env(def_id);
+        let mut fcx = if let Some(hir::FnSig { header, decl, .. }) = fn_sig {
+            let fn_sig = if crate::collect::get_infer_ret_ty(&decl.output).is_some() {
+                let fcx = FnCtxt::new(&inh, param_env, body.value.hir_id);
+                <dyn AstConv<'_>>::ty_of_fn(&fcx, id, header.unsafety, header.abi, decl, None, None)
+            } else {
+                tcx.fn_sig(def_id)
+            };
+
+            check_abi(tcx, id, span, fn_sig.abi());
+
+            // Compute the function signature from point of view of inside the fn.
+            let fn_sig = tcx.liberate_late_bound_regions(def_id.to_def_id(), fn_sig);
+            let fn_sig = inh.normalize_associated_types_in(
+                body.value.span,
+                body_id.hir_id,
+                param_env,
+                fn_sig,
+            );
+            check_fn(&inh, param_env, fn_sig, decl, id, body, None, true).0
+        } else {
+            let fcx = FnCtxt::new(&inh, param_env, body.value.hir_id);
+            let expected_type = body_ty
+                .and_then(|ty| match ty.kind {
+                    hir::TyKind::Infer => Some(<dyn AstConv<'_>>::ast_ty_to_ty(&fcx, ty)),
+                    _ => None,
+                })
+                .unwrap_or_else(|| match tcx.hir().get(id) {
+                    Node::AnonConst(_) => match tcx.hir().get(tcx.hir().get_parent_node(id)) {
+                        Node::Expr(&hir::Expr {
+                            kind: hir::ExprKind::ConstBlock(ref anon_const),
+                            ..
+                        }) if anon_const.hir_id == id => fcx.next_ty_var(TypeVariableOrigin {
+                            kind: TypeVariableOriginKind::TypeInference,
+                            span,
+                        }),
+                        Node::Ty(&hir::Ty {
+                            kind: hir::TyKind::Typeof(ref anon_const), ..
+                        }) if anon_const.hir_id == id => fcx.next_ty_var(TypeVariableOrigin {
+                            kind: TypeVariableOriginKind::TypeInference,
+                            span,
+                        }),
+                        Node::Expr(&hir::Expr { kind: hir::ExprKind::InlineAsm(asm), .. })
+                        | Node::Item(&hir::Item { kind: hir::ItemKind::GlobalAsm(asm), .. }) => {
+                            let operand_ty = asm
+                                .operands
+                                .iter()
+                                .filter_map(|(op, _op_sp)| match op {
+                                    hir::InlineAsmOperand::Const { anon_const }
+                                        if anon_const.hir_id == id =>
+                                    {
+                                        // Inline assembly constants must be integers.
+                                        Some(fcx.next_int_var())
+                                    }
+                                    hir::InlineAsmOperand::SymFn { anon_const }
+                                        if anon_const.hir_id == id =>
+                                    {
+                                        Some(fcx.next_ty_var(TypeVariableOrigin {
+                                            kind: TypeVariableOriginKind::MiscVariable,
+                                            span,
+                                        }))
+                                    }
+                                    _ => None,
+                                })
+                                .next();
+                            operand_ty.unwrap_or_else(fallback)
+                        }
+                        _ => fallback(),
+                    },
+                    _ => fallback(),
+                });
+
+            let expected_type = fcx.normalize_associated_types_in(body.value.span, expected_type);
+            fcx.require_type_is_sized(expected_type, body.value.span, traits::ConstSized);
+
+            // Gather locals in statics (because of block expressions).
+            GatherLocalsVisitor::new(&fcx).visit_body(body);
+
+            fcx.check_expr_coercable_to_type(&body.value, expected_type, None);
+
+            fcx.write_ty(id, expected_type);
+
+            fcx
+        };
+
+        let fallback_has_occurred = fcx.type_inference_fallback();
+
+        // Even though coercion casts provide type hints, we check casts after fallback for
+        // backwards compatibility. This makes fallback a stronger type hint than a cast coercion.
+        fcx.check_casts();
+        fcx.select_obligations_where_possible(fallback_has_occurred, |_| {});
+
+        // Closure and generator analysis may run after fallback
+        // because they don't constrain other type variables.
+        // Closure analysis only runs on closures. Therefore they only need to fulfill non-const predicates (as of now)
+        let prev_constness = fcx.param_env.constness();
+        fcx.param_env = fcx.param_env.without_const();
+        fcx.closure_analyze(body);
+        fcx.param_env = fcx.param_env.with_constness(prev_constness);
+        assert!(fcx.deferred_call_resolutions.borrow().is_empty());
+        // Before the generator analysis, temporary scopes shall be marked to provide more
+        // precise information on types to be captured.
+        fcx.resolve_rvalue_scopes(def_id.to_def_id());
+        fcx.resolve_generator_interiors(def_id.to_def_id());
+
+        for (ty, span, code) in fcx.deferred_sized_obligations.borrow_mut().drain(..) {
+            let ty = fcx.normalize_ty(span, ty);
+            fcx.require_type_is_sized(ty, span, code);
+        }
+
+        fcx.select_all_obligations_or_error();
+
+        if !fcx.infcx.is_tainted_by_errors() {
+            fcx.check_transmutes();
+        }
+
+        fcx.check_asms();
+
+        fcx.infcx.skip_region_resolution();
+
+        fcx.resolve_type_vars_in_body(body)
+    });
+
+    // Consistency check our TypeckResults instance can hold all ItemLocalIds
+    // it will need to hold.
+    assert_eq!(typeck_results.hir_owner, id.owner);
+
+    typeck_results
+}
+
+/// When `check_fn` is invoked on a generator (i.e., a body that
+/// includes yield), it returns back some information about the yield
+/// points.
+struct GeneratorTypes<'tcx> {
+    /// Type of generator argument / values returned by `yield`.
+    resume_ty: Ty<'tcx>,
+
+    /// Type of value that is yielded.
+    yield_ty: Ty<'tcx>,
+
+    /// Types that are captured (see `GeneratorInterior` for more).
+    interior: Ty<'tcx>,
+
+    /// Indicates if the generator is movable or static (immovable).
+    movability: hir::Movability,
+}
+
+/// 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.
+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!() }}")
+}
+
+/// 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 = expr::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();
+}
+
+fn report_unexpected_variant_res(tcx: TyCtxt<'_>, res: Res, qpath: &hir::QPath<'_>, span: Span) {
+    struct_span_err!(
+        tcx.sess,
+        span,
+        E0533,
+        "expected unit struct, unit variant or constant, found {} `{}`",
+        res.descr(),
+        rustc_hir_pretty::qpath_to_string(qpath),
+    )
+    .emit();
+}
+
+/// Controls whether the arguments are tupled. This is used for the call
+/// operator.
+///
+/// Tupling means that all call-side arguments are packed into a tuple and
+/// passed as a single parameter. For example, if tupling is enabled, this
+/// function:
+/// ```
+/// fn f(x: (isize, isize)) {}
+/// ```
+/// Can be called as:
+/// ```ignore UNSOLVED (can this be done in user code?)
+/// # fn f(x: (isize, isize)) {}
+/// f(1, 2);
+/// ```
+/// Instead of:
+/// ```
+/// # fn f(x: (isize, isize)) {}
+/// f((1, 2));
+/// ```
+#[derive(Clone, Eq, PartialEq)]
+enum TupleArgumentsFlag {
+    DontTupleArguments,
+    TupleArguments,
+}
+
+fn typeck_item_bodies(tcx: TyCtxt<'_>, (): ()) {
+    tcx.hir().par_body_owners(|body_owner_def_id| tcx.ensure().typeck(body_owner_def_id));
+}
+
+fn fatally_break_rust(sess: &Session) {
+    let handler = sess.diagnostic();
+    handler.span_bug_no_panic(
+        MultiSpan::new(),
+        "It looks like you're trying to break rust; would you like some ICE?",
+    );
+    handler.note_without_error("the compiler expectedly panicked. this is a feature.");
+    handler.note_without_error(
+        "we would appreciate a joke overview: \
+         https://github.com/rust-lang/rust/issues/43162#issuecomment-320764675",
+    );
+    handler.note_without_error(&format!(
+        "rustc {} running on {}",
+        option_env!("CFG_VERSION").unwrap_or("unknown_version"),
+        config::host_triple(),
+    ));
+}
+
+fn potentially_plural_count(count: usize, word: &str) -> String {
+    format!("{} {}{}", count, word, pluralize!(count))
+}
+
+fn has_expected_num_generic_args<'tcx>(
+    tcx: TyCtxt<'tcx>,
+    trait_did: Option<DefId>,
+    expected: usize,
+) -> bool {
+    trait_did.map_or(true, |trait_did| {
+        let generics = tcx.generics_of(trait_did);
+        generics.count() == expected + if generics.has_self { 1 } else { 0 }
+    })
+}