diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index 899527ebb9568..1a505a39e4637 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -1,5 +1,3 @@ -// ignore-tidy-filelength - //! "Collection" is the process of determining the type and other external //! details of each item in Rust. Collection is specifically concerned //! with *inter-procedural* things -- for example, for a function @@ -27,18 +25,16 @@ use rustc::hir::map::Map; use rustc::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs}; use rustc::mir::mono::Linkage; use rustc::session::parse::feature_err; -use rustc::traits; use rustc::ty::query::Providers; -use rustc::ty::subst::GenericArgKind; use rustc::ty::subst::{InternalSubsts, Subst}; use rustc::ty::util::Discr; use rustc::ty::util::IntTypeExt; -use rustc::ty::{self, AdtKind, Const, DefIdTree, ToPolyTraitRef, Ty, TyCtxt, TypeFoldable}; +use rustc::ty::{self, AdtKind, Const, ToPolyTraitRef, Ty, TyCtxt}; use rustc::ty::{ReprOptions, ToPredicate, WithConstness}; use rustc_attr::{list_contains_name, mark_used, InlineAttr, OptimizeAttr}; use rustc_data_structures::captures::Captures; use rustc_data_structures::fx::FxHashMap; -use rustc_errors::{struct_span_err, Applicability, StashKey}; +use rustc_errors::{struct_span_err, Applicability}; use rustc_hir as hir; use rustc_hir::def::{CtorKind, DefKind, Res}; use rustc_hir::def_id::{DefId, LOCAL_CRATE}; @@ -50,6 +46,8 @@ use rustc_target::spec::abi; use syntax::ast; use syntax::ast::{Ident, MetaItemKind}; +mod type_of; + struct OnlySelfBounds(bool); /////////////////////////////////////////////////////////////////////////// @@ -64,7 +62,7 @@ fn collect_mod_item_types(tcx: TyCtxt<'_>, module_def_id: DefId) { pub fn provide(providers: &mut Providers<'_>) { *providers = Providers { - type_of, + type_of: type_of::type_of, generics_of, predicates_of, predicates_defined_on, @@ -1329,652 +1327,6 @@ fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> &ty::Generics { }) } -fn report_assoc_ty_on_inherent_impl(tcx: TyCtxt<'_>, span: Span) { - struct_span_err!( - tcx.sess, - span, - E0202, - "associated types are not yet supported in inherent impls (see #8995)" - ) - .emit(); -} - -fn infer_placeholder_type( - tcx: TyCtxt<'_>, - def_id: DefId, - body_id: hir::BodyId, - span: Span, - item_ident: Ident, -) -> Ty<'_> { - let ty = tcx.diagnostic_only_typeck_tables_of(def_id).node_type(body_id.hir_id); - - // If this came from a free `const` or `static mut?` item, - // then the user may have written e.g. `const A = 42;`. - // In this case, the parser has stashed a diagnostic for - // us to improve in typeck so we do that now. - match tcx.sess.diagnostic().steal_diagnostic(span, StashKey::ItemNoType) { - Some(mut err) => { - // The parser provided a sub-optimal `HasPlaceholders` suggestion for the type. - // We are typeck and have the real type, so remove that and suggest the actual type. - err.suggestions.clear(); - err.span_suggestion( - span, - "provide a type for the item", - format!("{}: {}", item_ident, ty), - Applicability::MachineApplicable, - ) - .emit(); - } - None => { - let mut diag = bad_placeholder_type(tcx, vec![span]); - if ty != tcx.types.err { - diag.span_suggestion( - span, - "replace `_` with the correct type", - ty.to_string(), - Applicability::MaybeIncorrect, - ); - } - diag.emit(); - } - } - - ty -} - -fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> { - use rustc_hir::*; - - let hir_id = tcx.hir().as_local_hir_id(def_id).unwrap(); - - let icx = ItemCtxt::new(tcx, def_id); - - match tcx.hir().get(hir_id) { - Node::TraitItem(item) => match item.kind { - TraitItemKind::Method(..) => { - let substs = InternalSubsts::identity_for_item(tcx, def_id); - tcx.mk_fn_def(def_id, substs) - } - TraitItemKind::Const(ref ty, body_id) => body_id - .and_then(|body_id| { - if is_suggestable_infer_ty(ty) { - Some(infer_placeholder_type(tcx, def_id, body_id, ty.span, item.ident)) - } else { - None - } - }) - .unwrap_or_else(|| icx.to_ty(ty)), - TraitItemKind::Type(_, Some(ref ty)) => icx.to_ty(ty), - TraitItemKind::Type(_, None) => { - span_bug!(item.span, "associated type missing default"); - } - }, - - Node::ImplItem(item) => match item.kind { - ImplItemKind::Method(..) => { - let substs = InternalSubsts::identity_for_item(tcx, def_id); - tcx.mk_fn_def(def_id, substs) - } - ImplItemKind::Const(ref ty, body_id) => { - if is_suggestable_infer_ty(ty) { - infer_placeholder_type(tcx, def_id, body_id, ty.span, item.ident) - } else { - icx.to_ty(ty) - } - } - ImplItemKind::OpaqueTy(_) => { - if tcx.impl_trait_ref(tcx.hir().get_parent_did(hir_id)).is_none() { - report_assoc_ty_on_inherent_impl(tcx, item.span); - } - - find_opaque_ty_constraints(tcx, def_id) - } - ImplItemKind::TyAlias(ref ty) => { - if tcx.impl_trait_ref(tcx.hir().get_parent_did(hir_id)).is_none() { - report_assoc_ty_on_inherent_impl(tcx, item.span); - } - - icx.to_ty(ty) - } - }, - - Node::Item(item) => { - match item.kind { - ItemKind::Static(ref ty, .., body_id) | ItemKind::Const(ref ty, body_id) => { - if is_suggestable_infer_ty(ty) { - infer_placeholder_type(tcx, def_id, body_id, ty.span, item.ident) - } else { - icx.to_ty(ty) - } - } - ItemKind::TyAlias(ref self_ty, _) | ItemKind::Impl { ref self_ty, .. } => { - icx.to_ty(self_ty) - } - ItemKind::Fn(..) => { - let substs = InternalSubsts::identity_for_item(tcx, def_id); - tcx.mk_fn_def(def_id, substs) - } - ItemKind::Enum(..) | ItemKind::Struct(..) | ItemKind::Union(..) => { - let def = tcx.adt_def(def_id); - let substs = InternalSubsts::identity_for_item(tcx, def_id); - tcx.mk_adt(def, substs) - } - ItemKind::OpaqueTy(hir::OpaqueTy { impl_trait_fn: None, .. }) => { - find_opaque_ty_constraints(tcx, def_id) - } - // Opaque types desugared from `impl Trait`. - ItemKind::OpaqueTy(hir::OpaqueTy { - impl_trait_fn: Some(owner), origin, .. - }) => { - let concrete_types = match origin { - hir::OpaqueTyOrigin::FnReturn | hir::OpaqueTyOrigin::AsyncFn => { - &tcx.mir_borrowck(owner).concrete_opaque_types - } - hir::OpaqueTyOrigin::Misc => { - // We shouldn't leak borrowck results through impl trait in bindings. - // For example, we shouldn't be able to tell if `x` in - // `let x: impl Sized + 'a = &()` has type `&'static ()` or `&'a ()`. - &tcx.typeck_tables_of(owner).concrete_opaque_types - } - hir::OpaqueTyOrigin::TypeAlias => { - span_bug!(item.span, "Type alias impl trait shouldn't have an owner") - } - }; - let concrete_ty = concrete_types - .get(&def_id) - .map(|opaque| opaque.concrete_type) - .unwrap_or_else(|| { - tcx.sess.delay_span_bug( - DUMMY_SP, - &format!( - "owner {:?} has no opaque type for {:?} in its tables", - owner, def_id, - ), - ); - if tcx.typeck_tables_of(owner).tainted_by_errors { - // Some error in the - // owner fn prevented us from populating - // the `concrete_opaque_types` table. - tcx.types.err - } else { - // We failed to resolve the opaque type or it - // resolves to itself. Return the non-revealed - // type, which should result in E0720. - tcx.mk_opaque( - def_id, - InternalSubsts::identity_for_item(tcx, def_id), - ) - } - }); - debug!("concrete_ty = {:?}", concrete_ty); - if concrete_ty.has_erased_regions() { - // FIXME(impl_trait_in_bindings) Handle this case. - tcx.sess.span_fatal( - item.span, - "lifetimes in impl Trait types in bindings are not currently supported", - ); - } - concrete_ty - } - ItemKind::Trait(..) - | ItemKind::TraitAlias(..) - | ItemKind::Mod(..) - | ItemKind::ForeignMod(..) - | ItemKind::GlobalAsm(..) - | ItemKind::ExternCrate(..) - | ItemKind::Use(..) => { - span_bug!( - item.span, - "compute_type_of_item: unexpected item type: {:?}", - item.kind - ); - } - } - } - - Node::ForeignItem(foreign_item) => match foreign_item.kind { - ForeignItemKind::Fn(..) => { - let substs = InternalSubsts::identity_for_item(tcx, def_id); - tcx.mk_fn_def(def_id, substs) - } - ForeignItemKind::Static(ref t, _) => icx.to_ty(t), - ForeignItemKind::Type => tcx.mk_foreign(def_id), - }, - - Node::Ctor(&ref def) | Node::Variant(hir::Variant { data: ref def, .. }) => match *def { - VariantData::Unit(..) | VariantData::Struct(..) => { - tcx.type_of(tcx.hir().get_parent_did(hir_id)) - } - VariantData::Tuple(..) => { - let substs = InternalSubsts::identity_for_item(tcx, def_id); - tcx.mk_fn_def(def_id, substs) - } - }, - - Node::Field(field) => icx.to_ty(&field.ty), - - Node::Expr(&hir::Expr { kind: hir::ExprKind::Closure(.., gen), .. }) => { - if gen.is_some() { - return tcx.typeck_tables_of(def_id).node_type(hir_id); - } - - let substs = InternalSubsts::identity_for_item(tcx, def_id); - tcx.mk_closure(def_id, substs) - } - - Node::AnonConst(_) => { - let parent_node = tcx.hir().get(tcx.hir().get_parent_node(hir_id)); - match parent_node { - Node::Ty(&hir::Ty { kind: hir::TyKind::Array(_, ref constant), .. }) - | Node::Ty(&hir::Ty { kind: hir::TyKind::Typeof(ref constant), .. }) - | Node::Expr(&hir::Expr { kind: ExprKind::Repeat(_, ref constant), .. }) - if constant.hir_id == hir_id => - { - tcx.types.usize - } - - Node::Variant(Variant { disr_expr: Some(ref e), .. }) if e.hir_id == hir_id => { - tcx.adt_def(tcx.hir().get_parent_did(hir_id)).repr.discr_type().to_ty(tcx) - } - - Node::Ty(&hir::Ty { kind: hir::TyKind::Path(_), .. }) - | Node::Expr(&hir::Expr { kind: ExprKind::Struct(..), .. }) - | Node::Expr(&hir::Expr { kind: ExprKind::Path(_), .. }) - | Node::TraitRef(..) => { - let path = match parent_node { - Node::Ty(&hir::Ty { - kind: hir::TyKind::Path(QPath::Resolved(_, ref path)), - .. - }) - | Node::Expr(&hir::Expr { - kind: ExprKind::Path(QPath::Resolved(_, ref path)), - .. - }) => Some(&**path), - Node::Expr(&hir::Expr { kind: ExprKind::Struct(ref path, ..), .. }) => { - if let QPath::Resolved(_, ref path) = **path { - Some(&**path) - } else { - None - } - } - Node::TraitRef(&hir::TraitRef { ref path, .. }) => Some(&**path), - _ => None, - }; - - if let Some(path) = path { - let arg_index = path - .segments - .iter() - .filter_map(|seg| seg.args.as_ref()) - .map(|generic_args| generic_args.args.as_ref()) - .find_map(|args| { - args.iter() - .filter(|arg| arg.is_const()) - .enumerate() - .filter(|(_, arg)| arg.id() == hir_id) - .map(|(index, _)| index) - .next() - }) - .unwrap_or_else(|| { - bug!("no arg matching AnonConst in path"); - }); - - // We've encountered an `AnonConst` in some path, so we need to - // figure out which generic parameter it corresponds to and return - // the relevant type. - let generics = match path.res { - Res::Def(DefKind::Ctor(..), def_id) => { - tcx.generics_of(tcx.parent(def_id).unwrap()) - } - Res::Def(_, def_id) => tcx.generics_of(def_id), - Res::Err => return tcx.types.err, - res => { - tcx.sess.delay_span_bug( - DUMMY_SP, - &format!("unexpected const parent path def {:?}", res,), - ); - return tcx.types.err; - } - }; - - generics - .params - .iter() - .filter(|param| { - if let ty::GenericParamDefKind::Const = param.kind { - true - } else { - false - } - }) - .nth(arg_index) - .map(|param| tcx.type_of(param.def_id)) - // This is no generic parameter associated with the arg. This is - // probably from an extra arg where one is not needed. - .unwrap_or(tcx.types.err) - } else { - tcx.sess.delay_span_bug( - DUMMY_SP, - &format!("unexpected const parent path {:?}", parent_node,), - ); - return tcx.types.err; - } - } - - x => { - tcx.sess.delay_span_bug( - DUMMY_SP, - &format!("unexpected const parent in type_of_def_id(): {:?}", x), - ); - tcx.types.err - } - } - } - - Node::GenericParam(param) => match ¶m.kind { - hir::GenericParamKind::Type { default: Some(ref ty), .. } => icx.to_ty(ty), - hir::GenericParamKind::Const { ty: ref hir_ty, .. } => { - let ty = icx.to_ty(hir_ty); - if !tcx.features().const_compare_raw_pointers { - let err = match ty.peel_refs().kind { - ty::FnPtr(_) => Some("function pointers"), - ty::RawPtr(_) => Some("raw pointers"), - _ => None, - }; - if let Some(unsupported_type) = err { - feature_err( - &tcx.sess.parse_sess, - sym::const_compare_raw_pointers, - hir_ty.span, - &format!( - "using {} as const generic parameters is unstable", - unsupported_type - ), - ) - .emit(); - }; - } - if traits::search_for_structural_match_violation(param.hir_id, param.span, tcx, ty) - .is_some() - { - struct_span_err!( - tcx.sess, - hir_ty.span, - E0741, - "the types of const generic parameters must derive `PartialEq` and `Eq`", - ) - .span_label( - hir_ty.span, - format!("`{}` doesn't derive both `PartialEq` and `Eq`", ty), - ) - .emit(); - } - ty - } - x => bug!("unexpected non-type Node::GenericParam: {:?}", x), - }, - - x => { - bug!("unexpected sort of node in type_of_def_id(): {:?}", x); - } - } -} - -fn find_opaque_ty_constraints(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> { - use rustc_hir::{Expr, ImplItem, Item, TraitItem}; - - debug!("find_opaque_ty_constraints({:?})", def_id); - - struct ConstraintLocator<'tcx> { - tcx: TyCtxt<'tcx>, - def_id: DefId, - // (first found type span, actual type, mapping from the opaque type's generic - // parameters to the concrete type's generic parameters) - // - // The mapping is an index for each use site of a generic parameter in the concrete type - // - // The indices index into the generic parameters on the opaque type. - found: Option<(Span, Ty<'tcx>, Vec)>, - } - - impl ConstraintLocator<'tcx> { - fn check(&mut self, def_id: DefId) { - // Don't try to check items that cannot possibly constrain the type. - if !self.tcx.has_typeck_tables(def_id) { - debug!( - "find_opaque_ty_constraints: no constraint for `{:?}` at `{:?}`: no tables", - self.def_id, def_id, - ); - return; - } - // Calling `mir_borrowck` can lead to cycle errors through - // const-checking, avoid calling it if we don't have to. - if !self.tcx.typeck_tables_of(def_id).concrete_opaque_types.contains_key(&self.def_id) { - debug!( - "find_opaque_ty_constraints: no constraint for `{:?}` at `{:?}`", - self.def_id, def_id, - ); - return; - } - // Use borrowck to get the type with unerased regions. - let ty = self.tcx.mir_borrowck(def_id).concrete_opaque_types.get(&self.def_id); - if let Some(ty::ResolvedOpaqueTy { concrete_type, substs }) = ty { - debug!( - "find_opaque_ty_constraints: found constraint for `{:?}` at `{:?}`: {:?}", - self.def_id, def_id, ty, - ); - - // FIXME(oli-obk): trace the actual span from inference to improve errors. - let span = self.tcx.def_span(def_id); - // used to quickly look up the position of a generic parameter - let mut index_map: FxHashMap = FxHashMap::default(); - // Skipping binder is ok, since we only use this to find generic parameters and - // their positions. - for (idx, subst) in substs.iter().enumerate() { - if let GenericArgKind::Type(ty) = subst.unpack() { - if let ty::Param(p) = ty.kind { - if index_map.insert(p, idx).is_some() { - // There was already an entry for `p`, meaning a generic parameter - // was used twice. - self.tcx.sess.span_err( - span, - &format!( - "defining opaque type use restricts opaque \ - type by using the generic parameter `{}` twice", - p, - ), - ); - return; - } - } else { - self.tcx.sess.delay_span_bug( - span, - &format!( - "non-defining opaque ty use in defining scope: {:?}, {:?}", - concrete_type, substs, - ), - ); - } - } - } - // Compute the index within the opaque type for each generic parameter used in - // the concrete type. - let indices = concrete_type - .subst(self.tcx, substs) - .walk() - .filter_map(|t| match &t.kind { - ty::Param(p) => Some(*index_map.get(p).unwrap()), - _ => None, - }) - .collect(); - let is_param = |ty: Ty<'_>| match ty.kind { - ty::Param(_) => true, - _ => false, - }; - let bad_substs: Vec<_> = substs - .iter() - .enumerate() - .filter_map(|(i, k)| { - if let GenericArgKind::Type(ty) = k.unpack() { Some((i, ty)) } else { None } - }) - .filter(|(_, ty)| !is_param(ty)) - .collect(); - - if !bad_substs.is_empty() { - let identity_substs = InternalSubsts::identity_for_item(self.tcx, self.def_id); - for (i, bad_subst) in bad_substs { - self.tcx.sess.span_err( - span, - &format!( - "defining opaque type use does not fully define opaque type: \ - generic parameter `{}` is specified as concrete type `{}`", - identity_substs.type_at(i), - bad_subst - ), - ); - } - } else if let Some((prev_span, prev_ty, ref prev_indices)) = self.found { - let mut ty = concrete_type.walk().fuse(); - let mut p_ty = prev_ty.walk().fuse(); - let iter_eq = (&mut ty).zip(&mut p_ty).all(|(t, p)| match (&t.kind, &p.kind) { - // Type parameters are equal to any other type parameter for the purpose of - // concrete type equality, as it is possible to obtain the same type just - // by passing matching parameters to a function. - (ty::Param(_), ty::Param(_)) => true, - _ => t == p, - }); - if !iter_eq || ty.next().is_some() || p_ty.next().is_some() { - debug!("find_opaque_ty_constraints: span={:?}", span); - // Found different concrete types for the opaque type. - let mut err = self.tcx.sess.struct_span_err( - span, - "concrete type differs from previous defining opaque type use", - ); - err.span_label( - span, - format!("expected `{}`, got `{}`", prev_ty, concrete_type), - ); - err.span_note(prev_span, "previous use here"); - err.emit(); - } else if indices != *prev_indices { - // Found "same" concrete types, but the generic parameter order differs. - let mut err = self.tcx.sess.struct_span_err( - span, - "concrete type's generic parameters differ from previous defining use", - ); - use std::fmt::Write; - let mut s = String::new(); - write!(s, "expected [").unwrap(); - let list = |s: &mut String, indices: &Vec| { - let mut indices = indices.iter().cloned(); - if let Some(first) = indices.next() { - write!(s, "`{}`", substs[first]).unwrap(); - for i in indices { - write!(s, ", `{}`", substs[i]).unwrap(); - } - } - }; - list(&mut s, prev_indices); - write!(s, "], got [").unwrap(); - list(&mut s, &indices); - write!(s, "]").unwrap(); - err.span_label(span, s); - err.span_note(prev_span, "previous use here"); - err.emit(); - } - } else { - self.found = Some((span, concrete_type, indices)); - } - } else { - debug!( - "find_opaque_ty_constraints: no constraint for `{:?}` at `{:?}`", - self.def_id, def_id, - ); - } - } - } - - impl<'tcx> intravisit::Visitor<'tcx> for ConstraintLocator<'tcx> { - type Map = Map<'tcx>; - - fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<'_, Self::Map> { - intravisit::NestedVisitorMap::All(&self.tcx.hir()) - } - fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) { - if let hir::ExprKind::Closure(..) = ex.kind { - let def_id = self.tcx.hir().local_def_id(ex.hir_id); - self.check(def_id); - } - intravisit::walk_expr(self, ex); - } - fn visit_item(&mut self, it: &'tcx Item<'tcx>) { - debug!("find_existential_constraints: visiting {:?}", it); - let def_id = self.tcx.hir().local_def_id(it.hir_id); - // The opaque type itself or its children are not within its reveal scope. - if def_id != self.def_id { - self.check(def_id); - intravisit::walk_item(self, it); - } - } - fn visit_impl_item(&mut self, it: &'tcx ImplItem<'tcx>) { - debug!("find_existential_constraints: visiting {:?}", it); - let def_id = self.tcx.hir().local_def_id(it.hir_id); - // The opaque type itself or its children are not within its reveal scope. - if def_id != self.def_id { - self.check(def_id); - intravisit::walk_impl_item(self, it); - } - } - fn visit_trait_item(&mut self, it: &'tcx TraitItem<'tcx>) { - debug!("find_existential_constraints: visiting {:?}", it); - let def_id = self.tcx.hir().local_def_id(it.hir_id); - self.check(def_id); - intravisit::walk_trait_item(self, it); - } - } - - let hir_id = tcx.hir().as_local_hir_id(def_id).unwrap(); - let scope = tcx.hir().get_defining_scope(hir_id); - let mut locator = ConstraintLocator { def_id, tcx, found: None }; - - debug!("find_opaque_ty_constraints: scope={:?}", scope); - - if scope == hir::CRATE_HIR_ID { - intravisit::walk_crate(&mut locator, tcx.hir().krate()); - } else { - debug!("find_opaque_ty_constraints: scope={:?}", tcx.hir().get(scope)); - match tcx.hir().get(scope) { - // We explicitly call `visit_*` methods, instead of using `intravisit::walk_*` methods - // This allows our visitor to process the defining item itself, causing - // it to pick up any 'sibling' defining uses. - // - // For example, this code: - // ``` - // fn foo() { - // type Blah = impl Debug; - // let my_closure = || -> Blah { true }; - // } - // ``` - // - // requires us to explicitly process `foo()` in order - // to notice the defining usage of `Blah`. - Node::Item(ref it) => locator.visit_item(it), - Node::ImplItem(ref it) => locator.visit_impl_item(it), - Node::TraitItem(ref it) => locator.visit_trait_item(it), - other => bug!("{:?} is not a valid scope for an opaque type item", other), - } - } - - match locator.found { - Some((_, ty, _)) => ty, - None => { - let span = tcx.def_span(def_id); - tcx.sess.span_err(span, "could not find defining uses"); - tcx.types.err - } - } -} - fn are_suggestable_generic_args(generic_args: &[hir::GenericArg<'_>]) -> bool { generic_args .iter() diff --git a/src/librustc_typeck/collect/type_of.rs b/src/librustc_typeck/collect/type_of.rs new file mode 100644 index 0000000000000..8b6dba749a6ef --- /dev/null +++ b/src/librustc_typeck/collect/type_of.rs @@ -0,0 +1,661 @@ +use rustc::hir::map::Map; +use rustc::session::parse::feature_err; +use rustc::traits; +use rustc::ty::subst::{GenericArgKind, InternalSubsts, Subst}; +use rustc::ty::util::IntTypeExt; +use rustc::ty::{self, DefIdTree, Ty, TyCtxt, TypeFoldable}; +use rustc_data_structures::fx::FxHashMap; +use rustc_errors::{struct_span_err, Applicability, StashKey}; +use rustc_hir as hir; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::def_id::DefId; +use rustc_hir::intravisit; +use rustc_hir::intravisit::Visitor; +use rustc_hir::Node; +use rustc_span::symbol::{sym, Ident}; +use rustc_span::{Span, DUMMY_SP}; + +use super::ItemCtxt; +use super::{bad_placeholder_type, is_suggestable_infer_ty}; + +pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> { + use rustc_hir::*; + + let hir_id = tcx.hir().as_local_hir_id(def_id).unwrap(); + + let icx = ItemCtxt::new(tcx, def_id); + + match tcx.hir().get(hir_id) { + Node::TraitItem(item) => match item.kind { + TraitItemKind::Method(..) => { + let substs = InternalSubsts::identity_for_item(tcx, def_id); + tcx.mk_fn_def(def_id, substs) + } + TraitItemKind::Const(ref ty, body_id) => body_id + .and_then(|body_id| { + if is_suggestable_infer_ty(ty) { + Some(infer_placeholder_type(tcx, def_id, body_id, ty.span, item.ident)) + } else { + None + } + }) + .unwrap_or_else(|| icx.to_ty(ty)), + TraitItemKind::Type(_, Some(ref ty)) => icx.to_ty(ty), + TraitItemKind::Type(_, None) => { + span_bug!(item.span, "associated type missing default"); + } + }, + + Node::ImplItem(item) => match item.kind { + ImplItemKind::Method(..) => { + let substs = InternalSubsts::identity_for_item(tcx, def_id); + tcx.mk_fn_def(def_id, substs) + } + ImplItemKind::Const(ref ty, body_id) => { + if is_suggestable_infer_ty(ty) { + infer_placeholder_type(tcx, def_id, body_id, ty.span, item.ident) + } else { + icx.to_ty(ty) + } + } + ImplItemKind::OpaqueTy(_) => { + if tcx.impl_trait_ref(tcx.hir().get_parent_did(hir_id)).is_none() { + report_assoc_ty_on_inherent_impl(tcx, item.span); + } + + find_opaque_ty_constraints(tcx, def_id) + } + ImplItemKind::TyAlias(ref ty) => { + if tcx.impl_trait_ref(tcx.hir().get_parent_did(hir_id)).is_none() { + report_assoc_ty_on_inherent_impl(tcx, item.span); + } + + icx.to_ty(ty) + } + }, + + Node::Item(item) => { + match item.kind { + ItemKind::Static(ref ty, .., body_id) | ItemKind::Const(ref ty, body_id) => { + if is_suggestable_infer_ty(ty) { + infer_placeholder_type(tcx, def_id, body_id, ty.span, item.ident) + } else { + icx.to_ty(ty) + } + } + ItemKind::TyAlias(ref self_ty, _) | ItemKind::Impl { ref self_ty, .. } => { + icx.to_ty(self_ty) + } + ItemKind::Fn(..) => { + let substs = InternalSubsts::identity_for_item(tcx, def_id); + tcx.mk_fn_def(def_id, substs) + } + ItemKind::Enum(..) | ItemKind::Struct(..) | ItemKind::Union(..) => { + let def = tcx.adt_def(def_id); + let substs = InternalSubsts::identity_for_item(tcx, def_id); + tcx.mk_adt(def, substs) + } + ItemKind::OpaqueTy(OpaqueTy { impl_trait_fn: None, .. }) => { + find_opaque_ty_constraints(tcx, def_id) + } + // Opaque types desugared from `impl Trait`. + ItemKind::OpaqueTy(OpaqueTy { impl_trait_fn: Some(owner), origin, .. }) => { + let concrete_types = match origin { + OpaqueTyOrigin::FnReturn | OpaqueTyOrigin::AsyncFn => { + &tcx.mir_borrowck(owner).concrete_opaque_types + } + OpaqueTyOrigin::Misc => { + // We shouldn't leak borrowck results through impl trait in bindings. + // For example, we shouldn't be able to tell if `x` in + // `let x: impl Sized + 'a = &()` has type `&'static ()` or `&'a ()`. + &tcx.typeck_tables_of(owner).concrete_opaque_types + } + OpaqueTyOrigin::TypeAlias => { + span_bug!(item.span, "Type alias impl trait shouldn't have an owner") + } + }; + let concrete_ty = concrete_types + .get(&def_id) + .map(|opaque| opaque.concrete_type) + .unwrap_or_else(|| { + tcx.sess.delay_span_bug( + DUMMY_SP, + &format!( + "owner {:?} has no opaque type for {:?} in its tables", + owner, def_id, + ), + ); + if tcx.typeck_tables_of(owner).tainted_by_errors { + // Some error in the + // owner fn prevented us from populating + // the `concrete_opaque_types` table. + tcx.types.err + } else { + // We failed to resolve the opaque type or it + // resolves to itself. Return the non-revealed + // type, which should result in E0720. + tcx.mk_opaque( + def_id, + InternalSubsts::identity_for_item(tcx, def_id), + ) + } + }); + debug!("concrete_ty = {:?}", concrete_ty); + if concrete_ty.has_erased_regions() { + // FIXME(impl_trait_in_bindings) Handle this case. + tcx.sess.span_fatal( + item.span, + "lifetimes in impl Trait types in bindings are not currently supported", + ); + } + concrete_ty + } + ItemKind::Trait(..) + | ItemKind::TraitAlias(..) + | ItemKind::Mod(..) + | ItemKind::ForeignMod(..) + | ItemKind::GlobalAsm(..) + | ItemKind::ExternCrate(..) + | ItemKind::Use(..) => { + span_bug!( + item.span, + "compute_type_of_item: unexpected item type: {:?}", + item.kind + ); + } + } + } + + Node::ForeignItem(foreign_item) => match foreign_item.kind { + ForeignItemKind::Fn(..) => { + let substs = InternalSubsts::identity_for_item(tcx, def_id); + tcx.mk_fn_def(def_id, substs) + } + ForeignItemKind::Static(ref t, _) => icx.to_ty(t), + ForeignItemKind::Type => tcx.mk_foreign(def_id), + }, + + Node::Ctor(&ref def) | Node::Variant(Variant { data: ref def, .. }) => match *def { + VariantData::Unit(..) | VariantData::Struct(..) => { + tcx.type_of(tcx.hir().get_parent_did(hir_id)) + } + VariantData::Tuple(..) => { + let substs = InternalSubsts::identity_for_item(tcx, def_id); + tcx.mk_fn_def(def_id, substs) + } + }, + + Node::Field(field) => icx.to_ty(&field.ty), + + Node::Expr(&Expr { kind: ExprKind::Closure(.., gen), .. }) => { + if gen.is_some() { + return tcx.typeck_tables_of(def_id).node_type(hir_id); + } + + let substs = InternalSubsts::identity_for_item(tcx, def_id); + tcx.mk_closure(def_id, substs) + } + + Node::AnonConst(_) => { + let parent_node = tcx.hir().get(tcx.hir().get_parent_node(hir_id)); + match parent_node { + Node::Ty(&Ty { kind: TyKind::Array(_, ref constant), .. }) + | Node::Ty(&Ty { kind: TyKind::Typeof(ref constant), .. }) + | Node::Expr(&Expr { kind: ExprKind::Repeat(_, ref constant), .. }) + if constant.hir_id == hir_id => + { + tcx.types.usize + } + + Node::Variant(Variant { disr_expr: Some(ref e), .. }) if e.hir_id == hir_id => { + tcx.adt_def(tcx.hir().get_parent_did(hir_id)).repr.discr_type().to_ty(tcx) + } + + Node::Ty(&Ty { kind: TyKind::Path(_), .. }) + | Node::Expr(&Expr { kind: ExprKind::Struct(..), .. }) + | Node::Expr(&Expr { kind: ExprKind::Path(_), .. }) + | Node::TraitRef(..) => { + let path = match parent_node { + Node::Ty(&Ty { + kind: TyKind::Path(QPath::Resolved(_, ref path)), .. + }) + | Node::Expr(&Expr { + kind: ExprKind::Path(QPath::Resolved(_, ref path)), + .. + }) => Some(&**path), + Node::Expr(&Expr { kind: ExprKind::Struct(ref path, ..), .. }) => { + if let QPath::Resolved(_, ref path) = **path { + Some(&**path) + } else { + None + } + } + Node::TraitRef(&TraitRef { ref path, .. }) => Some(&**path), + _ => None, + }; + + if let Some(path) = path { + let arg_index = path + .segments + .iter() + .filter_map(|seg| seg.args.as_ref()) + .map(|generic_args| generic_args.args.as_ref()) + .find_map(|args| { + args.iter() + .filter(|arg| arg.is_const()) + .enumerate() + .filter(|(_, arg)| arg.id() == hir_id) + .map(|(index, _)| index) + .next() + }) + .unwrap_or_else(|| { + bug!("no arg matching AnonConst in path"); + }); + + // We've encountered an `AnonConst` in some path, so we need to + // figure out which generic parameter it corresponds to and return + // the relevant type. + let generics = match path.res { + Res::Def(DefKind::Ctor(..), def_id) => { + tcx.generics_of(tcx.parent(def_id).unwrap()) + } + Res::Def(_, def_id) => tcx.generics_of(def_id), + Res::Err => return tcx.types.err, + res => { + tcx.sess.delay_span_bug( + DUMMY_SP, + &format!("unexpected const parent path def {:?}", res,), + ); + return tcx.types.err; + } + }; + + generics + .params + .iter() + .filter(|param| { + if let ty::GenericParamDefKind::Const = param.kind { + true + } else { + false + } + }) + .nth(arg_index) + .map(|param| tcx.type_of(param.def_id)) + // This is no generic parameter associated with the arg. This is + // probably from an extra arg where one is not needed. + .unwrap_or(tcx.types.err) + } else { + tcx.sess.delay_span_bug( + DUMMY_SP, + &format!("unexpected const parent path {:?}", parent_node,), + ); + return tcx.types.err; + } + } + + x => { + tcx.sess.delay_span_bug( + DUMMY_SP, + &format!("unexpected const parent in type_of_def_id(): {:?}", x), + ); + tcx.types.err + } + } + } + + Node::GenericParam(param) => match ¶m.kind { + GenericParamKind::Type { default: Some(ref ty), .. } => icx.to_ty(ty), + GenericParamKind::Const { ty: ref hir_ty, .. } => { + let ty = icx.to_ty(hir_ty); + if !tcx.features().const_compare_raw_pointers { + let err = match ty.peel_refs().kind { + ty::FnPtr(_) => Some("function pointers"), + ty::RawPtr(_) => Some("raw pointers"), + _ => None, + }; + if let Some(unsupported_type) = err { + feature_err( + &tcx.sess.parse_sess, + sym::const_compare_raw_pointers, + hir_ty.span, + &format!( + "using {} as const generic parameters is unstable", + unsupported_type + ), + ) + .emit(); + }; + } + if traits::search_for_structural_match_violation(param.hir_id, param.span, tcx, ty) + .is_some() + { + struct_span_err!( + tcx.sess, + hir_ty.span, + E0741, + "the types of const generic parameters must derive `PartialEq` and `Eq`", + ) + .span_label( + hir_ty.span, + format!("`{}` doesn't derive both `PartialEq` and `Eq`", ty), + ) + .emit(); + } + ty + } + x => bug!("unexpected non-type Node::GenericParam: {:?}", x), + }, + + x => { + bug!("unexpected sort of node in type_of_def_id(): {:?}", x); + } + } +} + +fn find_opaque_ty_constraints(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> { + use rustc_hir::{Expr, ImplItem, Item, TraitItem}; + + debug!("find_opaque_ty_constraints({:?})", def_id); + + struct ConstraintLocator<'tcx> { + tcx: TyCtxt<'tcx>, + def_id: DefId, + // (first found type span, actual type, mapping from the opaque type's generic + // parameters to the concrete type's generic parameters) + // + // The mapping is an index for each use site of a generic parameter in the concrete type + // + // The indices index into the generic parameters on the opaque type. + found: Option<(Span, Ty<'tcx>, Vec)>, + } + + impl ConstraintLocator<'_> { + fn check(&mut self, def_id: DefId) { + // Don't try to check items that cannot possibly constrain the type. + if !self.tcx.has_typeck_tables(def_id) { + debug!( + "find_opaque_ty_constraints: no constraint for `{:?}` at `{:?}`: no tables", + self.def_id, def_id, + ); + return; + } + // Calling `mir_borrowck` can lead to cycle errors through + // const-checking, avoid calling it if we don't have to. + if !self.tcx.typeck_tables_of(def_id).concrete_opaque_types.contains_key(&self.def_id) { + debug!( + "find_opaque_ty_constraints: no constraint for `{:?}` at `{:?}`", + self.def_id, def_id, + ); + return; + } + // Use borrowck to get the type with unerased regions. + let ty = self.tcx.mir_borrowck(def_id).concrete_opaque_types.get(&self.def_id); + if let Some(ty::ResolvedOpaqueTy { concrete_type, substs }) = ty { + debug!( + "find_opaque_ty_constraints: found constraint for `{:?}` at `{:?}`: {:?}", + self.def_id, def_id, ty, + ); + + // FIXME(oli-obk): trace the actual span from inference to improve errors. + let span = self.tcx.def_span(def_id); + // used to quickly look up the position of a generic parameter + let mut index_map: FxHashMap = FxHashMap::default(); + // Skipping binder is ok, since we only use this to find generic parameters and + // their positions. + for (idx, subst) in substs.iter().enumerate() { + if let GenericArgKind::Type(ty) = subst.unpack() { + if let ty::Param(p) = ty.kind { + if index_map.insert(p, idx).is_some() { + // There was already an entry for `p`, meaning a generic parameter + // was used twice. + self.tcx.sess.span_err( + span, + &format!( + "defining opaque type use restricts opaque \ + type by using the generic parameter `{}` twice", + p, + ), + ); + return; + } + } else { + self.tcx.sess.delay_span_bug( + span, + &format!( + "non-defining opaque ty use in defining scope: {:?}, {:?}", + concrete_type, substs, + ), + ); + } + } + } + // Compute the index within the opaque type for each generic parameter used in + // the concrete type. + let indices = concrete_type + .subst(self.tcx, substs) + .walk() + .filter_map(|t| match &t.kind { + ty::Param(p) => Some(*index_map.get(p).unwrap()), + _ => None, + }) + .collect(); + let is_param = |ty: Ty<'_>| match ty.kind { + ty::Param(_) => true, + _ => false, + }; + let bad_substs: Vec<_> = substs + .iter() + .enumerate() + .filter_map(|(i, k)| { + if let GenericArgKind::Type(ty) = k.unpack() { Some((i, ty)) } else { None } + }) + .filter(|(_, ty)| !is_param(ty)) + .collect(); + if !bad_substs.is_empty() { + let identity_substs = InternalSubsts::identity_for_item(self.tcx, self.def_id); + for (i, bad_subst) in bad_substs { + self.tcx.sess.span_err( + span, + &format!( + "defining opaque type use does not fully define opaque type: \ + generic parameter `{}` is specified as concrete type `{}`", + identity_substs.type_at(i), + bad_subst + ), + ); + } + } else if let Some((prev_span, prev_ty, ref prev_indices)) = self.found { + let mut ty = concrete_type.walk().fuse(); + let mut p_ty = prev_ty.walk().fuse(); + let iter_eq = (&mut ty).zip(&mut p_ty).all(|(t, p)| match (&t.kind, &p.kind) { + // Type parameters are equal to any other type parameter for the purpose of + // concrete type equality, as it is possible to obtain the same type just + // by passing matching parameters to a function. + (ty::Param(_), ty::Param(_)) => true, + _ => t == p, + }); + if !iter_eq || ty.next().is_some() || p_ty.next().is_some() { + debug!("find_opaque_ty_constraints: span={:?}", span); + // Found different concrete types for the opaque type. + let mut err = self.tcx.sess.struct_span_err( + span, + "concrete type differs from previous defining opaque type use", + ); + err.span_label( + span, + format!("expected `{}`, got `{}`", prev_ty, concrete_type), + ); + err.span_note(prev_span, "previous use here"); + err.emit(); + } else if indices != *prev_indices { + // Found "same" concrete types, but the generic parameter order differs. + let mut err = self.tcx.sess.struct_span_err( + span, + "concrete type's generic parameters differ from previous defining use", + ); + use std::fmt::Write; + let mut s = String::new(); + write!(s, "expected [").unwrap(); + let list = |s: &mut String, indices: &Vec| { + let mut indices = indices.iter().cloned(); + if let Some(first) = indices.next() { + write!(s, "`{}`", substs[first]).unwrap(); + for i in indices { + write!(s, ", `{}`", substs[i]).unwrap(); + } + } + }; + list(&mut s, prev_indices); + write!(s, "], got [").unwrap(); + list(&mut s, &indices); + write!(s, "]").unwrap(); + err.span_label(span, s); + err.span_note(prev_span, "previous use here"); + err.emit(); + } + } else { + self.found = Some((span, concrete_type, indices)); + } + } else { + debug!( + "find_opaque_ty_constraints: no constraint for `{:?}` at `{:?}`", + self.def_id, def_id, + ); + } + } + } + + impl<'tcx> intravisit::Visitor<'tcx> for ConstraintLocator<'tcx> { + type Map = Map<'tcx>; + + fn nested_visit_map(&mut self) -> intravisit::NestedVisitorMap<'_, Self::Map> { + intravisit::NestedVisitorMap::All(&self.tcx.hir()) + } + fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) { + if let hir::ExprKind::Closure(..) = ex.kind { + let def_id = self.tcx.hir().local_def_id(ex.hir_id); + self.check(def_id); + } + intravisit::walk_expr(self, ex); + } + fn visit_item(&mut self, it: &'tcx Item<'tcx>) { + debug!("find_existential_constraints: visiting {:?}", it); + let def_id = self.tcx.hir().local_def_id(it.hir_id); + // The opaque type itself or its children are not within its reveal scope. + if def_id != self.def_id { + self.check(def_id); + intravisit::walk_item(self, it); + } + } + fn visit_impl_item(&mut self, it: &'tcx ImplItem<'tcx>) { + debug!("find_existential_constraints: visiting {:?}", it); + let def_id = self.tcx.hir().local_def_id(it.hir_id); + // The opaque type itself or its children are not within its reveal scope. + if def_id != self.def_id { + self.check(def_id); + intravisit::walk_impl_item(self, it); + } + } + fn visit_trait_item(&mut self, it: &'tcx TraitItem<'tcx>) { + debug!("find_existential_constraints: visiting {:?}", it); + let def_id = self.tcx.hir().local_def_id(it.hir_id); + self.check(def_id); + intravisit::walk_trait_item(self, it); + } + } + + let hir_id = tcx.hir().as_local_hir_id(def_id).unwrap(); + let scope = tcx.hir().get_defining_scope(hir_id); + let mut locator = ConstraintLocator { def_id, tcx, found: None }; + + debug!("find_opaque_ty_constraints: scope={:?}", scope); + + if scope == hir::CRATE_HIR_ID { + intravisit::walk_crate(&mut locator, tcx.hir().krate()); + } else { + debug!("find_opaque_ty_constraints: scope={:?}", tcx.hir().get(scope)); + match tcx.hir().get(scope) { + // We explicitly call `visit_*` methods, instead of using `intravisit::walk_*` methods + // This allows our visitor to process the defining item itself, causing + // it to pick up any 'sibling' defining uses. + // + // For example, this code: + // ``` + // fn foo() { + // type Blah = impl Debug; + // let my_closure = || -> Blah { true }; + // } + // ``` + // + // requires us to explicitly process `foo()` in order + // to notice the defining usage of `Blah`. + Node::Item(ref it) => locator.visit_item(it), + Node::ImplItem(ref it) => locator.visit_impl_item(it), + Node::TraitItem(ref it) => locator.visit_trait_item(it), + other => bug!("{:?} is not a valid scope for an opaque type item", other), + } + } + + match locator.found { + Some((_, ty, _)) => ty, + None => { + let span = tcx.def_span(def_id); + tcx.sess.span_err(span, "could not find defining uses"); + tcx.types.err + } + } +} + +fn infer_placeholder_type( + tcx: TyCtxt<'_>, + def_id: DefId, + body_id: hir::BodyId, + span: Span, + item_ident: Ident, +) -> Ty<'_> { + let ty = tcx.diagnostic_only_typeck_tables_of(def_id).node_type(body_id.hir_id); + + // If this came from a free `const` or `static mut?` item, + // then the user may have written e.g. `const A = 42;`. + // In this case, the parser has stashed a diagnostic for + // us to improve in typeck so we do that now. + match tcx.sess.diagnostic().steal_diagnostic(span, StashKey::ItemNoType) { + Some(mut err) => { + // The parser provided a sub-optimal `HasPlaceholders` suggestion for the type. + // We are typeck and have the real type, so remove that and suggest the actual type. + err.suggestions.clear(); + err.span_suggestion( + span, + "provide a type for the item", + format!("{}: {}", item_ident, ty), + Applicability::MachineApplicable, + ) + .emit(); + } + None => { + let mut diag = bad_placeholder_type(tcx, vec![span]); + if ty != tcx.types.err { + diag.span_suggestion( + span, + "replace `_` with the correct type", + ty.to_string(), + Applicability::MaybeIncorrect, + ); + } + diag.emit(); + } + } + + ty +} + +fn report_assoc_ty_on_inherent_impl(tcx: TyCtxt<'_>, span: Span) { + struct_span_err!( + tcx.sess, + span, + E0202, + "associated types are not yet supported in inherent impls (see #8995)" + ) + .emit(); +}