summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_ty_utils
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:02:58 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-17 12:02:58 +0000
commit698f8c2f01ea549d77d7dc3338a12e04c11057b9 (patch)
tree173a775858bd501c378080a10dca74132f05bc50 /compiler/rustc_ty_utils
parentInitial commit. (diff)
downloadrustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.tar.xz
rustc-698f8c2f01ea549d77d7dc3338a12e04c11057b9.zip
Adding upstream version 1.64.0+dfsg1.upstream/1.64.0+dfsg1
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to 'compiler/rustc_ty_utils')
-rw-r--r--compiler/rustc_ty_utils/Cargo.toml18
-rw-r--r--compiler/rustc_ty_utils/src/assoc.rs111
-rw-r--r--compiler/rustc_ty_utils/src/common_traits.rs51
-rw-r--r--compiler/rustc_ty_utils/src/consts.rs469
-rw-r--r--compiler/rustc_ty_utils/src/instance.rs407
-rw-r--r--compiler/rustc_ty_utils/src/lib.rs36
-rw-r--r--compiler/rustc_ty_utils/src/needs_drop.rs323
-rw-r--r--compiler/rustc_ty_utils/src/representability.rs386
-rw-r--r--compiler/rustc_ty_utils/src/ty.rs481
9 files changed, 2282 insertions, 0 deletions
diff --git a/compiler/rustc_ty_utils/Cargo.toml b/compiler/rustc_ty_utils/Cargo.toml
new file mode 100644
index 000000000..caad2ed42
--- /dev/null
+++ b/compiler/rustc_ty_utils/Cargo.toml
@@ -0,0 +1,18 @@
+[package]
+name = "rustc_ty_utils"
+version = "0.0.0"
+edition = "2021"
+
+[dependencies]
+tracing = "0.1"
+rustc_middle = { path = "../rustc_middle" }
+rustc_data_structures = { path = "../rustc_data_structures" }
+rustc_errors = { path = "../rustc_errors" }
+rustc_hir = { path = "../rustc_hir" }
+rustc_infer = { path = "../rustc_infer" }
+rustc_span = { path = "../rustc_span" }
+rustc_session = { path = "../rustc_session" }
+rustc_target = { path = "../rustc_target" }
+rustc_trait_selection = { path = "../rustc_trait_selection" }
+rustc_type_ir = { path = "../rustc_type_ir" }
+rustc_index = { path = "../rustc_index" }
diff --git a/compiler/rustc_ty_utils/src/assoc.rs b/compiler/rustc_ty_utils/src/assoc.rs
new file mode 100644
index 000000000..515a73ead
--- /dev/null
+++ b/compiler/rustc_ty_utils/src/assoc.rs
@@ -0,0 +1,111 @@
+use rustc_data_structures::fx::FxHashMap;
+use rustc_hir as hir;
+use rustc_hir::def_id::DefId;
+use rustc_middle::ty::{self, TyCtxt};
+
+pub fn provide(providers: &mut ty::query::Providers) {
+ *providers = ty::query::Providers {
+ associated_item,
+ associated_item_def_ids,
+ associated_items,
+ impl_item_implementor_ids,
+ ..*providers
+ };
+}
+
+fn associated_item_def_ids(tcx: TyCtxt<'_>, def_id: DefId) -> &[DefId] {
+ let item = tcx.hir().expect_item(def_id.expect_local());
+ match item.kind {
+ hir::ItemKind::Trait(.., ref trait_item_refs) => tcx.arena.alloc_from_iter(
+ trait_item_refs.iter().map(|trait_item_ref| trait_item_ref.id.def_id.to_def_id()),
+ ),
+ hir::ItemKind::Impl(ref impl_) => tcx.arena.alloc_from_iter(
+ impl_.items.iter().map(|impl_item_ref| impl_item_ref.id.def_id.to_def_id()),
+ ),
+ hir::ItemKind::TraitAlias(..) => &[],
+ _ => span_bug!(item.span, "associated_item_def_ids: not impl or trait"),
+ }
+}
+
+fn associated_items(tcx: TyCtxt<'_>, def_id: DefId) -> ty::AssocItems<'_> {
+ let items = tcx.associated_item_def_ids(def_id).iter().map(|did| tcx.associated_item(*did));
+ ty::AssocItems::new(items)
+}
+
+fn impl_item_implementor_ids(tcx: TyCtxt<'_>, impl_id: DefId) -> FxHashMap<DefId, DefId> {
+ tcx.associated_items(impl_id)
+ .in_definition_order()
+ .filter_map(|item| item.trait_item_def_id.map(|trait_item| (trait_item, item.def_id)))
+ .collect()
+}
+
+fn associated_item(tcx: TyCtxt<'_>, def_id: DefId) -> ty::AssocItem {
+ let id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local());
+ let parent_def_id = tcx.hir().get_parent_item(id);
+ let parent_item = tcx.hir().expect_item(parent_def_id);
+ match parent_item.kind {
+ hir::ItemKind::Impl(ref impl_) => {
+ if let Some(impl_item_ref) =
+ impl_.items.iter().find(|i| i.id.def_id.to_def_id() == def_id)
+ {
+ let assoc_item = associated_item_from_impl_item_ref(impl_item_ref);
+ debug_assert_eq!(assoc_item.def_id, def_id);
+ return assoc_item;
+ }
+ }
+
+ hir::ItemKind::Trait(.., ref trait_item_refs) => {
+ if let Some(trait_item_ref) =
+ trait_item_refs.iter().find(|i| i.id.def_id.to_def_id() == def_id)
+ {
+ let assoc_item = associated_item_from_trait_item_ref(trait_item_ref);
+ debug_assert_eq!(assoc_item.def_id, def_id);
+ return assoc_item;
+ }
+ }
+
+ _ => {}
+ }
+
+ span_bug!(
+ parent_item.span,
+ "unexpected parent of trait or impl item or item not found: {:?}",
+ parent_item.kind
+ )
+}
+
+fn associated_item_from_trait_item_ref(trait_item_ref: &hir::TraitItemRef) -> ty::AssocItem {
+ let def_id = trait_item_ref.id.def_id;
+ let (kind, has_self) = match trait_item_ref.kind {
+ hir::AssocItemKind::Const => (ty::AssocKind::Const, false),
+ hir::AssocItemKind::Fn { has_self } => (ty::AssocKind::Fn, has_self),
+ hir::AssocItemKind::Type => (ty::AssocKind::Type, false),
+ };
+
+ ty::AssocItem {
+ name: trait_item_ref.ident.name,
+ kind,
+ def_id: def_id.to_def_id(),
+ trait_item_def_id: Some(def_id.to_def_id()),
+ container: ty::TraitContainer,
+ fn_has_self_parameter: has_self,
+ }
+}
+
+fn associated_item_from_impl_item_ref(impl_item_ref: &hir::ImplItemRef) -> ty::AssocItem {
+ let def_id = impl_item_ref.id.def_id;
+ let (kind, has_self) = match impl_item_ref.kind {
+ hir::AssocItemKind::Const => (ty::AssocKind::Const, false),
+ hir::AssocItemKind::Fn { has_self } => (ty::AssocKind::Fn, has_self),
+ hir::AssocItemKind::Type => (ty::AssocKind::Type, false),
+ };
+
+ ty::AssocItem {
+ name: impl_item_ref.ident.name,
+ kind,
+ def_id: def_id.to_def_id(),
+ trait_item_def_id: impl_item_ref.trait_item_def_id,
+ container: ty::ImplContainer,
+ fn_has_self_parameter: has_self,
+ }
+}
diff --git a/compiler/rustc_ty_utils/src/common_traits.rs b/compiler/rustc_ty_utils/src/common_traits.rs
new file mode 100644
index 000000000..cedc84d97
--- /dev/null
+++ b/compiler/rustc_ty_utils/src/common_traits.rs
@@ -0,0 +1,51 @@
+//! Queries for checking whether a type implements one of a few common traits.
+
+use rustc_hir::lang_items::LangItem;
+use rustc_infer::infer::TyCtxtInferExt;
+use rustc_middle::ty::{self, Ty, TyCtxt};
+use rustc_span::DUMMY_SP;
+use rustc_trait_selection::traits;
+
+fn is_copy_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
+ is_item_raw(tcx, query, LangItem::Copy)
+}
+
+fn is_sized_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
+ is_item_raw(tcx, query, LangItem::Sized)
+}
+
+fn is_freeze_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
+ is_item_raw(tcx, query, LangItem::Freeze)
+}
+
+fn is_unpin_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
+ is_item_raw(tcx, query, LangItem::Unpin)
+}
+
+fn is_item_raw<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>,
+ item: LangItem,
+) -> bool {
+ let (param_env, ty) = query.into_parts();
+ let trait_def_id = tcx.require_lang_item(item, None);
+ tcx.infer_ctxt().enter(|infcx| {
+ traits::type_known_to_meet_bound_modulo_regions(
+ &infcx,
+ param_env,
+ ty,
+ trait_def_id,
+ DUMMY_SP,
+ )
+ })
+}
+
+pub(crate) fn provide(providers: &mut ty::query::Providers) {
+ *providers = ty::query::Providers {
+ is_copy_raw,
+ is_sized_raw,
+ is_freeze_raw,
+ is_unpin_raw,
+ ..*providers
+ };
+}
diff --git a/compiler/rustc_ty_utils/src/consts.rs b/compiler/rustc_ty_utils/src/consts.rs
new file mode 100644
index 000000000..7c2f4db94
--- /dev/null
+++ b/compiler/rustc_ty_utils/src/consts.rs
@@ -0,0 +1,469 @@
+use rustc_errors::ErrorGuaranteed;
+use rustc_hir::def::DefKind;
+use rustc_hir::def_id::LocalDefId;
+use rustc_index::vec::IndexVec;
+use rustc_middle::mir::interpret::{LitToConstError, LitToConstInput};
+use rustc_middle::ty::abstract_const::{CastKind, Node, NodeId};
+use rustc_middle::ty::{self, TyCtxt, TypeVisitable};
+use rustc_middle::{mir, thir};
+use rustc_span::Span;
+use rustc_target::abi::VariantIdx;
+
+use std::iter;
+
+/// Destructures array, ADT or tuple constants into the constants
+/// of their fields.
+pub(crate) fn destructure_const<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ const_: ty::Const<'tcx>,
+) -> ty::DestructuredConst<'tcx> {
+ let ty::ConstKind::Value(valtree) = const_.kind() else {
+ bug!("cannot destructure constant {:?}", const_)
+ };
+
+ let branches = match valtree {
+ ty::ValTree::Branch(b) => b,
+ _ => bug!("cannot destructure constant {:?}", const_),
+ };
+
+ let (fields, variant) = match const_.ty().kind() {
+ ty::Array(inner_ty, _) | ty::Slice(inner_ty) => {
+ // construct the consts for the elements of the array/slice
+ let field_consts = branches
+ .iter()
+ .map(|b| tcx.mk_const(ty::ConstS { kind: ty::ConstKind::Value(*b), ty: *inner_ty }))
+ .collect::<Vec<_>>();
+ debug!(?field_consts);
+
+ (field_consts, None)
+ }
+ ty::Adt(def, _) if def.variants().is_empty() => bug!("unreachable"),
+ ty::Adt(def, substs) => {
+ let (variant_idx, branches) = if def.is_enum() {
+ let (head, rest) = branches.split_first().unwrap();
+ (VariantIdx::from_u32(head.unwrap_leaf().try_to_u32().unwrap()), rest)
+ } else {
+ (VariantIdx::from_u32(0), branches)
+ };
+ let fields = &def.variant(variant_idx).fields;
+ let mut field_consts = Vec::with_capacity(fields.len());
+
+ for (field, field_valtree) in iter::zip(fields, branches) {
+ let field_ty = field.ty(tcx, substs);
+ let field_const = tcx.mk_const(ty::ConstS {
+ kind: ty::ConstKind::Value(*field_valtree),
+ ty: field_ty,
+ });
+ field_consts.push(field_const);
+ }
+ debug!(?field_consts);
+
+ (field_consts, Some(variant_idx))
+ }
+ ty::Tuple(elem_tys) => {
+ let fields = iter::zip(*elem_tys, branches)
+ .map(|(elem_ty, elem_valtree)| {
+ tcx.mk_const(ty::ConstS {
+ kind: ty::ConstKind::Value(*elem_valtree),
+ ty: elem_ty,
+ })
+ })
+ .collect::<Vec<_>>();
+
+ (fields, None)
+ }
+ _ => bug!("cannot destructure constant {:?}", const_),
+ };
+
+ let fields = tcx.arena.alloc_from_iter(fields.into_iter());
+
+ ty::DestructuredConst { variant, fields }
+}
+
+pub struct AbstractConstBuilder<'a, 'tcx> {
+ tcx: TyCtxt<'tcx>,
+ body_id: thir::ExprId,
+ body: &'a thir::Thir<'tcx>,
+ /// The current WIP node tree.
+ nodes: IndexVec<NodeId, Node<'tcx>>,
+}
+
+impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> {
+ fn root_span(&self) -> Span {
+ self.body.exprs[self.body_id].span
+ }
+
+ fn error(&mut self, span: Span, msg: &str) -> Result<!, ErrorGuaranteed> {
+ let reported = self
+ .tcx
+ .sess
+ .struct_span_err(self.root_span(), "overly complex generic constant")
+ .span_label(span, msg)
+ .help("consider moving this anonymous constant into a `const` function")
+ .emit();
+
+ Err(reported)
+ }
+ fn maybe_supported_error(&mut self, span: Span, msg: &str) -> Result<!, ErrorGuaranteed> {
+ let reported = self
+ .tcx
+ .sess
+ .struct_span_err(self.root_span(), "overly complex generic constant")
+ .span_label(span, msg)
+ .help("consider moving this anonymous constant into a `const` function")
+ .note("this operation may be supported in the future")
+ .emit();
+
+ Err(reported)
+ }
+
+ #[instrument(skip(tcx, body, body_id), level = "debug")]
+ pub fn new(
+ tcx: TyCtxt<'tcx>,
+ (body, body_id): (&'a thir::Thir<'tcx>, thir::ExprId),
+ ) -> Result<Option<AbstractConstBuilder<'a, 'tcx>>, ErrorGuaranteed> {
+ let builder = AbstractConstBuilder { tcx, body_id, body, nodes: IndexVec::new() };
+
+ struct IsThirPolymorphic<'a, 'tcx> {
+ is_poly: bool,
+ thir: &'a thir::Thir<'tcx>,
+ }
+
+ use crate::rustc_middle::thir::visit::Visitor;
+ use thir::visit;
+
+ impl<'a, 'tcx> IsThirPolymorphic<'a, 'tcx> {
+ fn expr_is_poly(&mut self, expr: &thir::Expr<'tcx>) -> bool {
+ if expr.ty.has_param_types_or_consts() {
+ return true;
+ }
+
+ match expr.kind {
+ thir::ExprKind::NamedConst { substs, .. } => substs.has_param_types_or_consts(),
+ thir::ExprKind::ConstParam { .. } => true,
+ thir::ExprKind::Repeat { value, count } => {
+ self.visit_expr(&self.thir()[value]);
+ count.has_param_types_or_consts()
+ }
+ _ => false,
+ }
+ }
+
+ fn pat_is_poly(&mut self, pat: &thir::Pat<'tcx>) -> bool {
+ if pat.ty.has_param_types_or_consts() {
+ return true;
+ }
+
+ match pat.kind.as_ref() {
+ thir::PatKind::Constant { value } => value.has_param_types_or_consts(),
+ thir::PatKind::Range(thir::PatRange { lo, hi, .. }) => {
+ lo.has_param_types_or_consts() || hi.has_param_types_or_consts()
+ }
+ _ => false,
+ }
+ }
+ }
+
+ impl<'a, 'tcx> visit::Visitor<'a, 'tcx> for IsThirPolymorphic<'a, 'tcx> {
+ fn thir(&self) -> &'a thir::Thir<'tcx> {
+ &self.thir
+ }
+
+ #[instrument(skip(self), level = "debug")]
+ fn visit_expr(&mut self, expr: &thir::Expr<'tcx>) {
+ self.is_poly |= self.expr_is_poly(expr);
+ if !self.is_poly {
+ visit::walk_expr(self, expr)
+ }
+ }
+
+ #[instrument(skip(self), level = "debug")]
+ fn visit_pat(&mut self, pat: &thir::Pat<'tcx>) {
+ self.is_poly |= self.pat_is_poly(pat);
+ if !self.is_poly {
+ visit::walk_pat(self, pat);
+ }
+ }
+ }
+
+ let mut is_poly_vis = IsThirPolymorphic { is_poly: false, thir: body };
+ visit::walk_expr(&mut is_poly_vis, &body[body_id]);
+ debug!("AbstractConstBuilder: is_poly={}", is_poly_vis.is_poly);
+ if !is_poly_vis.is_poly {
+ return Ok(None);
+ }
+
+ Ok(Some(builder))
+ }
+
+ /// We do not allow all binary operations in abstract consts, so filter disallowed ones.
+ fn check_binop(op: mir::BinOp) -> bool {
+ use mir::BinOp::*;
+ match op {
+ Add | Sub | Mul | Div | Rem | BitXor | BitAnd | BitOr | Shl | Shr | Eq | Lt | Le
+ | Ne | Ge | Gt => true,
+ Offset => false,
+ }
+ }
+
+ /// While we currently allow all unary operations, we still want to explicitly guard against
+ /// future changes here.
+ fn check_unop(op: mir::UnOp) -> bool {
+ use mir::UnOp::*;
+ match op {
+ Not | Neg => true,
+ }
+ }
+
+ /// Builds the abstract const by walking the thir and bailing out when
+ /// encountering an unsupported operation.
+ pub fn build(mut self) -> Result<&'tcx [Node<'tcx>], ErrorGuaranteed> {
+ debug!("AbstractConstBuilder::build: body={:?}", &*self.body);
+ self.recurse_build(self.body_id)?;
+
+ for n in self.nodes.iter() {
+ if let Node::Leaf(ct) = n {
+ if let ty::ConstKind::Unevaluated(ct) = ct.kind() {
+ // `AbstractConst`s should not contain any promoteds as they require references which
+ // are not allowed.
+ assert_eq!(ct.promoted, None);
+ assert_eq!(ct, self.tcx.erase_regions(ct));
+ }
+ }
+ }
+
+ Ok(self.tcx.arena.alloc_from_iter(self.nodes.into_iter()))
+ }
+
+ fn recurse_build(&mut self, node: thir::ExprId) -> Result<NodeId, ErrorGuaranteed> {
+ use thir::ExprKind;
+ let node = &self.body.exprs[node];
+ Ok(match &node.kind {
+ // I dont know if handling of these 3 is correct
+ &ExprKind::Scope { value, .. } => self.recurse_build(value)?,
+ &ExprKind::PlaceTypeAscription { source, .. }
+ | &ExprKind::ValueTypeAscription { source, .. } => self.recurse_build(source)?,
+ &ExprKind::Literal { lit, neg} => {
+ let sp = node.span;
+ let constant =
+ match self.tcx.at(sp).lit_to_const(LitToConstInput { lit: &lit.node, ty: node.ty, neg }) {
+ Ok(c) => c,
+ Err(LitToConstError::Reported) => {
+ self.tcx.const_error(node.ty)
+ }
+ Err(LitToConstError::TypeError) => {
+ bug!("encountered type error in lit_to_const")
+ }
+ };
+
+ self.nodes.push(Node::Leaf(constant))
+ }
+ &ExprKind::NonHirLiteral { lit , user_ty: _} => {
+ let val = ty::ValTree::from_scalar_int(lit);
+ self.nodes.push(Node::Leaf(ty::Const::from_value(self.tcx, val, node.ty)))
+ }
+ &ExprKind::ZstLiteral { user_ty: _ } => {
+ let val = ty::ValTree::zst();
+ self.nodes.push(Node::Leaf(ty::Const::from_value(self.tcx, val, node.ty)))
+ }
+ &ExprKind::NamedConst { def_id, substs, user_ty: _ } => {
+ let uneval = ty::Unevaluated::new(ty::WithOptConstParam::unknown(def_id), substs);
+
+ let constant = self.tcx.mk_const(ty::ConstS {
+ kind: ty::ConstKind::Unevaluated(uneval),
+ ty: node.ty,
+ });
+
+ self.nodes.push(Node::Leaf(constant))
+ }
+
+ ExprKind::ConstParam {param, ..} => {
+ let const_param = self.tcx.mk_const(ty::ConstS {
+ kind: ty::ConstKind::Param(*param),
+ ty: node.ty,
+ });
+ self.nodes.push(Node::Leaf(const_param))
+ }
+
+ ExprKind::Call { fun, args, .. } => {
+ let fun = self.recurse_build(*fun)?;
+
+ let mut new_args = Vec::<NodeId>::with_capacity(args.len());
+ for &id in args.iter() {
+ new_args.push(self.recurse_build(id)?);
+ }
+ let new_args = self.tcx.arena.alloc_slice(&new_args);
+ self.nodes.push(Node::FunctionCall(fun, new_args))
+ }
+ &ExprKind::Binary { op, lhs, rhs } if Self::check_binop(op) => {
+ let lhs = self.recurse_build(lhs)?;
+ let rhs = self.recurse_build(rhs)?;
+ self.nodes.push(Node::Binop(op, lhs, rhs))
+ }
+ &ExprKind::Unary { op, arg } if Self::check_unop(op) => {
+ let arg = self.recurse_build(arg)?;
+ self.nodes.push(Node::UnaryOp(op, arg))
+ }
+ // This is necessary so that the following compiles:
+ //
+ // ```
+ // fn foo<const N: usize>(a: [(); N + 1]) {
+ // bar::<{ N + 1 }>();
+ // }
+ // ```
+ ExprKind::Block { body: thir::Block { stmts: box [], expr: Some(e), .. } } => {
+ self.recurse_build(*e)?
+ }
+ // `ExprKind::Use` happens when a `hir::ExprKind::Cast` is a
+ // "coercion cast" i.e. using a coercion or is a no-op.
+ // This is important so that `N as usize as usize` doesnt unify with `N as usize`. (untested)
+ &ExprKind::Use { source } => {
+ let arg = self.recurse_build(source)?;
+ self.nodes.push(Node::Cast(CastKind::Use, arg, node.ty))
+ }
+ &ExprKind::Cast { source } => {
+ let arg = self.recurse_build(source)?;
+ self.nodes.push(Node::Cast(CastKind::As, arg, node.ty))
+ }
+ ExprKind::Borrow{ arg, ..} => {
+ let arg_node = &self.body.exprs[*arg];
+
+ // Skip reborrows for now until we allow Deref/Borrow/AddressOf
+ // expressions.
+ // FIXME(generic_const_exprs): Verify/explain why this is sound
+ if let ExprKind::Deref { arg } = arg_node.kind {
+ self.recurse_build(arg)?
+ } else {
+ self.maybe_supported_error(
+ node.span,
+ "borrowing is not supported in generic constants",
+ )?
+ }
+ }
+ // FIXME(generic_const_exprs): We may want to support these.
+ ExprKind::AddressOf { .. } | ExprKind::Deref {..}=> self.maybe_supported_error(
+ node.span,
+ "dereferencing or taking the address is not supported in generic constants",
+ )?,
+ ExprKind::Repeat { .. } | ExprKind::Array { .. } => self.maybe_supported_error(
+ node.span,
+ "array construction is not supported in generic constants",
+ )?,
+ ExprKind::Block { .. } => self.maybe_supported_error(
+ node.span,
+ "blocks are not supported in generic constant",
+ )?,
+ ExprKind::NeverToAny { .. } => self.maybe_supported_error(
+ node.span,
+ "converting nevers to any is not supported in generic constant",
+ )?,
+ ExprKind::Tuple { .. } => self.maybe_supported_error(
+ node.span,
+ "tuple construction is not supported in generic constants",
+ )?,
+ ExprKind::Index { .. } => self.maybe_supported_error(
+ node.span,
+ "indexing is not supported in generic constant",
+ )?,
+ ExprKind::Field { .. } => self.maybe_supported_error(
+ node.span,
+ "field access is not supported in generic constant",
+ )?,
+ ExprKind::ConstBlock { .. } => self.maybe_supported_error(
+ node.span,
+ "const blocks are not supported in generic constant",
+ )?,
+ ExprKind::Adt(_) => self.maybe_supported_error(
+ node.span,
+ "struct/enum construction is not supported in generic constants",
+ )?,
+ // dont know if this is correct
+ ExprKind::Pointer { .. } =>
+ self.error(node.span, "pointer casts are not allowed in generic constants")?,
+ ExprKind::Yield { .. } =>
+ self.error(node.span, "generator control flow is not allowed in generic constants")?,
+ ExprKind::Continue { .. } | ExprKind::Break { .. } | ExprKind::Loop { .. } => self
+ .error(
+ node.span,
+ "loops and loop control flow are not supported in generic constants",
+ )?,
+ ExprKind::Box { .. } =>
+ self.error(node.span, "allocations are not allowed in generic constants")?,
+
+ ExprKind::Unary { .. } => unreachable!(),
+ // we handle valid unary/binary ops above
+ ExprKind::Binary { .. } =>
+ self.error(node.span, "unsupported binary operation in generic constants")?,
+ ExprKind::LogicalOp { .. } =>
+ self.error(node.span, "unsupported operation in generic constants, short-circuiting operations would imply control flow")?,
+ ExprKind::Assign { .. } | ExprKind::AssignOp { .. } => {
+ self.error(node.span, "assignment is not supported in generic constants")?
+ }
+ ExprKind::Closure { .. } | ExprKind::Return { .. } => self.error(
+ node.span,
+ "closures and function keywords are not supported in generic constants",
+ )?,
+ // let expressions imply control flow
+ ExprKind::Match { .. } | ExprKind::If { .. } | ExprKind::Let { .. } =>
+ self.error(node.span, "control flow is not supported in generic constants")?,
+ ExprKind::InlineAsm { .. } => {
+ self.error(node.span, "assembly is not supported in generic constants")?
+ }
+
+ // we dont permit let stmts so `VarRef` and `UpvarRef` cant happen
+ ExprKind::VarRef { .. }
+ | ExprKind::UpvarRef { .. }
+ | ExprKind::StaticRef { .. }
+ | ExprKind::ThreadLocalRef(_) => {
+ self.error(node.span, "unsupported operation in generic constant")?
+ }
+ })
+ }
+}
+
+/// Builds an abstract const, do not use this directly, but use `AbstractConst::new` instead.
+pub fn thir_abstract_const<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ def: ty::WithOptConstParam<LocalDefId>,
+) -> Result<Option<&'tcx [Node<'tcx>]>, ErrorGuaranteed> {
+ if tcx.features().generic_const_exprs {
+ match tcx.def_kind(def.did) {
+ // FIXME(generic_const_exprs): We currently only do this for anonymous constants,
+ // meaning that we do not look into associated constants. I(@lcnr) am not yet sure whether
+ // we want to look into them or treat them as opaque projections.
+ //
+ // Right now we do neither of that and simply always fail to unify them.
+ DefKind::AnonConst | DefKind::InlineConst => (),
+ _ => return Ok(None),
+ }
+
+ let body = tcx.thir_body(def)?;
+
+ AbstractConstBuilder::new(tcx, (&*body.0.borrow(), body.1))?
+ .map(AbstractConstBuilder::build)
+ .transpose()
+ } else {
+ Ok(None)
+ }
+}
+
+pub fn provide(providers: &mut ty::query::Providers) {
+ *providers = ty::query::Providers {
+ destructure_const,
+ thir_abstract_const: |tcx, def_id| {
+ let def_id = def_id.expect_local();
+ if let Some(def) = ty::WithOptConstParam::try_lookup(def_id, tcx) {
+ tcx.thir_abstract_const_of_const_arg(def)
+ } else {
+ thir_abstract_const(tcx, ty::WithOptConstParam::unknown(def_id))
+ }
+ },
+ thir_abstract_const_of_const_arg: |tcx, (did, param_did)| {
+ thir_abstract_const(
+ tcx,
+ ty::WithOptConstParam { did, const_param_did: Some(param_did) },
+ )
+ },
+ ..*providers
+ };
+}
diff --git a/compiler/rustc_ty_utils/src/instance.rs b/compiler/rustc_ty_utils/src/instance.rs
new file mode 100644
index 000000000..bd1d568cd
--- /dev/null
+++ b/compiler/rustc_ty_utils/src/instance.rs
@@ -0,0 +1,407 @@
+use rustc_errors::ErrorGuaranteed;
+use rustc_hir::def_id::{DefId, LocalDefId};
+use rustc_infer::infer::TyCtxtInferExt;
+use rustc_middle::traits::CodegenObligationError;
+use rustc_middle::ty::subst::SubstsRef;
+use rustc_middle::ty::{
+ self, Binder, Instance, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor,
+};
+use rustc_span::{sym, DUMMY_SP};
+use rustc_trait_selection::traits;
+use traits::{translate_substs, Reveal};
+
+use rustc_data_structures::sso::SsoHashSet;
+use std::collections::btree_map::Entry;
+use std::collections::BTreeMap;
+use std::ops::ControlFlow;
+
+use tracing::debug;
+
+// FIXME(#86795): `BoundVarsCollector` here should **NOT** be used
+// outside of `resolve_associated_item`. It's just to address #64494,
+// #83765, and #85848 which are creating bound types/regions that lose
+// their `Binder` *unintentionally*.
+// It's ideal to remove `BoundVarsCollector` and just use
+// `ty::Binder::*` methods but we use this stopgap until we figure out
+// the "real" fix.
+struct BoundVarsCollector<'tcx> {
+ binder_index: ty::DebruijnIndex,
+ vars: BTreeMap<u32, ty::BoundVariableKind>,
+ // We may encounter the same variable at different levels of binding, so
+ // this can't just be `Ty`
+ visited: SsoHashSet<(ty::DebruijnIndex, Ty<'tcx>)>,
+}
+
+impl<'tcx> BoundVarsCollector<'tcx> {
+ fn new() -> Self {
+ BoundVarsCollector {
+ binder_index: ty::INNERMOST,
+ vars: BTreeMap::new(),
+ visited: SsoHashSet::default(),
+ }
+ }
+
+ fn into_vars(self, tcx: TyCtxt<'tcx>) -> &'tcx ty::List<ty::BoundVariableKind> {
+ let max = self.vars.iter().map(|(k, _)| *k).max().unwrap_or(0);
+ for i in 0..max {
+ if let None = self.vars.get(&i) {
+ panic!("Unknown variable: {:?}", i);
+ }
+ }
+
+ tcx.mk_bound_variable_kinds(self.vars.into_iter().map(|(_, v)| v))
+ }
+}
+
+impl<'tcx> TypeVisitor<'tcx> for BoundVarsCollector<'tcx> {
+ type BreakTy = ();
+
+ fn visit_binder<T: TypeVisitable<'tcx>>(
+ &mut self,
+ t: &Binder<'tcx, T>,
+ ) -> ControlFlow<Self::BreakTy> {
+ self.binder_index.shift_in(1);
+ let result = t.super_visit_with(self);
+ self.binder_index.shift_out(1);
+ result
+ }
+
+ fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
+ if t.outer_exclusive_binder() < self.binder_index
+ || !self.visited.insert((self.binder_index, t))
+ {
+ return ControlFlow::CONTINUE;
+ }
+ match *t.kind() {
+ ty::Bound(debruijn, bound_ty) if debruijn == self.binder_index => {
+ match self.vars.entry(bound_ty.var.as_u32()) {
+ Entry::Vacant(entry) => {
+ entry.insert(ty::BoundVariableKind::Ty(bound_ty.kind));
+ }
+ Entry::Occupied(entry) => match entry.get() {
+ ty::BoundVariableKind::Ty(_) => {}
+ _ => bug!("Conflicting bound vars"),
+ },
+ }
+ }
+
+ _ => (),
+ };
+
+ t.super_visit_with(self)
+ }
+
+ fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
+ match *r {
+ ty::ReLateBound(index, br) if index == self.binder_index => {
+ match self.vars.entry(br.var.as_u32()) {
+ Entry::Vacant(entry) => {
+ entry.insert(ty::BoundVariableKind::Region(br.kind));
+ }
+ Entry::Occupied(entry) => match entry.get() {
+ ty::BoundVariableKind::Region(_) => {}
+ _ => bug!("Conflicting bound vars"),
+ },
+ }
+ }
+
+ _ => (),
+ };
+
+ r.super_visit_with(self)
+ }
+}
+
+fn resolve_instance<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ key: ty::ParamEnvAnd<'tcx, (DefId, SubstsRef<'tcx>)>,
+) -> Result<Option<Instance<'tcx>>, ErrorGuaranteed> {
+ let (param_env, (did, substs)) = key.into_parts();
+ if let Some(did) = did.as_local() {
+ if let Some(param_did) = tcx.opt_const_param_of(did) {
+ return tcx.resolve_instance_of_const_arg(param_env.and((did, param_did, substs)));
+ }
+ }
+
+ inner_resolve_instance(tcx, param_env.and((ty::WithOptConstParam::unknown(did), substs)))
+}
+
+fn resolve_instance_of_const_arg<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ key: ty::ParamEnvAnd<'tcx, (LocalDefId, DefId, SubstsRef<'tcx>)>,
+) -> Result<Option<Instance<'tcx>>, ErrorGuaranteed> {
+ let (param_env, (did, const_param_did, substs)) = key.into_parts();
+ inner_resolve_instance(
+ tcx,
+ param_env.and((
+ ty::WithOptConstParam { did: did.to_def_id(), const_param_did: Some(const_param_did) },
+ substs,
+ )),
+ )
+}
+
+fn inner_resolve_instance<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ key: ty::ParamEnvAnd<'tcx, (ty::WithOptConstParam<DefId>, SubstsRef<'tcx>)>,
+) -> Result<Option<Instance<'tcx>>, ErrorGuaranteed> {
+ let (param_env, (def, substs)) = key.into_parts();
+
+ let result = if let Some(trait_def_id) = tcx.trait_of_item(def.did) {
+ debug!(" => associated item, attempting to find impl in param_env {:#?}", param_env);
+ resolve_associated_item(tcx, def.did, param_env, trait_def_id, substs)
+ } else {
+ let ty = tcx.type_of(def.def_id_for_type_of());
+ let item_type = tcx.subst_and_normalize_erasing_regions(substs, param_env, ty);
+
+ let def = match *item_type.kind() {
+ ty::FnDef(def_id, ..) if tcx.is_intrinsic(def_id) => {
+ debug!(" => intrinsic");
+ ty::InstanceDef::Intrinsic(def.did)
+ }
+ ty::FnDef(def_id, substs) if Some(def_id) == tcx.lang_items().drop_in_place_fn() => {
+ let ty = substs.type_at(0);
+
+ if ty.needs_drop(tcx, param_env) {
+ debug!(" => nontrivial drop glue");
+ match *ty.kind() {
+ ty::Closure(..)
+ | ty::Generator(..)
+ | ty::Tuple(..)
+ | ty::Adt(..)
+ | ty::Dynamic(..)
+ | ty::Array(..)
+ | ty::Slice(..) => {}
+ // Drop shims can only be built from ADTs.
+ _ => return Ok(None),
+ }
+
+ ty::InstanceDef::DropGlue(def_id, Some(ty))
+ } else {
+ debug!(" => trivial drop glue");
+ ty::InstanceDef::DropGlue(def_id, None)
+ }
+ }
+ _ => {
+ debug!(" => free item");
+ ty::InstanceDef::Item(def)
+ }
+ };
+ Ok(Some(Instance { def, substs }))
+ };
+ debug!("inner_resolve_instance: result={:?}", result);
+ result
+}
+
+fn resolve_associated_item<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ trait_item_id: DefId,
+ param_env: ty::ParamEnv<'tcx>,
+ trait_id: DefId,
+ rcvr_substs: SubstsRef<'tcx>,
+) -> Result<Option<Instance<'tcx>>, ErrorGuaranteed> {
+ debug!(?trait_item_id, ?param_env, ?trait_id, ?rcvr_substs, "resolve_associated_item");
+
+ let trait_ref = ty::TraitRef::from_method(tcx, trait_id, rcvr_substs);
+
+ // See FIXME on `BoundVarsCollector`.
+ let mut bound_vars_collector = BoundVarsCollector::new();
+ trait_ref.visit_with(&mut bound_vars_collector);
+ let trait_binder = ty::Binder::bind_with_vars(trait_ref, bound_vars_collector.into_vars(tcx));
+ let vtbl = match tcx.codegen_fulfill_obligation((param_env, trait_binder)) {
+ Ok(vtbl) => vtbl,
+ Err(CodegenObligationError::Ambiguity) => {
+ let reported = tcx.sess.delay_span_bug(
+ tcx.def_span(trait_item_id),
+ &format!(
+ "encountered ambiguity selecting `{:?}` during codegen, presuming due to \
+ overflow or prior type error",
+ trait_binder
+ ),
+ );
+ return Err(reported);
+ }
+ Err(CodegenObligationError::Unimplemented) => return Ok(None),
+ Err(CodegenObligationError::FulfillmentError) => return Ok(None),
+ };
+
+ // Now that we know which impl is being used, we can dispatch to
+ // the actual function:
+ Ok(match vtbl {
+ traits::ImplSource::UserDefined(impl_data) => {
+ debug!(
+ "resolving ImplSource::UserDefined: {:?}, {:?}, {:?}, {:?}",
+ param_env, trait_item_id, rcvr_substs, impl_data
+ );
+ assert!(!rcvr_substs.needs_infer());
+ assert!(!trait_ref.needs_infer());
+
+ let trait_def_id = tcx.trait_id_of_impl(impl_data.impl_def_id).unwrap();
+ let trait_def = tcx.trait_def(trait_def_id);
+ let leaf_def = trait_def
+ .ancestors(tcx, impl_data.impl_def_id)?
+ .leaf_def(tcx, trait_item_id)
+ .unwrap_or_else(|| {
+ bug!("{:?} not found in {:?}", trait_item_id, impl_data.impl_def_id);
+ });
+
+ let substs = tcx.infer_ctxt().enter(|infcx| {
+ let param_env = param_env.with_reveal_all_normalized(tcx);
+ let substs = rcvr_substs.rebase_onto(tcx, trait_def_id, impl_data.substs);
+ let substs = translate_substs(
+ &infcx,
+ param_env,
+ impl_data.impl_def_id,
+ substs,
+ leaf_def.defining_node,
+ );
+ infcx.tcx.erase_regions(substs)
+ });
+
+ // Since this is a trait item, we need to see if the item is either a trait default item
+ // or a specialization because we can't resolve those unless we can `Reveal::All`.
+ // NOTE: This should be kept in sync with the similar code in
+ // `rustc_trait_selection::traits::project::assemble_candidates_from_impls()`.
+ let eligible = if leaf_def.is_final() {
+ // Non-specializable items are always projectable.
+ true
+ } else {
+ // Only reveal a specializable default if we're past type-checking
+ // and the obligation is monomorphic, otherwise passes such as
+ // transmute checking and polymorphic MIR optimizations could
+ // get a result which isn't correct for all monomorphizations.
+ if param_env.reveal() == Reveal::All {
+ !trait_ref.still_further_specializable()
+ } else {
+ false
+ }
+ };
+
+ if !eligible {
+ return Ok(None);
+ }
+
+ // If the item does not have a value, then we cannot return an instance.
+ if !leaf_def.item.defaultness(tcx).has_value() {
+ return Ok(None);
+ }
+
+ let substs = tcx.erase_regions(substs);
+
+ // Check if we just resolved an associated `const` declaration from
+ // a `trait` to an associated `const` definition in an `impl`, where
+ // the definition in the `impl` has the wrong type (for which an
+ // error has already been/will be emitted elsewhere).
+ //
+ // NB: this may be expensive, we try to skip it in all the cases where
+ // we know the error would've been caught (e.g. in an upstream crate).
+ //
+ // A better approach might be to just introduce a query (returning
+ // `Result<(), ErrorGuaranteed>`) for the check that `rustc_typeck`
+ // performs (i.e. that the definition's type in the `impl` matches
+ // the declaration in the `trait`), so that we can cheaply check
+ // here if it failed, instead of approximating it.
+ if leaf_def.item.kind == ty::AssocKind::Const
+ && trait_item_id != leaf_def.item.def_id
+ && leaf_def.item.def_id.is_local()
+ {
+ let normalized_type_of = |def_id, substs| {
+ tcx.subst_and_normalize_erasing_regions(substs, param_env, tcx.type_of(def_id))
+ };
+
+ let original_ty = normalized_type_of(trait_item_id, rcvr_substs);
+ let resolved_ty = normalized_type_of(leaf_def.item.def_id, substs);
+
+ if original_ty != resolved_ty {
+ let msg = format!(
+ "Instance::resolve: inconsistent associated `const` type: \
+ was `{}: {}` but resolved to `{}: {}`",
+ tcx.def_path_str_with_substs(trait_item_id, rcvr_substs),
+ original_ty,
+ tcx.def_path_str_with_substs(leaf_def.item.def_id, substs),
+ resolved_ty,
+ );
+ let span = tcx.def_span(leaf_def.item.def_id);
+ let reported = tcx.sess.delay_span_bug(span, &msg);
+
+ return Err(reported);
+ }
+ }
+
+ Some(ty::Instance::new(leaf_def.item.def_id, substs))
+ }
+ traits::ImplSource::Generator(generator_data) => Some(Instance {
+ def: ty::InstanceDef::Item(ty::WithOptConstParam::unknown(
+ generator_data.generator_def_id,
+ )),
+ substs: generator_data.substs,
+ }),
+ traits::ImplSource::Closure(closure_data) => {
+ let trait_closure_kind = tcx.fn_trait_kind_from_lang_item(trait_id).unwrap();
+ Instance::resolve_closure(
+ tcx,
+ closure_data.closure_def_id,
+ closure_data.substs,
+ trait_closure_kind,
+ )
+ }
+ traits::ImplSource::FnPointer(ref data) => match data.fn_ty.kind() {
+ ty::FnDef(..) | ty::FnPtr(..) => Some(Instance {
+ def: ty::InstanceDef::FnPtrShim(trait_item_id, data.fn_ty),
+ substs: rcvr_substs,
+ }),
+ _ => None,
+ },
+ traits::ImplSource::Object(ref data) => {
+ if let Some(index) = traits::get_vtable_index_of_object_method(tcx, data, trait_item_id)
+ {
+ Some(Instance {
+ def: ty::InstanceDef::Virtual(trait_item_id, index),
+ substs: rcvr_substs,
+ })
+ } else {
+ None
+ }
+ }
+ traits::ImplSource::Builtin(..) => {
+ if Some(trait_ref.def_id) == tcx.lang_items().clone_trait() {
+ // FIXME(eddyb) use lang items for methods instead of names.
+ let name = tcx.item_name(trait_item_id);
+ if name == sym::clone {
+ let self_ty = trait_ref.self_ty();
+
+ let is_copy = self_ty.is_copy_modulo_regions(tcx.at(DUMMY_SP), param_env);
+ match self_ty.kind() {
+ _ if is_copy => (),
+ ty::Closure(..) | ty::Tuple(..) => {}
+ _ => return Ok(None),
+ };
+
+ Some(Instance {
+ def: ty::InstanceDef::CloneShim(trait_item_id, self_ty),
+ substs: rcvr_substs,
+ })
+ } else {
+ assert_eq!(name, sym::clone_from);
+
+ // Use the default `fn clone_from` from `trait Clone`.
+ let substs = tcx.erase_regions(rcvr_substs);
+ Some(ty::Instance::new(trait_item_id, substs))
+ }
+ } else {
+ None
+ }
+ }
+ traits::ImplSource::AutoImpl(..)
+ | traits::ImplSource::Param(..)
+ | traits::ImplSource::TraitAlias(..)
+ | traits::ImplSource::DiscriminantKind(..)
+ | traits::ImplSource::Pointee(..)
+ | traits::ImplSource::TraitUpcasting(_)
+ | traits::ImplSource::ConstDestruct(_) => None,
+ })
+}
+
+pub fn provide(providers: &mut ty::query::Providers) {
+ *providers =
+ ty::query::Providers { resolve_instance, resolve_instance_of_const_arg, ..*providers };
+}
diff --git a/compiler/rustc_ty_utils/src/lib.rs b/compiler/rustc_ty_utils/src/lib.rs
new file mode 100644
index 000000000..09f5c2a11
--- /dev/null
+++ b/compiler/rustc_ty_utils/src/lib.rs
@@ -0,0 +1,36 @@
+//! Various checks
+//!
+//! # Note
+//!
+//! This API is completely unstable and subject to change.
+
+#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
+#![feature(control_flow_enum)]
+#![feature(let_else)]
+#![feature(never_type)]
+#![feature(box_patterns)]
+#![recursion_limit = "256"]
+
+#[macro_use]
+extern crate rustc_middle;
+#[macro_use]
+extern crate tracing;
+
+use rustc_middle::ty::query::Providers;
+
+mod assoc;
+mod common_traits;
+mod consts;
+pub mod instance;
+mod needs_drop;
+pub mod representability;
+mod ty;
+
+pub fn provide(providers: &mut Providers) {
+ assoc::provide(providers);
+ common_traits::provide(providers);
+ consts::provide(providers);
+ needs_drop::provide(providers);
+ ty::provide(providers);
+ instance::provide(providers);
+}
diff --git a/compiler/rustc_ty_utils/src/needs_drop.rs b/compiler/rustc_ty_utils/src/needs_drop.rs
new file mode 100644
index 000000000..9ad44d14d
--- /dev/null
+++ b/compiler/rustc_ty_utils/src/needs_drop.rs
@@ -0,0 +1,323 @@
+//! Check whether a type has (potentially) non-trivial drop glue.
+
+use rustc_data_structures::fx::FxHashSet;
+use rustc_hir::def_id::DefId;
+use rustc_middle::ty::subst::Subst;
+use rustc_middle::ty::subst::SubstsRef;
+use rustc_middle::ty::util::{needs_drop_components, AlwaysRequiresDrop};
+use rustc_middle::ty::{self, EarlyBinder, Ty, TyCtxt};
+use rustc_session::Limit;
+use rustc_span::{sym, DUMMY_SP};
+
+type NeedsDropResult<T> = Result<T, AlwaysRequiresDrop>;
+
+fn needs_drop_raw<'tcx>(tcx: TyCtxt<'tcx>, query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>) -> bool {
+ // If we don't know a type doesn't need drop, for example if it's a type
+ // parameter without a `Copy` bound, then we conservatively return that it
+ // needs drop.
+ let adt_has_dtor =
+ |adt_def: ty::AdtDef<'tcx>| adt_def.destructor(tcx).map(|_| DtorType::Significant);
+ let res =
+ drop_tys_helper(tcx, query.value, query.param_env, adt_has_dtor, false).next().is_some();
+
+ debug!("needs_drop_raw({:?}) = {:?}", query, res);
+ res
+}
+
+fn has_significant_drop_raw<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ query: ty::ParamEnvAnd<'tcx, Ty<'tcx>>,
+) -> bool {
+ let res = drop_tys_helper(
+ tcx,
+ query.value,
+ query.param_env,
+ adt_consider_insignificant_dtor(tcx),
+ true,
+ )
+ .next()
+ .is_some();
+ debug!("has_significant_drop_raw({:?}) = {:?}", query, res);
+ res
+}
+
+struct NeedsDropTypes<'tcx, F> {
+ tcx: TyCtxt<'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+ query_ty: Ty<'tcx>,
+ seen_tys: FxHashSet<Ty<'tcx>>,
+ /// A stack of types left to process, and the recursion depth when we
+ /// pushed that type. Each round, we pop something from the stack and check
+ /// if it needs drop. If the result depends on whether some other types
+ /// need drop we push them onto the stack.
+ unchecked_tys: Vec<(Ty<'tcx>, usize)>,
+ recursion_limit: Limit,
+ adt_components: F,
+}
+
+impl<'tcx, F> NeedsDropTypes<'tcx, F> {
+ fn new(
+ tcx: TyCtxt<'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+ ty: Ty<'tcx>,
+ adt_components: F,
+ ) -> Self {
+ let mut seen_tys = FxHashSet::default();
+ seen_tys.insert(ty);
+ Self {
+ tcx,
+ param_env,
+ seen_tys,
+ query_ty: ty,
+ unchecked_tys: vec![(ty, 0)],
+ recursion_limit: tcx.recursion_limit(),
+ adt_components,
+ }
+ }
+}
+
+impl<'tcx, F, I> Iterator for NeedsDropTypes<'tcx, F>
+where
+ F: Fn(ty::AdtDef<'tcx>, SubstsRef<'tcx>) -> NeedsDropResult<I>,
+ I: Iterator<Item = Ty<'tcx>>,
+{
+ type Item = NeedsDropResult<Ty<'tcx>>;
+
+ fn next(&mut self) -> Option<NeedsDropResult<Ty<'tcx>>> {
+ let tcx = self.tcx;
+
+ while let Some((ty, level)) = self.unchecked_tys.pop() {
+ if !self.recursion_limit.value_within_limit(level) {
+ // Not having a `Span` isn't great. But there's hopefully some other
+ // recursion limit error as well.
+ tcx.sess.span_err(
+ DUMMY_SP,
+ &format!("overflow while checking whether `{}` requires drop", self.query_ty),
+ );
+ return Some(Err(AlwaysRequiresDrop));
+ }
+
+ let components = match needs_drop_components(ty, &tcx.data_layout) {
+ Err(e) => return Some(Err(e)),
+ Ok(components) => components,
+ };
+ debug!("needs_drop_components({:?}) = {:?}", ty, components);
+
+ let queue_type = move |this: &mut Self, component: Ty<'tcx>| {
+ if this.seen_tys.insert(component) {
+ this.unchecked_tys.push((component, level + 1));
+ }
+ };
+
+ for component in components {
+ match *component.kind() {
+ _ if component.is_copy_modulo_regions(tcx.at(DUMMY_SP), self.param_env) => (),
+
+ ty::Closure(_, substs) => {
+ queue_type(self, substs.as_closure().tupled_upvars_ty());
+ }
+
+ ty::Generator(def_id, substs, _) => {
+ let substs = substs.as_generator();
+ queue_type(self, substs.tupled_upvars_ty());
+
+ let witness = substs.witness();
+ let interior_tys = match witness.kind() {
+ &ty::GeneratorWitness(tys) => tcx.erase_late_bound_regions(tys),
+ _ => {
+ tcx.sess.delay_span_bug(
+ tcx.hir().span_if_local(def_id).unwrap_or(DUMMY_SP),
+ &format!("unexpected generator witness type {:?}", witness),
+ );
+ return Some(Err(AlwaysRequiresDrop));
+ }
+ };
+
+ for interior_ty in interior_tys {
+ queue_type(self, interior_ty);
+ }
+ }
+
+ // Check for a `Drop` impl and whether this is a union or
+ // `ManuallyDrop`. If it's a struct or enum without a `Drop`
+ // impl then check whether the field types need `Drop`.
+ ty::Adt(adt_def, substs) => {
+ let tys = match (self.adt_components)(adt_def, substs) {
+ Err(e) => return Some(Err(e)),
+ Ok(tys) => tys,
+ };
+ for required_ty in tys {
+ let required = tcx
+ .try_normalize_erasing_regions(self.param_env, required_ty)
+ .unwrap_or(required_ty);
+
+ queue_type(self, required);
+ }
+ }
+ ty::Array(..) | ty::Opaque(..) | ty::Projection(..) | ty::Param(_) => {
+ if ty == component {
+ // Return the type to the caller: they may be able
+ // to normalize further than we can.
+ return Some(Ok(component));
+ } else {
+ // Store the type for later. We can't return here
+ // because we would then lose any other components
+ // of the type.
+ queue_type(self, component);
+ }
+ }
+ _ => return Some(Err(AlwaysRequiresDrop)),
+ }
+ }
+ }
+
+ None
+ }
+}
+
+enum DtorType {
+ /// Type has a `Drop` but it is considered insignificant.
+ /// Check the query `adt_significant_drop_tys` for understanding
+ /// "significant" / "insignificant".
+ Insignificant,
+
+ /// Type has a `Drop` implantation.
+ Significant,
+}
+
+// This is a helper function for `adt_drop_tys` and `adt_significant_drop_tys`.
+// Depending on the implantation of `adt_has_dtor`, it is used to check if the
+// ADT has a destructor or if the ADT only has a significant destructor. For
+// understanding significant destructor look at `adt_significant_drop_tys`.
+fn drop_tys_helper<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ ty: Ty<'tcx>,
+ param_env: rustc_middle::ty::ParamEnv<'tcx>,
+ adt_has_dtor: impl Fn(ty::AdtDef<'tcx>) -> Option<DtorType>,
+ only_significant: bool,
+) -> impl Iterator<Item = NeedsDropResult<Ty<'tcx>>> {
+ fn with_query_cache<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ iter: impl IntoIterator<Item = Ty<'tcx>>,
+ ) -> NeedsDropResult<Vec<Ty<'tcx>>> {
+ iter.into_iter().try_fold(Vec::new(), |mut vec, subty| {
+ match subty.kind() {
+ ty::Adt(adt_id, subst) => {
+ for subty in tcx.adt_drop_tys(adt_id.did())? {
+ vec.push(EarlyBinder(subty).subst(tcx, subst));
+ }
+ }
+ _ => vec.push(subty),
+ };
+ Ok(vec)
+ })
+ }
+
+ let adt_components = move |adt_def: ty::AdtDef<'tcx>, substs: SubstsRef<'tcx>| {
+ if adt_def.is_manually_drop() {
+ debug!("drop_tys_helper: `{:?}` is manually drop", adt_def);
+ Ok(Vec::new())
+ } else if let Some(dtor_info) = adt_has_dtor(adt_def) {
+ match dtor_info {
+ DtorType::Significant => {
+ debug!("drop_tys_helper: `{:?}` implements `Drop`", adt_def);
+ Err(AlwaysRequiresDrop)
+ }
+ DtorType::Insignificant => {
+ debug!("drop_tys_helper: `{:?}` drop is insignificant", adt_def);
+
+ // Since the destructor is insignificant, we just want to make sure all of
+ // the passed in type parameters are also insignificant.
+ // Eg: Vec<T> dtor is insignificant when T=i32 but significant when T=Mutex.
+ Ok(substs.types().collect())
+ }
+ }
+ } else if adt_def.is_union() {
+ debug!("drop_tys_helper: `{:?}` is a union", adt_def);
+ Ok(Vec::new())
+ } else {
+ let field_tys = adt_def.all_fields().map(|field| {
+ let r = tcx.bound_type_of(field.did).subst(tcx, substs);
+ debug!("drop_tys_helper: Subst into {:?} with {:?} gettng {:?}", field, substs, r);
+ r
+ });
+ if only_significant {
+ // We can't recurse through the query system here because we might induce a cycle
+ Ok(field_tys.collect())
+ } else {
+ // We can use the query system if we consider all drops significant. In that case,
+ // ADTs are `needs_drop` exactly if they `impl Drop` or if any of their "transitive"
+ // fields do. There can be no cycles here, because ADTs cannot contain themselves as
+ // fields.
+ with_query_cache(tcx, field_tys)
+ }
+ }
+ .map(|v| v.into_iter())
+ };
+
+ NeedsDropTypes::new(tcx, param_env, ty, adt_components)
+}
+
+fn adt_consider_insignificant_dtor<'tcx>(
+ tcx: TyCtxt<'tcx>,
+) -> impl Fn(ty::AdtDef<'tcx>) -> Option<DtorType> + 'tcx {
+ move |adt_def: ty::AdtDef<'tcx>| {
+ let is_marked_insig = tcx.has_attr(adt_def.did(), sym::rustc_insignificant_dtor);
+ if is_marked_insig {
+ // In some cases like `std::collections::HashMap` where the struct is a wrapper around
+ // a type that is a Drop type, and the wrapped type (eg: `hashbrown::HashMap`) lies
+ // outside stdlib, we might choose to still annotate the the wrapper (std HashMap) with
+ // `rustc_insignificant_dtor`, even if the type itself doesn't have a `Drop` impl.
+ Some(DtorType::Insignificant)
+ } else if adt_def.destructor(tcx).is_some() {
+ // There is a Drop impl and the type isn't marked insignificant, therefore Drop must be
+ // significant.
+ Some(DtorType::Significant)
+ } else {
+ // No destructor found nor the type is annotated with `rustc_insignificant_dtor`, we
+ // treat this as the simple case of Drop impl for type.
+ None
+ }
+ }
+}
+
+fn adt_drop_tys<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ def_id: DefId,
+) -> Result<&ty::List<Ty<'tcx>>, AlwaysRequiresDrop> {
+ // This is for the "adt_drop_tys" query, that considers all `Drop` impls, therefore all dtors are
+ // significant.
+ let adt_has_dtor =
+ |adt_def: ty::AdtDef<'tcx>| adt_def.destructor(tcx).map(|_| DtorType::Significant);
+ // `tcx.type_of(def_id)` identical to `tcx.make_adt(def, identity_substs)`
+ drop_tys_helper(tcx, tcx.type_of(def_id), tcx.param_env(def_id), adt_has_dtor, false)
+ .collect::<Result<Vec<_>, _>>()
+ .map(|components| tcx.intern_type_list(&components))
+}
+// If `def_id` refers to a generic ADT, the queries above and below act as if they had been handed
+// a `tcx.make_ty(def, identity_substs)` and as such it is legal to substitute the generic parameters
+// of the ADT into the outputted `ty`s.
+fn adt_significant_drop_tys(
+ tcx: TyCtxt<'_>,
+ def_id: DefId,
+) -> Result<&ty::List<Ty<'_>>, AlwaysRequiresDrop> {
+ drop_tys_helper(
+ tcx,
+ tcx.type_of(def_id), // identical to `tcx.make_adt(def, identity_substs)`
+ tcx.param_env(def_id),
+ adt_consider_insignificant_dtor(tcx),
+ true,
+ )
+ .collect::<Result<Vec<_>, _>>()
+ .map(|components| tcx.intern_type_list(&components))
+}
+
+pub(crate) fn provide(providers: &mut ty::query::Providers) {
+ *providers = ty::query::Providers {
+ needs_drop_raw,
+ has_significant_drop_raw,
+ adt_drop_tys,
+ adt_significant_drop_tys,
+ ..*providers
+ };
+}
diff --git a/compiler/rustc_ty_utils/src/representability.rs b/compiler/rustc_ty_utils/src/representability.rs
new file mode 100644
index 000000000..eded78916
--- /dev/null
+++ b/compiler/rustc_ty_utils/src/representability.rs
@@ -0,0 +1,386 @@
+//! Check whether a type is representable.
+use rustc_data_structures::fx::FxHashMap;
+use rustc_hir as hir;
+use rustc_middle::ty::{self, Ty, TyCtxt};
+use rustc_span::Span;
+use std::cmp;
+
+/// Describes whether a type is representable. For types that are not
+/// representable, 'SelfRecursive' and 'ContainsRecursive' are used to
+/// distinguish between types that are recursive with themselves and types that
+/// contain a different recursive type. These cases can therefore be treated
+/// differently when reporting errors.
+///
+/// The ordering of the cases is significant. They are sorted so that cmp::max
+/// will keep the "more erroneous" of two values.
+#[derive(Clone, PartialOrd, Ord, Eq, PartialEq, Debug)]
+pub enum Representability {
+ Representable,
+ ContainsRecursive,
+ /// Return a list of types that are included in themselves:
+ /// the spans where they are self-included, and (if found)
+ /// the HirId of the FieldDef that defines the self-inclusion.
+ SelfRecursive(Vec<(Span, Option<hir::HirId>)>),
+}
+
+/// Check whether a type is representable. This means it cannot contain unboxed
+/// structural recursion. This check is needed for structs and enums.
+pub fn ty_is_representable<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ ty: Ty<'tcx>,
+ sp: Span,
+ field_id: Option<hir::HirId>,
+) -> Representability {
+ debug!("is_type_representable: {:?}", ty);
+ // To avoid a stack overflow when checking an enum variant or struct that
+ // contains a different, structurally recursive type, maintain a stack of
+ // seen types and check recursion for each of them (issues #3008, #3779,
+ // #74224, #84611). `shadow_seen` contains the full stack and `seen` only
+ // the one for the current type (e.g. if we have structs A and B, B contains
+ // a field of type A, and we're currently looking at B, then `seen` will be
+ // cleared when recursing to check A, but `shadow_seen` won't, so that we
+ // can catch cases of mutual recursion where A also contains B).
+ let mut seen: Vec<Ty<'_>> = Vec::new();
+ let mut shadow_seen: Vec<ty::AdtDef<'tcx>> = Vec::new();
+ let mut representable_cache = FxHashMap::default();
+ let mut force_result = false;
+ let r = is_type_structurally_recursive(
+ tcx,
+ &mut seen,
+ &mut shadow_seen,
+ &mut representable_cache,
+ ty,
+ sp,
+ field_id,
+ &mut force_result,
+ );
+ debug!("is_type_representable: {:?} is {:?}", ty, r);
+ r
+}
+
+// Iterate until something non-representable is found
+fn fold_repr<It: Iterator<Item = Representability>>(iter: It) -> Representability {
+ iter.fold(Representability::Representable, |r1, r2| match (r1, r2) {
+ (Representability::SelfRecursive(v1), Representability::SelfRecursive(v2)) => {
+ Representability::SelfRecursive(v1.into_iter().chain(v2).collect())
+ }
+ (r1, r2) => cmp::max(r1, r2),
+ })
+}
+
+fn are_inner_types_recursive<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ seen: &mut Vec<Ty<'tcx>>,
+ shadow_seen: &mut Vec<ty::AdtDef<'tcx>>,
+ representable_cache: &mut FxHashMap<Ty<'tcx>, Representability>,
+ ty: Ty<'tcx>,
+ sp: Span,
+ field_id: Option<hir::HirId>,
+ force_result: &mut bool,
+) -> Representability {
+ debug!("are_inner_types_recursive({:?}, {:?}, {:?})", ty, seen, shadow_seen);
+ match ty.kind() {
+ ty::Tuple(fields) => {
+ // Find non representable
+ fold_repr(fields.iter().map(|ty| {
+ is_type_structurally_recursive(
+ tcx,
+ seen,
+ shadow_seen,
+ representable_cache,
+ ty,
+ sp,
+ field_id,
+ force_result,
+ )
+ }))
+ }
+ // Fixed-length vectors.
+ // FIXME(#11924) Behavior undecided for zero-length vectors.
+ ty::Array(ty, _) => is_type_structurally_recursive(
+ tcx,
+ seen,
+ shadow_seen,
+ representable_cache,
+ *ty,
+ sp,
+ field_id,
+ force_result,
+ ),
+ ty::Adt(def, substs) => {
+ // Find non representable fields with their spans
+ fold_repr(def.all_fields().map(|field| {
+ let ty = field.ty(tcx, substs);
+ let (sp, field_id) = match field
+ .did
+ .as_local()
+ .map(|id| tcx.hir().local_def_id_to_hir_id(id))
+ .and_then(|id| tcx.hir().find(id))
+ {
+ Some(hir::Node::Field(field)) => (field.ty.span, Some(field.hir_id)),
+ _ => (sp, field_id),
+ };
+
+ let mut result = None;
+
+ // First, we check whether the field type per se is representable.
+ // This catches cases as in #74224 and #84611. There is a special
+ // case related to mutual recursion, though; consider this example:
+ //
+ // struct A<T> {
+ // z: T,
+ // x: B<T>,
+ // }
+ //
+ // struct B<T> {
+ // y: A<T>
+ // }
+ //
+ // Here, without the following special case, both A and B are
+ // ContainsRecursive, which is a problem because we only report
+ // errors for SelfRecursive. We fix this by detecting this special
+ // case (shadow_seen.first() is the type we are originally
+ // interested in, and if we ever encounter the same AdtDef again,
+ // we know that it must be SelfRecursive) and "forcibly" returning
+ // SelfRecursive (by setting force_result, which tells the calling
+ // invocations of are_inner_types_representable to forward the
+ // result without adjusting).
+ if shadow_seen.len() > seen.len() && shadow_seen.first() == Some(def) {
+ *force_result = true;
+ result = Some(Representability::SelfRecursive(vec![(sp, field_id)]));
+ }
+
+ if result == None {
+ result = Some(Representability::Representable);
+
+ // Now, we check whether the field types per se are representable, e.g.
+ // for struct Foo { x: Option<Foo> }, we first check whether Option<_>
+ // by itself is representable (which it is), and the nesting of Foo
+ // will be detected later. This is necessary for #74224 and #84611.
+
+ // If we have encountered an ADT definition that we have not seen
+ // before (no need to check them twice), recurse to see whether that
+ // definition is SelfRecursive. If so, we must be ContainsRecursive.
+ if shadow_seen.len() > 1
+ && !shadow_seen
+ .iter()
+ .take(shadow_seen.len() - 1)
+ .any(|seen_def| seen_def == def)
+ {
+ let adt_def_id = def.did();
+ let raw_adt_ty = tcx.type_of(adt_def_id);
+ debug!("are_inner_types_recursive: checking nested type: {:?}", raw_adt_ty);
+
+ // Check independently whether the ADT is SelfRecursive. If so,
+ // we must be ContainsRecursive (except for the special case
+ // mentioned above).
+ let mut nested_seen: Vec<Ty<'_>> = vec![];
+ result = Some(
+ match is_type_structurally_recursive(
+ tcx,
+ &mut nested_seen,
+ shadow_seen,
+ representable_cache,
+ raw_adt_ty,
+ sp,
+ field_id,
+ force_result,
+ ) {
+ Representability::SelfRecursive(_) => {
+ if *force_result {
+ Representability::SelfRecursive(vec![(sp, field_id)])
+ } else {
+ Representability::ContainsRecursive
+ }
+ }
+ x => x,
+ },
+ );
+ }
+
+ // We only enter the following block if the type looks representable
+ // so far. This is necessary for cases such as this one (#74224):
+ //
+ // struct A<T> {
+ // x: T,
+ // y: A<A<T>>,
+ // }
+ //
+ // struct B {
+ // z: A<usize>
+ // }
+ //
+ // When checking B, we recurse into A and check field y of type
+ // A<A<usize>>. We haven't seen this exact type before, so we recurse
+ // into A<A<usize>>, which contains, A<A<A<usize>>>, and so forth,
+ // ad infinitum. We can prevent this from happening by first checking
+ // A separately (the code above) and only checking for nested Bs if
+ // A actually looks representable (which it wouldn't in this example).
+ if result == Some(Representability::Representable) {
+ // Now, even if the type is representable (e.g. Option<_>),
+ // it might still contribute to a recursive type, e.g.:
+ // struct Foo { x: Option<Option<Foo>> }
+ // These cases are handled by passing the full `seen`
+ // stack to is_type_structurally_recursive (instead of the
+ // empty `nested_seen` above):
+ result = Some(
+ match is_type_structurally_recursive(
+ tcx,
+ seen,
+ shadow_seen,
+ representable_cache,
+ ty,
+ sp,
+ field_id,
+ force_result,
+ ) {
+ Representability::SelfRecursive(_) => {
+ Representability::SelfRecursive(vec![(sp, field_id)])
+ }
+ x => x,
+ },
+ );
+ }
+ }
+
+ result.unwrap()
+ }))
+ }
+ ty::Closure(..) => {
+ // this check is run on type definitions, so we don't expect
+ // to see closure types
+ bug!("requires check invoked on inapplicable type: {:?}", ty)
+ }
+ _ => Representability::Representable,
+ }
+}
+
+fn same_adt<'tcx>(ty: Ty<'tcx>, def: ty::AdtDef<'tcx>) -> bool {
+ match *ty.kind() {
+ ty::Adt(ty_def, _) => ty_def == def,
+ _ => false,
+ }
+}
+
+// Does the type `ty` directly (without indirection through a pointer)
+// contain any types on stack `seen`?
+fn is_type_structurally_recursive<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ seen: &mut Vec<Ty<'tcx>>,
+ shadow_seen: &mut Vec<ty::AdtDef<'tcx>>,
+ representable_cache: &mut FxHashMap<Ty<'tcx>, Representability>,
+ ty: Ty<'tcx>,
+ sp: Span,
+ field_id: Option<hir::HirId>,
+ force_result: &mut bool,
+) -> Representability {
+ debug!("is_type_structurally_recursive: {:?} {:?} {:?}", ty, sp, field_id);
+ if let Some(representability) = representable_cache.get(&ty) {
+ debug!(
+ "is_type_structurally_recursive: {:?} {:?} {:?} - (cached) {:?}",
+ ty, sp, field_id, representability
+ );
+ return representability.clone();
+ }
+
+ let representability = is_type_structurally_recursive_inner(
+ tcx,
+ seen,
+ shadow_seen,
+ representable_cache,
+ ty,
+ sp,
+ field_id,
+ force_result,
+ );
+
+ representable_cache.insert(ty, representability.clone());
+ representability
+}
+
+fn is_type_structurally_recursive_inner<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ seen: &mut Vec<Ty<'tcx>>,
+ shadow_seen: &mut Vec<ty::AdtDef<'tcx>>,
+ representable_cache: &mut FxHashMap<Ty<'tcx>, Representability>,
+ ty: Ty<'tcx>,
+ sp: Span,
+ field_id: Option<hir::HirId>,
+ force_result: &mut bool,
+) -> Representability {
+ match ty.kind() {
+ ty::Adt(def, _) => {
+ {
+ debug!("is_type_structurally_recursive_inner: adt: {:?}, seen: {:?}", ty, seen);
+
+ // Iterate through stack of previously seen types.
+ let mut iter = seen.iter();
+
+ // The first item in `seen` is the type we are actually curious about.
+ // We want to return SelfRecursive if this type contains itself.
+ // It is important that we DON'T take generic parameters into account
+ // for this check, so that Bar<T> in this example counts as SelfRecursive:
+ //
+ // struct Foo;
+ // struct Bar<T> { x: Bar<Foo> }
+
+ if let Some(&seen_adt) = iter.next() {
+ if same_adt(seen_adt, *def) {
+ debug!("SelfRecursive: {:?} contains {:?}", seen_adt, ty);
+ return Representability::SelfRecursive(vec![(sp, field_id)]);
+ }
+ }
+
+ // We also need to know whether the first item contains other types
+ // that are structurally recursive. If we don't catch this case, we
+ // will recurse infinitely for some inputs.
+ //
+ // It is important that we DO take generic parameters into account
+ // here, because nesting e.g. Options is allowed (as long as the
+ // definition of Option doesn't itself include an Option field, which
+ // would be a case of SelfRecursive above). The following, too, counts
+ // as SelfRecursive:
+ //
+ // struct Foo { Option<Option<Foo>> }
+
+ for &seen_adt in iter {
+ if ty == seen_adt {
+ debug!("ContainsRecursive: {:?} contains {:?}", seen_adt, ty);
+ return Representability::ContainsRecursive;
+ }
+ }
+ }
+
+ // For structs and enums, track all previously seen types by pushing them
+ // onto the 'seen' stack.
+ seen.push(ty);
+ shadow_seen.push(*def);
+ let out = are_inner_types_recursive(
+ tcx,
+ seen,
+ shadow_seen,
+ representable_cache,
+ ty,
+ sp,
+ field_id,
+ force_result,
+ );
+ shadow_seen.pop();
+ seen.pop();
+ out
+ }
+ _ => {
+ // No need to push in other cases.
+ are_inner_types_recursive(
+ tcx,
+ seen,
+ shadow_seen,
+ representable_cache,
+ ty,
+ sp,
+ field_id,
+ force_result,
+ )
+ }
+ }
+}
diff --git a/compiler/rustc_ty_utils/src/ty.rs b/compiler/rustc_ty_utils/src/ty.rs
new file mode 100644
index 000000000..db0d45b86
--- /dev/null
+++ b/compiler/rustc_ty_utils/src/ty.rs
@@ -0,0 +1,481 @@
+use rustc_data_structures::fx::FxIndexSet;
+use rustc_hir as hir;
+use rustc_hir::def_id::DefId;
+use rustc_middle::ty::subst::Subst;
+use rustc_middle::ty::{self, Binder, Predicate, PredicateKind, ToPredicate, Ty, TyCtxt};
+use rustc_trait_selection::traits;
+
+fn sized_constraint_for_ty<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ adtdef: ty::AdtDef<'tcx>,
+ ty: Ty<'tcx>,
+) -> Vec<Ty<'tcx>> {
+ use rustc_type_ir::sty::TyKind::*;
+
+ let result = match ty.kind() {
+ Bool | Char | Int(..) | Uint(..) | Float(..) | RawPtr(..) | Ref(..) | FnDef(..)
+ | FnPtr(_) | Array(..) | Closure(..) | Generator(..) | Never => vec![],
+
+ Str | Dynamic(..) | Slice(_) | Foreign(..) | Error(_) | GeneratorWitness(..) => {
+ // these are never sized - return the target type
+ vec![ty]
+ }
+
+ Tuple(ref tys) => match tys.last() {
+ None => vec![],
+ Some(&ty) => sized_constraint_for_ty(tcx, adtdef, ty),
+ },
+
+ Adt(adt, substs) => {
+ // recursive case
+ let adt_tys = adt.sized_constraint(tcx);
+ debug!("sized_constraint_for_ty({:?}) intermediate = {:?}", ty, adt_tys);
+ adt_tys
+ .0
+ .iter()
+ .map(|ty| adt_tys.rebind(*ty).subst(tcx, substs))
+ .flat_map(|ty| sized_constraint_for_ty(tcx, adtdef, ty))
+ .collect()
+ }
+
+ Projection(..) | Opaque(..) => {
+ // must calculate explicitly.
+ // FIXME: consider special-casing always-Sized projections
+ vec![ty]
+ }
+
+ Param(..) => {
+ // perf hack: if there is a `T: Sized` bound, then
+ // we know that `T` is Sized and do not need to check
+ // it on the impl.
+
+ let Some(sized_trait) = tcx.lang_items().sized_trait() else { return vec![ty] };
+ let sized_predicate = ty::Binder::dummy(ty::TraitRef {
+ def_id: sized_trait,
+ substs: tcx.mk_substs_trait(ty, &[]),
+ })
+ .without_const()
+ .to_predicate(tcx);
+ let predicates = tcx.predicates_of(adtdef.did()).predicates;
+ if predicates.iter().any(|(p, _)| *p == sized_predicate) { vec![] } else { vec![ty] }
+ }
+
+ Placeholder(..) | Bound(..) | Infer(..) => {
+ bug!("unexpected type `{:?}` in sized_constraint_for_ty", ty)
+ }
+ };
+ debug!("sized_constraint_for_ty({:?}) = {:?}", ty, result);
+ result
+}
+
+fn impl_defaultness(tcx: TyCtxt<'_>, def_id: DefId) -> hir::Defaultness {
+ match tcx.hir().get_by_def_id(def_id.expect_local()) {
+ hir::Node::Item(hir::Item { kind: hir::ItemKind::Impl(impl_), .. }) => impl_.defaultness,
+ hir::Node::ImplItem(hir::ImplItem { defaultness, .. })
+ | hir::Node::TraitItem(hir::TraitItem { defaultness, .. }) => *defaultness,
+ node => {
+ bug!("`impl_defaultness` called on {:?}", node);
+ }
+ }
+}
+
+/// Calculates the `Sized` constraint.
+///
+/// In fact, there are only a few options for the types in the constraint:
+/// - an obviously-unsized type
+/// - a type parameter or projection whose Sizedness can't be known
+/// - a tuple of type parameters or projections, if there are multiple
+/// such.
+/// - an Error, if a type contained itself. The representability
+/// check should catch this case.
+fn adt_sized_constraint(tcx: TyCtxt<'_>, def_id: DefId) -> ty::AdtSizedConstraint<'_> {
+ let def = tcx.adt_def(def_id);
+
+ let result = tcx.mk_type_list(
+ def.variants()
+ .iter()
+ .flat_map(|v| v.fields.last())
+ .flat_map(|f| sized_constraint_for_ty(tcx, def, tcx.type_of(f.did))),
+ );
+
+ debug!("adt_sized_constraint: {:?} => {:?}", def, result);
+
+ ty::AdtSizedConstraint(result)
+}
+
+/// See `ParamEnv` struct definition for details.
+#[instrument(level = "debug", skip(tcx))]
+fn param_env(tcx: TyCtxt<'_>, def_id: DefId) -> ty::ParamEnv<'_> {
+ // The param_env of an impl Trait type is its defining function's param_env
+ if let Some(parent) = ty::is_impl_trait_defn(tcx, def_id) {
+ return param_env(tcx, parent.to_def_id());
+ }
+ // Compute the bounds on Self and the type parameters.
+
+ let ty::InstantiatedPredicates { mut predicates, .. } =
+ tcx.predicates_of(def_id).instantiate_identity(tcx);
+
+ // Finally, we have to normalize the bounds in the environment, in
+ // case they contain any associated type projections. This process
+ // can yield errors if the put in illegal associated types, like
+ // `<i32 as Foo>::Bar` where `i32` does not implement `Foo`. We
+ // report these errors right here; this doesn't actually feel
+ // right to me, because constructing the environment feels like a
+ // kind of an "idempotent" action, but I'm not sure where would be
+ // a better place. In practice, we construct environments for
+ // every fn once during type checking, and we'll abort if there
+ // are any errors at that point, so outside of type inference you can be
+ // sure that this will succeed without errors anyway.
+
+ if tcx.sess.opts.unstable_opts.chalk {
+ let environment = well_formed_types_in_env(tcx, def_id);
+ predicates.extend(environment);
+ }
+
+ let local_did = def_id.as_local();
+ let hir_id = local_did.map(|def_id| tcx.hir().local_def_id_to_hir_id(def_id));
+
+ let constness = match hir_id {
+ Some(hir_id) => match tcx.hir().get(hir_id) {
+ hir::Node::TraitItem(hir::TraitItem { kind: hir::TraitItemKind::Fn(..), .. })
+ if tcx.is_const_default_method(def_id) =>
+ {
+ hir::Constness::Const
+ }
+
+ hir::Node::Item(hir::Item { kind: hir::ItemKind::Const(..), .. })
+ | hir::Node::Item(hir::Item { kind: hir::ItemKind::Static(..), .. })
+ | hir::Node::TraitItem(hir::TraitItem {
+ kind: hir::TraitItemKind::Const(..), ..
+ })
+ | hir::Node::AnonConst(_)
+ | hir::Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Const(..), .. })
+ | hir::Node::ImplItem(hir::ImplItem {
+ kind:
+ hir::ImplItemKind::Fn(
+ hir::FnSig {
+ header: hir::FnHeader { constness: hir::Constness::Const, .. },
+ ..
+ },
+ ..,
+ ),
+ ..
+ }) => hir::Constness::Const,
+
+ hir::Node::ImplItem(hir::ImplItem {
+ kind: hir::ImplItemKind::TyAlias(..) | hir::ImplItemKind::Fn(..),
+ ..
+ }) => {
+ let parent_hir_id = tcx.hir().get_parent_node(hir_id);
+ match tcx.hir().get(parent_hir_id) {
+ hir::Node::Item(hir::Item {
+ kind: hir::ItemKind::Impl(hir::Impl { constness, .. }),
+ ..
+ }) => *constness,
+ _ => span_bug!(
+ tcx.def_span(parent_hir_id.owner),
+ "impl item's parent node is not an impl",
+ ),
+ }
+ }
+
+ hir::Node::Item(hir::Item {
+ kind:
+ hir::ItemKind::Fn(hir::FnSig { header: hir::FnHeader { constness, .. }, .. }, ..),
+ ..
+ })
+ | hir::Node::TraitItem(hir::TraitItem {
+ kind:
+ hir::TraitItemKind::Fn(
+ hir::FnSig { header: hir::FnHeader { constness, .. }, .. },
+ ..,
+ ),
+ ..
+ })
+ | hir::Node::Item(hir::Item {
+ kind: hir::ItemKind::Impl(hir::Impl { constness, .. }),
+ ..
+ }) => *constness,
+
+ _ => hir::Constness::NotConst,
+ },
+ None => hir::Constness::NotConst,
+ };
+
+ let unnormalized_env = ty::ParamEnv::new(
+ tcx.intern_predicates(&predicates),
+ traits::Reveal::UserFacing,
+ constness,
+ );
+
+ let body_id =
+ local_did.and_then(|id| tcx.hir().maybe_body_owned_by(id).map(|body| body.hir_id));
+ let body_id = match body_id {
+ Some(id) => id,
+ None if hir_id.is_some() => hir_id.unwrap(),
+ _ => hir::CRATE_HIR_ID,
+ };
+
+ let cause = traits::ObligationCause::misc(tcx.def_span(def_id), body_id);
+ traits::normalize_param_env_or_error(tcx, unnormalized_env, cause)
+}
+
+/// Elaborate the environment.
+///
+/// Collect a list of `Predicate`'s used for building the `ParamEnv`. Adds `TypeWellFormedFromEnv`'s
+/// that are assumed to be well-formed (because they come from the environment).
+///
+/// Used only in chalk mode.
+fn well_formed_types_in_env<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ def_id: DefId,
+) -> &'tcx ty::List<Predicate<'tcx>> {
+ use rustc_hir::{ForeignItemKind, ImplItemKind, ItemKind, Node, TraitItemKind};
+ use rustc_middle::ty::subst::GenericArgKind;
+
+ debug!("environment(def_id = {:?})", def_id);
+
+ // The environment of an impl Trait type is its defining function's environment.
+ if let Some(parent) = ty::is_impl_trait_defn(tcx, def_id) {
+ return well_formed_types_in_env(tcx, parent.to_def_id());
+ }
+
+ // Compute the bounds on `Self` and the type parameters.
+ let ty::InstantiatedPredicates { predicates, .. } =
+ tcx.predicates_of(def_id).instantiate_identity(tcx);
+
+ let clauses = predicates.into_iter();
+
+ if !def_id.is_local() {
+ return ty::List::empty();
+ }
+ let node = tcx.hir().get_by_def_id(def_id.expect_local());
+
+ enum NodeKind {
+ TraitImpl,
+ InherentImpl,
+ Fn,
+ Other,
+ }
+
+ let node_kind = match node {
+ Node::TraitItem(item) => match item.kind {
+ TraitItemKind::Fn(..) => NodeKind::Fn,
+ _ => NodeKind::Other,
+ },
+
+ Node::ImplItem(item) => match item.kind {
+ ImplItemKind::Fn(..) => NodeKind::Fn,
+ _ => NodeKind::Other,
+ },
+
+ Node::Item(item) => match item.kind {
+ ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }) => NodeKind::TraitImpl,
+ ItemKind::Impl(hir::Impl { of_trait: None, .. }) => NodeKind::InherentImpl,
+ ItemKind::Fn(..) => NodeKind::Fn,
+ _ => NodeKind::Other,
+ },
+
+ Node::ForeignItem(item) => match item.kind {
+ ForeignItemKind::Fn(..) => NodeKind::Fn,
+ _ => NodeKind::Other,
+ },
+
+ // FIXME: closures?
+ _ => NodeKind::Other,
+ };
+
+ // FIXME(eddyb) isn't the unordered nature of this a hazard?
+ let mut inputs = FxIndexSet::default();
+
+ match node_kind {
+ // In a trait impl, we assume that the header trait ref and all its
+ // constituents are well-formed.
+ NodeKind::TraitImpl => {
+ let trait_ref = tcx.impl_trait_ref(def_id).expect("not an impl");
+
+ // FIXME(chalk): this has problems because of late-bound regions
+ //inputs.extend(trait_ref.substs.iter().flat_map(|arg| arg.walk()));
+ inputs.extend(trait_ref.substs.iter());
+ }
+
+ // In an inherent impl, we assume that the receiver type and all its
+ // constituents are well-formed.
+ NodeKind::InherentImpl => {
+ let self_ty = tcx.type_of(def_id);
+ inputs.extend(self_ty.walk());
+ }
+
+ // In an fn, we assume that the arguments and all their constituents are
+ // well-formed.
+ NodeKind::Fn => {
+ let fn_sig = tcx.fn_sig(def_id);
+ let fn_sig = tcx.liberate_late_bound_regions(def_id, fn_sig);
+
+ inputs.extend(fn_sig.inputs().iter().flat_map(|ty| ty.walk()));
+ }
+
+ NodeKind::Other => (),
+ }
+ let input_clauses = inputs.into_iter().filter_map(|arg| {
+ match arg.unpack() {
+ GenericArgKind::Type(ty) => {
+ let binder = Binder::dummy(PredicateKind::TypeWellFormedFromEnv(ty));
+ Some(tcx.mk_predicate(binder))
+ }
+
+ // FIXME(eddyb) no WF conditions from lifetimes?
+ GenericArgKind::Lifetime(_) => None,
+
+ // FIXME(eddyb) support const generics in Chalk
+ GenericArgKind::Const(_) => None,
+ }
+ });
+
+ tcx.mk_predicates(clauses.chain(input_clauses))
+}
+
+fn param_env_reveal_all_normalized(tcx: TyCtxt<'_>, def_id: DefId) -> ty::ParamEnv<'_> {
+ tcx.param_env(def_id).with_reveal_all_normalized(tcx)
+}
+
+fn instance_def_size_estimate<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ instance_def: ty::InstanceDef<'tcx>,
+) -> usize {
+ use ty::InstanceDef;
+
+ match instance_def {
+ InstanceDef::Item(..) | InstanceDef::DropGlue(..) => {
+ let mir = tcx.instance_mir(instance_def);
+ mir.basic_blocks().iter().map(|bb| bb.statements.len() + 1).sum()
+ }
+ // Estimate the size of other compiler-generated shims to be 1.
+ _ => 1,
+ }
+}
+
+/// If `def_id` is an issue 33140 hack impl, returns its self type; otherwise, returns `None`.
+///
+/// See [`ty::ImplOverlapKind::Issue33140`] for more details.
+fn issue33140_self_ty(tcx: TyCtxt<'_>, def_id: DefId) -> Option<Ty<'_>> {
+ debug!("issue33140_self_ty({:?})", def_id);
+
+ let trait_ref = tcx
+ .impl_trait_ref(def_id)
+ .unwrap_or_else(|| bug!("issue33140_self_ty called on inherent impl {:?}", def_id));
+
+ debug!("issue33140_self_ty({:?}), trait-ref={:?}", def_id, trait_ref);
+
+ let is_marker_like = tcx.impl_polarity(def_id) == ty::ImplPolarity::Positive
+ && tcx.associated_item_def_ids(trait_ref.def_id).is_empty();
+
+ // Check whether these impls would be ok for a marker trait.
+ if !is_marker_like {
+ debug!("issue33140_self_ty - not marker-like!");
+ return None;
+ }
+
+ // impl must be `impl Trait for dyn Marker1 + Marker2 + ...`
+ if trait_ref.substs.len() != 1 {
+ debug!("issue33140_self_ty - impl has substs!");
+ return None;
+ }
+
+ let predicates = tcx.predicates_of(def_id);
+ if predicates.parent.is_some() || !predicates.predicates.is_empty() {
+ debug!("issue33140_self_ty - impl has predicates {:?}!", predicates);
+ return None;
+ }
+
+ let self_ty = trait_ref.self_ty();
+ let self_ty_matches = match self_ty.kind() {
+ ty::Dynamic(ref data, re) if re.is_static() => data.principal().is_none(),
+ _ => false,
+ };
+
+ if self_ty_matches {
+ debug!("issue33140_self_ty - MATCHES!");
+ Some(self_ty)
+ } else {
+ debug!("issue33140_self_ty - non-matching self type");
+ None
+ }
+}
+
+/// Check if a function is async.
+fn asyncness(tcx: TyCtxt<'_>, def_id: DefId) -> hir::IsAsync {
+ let node = tcx.hir().get_by_def_id(def_id.expect_local());
+ if let Some(fn_kind) = node.fn_kind() { fn_kind.asyncness() } else { hir::IsAsync::NotAsync }
+}
+
+/// Don't call this directly: use ``tcx.conservative_is_privately_uninhabited`` instead.
+#[instrument(level = "debug", skip(tcx))]
+pub fn conservative_is_privately_uninhabited_raw<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ param_env_and: ty::ParamEnvAnd<'tcx, Ty<'tcx>>,
+) -> bool {
+ let (param_env, ty) = param_env_and.into_parts();
+ match ty.kind() {
+ ty::Never => {
+ debug!("ty::Never =>");
+ true
+ }
+ ty::Adt(def, _) if def.is_union() => {
+ debug!("ty::Adt(def, _) if def.is_union() =>");
+ // For now, `union`s are never considered uninhabited.
+ false
+ }
+ ty::Adt(def, substs) => {
+ debug!("ty::Adt(def, _) if def.is_not_union() =>");
+ // Any ADT is uninhabited if either:
+ // (a) It has no variants (i.e. an empty `enum`);
+ // (b) Each of its variants (a single one in the case of a `struct`) has at least
+ // one uninhabited field.
+ def.variants().iter().all(|var| {
+ var.fields.iter().any(|field| {
+ let ty = tcx.bound_type_of(field.did).subst(tcx, substs);
+ tcx.conservative_is_privately_uninhabited(param_env.and(ty))
+ })
+ })
+ }
+ ty::Tuple(fields) => {
+ debug!("ty::Tuple(..) =>");
+ fields.iter().any(|ty| tcx.conservative_is_privately_uninhabited(param_env.and(ty)))
+ }
+ ty::Array(ty, len) => {
+ debug!("ty::Array(ty, len) =>");
+ match len.try_eval_usize(tcx, param_env) {
+ Some(0) | None => false,
+ // If the array is definitely non-empty, it's uninhabited if
+ // the type of its elements is uninhabited.
+ Some(1..) => tcx.conservative_is_privately_uninhabited(param_env.and(*ty)),
+ }
+ }
+ ty::Ref(..) => {
+ debug!("ty::Ref(..) =>");
+ // References to uninitialised memory is valid for any type, including
+ // uninhabited types, in unsafe code, so we treat all references as
+ // inhabited.
+ false
+ }
+ _ => {
+ debug!("_ =>");
+ false
+ }
+ }
+}
+
+pub fn provide(providers: &mut ty::query::Providers) {
+ *providers = ty::query::Providers {
+ asyncness,
+ adt_sized_constraint,
+ param_env,
+ param_env_reveal_all_normalized,
+ instance_def_size_estimate,
+ issue33140_self_ty,
+ impl_defaultness,
+ conservative_is_privately_uninhabited: conservative_is_privately_uninhabited_raw,
+ ..*providers
+ };
+}