summaryrefslogtreecommitdiffstats
path: root/compiler/rustc_hir_analysis
diff options
context:
space:
mode:
Diffstat (limited to '')
-rw-r--r--compiler/rustc_hir_analysis/Cargo.toml32
-rw-r--r--compiler/rustc_hir_analysis/README.md (renamed from compiler/rustc_typeck/README.md)0
-rw-r--r--compiler/rustc_hir_analysis/src/astconv/errors.rs411
-rw-r--r--compiler/rustc_hir_analysis/src/astconv/generics.rs662
-rw-r--r--compiler/rustc_hir_analysis/src/astconv/mod.rs3136
-rw-r--r--compiler/rustc_hir_analysis/src/bounds.rs (renamed from compiler/rustc_typeck/src/bounds.rs)0
-rw-r--r--compiler/rustc_hir_analysis/src/check/check.rs1443
-rw-r--r--compiler/rustc_hir_analysis/src/check/compare_method.rs1825
-rw-r--r--compiler/rustc_hir_analysis/src/check/dropck.rs (renamed from compiler/rustc_typeck/src/check/dropck.rs)14
-rw-r--r--compiler/rustc_hir_analysis/src/check/intrinsic.rs (renamed from compiler/rustc_typeck/src/check/intrinsic.rs)60
-rw-r--r--compiler/rustc_hir_analysis/src/check/intrinsicck.rs437
-rw-r--r--compiler/rustc_hir_analysis/src/check/mod.rs515
-rw-r--r--compiler/rustc_hir_analysis/src/check/region.rs (renamed from compiler/rustc_typeck/src/check/region.rs)51
-rw-r--r--compiler/rustc_hir_analysis/src/check/wfcheck.rs (renamed from compiler/rustc_typeck/src/check/wfcheck.rs)367
-rw-r--r--compiler/rustc_hir_analysis/src/check_unused.rs192
-rw-r--r--compiler/rustc_hir_analysis/src/coherence/builtin.rs572
-rw-r--r--compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs (renamed from compiler/rustc_typeck/src/coherence/inherent_impls.rs)18
-rw-r--r--compiler/rustc_hir_analysis/src/coherence/inherent_impls_overlap.rs (renamed from compiler/rustc_typeck/src/coherence/inherent_impls_overlap.rs)58
-rw-r--r--compiler/rustc_hir_analysis/src/coherence/mod.rs (renamed from compiler/rustc_typeck/src/coherence/mod.rs)0
-rw-r--r--compiler/rustc_hir_analysis/src/coherence/orphan.rs (renamed from compiler/rustc_typeck/src/coherence/orphan.rs)38
-rw-r--r--compiler/rustc_hir_analysis/src/coherence/unsafety.rs96
-rw-r--r--compiler/rustc_hir_analysis/src/collect.rs2263
-rw-r--r--compiler/rustc_hir_analysis/src/collect/generics_of.rs481
-rw-r--r--compiler/rustc_hir_analysis/src/collect/item_bounds.rs (renamed from compiler/rustc_typeck/src/collect/item_bounds.rs)16
-rw-r--r--compiler/rustc_hir_analysis/src/collect/lifetimes.rs1888
-rw-r--r--compiler/rustc_hir_analysis/src/collect/predicates_of.rs707
-rw-r--r--compiler/rustc_hir_analysis/src/collect/type_of.rs (renamed from compiler/rustc_typeck/src/collect/type_of.rs)265
-rw-r--r--compiler/rustc_hir_analysis/src/constrained_generic_params.rs (renamed from compiler/rustc_typeck/src/constrained_generic_params.rs)10
-rw-r--r--compiler/rustc_hir_analysis/src/errors.rs282
-rw-r--r--compiler/rustc_hir_analysis/src/hir_wf_check.rs (renamed from compiler/rustc_typeck/src/hir_wf_check.rs)68
-rw-r--r--compiler/rustc_hir_analysis/src/impl_wf_check.rs (renamed from compiler/rustc_typeck/src/impl_wf_check.rs)43
-rw-r--r--compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs (renamed from compiler/rustc_typeck/src/impl_wf_check/min_specialization.rs)83
-rw-r--r--compiler/rustc_hir_analysis/src/lib.rs552
-rw-r--r--compiler/rustc_hir_analysis/src/outlives/explicit.rs (renamed from compiler/rustc_typeck/src/outlives/explicit.rs)0
-rw-r--r--compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs (renamed from compiler/rustc_typeck/src/outlives/implicit_infer.rs)4
-rw-r--r--compiler/rustc_hir_analysis/src/outlives/mod.rs129
-rw-r--r--compiler/rustc_hir_analysis/src/outlives/test.rs21
-rw-r--r--compiler/rustc_hir_analysis/src/outlives/utils.rs186
-rw-r--r--compiler/rustc_hir_analysis/src/structured_errors.rs (renamed from compiler/rustc_typeck/src/structured_errors.rs)0
-rw-r--r--compiler/rustc_hir_analysis/src/structured_errors/missing_cast_for_variadic_arg.rs (renamed from compiler/rustc_typeck/src/structured_errors/missing_cast_for_variadic_arg.rs)0
-rw-r--r--compiler/rustc_hir_analysis/src/structured_errors/sized_unsized_cast.rs (renamed from compiler/rustc_typeck/src/structured_errors/sized_unsized_cast.rs)0
-rw-r--r--compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs (renamed from compiler/rustc_typeck/src/structured_errors/wrong_number_of_generic_args.rs)255
-rw-r--r--compiler/rustc_hir_analysis/src/variance/constraints.rs (renamed from compiler/rustc_typeck/src/variance/constraints.rs)14
-rw-r--r--compiler/rustc_hir_analysis/src/variance/mod.rs (renamed from compiler/rustc_typeck/src/variance/mod.rs)0
-rw-r--r--compiler/rustc_hir_analysis/src/variance/solve.rs (renamed from compiler/rustc_typeck/src/variance/solve.rs)0
-rw-r--r--compiler/rustc_hir_analysis/src/variance/terms.rs (renamed from compiler/rustc_typeck/src/variance/terms.rs)0
-rw-r--r--compiler/rustc_hir_analysis/src/variance/test.rs15
-rw-r--r--compiler/rustc_hir_analysis/src/variance/xform.rs (renamed from compiler/rustc_typeck/src/variance/xform.rs)0
48 files changed, 16672 insertions, 537 deletions
diff --git a/compiler/rustc_hir_analysis/Cargo.toml b/compiler/rustc_hir_analysis/Cargo.toml
new file mode 100644
index 000000000..0761d8cdb
--- /dev/null
+++ b/compiler/rustc_hir_analysis/Cargo.toml
@@ -0,0 +1,32 @@
+[package]
+name = "rustc_hir_analysis"
+version = "0.0.0"
+edition = "2021"
+
+[lib]
+test = false
+doctest = false
+
+[dependencies]
+rustc_arena = { path = "../rustc_arena" }
+tracing = "0.1"
+rustc_macros = { path = "../rustc_macros" }
+rustc_middle = { path = "../rustc_middle" }
+rustc_attr = { path = "../rustc_attr" }
+rustc_data_structures = { path = "../rustc_data_structures" }
+rustc_errors = { path = "../rustc_errors" }
+rustc_graphviz = { path = "../rustc_graphviz" }
+rustc_hir = { path = "../rustc_hir" }
+rustc_hir_pretty = { path = "../rustc_hir_pretty" }
+rustc_target = { path = "../rustc_target" }
+rustc_session = { path = "../rustc_session" }
+smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
+rustc_ast = { path = "../rustc_ast" }
+rustc_span = { path = "../rustc_span" }
+rustc_index = { path = "../rustc_index" }
+rustc_infer = { path = "../rustc_infer" }
+rustc_trait_selection = { path = "../rustc_trait_selection" }
+rustc_lint = { path = "../rustc_lint" }
+rustc_serialize = { path = "../rustc_serialize" }
+rustc_type_ir = { path = "../rustc_type_ir" }
+rustc_feature = { path = "../rustc_feature" }
diff --git a/compiler/rustc_typeck/README.md b/compiler/rustc_hir_analysis/README.md
index b61dbd8c9..b61dbd8c9 100644
--- a/compiler/rustc_typeck/README.md
+++ b/compiler/rustc_hir_analysis/README.md
diff --git a/compiler/rustc_hir_analysis/src/astconv/errors.rs b/compiler/rustc_hir_analysis/src/astconv/errors.rs
new file mode 100644
index 000000000..a9152bdc5
--- /dev/null
+++ b/compiler/rustc_hir_analysis/src/astconv/errors.rs
@@ -0,0 +1,411 @@
+use crate::astconv::AstConv;
+use crate::errors::{ManualImplementation, MissingTypeParams};
+use rustc_data_structures::fx::FxHashMap;
+use rustc_errors::{pluralize, struct_span_err, Applicability, ErrorGuaranteed};
+use rustc_hir as hir;
+use rustc_hir::def_id::DefId;
+use rustc_middle::ty;
+use rustc_session::parse::feature_err;
+use rustc_span::lev_distance::find_best_match_for_name;
+use rustc_span::symbol::{sym, Ident};
+use rustc_span::{Span, Symbol, DUMMY_SP};
+
+use std::collections::BTreeSet;
+
+impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
+ /// On missing type parameters, emit an E0393 error and provide a structured suggestion using
+ /// the type parameter's name as a placeholder.
+ pub(crate) fn complain_about_missing_type_params(
+ &self,
+ missing_type_params: Vec<Symbol>,
+ def_id: DefId,
+ span: Span,
+ empty_generic_args: bool,
+ ) {
+ if missing_type_params.is_empty() {
+ return;
+ }
+
+ self.tcx().sess.emit_err(MissingTypeParams {
+ span,
+ def_span: self.tcx().def_span(def_id),
+ span_snippet: self.tcx().sess.source_map().span_to_snippet(span).ok(),
+ missing_type_params,
+ empty_generic_args,
+ });
+ }
+
+ /// When the code is using the `Fn` traits directly, instead of the `Fn(A) -> B` syntax, emit
+ /// an error and attempt to build a reasonable structured suggestion.
+ pub(crate) fn complain_about_internal_fn_trait(
+ &self,
+ span: Span,
+ trait_def_id: DefId,
+ trait_segment: &'_ hir::PathSegment<'_>,
+ is_impl: bool,
+ ) {
+ if self.tcx().features().unboxed_closures {
+ return;
+ }
+
+ let trait_def = self.tcx().trait_def(trait_def_id);
+ if !trait_def.paren_sugar {
+ if trait_segment.args().parenthesized {
+ // For now, require that parenthetical notation be used only with `Fn()` etc.
+ let mut err = feature_err(
+ &self.tcx().sess.parse_sess,
+ sym::unboxed_closures,
+ span,
+ "parenthetical notation is only stable when used with `Fn`-family traits",
+ );
+ err.emit();
+ }
+
+ return;
+ }
+
+ let sess = self.tcx().sess;
+
+ if !trait_segment.args().parenthesized {
+ // For now, require that parenthetical notation be used only with `Fn()` etc.
+ let mut err = feature_err(
+ &sess.parse_sess,
+ sym::unboxed_closures,
+ span,
+ "the precise format of `Fn`-family traits' type parameters is subject to change",
+ );
+ // Do not suggest the other syntax if we are in trait impl:
+ // the desugaring would contain an associated type constraint.
+ if !is_impl {
+ let args = trait_segment
+ .args
+ .as_ref()
+ .and_then(|args| args.args.get(0))
+ .and_then(|arg| match arg {
+ hir::GenericArg::Type(ty) => match ty.kind {
+ hir::TyKind::Tup(t) => t
+ .iter()
+ .map(|e| sess.source_map().span_to_snippet(e.span))
+ .collect::<Result<Vec<_>, _>>()
+ .map(|a| a.join(", ")),
+ _ => sess.source_map().span_to_snippet(ty.span),
+ }
+ .map(|s| format!("({})", s))
+ .ok(),
+ _ => None,
+ })
+ .unwrap_or_else(|| "()".to_string());
+ let ret = trait_segment
+ .args()
+ .bindings
+ .iter()
+ .find_map(|b| match (b.ident.name == sym::Output, &b.kind) {
+ (true, hir::TypeBindingKind::Equality { term }) => {
+ let span = match term {
+ hir::Term::Ty(ty) => ty.span,
+ hir::Term::Const(c) => self.tcx().hir().span(c.hir_id),
+ };
+ sess.source_map().span_to_snippet(span).ok()
+ }
+ _ => None,
+ })
+ .unwrap_or_else(|| "()".to_string());
+ err.span_suggestion(
+ span,
+ "use parenthetical notation instead",
+ format!("{}{} -> {}", trait_segment.ident, args, ret),
+ Applicability::MaybeIncorrect,
+ );
+ }
+ err.emit();
+ }
+
+ if is_impl {
+ let trait_name = self.tcx().def_path_str(trait_def_id);
+ self.tcx().sess.emit_err(ManualImplementation { span, trait_name });
+ }
+ }
+
+ pub(crate) fn complain_about_assoc_type_not_found<I>(
+ &self,
+ all_candidates: impl Fn() -> I,
+ ty_param_name: &str,
+ assoc_name: Ident,
+ span: Span,
+ ) -> ErrorGuaranteed
+ where
+ I: Iterator<Item = ty::PolyTraitRef<'tcx>>,
+ {
+ // The fallback span is needed because `assoc_name` might be an `Fn()`'s `Output` without a
+ // valid span, so we point at the whole path segment instead.
+ let span = if assoc_name.span != DUMMY_SP { assoc_name.span } else { span };
+ let mut err = struct_span_err!(
+ self.tcx().sess,
+ span,
+ E0220,
+ "associated type `{}` not found for `{}`",
+ assoc_name,
+ ty_param_name
+ );
+
+ let all_candidate_names: Vec<_> = all_candidates()
+ .flat_map(|r| self.tcx().associated_items(r.def_id()).in_definition_order())
+ .filter_map(
+ |item| if item.kind == ty::AssocKind::Type { Some(item.name) } else { None },
+ )
+ .collect();
+
+ if let (Some(suggested_name), true) = (
+ find_best_match_for_name(&all_candidate_names, assoc_name.name, None),
+ assoc_name.span != DUMMY_SP,
+ ) {
+ err.span_suggestion(
+ assoc_name.span,
+ "there is an associated type with a similar name",
+ suggested_name,
+ Applicability::MaybeIncorrect,
+ );
+ return err.emit();
+ }
+
+ // If we didn't find a good item in the supertraits (or couldn't get
+ // the supertraits), like in ItemCtxt, then look more generally from
+ // all visible traits. If there's one clear winner, just suggest that.
+
+ let visible_traits: Vec<_> = self
+ .tcx()
+ .all_traits()
+ .filter(|trait_def_id| {
+ let viz = self.tcx().visibility(*trait_def_id);
+ if let Some(def_id) = self.item_def_id() {
+ viz.is_accessible_from(def_id, self.tcx())
+ } else {
+ viz.is_visible_locally()
+ }
+ })
+ .collect();
+
+ let wider_candidate_names: Vec<_> = visible_traits
+ .iter()
+ .flat_map(|trait_def_id| {
+ self.tcx().associated_items(*trait_def_id).in_definition_order()
+ })
+ .filter_map(
+ |item| if item.kind == ty::AssocKind::Type { Some(item.name) } else { None },
+ )
+ .collect();
+
+ if let (Some(suggested_name), true) = (
+ find_best_match_for_name(&wider_candidate_names, assoc_name.name, None),
+ assoc_name.span != DUMMY_SP,
+ ) {
+ if let [best_trait] = visible_traits
+ .iter()
+ .filter(|trait_def_id| {
+ self.tcx()
+ .associated_items(*trait_def_id)
+ .filter_by_name_unhygienic(suggested_name)
+ .any(|item| item.kind == ty::AssocKind::Type)
+ })
+ .collect::<Vec<_>>()[..]
+ {
+ err.span_label(
+ assoc_name.span,
+ format!(
+ "there is a similarly named associated type `{suggested_name}` in the trait `{}`",
+ self.tcx().def_path_str(*best_trait)
+ ),
+ );
+ return err.emit();
+ }
+ }
+
+ err.span_label(span, format!("associated type `{}` not found", assoc_name));
+ err.emit()
+ }
+
+ /// When there are any missing associated types, emit an E0191 error and attempt to supply a
+ /// reasonable suggestion on how to write it. For the case of multiple associated types in the
+ /// same trait bound have the same name (as they come from different supertraits), we instead
+ /// emit a generic note suggesting using a `where` clause to constraint instead.
+ pub(crate) fn complain_about_missing_associated_types(
+ &self,
+ associated_types: FxHashMap<Span, BTreeSet<DefId>>,
+ potential_assoc_types: Vec<Span>,
+ trait_bounds: &[hir::PolyTraitRef<'_>],
+ ) {
+ if associated_types.values().all(|v| v.is_empty()) {
+ return;
+ }
+ let tcx = self.tcx();
+ // FIXME: Marked `mut` so that we can replace the spans further below with a more
+ // appropriate one, but this should be handled earlier in the span assignment.
+ let mut associated_types: FxHashMap<Span, Vec<_>> = associated_types
+ .into_iter()
+ .map(|(span, def_ids)| {
+ (span, def_ids.into_iter().map(|did| tcx.associated_item(did)).collect())
+ })
+ .collect();
+ let mut names = vec![];
+
+ // Account for things like `dyn Foo + 'a`, like in tests `issue-22434.rs` and
+ // `issue-22560.rs`.
+ let mut trait_bound_spans: Vec<Span> = vec![];
+ for (span, items) in &associated_types {
+ if !items.is_empty() {
+ trait_bound_spans.push(*span);
+ }
+ for assoc_item in items {
+ let trait_def_id = assoc_item.container_id(tcx);
+ names.push(format!(
+ "`{}` (from trait `{}`)",
+ assoc_item.name,
+ tcx.def_path_str(trait_def_id),
+ ));
+ }
+ }
+ if let ([], [bound]) = (&potential_assoc_types[..], &trait_bounds) {
+ match bound.trait_ref.path.segments {
+ // FIXME: `trait_ref.path.span` can point to a full path with multiple
+ // segments, even though `trait_ref.path.segments` is of length `1`. Work
+ // around that bug here, even though it should be fixed elsewhere.
+ // This would otherwise cause an invalid suggestion. For an example, look at
+ // `src/test/ui/issues/issue-28344.rs` where instead of the following:
+ //
+ // error[E0191]: the value of the associated type `Output`
+ // (from trait `std::ops::BitXor`) must be specified
+ // --> $DIR/issue-28344.rs:4:17
+ // |
+ // LL | let x: u8 = BitXor::bitor(0 as u8, 0 as u8);
+ // | ^^^^^^ help: specify the associated type:
+ // | `BitXor<Output = Type>`
+ //
+ // we would output:
+ //
+ // error[E0191]: the value of the associated type `Output`
+ // (from trait `std::ops::BitXor`) must be specified
+ // --> $DIR/issue-28344.rs:4:17
+ // |
+ // LL | let x: u8 = BitXor::bitor(0 as u8, 0 as u8);
+ // | ^^^^^^^^^^^^^ help: specify the associated type:
+ // | `BitXor::bitor<Output = Type>`
+ [segment] if segment.args.is_none() => {
+ trait_bound_spans = vec![segment.ident.span];
+ associated_types = associated_types
+ .into_iter()
+ .map(|(_, items)| (segment.ident.span, items))
+ .collect();
+ }
+ _ => {}
+ }
+ }
+ names.sort();
+ trait_bound_spans.sort();
+ let mut err = struct_span_err!(
+ tcx.sess,
+ trait_bound_spans,
+ E0191,
+ "the value of the associated type{} {} must be specified",
+ pluralize!(names.len()),
+ names.join(", "),
+ );
+ let mut suggestions = vec![];
+ let mut types_count = 0;
+ let mut where_constraints = vec![];
+ let mut already_has_generics_args_suggestion = false;
+ for (span, assoc_items) in &associated_types {
+ let mut names: FxHashMap<_, usize> = FxHashMap::default();
+ for item in assoc_items {
+ types_count += 1;
+ *names.entry(item.name).or_insert(0) += 1;
+ }
+ let mut dupes = false;
+ for item in assoc_items {
+ let prefix = if names[&item.name] > 1 {
+ let trait_def_id = item.container_id(tcx);
+ dupes = true;
+ format!("{}::", tcx.def_path_str(trait_def_id))
+ } else {
+ String::new()
+ };
+ if let Some(sp) = tcx.hir().span_if_local(item.def_id) {
+ err.span_label(sp, format!("`{}{}` defined here", prefix, item.name));
+ }
+ }
+ if potential_assoc_types.len() == assoc_items.len() {
+ // When the amount of missing associated types equals the number of
+ // extra type arguments present. A suggesting to replace the generic args with
+ // associated types is already emitted.
+ already_has_generics_args_suggestion = true;
+ } else if let (Ok(snippet), false) =
+ (tcx.sess.source_map().span_to_snippet(*span), dupes)
+ {
+ let types: Vec<_> =
+ assoc_items.iter().map(|item| format!("{} = Type", item.name)).collect();
+ let code = if snippet.ends_with('>') {
+ // The user wrote `Trait<'a>` or similar and we don't have a type we can
+ // suggest, but at least we can clue them to the correct syntax
+ // `Trait<'a, Item = Type>` while accounting for the `<'a>` in the
+ // suggestion.
+ format!("{}, {}>", &snippet[..snippet.len() - 1], types.join(", "))
+ } else {
+ // The user wrote `Iterator`, so we don't have a type we can suggest, but at
+ // least we can clue them to the correct syntax `Iterator<Item = Type>`.
+ format!("{}<{}>", snippet, types.join(", "))
+ };
+ suggestions.push((*span, code));
+ } else if dupes {
+ where_constraints.push(*span);
+ }
+ }
+ let where_msg = "consider introducing a new type parameter, adding `where` constraints \
+ using the fully-qualified path to the associated types";
+ if !where_constraints.is_empty() && suggestions.is_empty() {
+ // If there are duplicates associated type names and a single trait bound do not
+ // use structured suggestion, it means that there are multiple supertraits with
+ // the same associated type name.
+ err.help(where_msg);
+ }
+ if suggestions.len() != 1 || already_has_generics_args_suggestion {
+ // We don't need this label if there's an inline suggestion, show otherwise.
+ for (span, assoc_items) in &associated_types {
+ let mut names: FxHashMap<_, usize> = FxHashMap::default();
+ for item in assoc_items {
+ types_count += 1;
+ *names.entry(item.name).or_insert(0) += 1;
+ }
+ let mut label = vec![];
+ for item in assoc_items {
+ let postfix = if names[&item.name] > 1 {
+ let trait_def_id = item.container_id(tcx);
+ format!(" (from trait `{}`)", tcx.def_path_str(trait_def_id))
+ } else {
+ String::new()
+ };
+ label.push(format!("`{}`{}", item.name, postfix));
+ }
+ if !label.is_empty() {
+ err.span_label(
+ *span,
+ format!(
+ "associated type{} {} must be specified",
+ pluralize!(label.len()),
+ label.join(", "),
+ ),
+ );
+ }
+ }
+ }
+ if !suggestions.is_empty() {
+ err.multipart_suggestion(
+ &format!("specify the associated type{}", pluralize!(types_count)),
+ suggestions,
+ Applicability::HasPlaceholders,
+ );
+ if !where_constraints.is_empty() {
+ err.span_help(where_constraints, where_msg);
+ }
+ }
+ err.emit();
+ }
+}
diff --git a/compiler/rustc_hir_analysis/src/astconv/generics.rs b/compiler/rustc_hir_analysis/src/astconv/generics.rs
new file mode 100644
index 000000000..47915b4bd
--- /dev/null
+++ b/compiler/rustc_hir_analysis/src/astconv/generics.rs
@@ -0,0 +1,662 @@
+use super::IsMethodCall;
+use crate::astconv::{
+ AstConv, CreateSubstsForGenericArgsCtxt, ExplicitLateBound, GenericArgCountMismatch,
+ GenericArgCountResult, GenericArgPosition,
+};
+use crate::errors::AssocTypeBindingNotAllowed;
+use crate::structured_errors::{GenericArgsInfo, StructuredDiagnostic, WrongNumberOfGenericArgs};
+use rustc_ast::ast::ParamKindOrd;
+use rustc_errors::{struct_span_err, Applicability, Diagnostic, MultiSpan};
+use rustc_hir as hir;
+use rustc_hir::def::{DefKind, Res};
+use rustc_hir::def_id::DefId;
+use rustc_hir::GenericArg;
+use rustc_infer::infer::TyCtxtInferExt;
+use rustc_middle::ty::{
+ self, subst, subst::SubstsRef, GenericParamDef, GenericParamDefKind, IsSuggestable, Ty, TyCtxt,
+};
+use rustc_session::lint::builtin::LATE_BOUND_LIFETIME_ARGUMENTS;
+use rustc_span::{symbol::kw, Span};
+use smallvec::SmallVec;
+
+impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
+ /// Report an error that a generic argument did not match the generic parameter that was
+ /// expected.
+ fn generic_arg_mismatch_err(
+ tcx: TyCtxt<'_>,
+ arg: &GenericArg<'_>,
+ param: &GenericParamDef,
+ possible_ordering_error: bool,
+ help: Option<&str>,
+ ) {
+ let sess = tcx.sess;
+ let mut err = struct_span_err!(
+ sess,
+ arg.span(),
+ E0747,
+ "{} provided when a {} was expected",
+ arg.descr(),
+ param.kind.descr(),
+ );
+
+ if let GenericParamDefKind::Const { .. } = param.kind {
+ if matches!(arg, GenericArg::Type(hir::Ty { kind: hir::TyKind::Infer, .. })) {
+ err.help("const arguments cannot yet be inferred with `_`");
+ if sess.is_nightly_build() {
+ err.help(
+ "add `#![feature(generic_arg_infer)]` to the crate attributes to enable",
+ );
+ }
+ }
+ }
+
+ let add_braces_suggestion = |arg: &GenericArg<'_>, err: &mut Diagnostic| {
+ let suggestions = vec![
+ (arg.span().shrink_to_lo(), String::from("{ ")),
+ (arg.span().shrink_to_hi(), String::from(" }")),
+ ];
+ err.multipart_suggestion(
+ "if this generic argument was intended as a const parameter, \
+ surround it with braces",
+ suggestions,
+ Applicability::MaybeIncorrect,
+ );
+ };
+
+ // Specific suggestion set for diagnostics
+ match (arg, &param.kind) {
+ (
+ GenericArg::Type(hir::Ty {
+ kind: hir::TyKind::Path(rustc_hir::QPath::Resolved(_, path)),
+ ..
+ }),
+ GenericParamDefKind::Const { .. },
+ ) => match path.res {
+ Res::Err => {
+ add_braces_suggestion(arg, &mut err);
+ err.set_primary_message(
+ "unresolved item provided when a constant was expected",
+ )
+ .emit();
+ return;
+ }
+ Res::Def(DefKind::TyParam, src_def_id) => {
+ if let Some(param_local_id) = param.def_id.as_local() {
+ let param_name = tcx.hir().ty_param_name(param_local_id);
+ let infcx = tcx.infer_ctxt().build();
+ let param_type =
+ infcx.resolve_numeric_literals_with_default(tcx.type_of(param.def_id));
+ if param_type.is_suggestable(tcx, false) {
+ err.span_suggestion(
+ tcx.def_span(src_def_id),
+ "consider changing this type parameter to be a `const` generic",
+ format!("const {}: {}", param_name, param_type),
+ Applicability::MaybeIncorrect,
+ );
+ };
+ }
+ }
+ _ => add_braces_suggestion(arg, &mut err),
+ },
+ (
+ GenericArg::Type(hir::Ty { kind: hir::TyKind::Path(_), .. }),
+ GenericParamDefKind::Const { .. },
+ ) => add_braces_suggestion(arg, &mut err),
+ (
+ GenericArg::Type(hir::Ty { kind: hir::TyKind::Array(_, len), .. }),
+ GenericParamDefKind::Const { .. },
+ ) if tcx.type_of(param.def_id) == tcx.types.usize => {
+ let snippet = sess.source_map().span_to_snippet(tcx.hir().span(len.hir_id()));
+ if let Ok(snippet) = snippet {
+ err.span_suggestion(
+ arg.span(),
+ "array type provided where a `usize` was expected, try",
+ format!("{{ {} }}", snippet),
+ Applicability::MaybeIncorrect,
+ );
+ }
+ }
+ (GenericArg::Const(cnst), GenericParamDefKind::Type { .. }) => {
+ let body = tcx.hir().body(cnst.value.body);
+ if let rustc_hir::ExprKind::Path(rustc_hir::QPath::Resolved(_, path)) =
+ body.value.kind
+ {
+ if let Res::Def(DefKind::Fn { .. }, id) = path.res {
+ err.help(&format!(
+ "`{}` is a function item, not a type",
+ tcx.item_name(id)
+ ));
+ err.help("function item types cannot be named directly");
+ }
+ }
+ }
+ _ => {}
+ }
+
+ let kind_ord = param.kind.to_ord();
+ let arg_ord = arg.to_ord();
+
+ // This note is only true when generic parameters are strictly ordered by their kind.
+ if possible_ordering_error && kind_ord.cmp(&arg_ord) != core::cmp::Ordering::Equal {
+ let (first, last) = if kind_ord < arg_ord {
+ (param.kind.descr(), arg.descr())
+ } else {
+ (arg.descr(), param.kind.descr())
+ };
+ err.note(&format!("{} arguments must be provided before {} arguments", first, last));
+ if let Some(help) = help {
+ err.help(help);
+ }
+ }
+
+ err.emit();
+ }
+
+ /// Creates the relevant generic argument substitutions
+ /// corresponding to a set of generic parameters. This is a
+ /// rather complex function. Let us try to explain the role
+ /// of each of its parameters:
+ ///
+ /// To start, we are given the `def_id` of the thing we are
+ /// creating the substitutions for, and a partial set of
+ /// substitutions `parent_substs`. In general, the substitutions
+ /// for an item begin with substitutions for all the "parents" of
+ /// that item -- e.g., for a method it might include the
+ /// parameters from the impl.
+ ///
+ /// Therefore, the method begins by walking down these parents,
+ /// starting with the outermost parent and proceed inwards until
+ /// it reaches `def_id`. For each parent `P`, it will check `parent_substs`
+ /// first to see if the parent's substitutions are listed in there. If so,
+ /// we can append those and move on. Otherwise, it invokes the
+ /// three callback functions:
+ ///
+ /// - `args_for_def_id`: given the `DefId` `P`, supplies back the
+ /// generic arguments that were given to that parent from within
+ /// the path; so e.g., if you have `<T as Foo>::Bar`, the `DefId`
+ /// might refer to the trait `Foo`, and the arguments might be
+ /// `[T]`. The boolean value indicates whether to infer values
+ /// for arguments whose values were not explicitly provided.
+ /// - `provided_kind`: given the generic parameter and the value from `args_for_def_id`,
+ /// instantiate a `GenericArg`.
+ /// - `inferred_kind`: if no parameter was provided, and inference is enabled, then
+ /// creates a suitable inference variable.
+ pub fn create_substs_for_generic_args<'a>(
+ tcx: TyCtxt<'tcx>,
+ def_id: DefId,
+ parent_substs: &[subst::GenericArg<'tcx>],
+ has_self: bool,
+ self_ty: Option<Ty<'tcx>>,
+ arg_count: &GenericArgCountResult,
+ ctx: &mut impl CreateSubstsForGenericArgsCtxt<'a, 'tcx>,
+ ) -> SubstsRef<'tcx> {
+ // Collect the segments of the path; we need to substitute arguments
+ // for parameters throughout the entire path (wherever there are
+ // generic parameters).
+ let mut parent_defs = tcx.generics_of(def_id);
+ let count = parent_defs.count();
+ let mut stack = vec![(def_id, parent_defs)];
+ while let Some(def_id) = parent_defs.parent {
+ parent_defs = tcx.generics_of(def_id);
+ stack.push((def_id, parent_defs));
+ }
+
+ // We manually build up the substitution, rather than using convenience
+ // methods in `subst.rs`, so that we can iterate over the arguments and
+ // parameters in lock-step linearly, instead of trying to match each pair.
+ let mut substs: SmallVec<[subst::GenericArg<'tcx>; 8]> = SmallVec::with_capacity(count);
+ // Iterate over each segment of the path.
+ while let Some((def_id, defs)) = stack.pop() {
+ let mut params = defs.params.iter().peekable();
+
+ // If we have already computed substitutions for parents, we can use those directly.
+ while let Some(&param) = params.peek() {
+ if let Some(&kind) = parent_substs.get(param.index as usize) {
+ substs.push(kind);
+ params.next();
+ } else {
+ break;
+ }
+ }
+
+ // `Self` is handled first, unless it's been handled in `parent_substs`.
+ if has_self {
+ if let Some(&param) = params.peek() {
+ if param.index == 0 {
+ if let GenericParamDefKind::Type { .. } = param.kind {
+ substs.push(
+ self_ty
+ .map(|ty| ty.into())
+ .unwrap_or_else(|| ctx.inferred_kind(None, param, true)),
+ );
+ params.next();
+ }
+ }
+ }
+ }
+
+ // Check whether this segment takes generic arguments and the user has provided any.
+ let (generic_args, infer_args) = ctx.args_for_def_id(def_id);
+
+ let args_iter = generic_args.iter().flat_map(|generic_args| generic_args.args.iter());
+ let mut args = args_iter.clone().peekable();
+
+ // If we encounter a type or const when we expect a lifetime, we infer the lifetimes.
+ // If we later encounter a lifetime, we know that the arguments were provided in the
+ // wrong order. `force_infer_lt` records the type or const that forced lifetimes to be
+ // inferred, so we can use it for diagnostics later.
+ let mut force_infer_lt = None;
+
+ loop {
+ // We're going to iterate through the generic arguments that the user
+ // provided, matching them with the generic parameters we expect.
+ // Mismatches can occur as a result of elided lifetimes, or for malformed
+ // input. We try to handle both sensibly.
+ match (args.peek(), params.peek()) {
+ (Some(&arg), Some(&param)) => {
+ match (arg, &param.kind, arg_count.explicit_late_bound) {
+ (GenericArg::Lifetime(_), GenericParamDefKind::Lifetime, _)
+ | (
+ GenericArg::Type(_) | GenericArg::Infer(_),
+ GenericParamDefKind::Type { .. },
+ _,
+ )
+ | (
+ GenericArg::Const(_) | GenericArg::Infer(_),
+ GenericParamDefKind::Const { .. },
+ _,
+ ) => {
+ substs.push(ctx.provided_kind(param, arg));
+ args.next();
+ params.next();
+ }
+ (
+ GenericArg::Infer(_) | GenericArg::Type(_) | GenericArg::Const(_),
+ GenericParamDefKind::Lifetime,
+ _,
+ ) => {
+ // We expected a lifetime argument, but got a type or const
+ // argument. That means we're inferring the lifetimes.
+ substs.push(ctx.inferred_kind(None, param, infer_args));
+ force_infer_lt = Some((arg, param));
+ params.next();
+ }
+ (GenericArg::Lifetime(_), _, ExplicitLateBound::Yes) => {
+ // We've come across a lifetime when we expected something else in
+ // the presence of explicit late bounds. This is most likely
+ // due to the presence of the explicit bound so we're just going to
+ // ignore it.
+ args.next();
+ }
+ (_, _, _) => {
+ // We expected one kind of parameter, but the user provided
+ // another. This is an error. However, if we already know that
+ // the arguments don't match up with the parameters, we won't issue
+ // an additional error, as the user already knows what's wrong.
+ if arg_count.correct.is_ok() {
+ // We're going to iterate over the parameters to sort them out, and
+ // show that order to the user as a possible order for the parameters
+ let mut param_types_present = defs
+ .params
+ .iter()
+ .map(|param| (param.kind.to_ord(), param.clone()))
+ .collect::<Vec<(ParamKindOrd, GenericParamDef)>>();
+ param_types_present.sort_by_key(|(ord, _)| *ord);
+ let (mut param_types_present, ordered_params): (
+ Vec<ParamKindOrd>,
+ Vec<GenericParamDef>,
+ ) = param_types_present.into_iter().unzip();
+ param_types_present.dedup();
+
+ Self::generic_arg_mismatch_err(
+ tcx,
+ arg,
+ param,
+ !args_iter.clone().is_sorted_by_key(|arg| arg.to_ord()),
+ Some(&format!(
+ "reorder the arguments: {}: `<{}>`",
+ param_types_present
+ .into_iter()
+ .map(|ord| format!("{}s", ord))
+ .collect::<Vec<String>>()
+ .join(", then "),
+ ordered_params
+ .into_iter()
+ .filter_map(|param| {
+ if param.name == kw::SelfUpper {
+ None
+ } else {
+ Some(param.name.to_string())
+ }
+ })
+ .collect::<Vec<String>>()
+ .join(", ")
+ )),
+ );
+ }
+
+ // We've reported the error, but we want to make sure that this
+ // problem doesn't bubble down and create additional, irrelevant
+ // errors. In this case, we're simply going to ignore the argument
+ // and any following arguments. The rest of the parameters will be
+ // inferred.
+ while args.next().is_some() {}
+ }
+ }
+ }
+
+ (Some(&arg), None) => {
+ // We should never be able to reach this point with well-formed input.
+ // There are three situations in which we can encounter this issue.
+ //
+ // 1. The number of arguments is incorrect. In this case, an error
+ // will already have been emitted, and we can ignore it.
+ // 2. There are late-bound lifetime parameters present, yet the
+ // lifetime arguments have also been explicitly specified by the
+ // user.
+ // 3. We've inferred some lifetimes, which have been provided later (i.e.
+ // after a type or const). We want to throw an error in this case.
+
+ if arg_count.correct.is_ok()
+ && arg_count.explicit_late_bound == ExplicitLateBound::No
+ {
+ let kind = arg.descr();
+ assert_eq!(kind, "lifetime");
+ let (provided_arg, param) =
+ force_infer_lt.expect("lifetimes ought to have been inferred");
+ Self::generic_arg_mismatch_err(tcx, provided_arg, param, false, None);
+ }
+
+ break;
+ }
+
+ (None, Some(&param)) => {
+ // If there are fewer arguments than parameters, it means
+ // we're inferring the remaining arguments.
+ substs.push(ctx.inferred_kind(Some(&substs), param, infer_args));
+ params.next();
+ }
+
+ (None, None) => break,
+ }
+ }
+ }
+
+ tcx.intern_substs(&substs)
+ }
+
+ /// Checks that the correct number of generic arguments have been provided.
+ /// Used specifically for function calls.
+ pub fn check_generic_arg_count_for_call(
+ tcx: TyCtxt<'_>,
+ span: Span,
+ def_id: DefId,
+ generics: &ty::Generics,
+ seg: &hir::PathSegment<'_>,
+ is_method_call: IsMethodCall,
+ ) -> GenericArgCountResult {
+ let empty_args = hir::GenericArgs::none();
+ let gen_args = seg.args.unwrap_or(&empty_args);
+ let gen_pos = if is_method_call == IsMethodCall::Yes {
+ GenericArgPosition::MethodCall
+ } else {
+ GenericArgPosition::Value
+ };
+ let has_self = generics.parent.is_none() && generics.has_self;
+
+ Self::check_generic_arg_count(
+ tcx,
+ span,
+ def_id,
+ seg,
+ generics,
+ gen_args,
+ gen_pos,
+ has_self,
+ seg.infer_args,
+ )
+ }
+
+ /// Checks that the correct number of generic arguments have been provided.
+ /// This is used both for datatypes and function calls.
+ #[instrument(skip(tcx, gen_pos), level = "debug")]
+ pub(crate) fn check_generic_arg_count(
+ tcx: TyCtxt<'_>,
+ span: Span,
+ def_id: DefId,
+ seg: &hir::PathSegment<'_>,
+ gen_params: &ty::Generics,
+ gen_args: &hir::GenericArgs<'_>,
+ gen_pos: GenericArgPosition,
+ has_self: bool,
+ infer_args: bool,
+ ) -> GenericArgCountResult {
+ let default_counts = gen_params.own_defaults();
+ let param_counts = gen_params.own_counts();
+
+ // Subtracting from param count to ensure type params synthesized from `impl Trait`
+ // cannot be explicitly specified.
+ let synth_type_param_count = gen_params
+ .params
+ .iter()
+ .filter(|param| {
+ matches!(param.kind, ty::GenericParamDefKind::Type { synthetic: true, .. })
+ })
+ .count();
+ let named_type_param_count =
+ param_counts.types - has_self as usize - synth_type_param_count;
+ let infer_lifetimes =
+ (gen_pos != GenericArgPosition::Type || infer_args) && !gen_args.has_lifetime_params();
+
+ if gen_pos != GenericArgPosition::Type && let Some(b) = gen_args.bindings.first() {
+ Self::prohibit_assoc_ty_binding(tcx, b.span);
+ }
+
+ let explicit_late_bound =
+ Self::prohibit_explicit_late_bound_lifetimes(tcx, gen_params, gen_args, gen_pos);
+
+ let mut invalid_args = vec![];
+
+ let mut check_lifetime_args =
+ |min_expected_args: usize,
+ max_expected_args: usize,
+ provided_args: usize,
+ late_bounds_ignore: bool| {
+ if (min_expected_args..=max_expected_args).contains(&provided_args) {
+ return Ok(());
+ }
+
+ if late_bounds_ignore {
+ return Ok(());
+ }
+
+ if provided_args > max_expected_args {
+ invalid_args.extend(
+ gen_args.args[max_expected_args..provided_args]
+ .iter()
+ .map(|arg| arg.span()),
+ );
+ };
+
+ let gen_args_info = if provided_args > min_expected_args {
+ invalid_args.extend(
+ gen_args.args[min_expected_args..provided_args]
+ .iter()
+ .map(|arg| arg.span()),
+ );
+ let num_redundant_args = provided_args - min_expected_args;
+ GenericArgsInfo::ExcessLifetimes { num_redundant_args }
+ } else {
+ let num_missing_args = min_expected_args - provided_args;
+ GenericArgsInfo::MissingLifetimes { num_missing_args }
+ };
+
+ let reported = WrongNumberOfGenericArgs::new(
+ tcx,
+ gen_args_info,
+ seg,
+ gen_params,
+ has_self as usize,
+ gen_args,
+ def_id,
+ )
+ .diagnostic()
+ .emit();
+
+ Err(reported)
+ };
+
+ let min_expected_lifetime_args = if infer_lifetimes { 0 } else { param_counts.lifetimes };
+ let max_expected_lifetime_args = param_counts.lifetimes;
+ let num_provided_lifetime_args = gen_args.num_lifetime_params();
+
+ let lifetimes_correct = check_lifetime_args(
+ min_expected_lifetime_args,
+ max_expected_lifetime_args,
+ num_provided_lifetime_args,
+ explicit_late_bound == ExplicitLateBound::Yes,
+ );
+
+ let mut check_types_and_consts = |expected_min,
+ expected_max,
+ expected_max_with_synth,
+ provided,
+ params_offset,
+ args_offset| {
+ debug!(
+ ?expected_min,
+ ?expected_max,
+ ?provided,
+ ?params_offset,
+ ?args_offset,
+ "check_types_and_consts"
+ );
+ if (expected_min..=expected_max).contains(&provided) {
+ return Ok(());
+ }
+
+ let num_default_params = expected_max - expected_min;
+
+ let gen_args_info = if provided > expected_max {
+ invalid_args.extend(
+ gen_args.args[args_offset + expected_max..args_offset + provided]
+ .iter()
+ .map(|arg| arg.span()),
+ );
+ let num_redundant_args = provided - expected_max;
+
+ // Provide extra note if synthetic arguments like `impl Trait` are specified.
+ let synth_provided = provided <= expected_max_with_synth;
+
+ GenericArgsInfo::ExcessTypesOrConsts {
+ num_redundant_args,
+ num_default_params,
+ args_offset,
+ synth_provided,
+ }
+ } else {
+ let num_missing_args = expected_max - provided;
+
+ GenericArgsInfo::MissingTypesOrConsts {
+ num_missing_args,
+ num_default_params,
+ args_offset,
+ }
+ };
+
+ debug!(?gen_args_info);
+
+ let reported = WrongNumberOfGenericArgs::new(
+ tcx,
+ gen_args_info,
+ seg,
+ gen_params,
+ params_offset,
+ gen_args,
+ def_id,
+ )
+ .diagnostic()
+ .emit_unless(gen_args.has_err());
+
+ Err(reported)
+ };
+
+ let args_correct = {
+ let expected_min = if infer_args {
+ 0
+ } else {
+ param_counts.consts + named_type_param_count
+ - default_counts.types
+ - default_counts.consts
+ };
+ debug!(?expected_min);
+ debug!(arg_counts.lifetimes=?gen_args.num_lifetime_params());
+
+ check_types_and_consts(
+ expected_min,
+ param_counts.consts + named_type_param_count,
+ param_counts.consts + named_type_param_count + synth_type_param_count,
+ gen_args.num_generic_params(),
+ param_counts.lifetimes + has_self as usize,
+ gen_args.num_lifetime_params(),
+ )
+ };
+
+ GenericArgCountResult {
+ explicit_late_bound,
+ correct: lifetimes_correct.and(args_correct).map_err(|reported| {
+ GenericArgCountMismatch { reported: Some(reported), invalid_args }
+ }),
+ }
+ }
+
+ /// Emits an error regarding forbidden type binding associations
+ pub fn prohibit_assoc_ty_binding(tcx: TyCtxt<'_>, span: Span) {
+ tcx.sess.emit_err(AssocTypeBindingNotAllowed { span });
+ }
+
+ /// Prohibits explicit lifetime arguments if late-bound lifetime parameters
+ /// are present. This is used both for datatypes and function calls.
+ pub(crate) fn prohibit_explicit_late_bound_lifetimes(
+ tcx: TyCtxt<'_>,
+ def: &ty::Generics,
+ args: &hir::GenericArgs<'_>,
+ position: GenericArgPosition,
+ ) -> ExplicitLateBound {
+ let param_counts = def.own_counts();
+ let infer_lifetimes = position != GenericArgPosition::Type && !args.has_lifetime_params();
+
+ if infer_lifetimes {
+ return ExplicitLateBound::No;
+ }
+
+ if let Some(span_late) = def.has_late_bound_regions {
+ let msg = "cannot specify lifetime arguments explicitly \
+ if late bound lifetime parameters are present";
+ let note = "the late bound lifetime parameter is introduced here";
+ let span = args.args[0].span();
+
+ if position == GenericArgPosition::Value
+ && args.num_lifetime_params() != param_counts.lifetimes
+ {
+ let mut err = tcx.sess.struct_span_err(span, msg);
+ err.span_note(span_late, note);
+ err.emit();
+ } else {
+ let mut multispan = MultiSpan::from_span(span);
+ multispan.push_span_label(span_late, note);
+ tcx.struct_span_lint_hir(
+ LATE_BOUND_LIFETIME_ARGUMENTS,
+ args.args[0].hir_id(),
+ multispan,
+ msg,
+ |lint| lint,
+ );
+ }
+
+ ExplicitLateBound::Yes
+ } else {
+ ExplicitLateBound::No
+ }
+ }
+}
diff --git a/compiler/rustc_hir_analysis/src/astconv/mod.rs b/compiler/rustc_hir_analysis/src/astconv/mod.rs
new file mode 100644
index 000000000..38f195dab
--- /dev/null
+++ b/compiler/rustc_hir_analysis/src/astconv/mod.rs
@@ -0,0 +1,3136 @@
+//! Conversion from AST representation of types to the `ty.rs` representation.
+//! The main routine here is `ast_ty_to_ty()`; each use is parameterized by an
+//! instance of `AstConv`.
+
+mod errors;
+mod generics;
+
+use crate::bounds::Bounds;
+use crate::collect::HirPlaceholderCollector;
+use crate::errors::{
+ AmbiguousLifetimeBound, MultipleRelaxedDefaultBounds, TraitObjectDeclaredWithNoTraits,
+ TypeofReservedKeywordUsed, ValueOfAssociatedStructAlreadySpecified,
+};
+use crate::middle::resolve_lifetime as rl;
+use crate::require_c_abi_if_c_variadic;
+use rustc_ast::TraitObjectSyntax;
+use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_errors::{
+ struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, FatalError,
+ MultiSpan,
+};
+use rustc_hir as hir;
+use rustc_hir::def::{CtorOf, DefKind, Namespace, Res};
+use rustc_hir::def_id::{DefId, LocalDefId};
+use rustc_hir::intravisit::{walk_generics, Visitor as _};
+use rustc_hir::lang_items::LangItem;
+use rustc_hir::{GenericArg, GenericArgs, OpaqueTyOrigin};
+use rustc_middle::middle::stability::AllowUnstable;
+use rustc_middle::ty::subst::{self, GenericArgKind, InternalSubsts, SubstsRef};
+use rustc_middle::ty::DynKind;
+use rustc_middle::ty::GenericParamDefKind;
+use rustc_middle::ty::{
+ self, Const, DefIdTree, EarlyBinder, IsSuggestable, Ty, TyCtxt, TypeVisitable,
+};
+use rustc_session::lint::builtin::{AMBIGUOUS_ASSOCIATED_ITEMS, BARE_TRAIT_OBJECTS};
+use rustc_span::edition::Edition;
+use rustc_span::lev_distance::find_best_match_for_name;
+use rustc_span::symbol::{kw, Ident, Symbol};
+use rustc_span::{sym, Span};
+use rustc_target::spec::abi;
+use rustc_trait_selection::traits;
+use rustc_trait_selection::traits::astconv_object_safety_violations;
+use rustc_trait_selection::traits::error_reporting::{
+ report_object_safety_error, suggestions::NextTypeParamName,
+};
+use rustc_trait_selection::traits::wf::object_region_bounds;
+
+use smallvec::{smallvec, SmallVec};
+use std::collections::BTreeSet;
+use std::slice;
+
+#[derive(Debug)]
+pub struct PathSeg(pub DefId, pub usize);
+
+pub trait AstConv<'tcx> {
+ fn tcx<'a>(&'a self) -> TyCtxt<'tcx>;
+
+ fn item_def_id(&self) -> Option<DefId>;
+
+ /// Returns predicates in scope of the form `X: Foo<T>`, where `X`
+ /// is a type parameter `X` with the given id `def_id` and T
+ /// matches `assoc_name`. This is a subset of the full set of
+ /// predicates.
+ ///
+ /// This is used for one specific purpose: resolving "short-hand"
+ /// associated type references like `T::Item`. In principle, we
+ /// would do that by first getting the full set of predicates in
+ /// scope and then filtering down to find those that apply to `T`,
+ /// but this can lead to cycle errors. The problem is that we have
+ /// to do this resolution *in order to create the predicates in
+ /// the first place*. Hence, we have this "special pass".
+ fn get_type_parameter_bounds(
+ &self,
+ span: Span,
+ def_id: DefId,
+ assoc_name: Ident,
+ ) -> ty::GenericPredicates<'tcx>;
+
+ /// Returns the lifetime to use when a lifetime is omitted (and not elided).
+ fn re_infer(&self, param: Option<&ty::GenericParamDef>, span: Span)
+ -> Option<ty::Region<'tcx>>;
+
+ /// Returns the type to use when a type is omitted.
+ fn ty_infer(&self, param: Option<&ty::GenericParamDef>, span: Span) -> Ty<'tcx>;
+
+ /// Returns `true` if `_` is allowed in type signatures in the current context.
+ fn allow_ty_infer(&self) -> bool;
+
+ /// Returns the const to use when a const is omitted.
+ fn ct_infer(
+ &self,
+ ty: Ty<'tcx>,
+ param: Option<&ty::GenericParamDef>,
+ span: Span,
+ ) -> Const<'tcx>;
+
+ /// Projecting an associated type from a (potentially)
+ /// higher-ranked trait reference is more complicated, because of
+ /// the possibility of late-bound regions appearing in the
+ /// associated type binding. This is not legal in function
+ /// signatures for that reason. In a function body, we can always
+ /// handle it because we can use inference variables to remove the
+ /// late-bound regions.
+ fn projected_ty_from_poly_trait_ref(
+ &self,
+ span: Span,
+ item_def_id: DefId,
+ item_segment: &hir::PathSegment<'_>,
+ poly_trait_ref: ty::PolyTraitRef<'tcx>,
+ ) -> Ty<'tcx>;
+
+ /// Normalize an associated type coming from the user.
+ fn normalize_ty(&self, span: Span, ty: Ty<'tcx>) -> Ty<'tcx>;
+
+ /// Invoked when we encounter an error from some prior pass
+ /// (e.g., resolve) that is translated into a ty-error. This is
+ /// used to help suppress derived errors typeck might otherwise
+ /// report.
+ fn set_tainted_by_errors(&self);
+
+ fn record_ty(&self, hir_id: hir::HirId, ty: Ty<'tcx>, span: Span);
+}
+
+#[derive(Debug)]
+struct ConvertedBinding<'a, 'tcx> {
+ hir_id: hir::HirId,
+ item_name: Ident,
+ kind: ConvertedBindingKind<'a, 'tcx>,
+ gen_args: &'a GenericArgs<'a>,
+ span: Span,
+}
+
+#[derive(Debug)]
+enum ConvertedBindingKind<'a, 'tcx> {
+ Equality(ty::Term<'tcx>),
+ Constraint(&'a [hir::GenericBound<'a>]),
+}
+
+/// New-typed boolean indicating whether explicit late-bound lifetimes
+/// are present in a set of generic arguments.
+///
+/// For example if we have some method `fn f<'a>(&'a self)` implemented
+/// for some type `T`, although `f` is generic in the lifetime `'a`, `'a`
+/// is late-bound so should not be provided explicitly. Thus, if `f` is
+/// instantiated with some generic arguments providing `'a` explicitly,
+/// we taint those arguments with `ExplicitLateBound::Yes` so that we
+/// can provide an appropriate diagnostic later.
+#[derive(Copy, Clone, PartialEq, Debug)]
+pub enum ExplicitLateBound {
+ Yes,
+ No,
+}
+
+#[derive(Copy, Clone, PartialEq)]
+pub enum IsMethodCall {
+ Yes,
+ No,
+}
+
+/// Denotes the "position" of a generic argument, indicating if it is a generic type,
+/// generic function or generic method call.
+#[derive(Copy, Clone, PartialEq)]
+pub(crate) enum GenericArgPosition {
+ Type,
+ Value, // e.g., functions
+ MethodCall,
+}
+
+/// A marker denoting that the generic arguments that were
+/// provided did not match the respective generic parameters.
+#[derive(Clone, Default, Debug)]
+pub struct GenericArgCountMismatch {
+ /// Indicates whether a fatal error was reported (`Some`), or just a lint (`None`).
+ pub reported: Option<ErrorGuaranteed>,
+ /// A list of spans of arguments provided that were not valid.
+ pub invalid_args: Vec<Span>,
+}
+
+/// Decorates the result of a generic argument count mismatch
+/// check with whether explicit late bounds were provided.
+#[derive(Clone, Debug)]
+pub struct GenericArgCountResult {
+ pub explicit_late_bound: ExplicitLateBound,
+ pub correct: Result<(), GenericArgCountMismatch>,
+}
+
+pub trait CreateSubstsForGenericArgsCtxt<'a, 'tcx> {
+ fn args_for_def_id(&mut self, def_id: DefId) -> (Option<&'a GenericArgs<'a>>, bool);
+
+ fn provided_kind(
+ &mut self,
+ param: &ty::GenericParamDef,
+ arg: &GenericArg<'_>,
+ ) -> subst::GenericArg<'tcx>;
+
+ fn inferred_kind(
+ &mut self,
+ substs: Option<&[subst::GenericArg<'tcx>]>,
+ param: &ty::GenericParamDef,
+ infer_args: bool,
+ ) -> subst::GenericArg<'tcx>;
+}
+
+impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
+ #[instrument(level = "debug", skip(self), ret)]
+ pub fn ast_region_to_region(
+ &self,
+ lifetime: &hir::Lifetime,
+ def: Option<&ty::GenericParamDef>,
+ ) -> ty::Region<'tcx> {
+ let tcx = self.tcx();
+ let lifetime_name = |def_id| tcx.hir().name(tcx.hir().local_def_id_to_hir_id(def_id));
+
+ match tcx.named_region(lifetime.hir_id) {
+ Some(rl::Region::Static) => tcx.lifetimes.re_static,
+
+ Some(rl::Region::LateBound(debruijn, index, def_id)) => {
+ let name = lifetime_name(def_id.expect_local());
+ let br = ty::BoundRegion {
+ var: ty::BoundVar::from_u32(index),
+ kind: ty::BrNamed(def_id, name),
+ };
+ tcx.mk_region(ty::ReLateBound(debruijn, br))
+ }
+
+ Some(rl::Region::EarlyBound(def_id)) => {
+ let name = tcx.hir().ty_param_name(def_id.expect_local());
+ let item_def_id = tcx.hir().ty_param_owner(def_id.expect_local());
+ let generics = tcx.generics_of(item_def_id);
+ let index = generics.param_def_id_to_index[&def_id];
+ tcx.mk_region(ty::ReEarlyBound(ty::EarlyBoundRegion { def_id, index, name }))
+ }
+
+ Some(rl::Region::Free(scope, id)) => {
+ let name = lifetime_name(id.expect_local());
+ tcx.mk_region(ty::ReFree(ty::FreeRegion {
+ scope,
+ bound_region: ty::BrNamed(id, name),
+ }))
+
+ // (*) -- not late-bound, won't change
+ }
+
+ None => {
+ self.re_infer(def, lifetime.span).unwrap_or_else(|| {
+ debug!(?lifetime, "unelided lifetime in signature");
+
+ // This indicates an illegal lifetime
+ // elision. `resolve_lifetime` should have
+ // reported an error in this case -- but if
+ // not, let's error out.
+ tcx.sess.delay_span_bug(lifetime.span, "unelided lifetime in signature");
+
+ // Supply some dummy value. We don't have an
+ // `re_error`, annoyingly, so use `'static`.
+ tcx.lifetimes.re_static
+ })
+ }
+ }
+ }
+
+ /// Given a path `path` that refers to an item `I` with the declared generics `decl_generics`,
+ /// returns an appropriate set of substitutions for this particular reference to `I`.
+ pub fn ast_path_substs_for_ty(
+ &self,
+ span: Span,
+ def_id: DefId,
+ item_segment: &hir::PathSegment<'_>,
+ ) -> SubstsRef<'tcx> {
+ let (substs, _) = self.create_substs_for_ast_path(
+ span,
+ def_id,
+ &[],
+ item_segment,
+ item_segment.args(),
+ item_segment.infer_args,
+ None,
+ None,
+ );
+ if let Some(b) = item_segment.args().bindings.first() {
+ Self::prohibit_assoc_ty_binding(self.tcx(), b.span);
+ }
+
+ substs
+ }
+
+ /// Given the type/lifetime/const arguments provided to some path (along with
+ /// an implicit `Self`, if this is a trait reference), returns the complete
+ /// set of substitutions. This may involve applying defaulted type parameters.
+ /// Constraints on associated types are created from `create_assoc_bindings_for_generic_args`.
+ ///
+ /// Example:
+ ///
+ /// ```ignore (illustrative)
+ /// T: std::ops::Index<usize, Output = u32>
+ /// // ^1 ^^^^^^^^^^^^^^2 ^^^^3 ^^^^^^^^^^^4
+ /// ```
+ ///
+ /// 1. The `self_ty` here would refer to the type `T`.
+ /// 2. The path in question is the path to the trait `std::ops::Index`,
+ /// which will have been resolved to a `def_id`
+ /// 3. The `generic_args` contains info on the `<...>` contents. The `usize` type
+ /// parameters are returned in the `SubstsRef`, the associated type bindings like
+ /// `Output = u32` are returned from `create_assoc_bindings_for_generic_args`.
+ ///
+ /// Note that the type listing given here is *exactly* what the user provided.
+ ///
+ /// For (generic) associated types
+ ///
+ /// ```ignore (illustrative)
+ /// <Vec<u8> as Iterable<u8>>::Iter::<'a>
+ /// ```
+ ///
+ /// We have the parent substs are the substs for the parent trait:
+ /// `[Vec<u8>, u8]` and `generic_args` are the arguments for the associated
+ /// type itself: `['a]`. The returned `SubstsRef` concatenates these two
+ /// lists: `[Vec<u8>, u8, 'a]`.
+ #[instrument(level = "debug", skip(self, span), ret)]
+ fn create_substs_for_ast_path<'a>(
+ &self,
+ span: Span,
+ def_id: DefId,
+ parent_substs: &[subst::GenericArg<'tcx>],
+ seg: &hir::PathSegment<'_>,
+ generic_args: &'a hir::GenericArgs<'_>,
+ infer_args: bool,
+ self_ty: Option<Ty<'tcx>>,
+ constness: Option<ty::BoundConstness>,
+ ) -> (SubstsRef<'tcx>, GenericArgCountResult) {
+ // If the type is parameterized by this region, then replace this
+ // region with the current anon region binding (in other words,
+ // whatever & would get replaced with).
+
+ let tcx = self.tcx();
+ let generics = tcx.generics_of(def_id);
+ debug!("generics: {:?}", generics);
+
+ if generics.has_self {
+ if generics.parent.is_some() {
+ // The parent is a trait so it should have at least one subst
+ // for the `Self` type.
+ assert!(!parent_substs.is_empty())
+ } else {
+ // This item (presumably a trait) needs a self-type.
+ assert!(self_ty.is_some());
+ }
+ } else {
+ assert!(self_ty.is_none() && parent_substs.is_empty());
+ }
+
+ let arg_count = Self::check_generic_arg_count(
+ tcx,
+ span,
+ def_id,
+ seg,
+ generics,
+ generic_args,
+ GenericArgPosition::Type,
+ self_ty.is_some(),
+ infer_args,
+ );
+
+ // Skip processing if type has no generic parameters.
+ // Traits always have `Self` as a generic parameter, which means they will not return early
+ // here and so associated type bindings will be handled regardless of whether there are any
+ // non-`Self` generic parameters.
+ if generics.params.is_empty() {
+ return (tcx.intern_substs(parent_substs), arg_count);
+ }
+
+ struct SubstsForAstPathCtxt<'a, 'tcx> {
+ astconv: &'a (dyn AstConv<'tcx> + 'a),
+ def_id: DefId,
+ generic_args: &'a GenericArgs<'a>,
+ span: Span,
+ inferred_params: Vec<Span>,
+ infer_args: bool,
+ }
+
+ impl<'a, 'tcx> CreateSubstsForGenericArgsCtxt<'a, 'tcx> for SubstsForAstPathCtxt<'a, 'tcx> {
+ fn args_for_def_id(&mut self, did: DefId) -> (Option<&'a GenericArgs<'a>>, bool) {
+ if did == self.def_id {
+ (Some(self.generic_args), self.infer_args)
+ } else {
+ // The last component of this tuple is unimportant.
+ (None, false)
+ }
+ }
+
+ fn provided_kind(
+ &mut self,
+ param: &ty::GenericParamDef,
+ arg: &GenericArg<'_>,
+ ) -> subst::GenericArg<'tcx> {
+ let tcx = self.astconv.tcx();
+
+ let mut handle_ty_args = |has_default, ty: &hir::Ty<'_>| {
+ if has_default {
+ tcx.check_optional_stability(
+ param.def_id,
+ Some(arg.hir_id()),
+ arg.span(),
+ None,
+ AllowUnstable::No,
+ |_, _| {
+ // Default generic parameters may not be marked
+ // with stability attributes, i.e. when the
+ // default parameter was defined at the same time
+ // as the rest of the type. As such, we ignore missing
+ // stability attributes.
+ },
+ );
+ }
+ if let (hir::TyKind::Infer, false) = (&ty.kind, self.astconv.allow_ty_infer()) {
+ self.inferred_params.push(ty.span);
+ tcx.ty_error().into()
+ } else {
+ self.astconv.ast_ty_to_ty(ty).into()
+ }
+ };
+
+ match (&param.kind, arg) {
+ (GenericParamDefKind::Lifetime, GenericArg::Lifetime(lt)) => {
+ self.astconv.ast_region_to_region(lt, Some(param)).into()
+ }
+ (&GenericParamDefKind::Type { has_default, .. }, GenericArg::Type(ty)) => {
+ handle_ty_args(has_default, ty)
+ }
+ (&GenericParamDefKind::Type { has_default, .. }, GenericArg::Infer(inf)) => {
+ handle_ty_args(has_default, &inf.to_ty())
+ }
+ (GenericParamDefKind::Const { .. }, GenericArg::Const(ct)) => {
+ ty::Const::from_opt_const_arg_anon_const(
+ tcx,
+ ty::WithOptConstParam {
+ did: tcx.hir().local_def_id(ct.value.hir_id),
+ const_param_did: Some(param.def_id),
+ },
+ )
+ .into()
+ }
+ (&GenericParamDefKind::Const { .. }, hir::GenericArg::Infer(inf)) => {
+ let ty = tcx.at(self.span).type_of(param.def_id);
+ if self.astconv.allow_ty_infer() {
+ self.astconv.ct_infer(ty, Some(param), inf.span).into()
+ } else {
+ self.inferred_params.push(inf.span);
+ tcx.const_error(ty).into()
+ }
+ }
+ _ => unreachable!(),
+ }
+ }
+
+ fn inferred_kind(
+ &mut self,
+ substs: Option<&[subst::GenericArg<'tcx>]>,
+ param: &ty::GenericParamDef,
+ infer_args: bool,
+ ) -> subst::GenericArg<'tcx> {
+ let tcx = self.astconv.tcx();
+ match param.kind {
+ GenericParamDefKind::Lifetime => self
+ .astconv
+ .re_infer(Some(param), self.span)
+ .unwrap_or_else(|| {
+ debug!(?param, "unelided lifetime in signature");
+
+ // This indicates an illegal lifetime in a non-assoc-trait position
+ tcx.sess.delay_span_bug(self.span, "unelided lifetime in signature");
+
+ // Supply some dummy value. We don't have an
+ // `re_error`, annoyingly, so use `'static`.
+ tcx.lifetimes.re_static
+ })
+ .into(),
+ GenericParamDefKind::Type { has_default, .. } => {
+ if !infer_args && has_default {
+ // No type parameter provided, but a default exists.
+ let substs = substs.unwrap();
+ if substs.iter().any(|arg| match arg.unpack() {
+ GenericArgKind::Type(ty) => ty.references_error(),
+ _ => false,
+ }) {
+ // Avoid ICE #86756 when type error recovery goes awry.
+ return tcx.ty_error().into();
+ }
+ self.astconv
+ .normalize_ty(
+ self.span,
+ EarlyBinder(tcx.at(self.span).type_of(param.def_id))
+ .subst(tcx, substs),
+ )
+ .into()
+ } else if infer_args {
+ self.astconv.ty_infer(Some(param), self.span).into()
+ } else {
+ // We've already errored above about the mismatch.
+ tcx.ty_error().into()
+ }
+ }
+ GenericParamDefKind::Const { has_default } => {
+ let ty = tcx.at(self.span).type_of(param.def_id);
+ if !infer_args && has_default {
+ tcx.bound_const_param_default(param.def_id)
+ .subst(tcx, substs.unwrap())
+ .into()
+ } else {
+ if infer_args {
+ self.astconv.ct_infer(ty, Some(param), self.span).into()
+ } else {
+ // We've already errored above about the mismatch.
+ tcx.const_error(ty).into()
+ }
+ }
+ }
+ }
+ }
+ }
+
+ let mut substs_ctx = SubstsForAstPathCtxt {
+ astconv: self,
+ def_id,
+ span,
+ generic_args,
+ inferred_params: vec![],
+ infer_args,
+ };
+ let substs = Self::create_substs_for_generic_args(
+ tcx,
+ def_id,
+ parent_substs,
+ self_ty.is_some(),
+ self_ty,
+ &arg_count,
+ &mut substs_ctx,
+ );
+
+ if let Some(ty::BoundConstness::ConstIfConst) = constness
+ && generics.has_self && !tcx.has_attr(def_id, sym::const_trait)
+ {
+ tcx.sess.emit_err(crate::errors::ConstBoundForNonConstTrait { span } );
+ }
+
+ (substs, arg_count)
+ }
+
+ fn create_assoc_bindings_for_generic_args<'a>(
+ &self,
+ generic_args: &'a hir::GenericArgs<'_>,
+ ) -> Vec<ConvertedBinding<'a, 'tcx>> {
+ // Convert associated-type bindings or constraints into a separate vector.
+ // Example: Given this:
+ //
+ // T: Iterator<Item = u32>
+ //
+ // The `T` is passed in as a self-type; the `Item = u32` is
+ // not a "type parameter" of the `Iterator` trait, but rather
+ // a restriction on `<T as Iterator>::Item`, so it is passed
+ // back separately.
+ let assoc_bindings = generic_args
+ .bindings
+ .iter()
+ .map(|binding| {
+ let kind = match binding.kind {
+ hir::TypeBindingKind::Equality { ref term } => match term {
+ hir::Term::Ty(ref ty) => {
+ ConvertedBindingKind::Equality(self.ast_ty_to_ty(ty).into())
+ }
+ hir::Term::Const(ref c) => {
+ let local_did = self.tcx().hir().local_def_id(c.hir_id);
+ let c = Const::from_anon_const(self.tcx(), local_did);
+ ConvertedBindingKind::Equality(c.into())
+ }
+ },
+ hir::TypeBindingKind::Constraint { ref bounds } => {
+ ConvertedBindingKind::Constraint(bounds)
+ }
+ };
+ ConvertedBinding {
+ hir_id: binding.hir_id,
+ item_name: binding.ident,
+ kind,
+ gen_args: binding.gen_args,
+ span: binding.span,
+ }
+ })
+ .collect();
+
+ assoc_bindings
+ }
+
+ pub fn create_substs_for_associated_item(
+ &self,
+ span: Span,
+ item_def_id: DefId,
+ item_segment: &hir::PathSegment<'_>,
+ parent_substs: SubstsRef<'tcx>,
+ ) -> SubstsRef<'tcx> {
+ debug!(
+ "create_substs_for_associated_item(span: {:?}, item_def_id: {:?}, item_segment: {:?}",
+ span, item_def_id, item_segment
+ );
+ let (args, _) = self.create_substs_for_ast_path(
+ span,
+ item_def_id,
+ parent_substs,
+ item_segment,
+ item_segment.args(),
+ item_segment.infer_args,
+ None,
+ None,
+ );
+
+ if let Some(b) = item_segment.args().bindings.first() {
+ Self::prohibit_assoc_ty_binding(self.tcx(), b.span);
+ }
+
+ args
+ }
+
+ /// Instantiates the path for the given trait reference, assuming that it's
+ /// bound to a valid trait type. Returns the `DefId` of the defining trait.
+ /// The type _cannot_ be a type other than a trait type.
+ ///
+ /// If the `projections` argument is `None`, then assoc type bindings like `Foo<T = X>`
+ /// are disallowed. Otherwise, they are pushed onto the vector given.
+ pub fn instantiate_mono_trait_ref(
+ &self,
+ trait_ref: &hir::TraitRef<'_>,
+ self_ty: Ty<'tcx>,
+ constness: ty::BoundConstness,
+ ) -> ty::TraitRef<'tcx> {
+ self.prohibit_generics(trait_ref.path.segments.split_last().unwrap().1.iter(), |_| {});
+
+ self.ast_path_to_mono_trait_ref(
+ trait_ref.path.span,
+ trait_ref.trait_def_id().unwrap_or_else(|| FatalError.raise()),
+ self_ty,
+ trait_ref.path.segments.last().unwrap(),
+ true,
+ Some(constness),
+ )
+ }
+
+ fn instantiate_poly_trait_ref_inner(
+ &self,
+ hir_id: hir::HirId,
+ span: Span,
+ binding_span: Option<Span>,
+ constness: ty::BoundConstness,
+ bounds: &mut Bounds<'tcx>,
+ speculative: bool,
+ trait_ref_span: Span,
+ trait_def_id: DefId,
+ trait_segment: &hir::PathSegment<'_>,
+ args: &GenericArgs<'_>,
+ infer_args: bool,
+ self_ty: Ty<'tcx>,
+ ) -> GenericArgCountResult {
+ let (substs, arg_count) = self.create_substs_for_ast_path(
+ trait_ref_span,
+ trait_def_id,
+ &[],
+ trait_segment,
+ args,
+ infer_args,
+ Some(self_ty),
+ Some(constness),
+ );
+
+ let tcx = self.tcx();
+ let bound_vars = tcx.late_bound_vars(hir_id);
+ debug!(?bound_vars);
+
+ let assoc_bindings = self.create_assoc_bindings_for_generic_args(args);
+
+ let poly_trait_ref =
+ ty::Binder::bind_with_vars(ty::TraitRef::new(trait_def_id, substs), bound_vars);
+
+ debug!(?poly_trait_ref, ?assoc_bindings);
+ bounds.trait_bounds.push((poly_trait_ref, span, constness));
+
+ let mut dup_bindings = FxHashMap::default();
+ for binding in &assoc_bindings {
+ // Specify type to assert that error was already reported in `Err` case.
+ let _: Result<_, ErrorGuaranteed> = self.add_predicates_for_ast_type_binding(
+ hir_id,
+ poly_trait_ref,
+ binding,
+ bounds,
+ speculative,
+ &mut dup_bindings,
+ binding_span.unwrap_or(binding.span),
+ constness,
+ );
+ // Okay to ignore `Err` because of `ErrorGuaranteed` (see above).
+ }
+
+ arg_count
+ }
+
+ /// Given a trait bound like `Debug`, applies that trait bound the given self-type to construct
+ /// a full trait reference. The resulting trait reference is returned. This may also generate
+ /// auxiliary bounds, which are added to `bounds`.
+ ///
+ /// Example:
+ ///
+ /// ```ignore (illustrative)
+ /// poly_trait_ref = Iterator<Item = u32>
+ /// self_ty = Foo
+ /// ```
+ ///
+ /// this would return `Foo: Iterator` and add `<Foo as Iterator>::Item = u32` into `bounds`.
+ ///
+ /// **A note on binders:** against our usual convention, there is an implied bounder around
+ /// the `self_ty` and `poly_trait_ref` parameters here. So they may reference bound regions.
+ /// If for example you had `for<'a> Foo<'a>: Bar<'a>`, then the `self_ty` would be `Foo<'a>`
+ /// where `'a` is a bound region at depth 0. Similarly, the `poly_trait_ref` would be
+ /// `Bar<'a>`. The returned poly-trait-ref will have this binder instantiated explicitly,
+ /// however.
+ #[instrument(level = "debug", skip(self, span, constness, bounds, speculative))]
+ pub(crate) fn instantiate_poly_trait_ref(
+ &self,
+ trait_ref: &hir::TraitRef<'_>,
+ span: Span,
+ constness: ty::BoundConstness,
+ self_ty: Ty<'tcx>,
+ bounds: &mut Bounds<'tcx>,
+ speculative: bool,
+ ) -> GenericArgCountResult {
+ let hir_id = trait_ref.hir_ref_id;
+ let binding_span = None;
+ let trait_ref_span = trait_ref.path.span;
+ let trait_def_id = trait_ref.trait_def_id().unwrap_or_else(|| FatalError.raise());
+ let trait_segment = trait_ref.path.segments.last().unwrap();
+ let args = trait_segment.args();
+ let infer_args = trait_segment.infer_args;
+
+ self.prohibit_generics(trait_ref.path.segments.split_last().unwrap().1.iter(), |_| {});
+ self.complain_about_internal_fn_trait(span, trait_def_id, trait_segment, false);
+
+ self.instantiate_poly_trait_ref_inner(
+ hir_id,
+ span,
+ binding_span,
+ constness,
+ bounds,
+ speculative,
+ trait_ref_span,
+ trait_def_id,
+ trait_segment,
+ args,
+ infer_args,
+ self_ty,
+ )
+ }
+
+ pub(crate) fn instantiate_lang_item_trait_ref(
+ &self,
+ lang_item: hir::LangItem,
+ span: Span,
+ hir_id: hir::HirId,
+ args: &GenericArgs<'_>,
+ self_ty: Ty<'tcx>,
+ bounds: &mut Bounds<'tcx>,
+ ) {
+ let binding_span = Some(span);
+ let constness = ty::BoundConstness::NotConst;
+ let speculative = false;
+ let trait_ref_span = span;
+ let trait_def_id = self.tcx().require_lang_item(lang_item, Some(span));
+ let trait_segment = &hir::PathSegment::invalid();
+ let infer_args = false;
+
+ self.instantiate_poly_trait_ref_inner(
+ hir_id,
+ span,
+ binding_span,
+ constness,
+ bounds,
+ speculative,
+ trait_ref_span,
+ trait_def_id,
+ trait_segment,
+ args,
+ infer_args,
+ self_ty,
+ );
+ }
+
+ fn ast_path_to_mono_trait_ref(
+ &self,
+ span: Span,
+ trait_def_id: DefId,
+ self_ty: Ty<'tcx>,
+ trait_segment: &hir::PathSegment<'_>,
+ is_impl: bool,
+ constness: Option<ty::BoundConstness>,
+ ) -> ty::TraitRef<'tcx> {
+ let (substs, _) = self.create_substs_for_ast_trait_ref(
+ span,
+ trait_def_id,
+ self_ty,
+ trait_segment,
+ is_impl,
+ constness,
+ );
+ if let Some(b) = trait_segment.args().bindings.first() {
+ Self::prohibit_assoc_ty_binding(self.tcx(), b.span);
+ }
+ ty::TraitRef::new(trait_def_id, substs)
+ }
+
+ #[instrument(level = "debug", skip(self, span))]
+ fn create_substs_for_ast_trait_ref<'a>(
+ &self,
+ span: Span,
+ trait_def_id: DefId,
+ self_ty: Ty<'tcx>,
+ trait_segment: &'a hir::PathSegment<'a>,
+ is_impl: bool,
+ constness: Option<ty::BoundConstness>,
+ ) -> (SubstsRef<'tcx>, GenericArgCountResult) {
+ self.complain_about_internal_fn_trait(span, trait_def_id, trait_segment, is_impl);
+
+ self.create_substs_for_ast_path(
+ span,
+ trait_def_id,
+ &[],
+ trait_segment,
+ trait_segment.args(),
+ trait_segment.infer_args,
+ Some(self_ty),
+ constness,
+ )
+ }
+
+ fn trait_defines_associated_type_named(&self, trait_def_id: DefId, assoc_name: Ident) -> bool {
+ self.tcx()
+ .associated_items(trait_def_id)
+ .find_by_name_and_kind(self.tcx(), assoc_name, ty::AssocKind::Type, trait_def_id)
+ .is_some()
+ }
+ fn trait_defines_associated_const_named(&self, trait_def_id: DefId, assoc_name: Ident) -> bool {
+ self.tcx()
+ .associated_items(trait_def_id)
+ .find_by_name_and_kind(self.tcx(), assoc_name, ty::AssocKind::Const, trait_def_id)
+ .is_some()
+ }
+
+ // Sets `implicitly_sized` to true on `Bounds` if necessary
+ pub(crate) fn add_implicitly_sized<'hir>(
+ &self,
+ bounds: &mut Bounds<'hir>,
+ ast_bounds: &'hir [hir::GenericBound<'hir>],
+ self_ty_where_predicates: Option<(hir::HirId, &'hir [hir::WherePredicate<'hir>])>,
+ span: Span,
+ ) {
+ let tcx = self.tcx();
+
+ // Try to find an unbound in bounds.
+ let mut unbound = None;
+ let mut search_bounds = |ast_bounds: &'hir [hir::GenericBound<'hir>]| {
+ for ab in ast_bounds {
+ if let hir::GenericBound::Trait(ptr, hir::TraitBoundModifier::Maybe) = ab {
+ if unbound.is_none() {
+ unbound = Some(&ptr.trait_ref);
+ } else {
+ tcx.sess.emit_err(MultipleRelaxedDefaultBounds { span });
+ }
+ }
+ }
+ };
+ search_bounds(ast_bounds);
+ if let Some((self_ty, where_clause)) = self_ty_where_predicates {
+ let self_ty_def_id = tcx.hir().local_def_id(self_ty).to_def_id();
+ for clause in where_clause {
+ if let hir::WherePredicate::BoundPredicate(pred) = clause {
+ if pred.is_param_bound(self_ty_def_id) {
+ search_bounds(pred.bounds);
+ }
+ }
+ }
+ }
+
+ let sized_def_id = tcx.lang_items().require(LangItem::Sized);
+ match (&sized_def_id, unbound) {
+ (Ok(sized_def_id), Some(tpb))
+ if tpb.path.res == Res::Def(DefKind::Trait, *sized_def_id) =>
+ {
+ // There was in fact a `?Sized` bound, return without doing anything
+ return;
+ }
+ (_, Some(_)) => {
+ // There was a `?Trait` bound, but it was not `?Sized`; warn.
+ tcx.sess.span_warn(
+ span,
+ "default bound relaxed for a type parameter, but \
+ this does nothing because the given bound is not \
+ a default; only `?Sized` is supported",
+ );
+ // Otherwise, add implicitly sized if `Sized` is available.
+ }
+ _ => {
+ // There was no `?Sized` bound; add implicitly sized if `Sized` is available.
+ }
+ }
+ if sized_def_id.is_err() {
+ // No lang item for `Sized`, so we can't add it as a bound.
+ return;
+ }
+ bounds.implicitly_sized = Some(span);
+ }
+
+ /// This helper takes a *converted* parameter type (`param_ty`)
+ /// and an *unconverted* list of bounds:
+ ///
+ /// ```text
+ /// fn foo<T: Debug>
+ /// ^ ^^^^^ `ast_bounds` parameter, in HIR form
+ /// |
+ /// `param_ty`, in ty form
+ /// ```
+ ///
+ /// It adds these `ast_bounds` into the `bounds` structure.
+ ///
+ /// **A note on binders:** there is an implied binder around
+ /// `param_ty` and `ast_bounds`. See `instantiate_poly_trait_ref`
+ /// for more details.
+ #[instrument(level = "debug", skip(self, ast_bounds, bounds))]
+ pub(crate) fn add_bounds<'hir, I: Iterator<Item = &'hir hir::GenericBound<'hir>>>(
+ &self,
+ param_ty: Ty<'tcx>,
+ ast_bounds: I,
+ bounds: &mut Bounds<'tcx>,
+ bound_vars: &'tcx ty::List<ty::BoundVariableKind>,
+ ) {
+ for ast_bound in ast_bounds {
+ match ast_bound {
+ hir::GenericBound::Trait(poly_trait_ref, modifier) => {
+ let constness = match modifier {
+ hir::TraitBoundModifier::MaybeConst => ty::BoundConstness::ConstIfConst,
+ hir::TraitBoundModifier::None => ty::BoundConstness::NotConst,
+ hir::TraitBoundModifier::Maybe => continue,
+ };
+
+ let _ = self.instantiate_poly_trait_ref(
+ &poly_trait_ref.trait_ref,
+ poly_trait_ref.span,
+ constness,
+ param_ty,
+ bounds,
+ false,
+ );
+ }
+ &hir::GenericBound::LangItemTrait(lang_item, span, hir_id, args) => {
+ self.instantiate_lang_item_trait_ref(
+ lang_item, span, hir_id, args, param_ty, bounds,
+ );
+ }
+ hir::GenericBound::Outlives(lifetime) => {
+ let region = self.ast_region_to_region(lifetime, None);
+ bounds
+ .region_bounds
+ .push((ty::Binder::bind_with_vars(region, bound_vars), lifetime.span));
+ }
+ }
+ }
+ }
+
+ /// Translates a list of bounds from the HIR into the `Bounds` data structure.
+ /// The self-type for the bounds is given by `param_ty`.
+ ///
+ /// Example:
+ ///
+ /// ```ignore (illustrative)
+ /// fn foo<T: Bar + Baz>() { }
+ /// // ^ ^^^^^^^^^ ast_bounds
+ /// // param_ty
+ /// ```
+ ///
+ /// The `sized_by_default` parameter indicates if, in this context, the `param_ty` should be
+ /// considered `Sized` unless there is an explicit `?Sized` bound. This would be true in the
+ /// example above, but is not true in supertrait listings like `trait Foo: Bar + Baz`.
+ ///
+ /// `span` should be the declaration size of the parameter.
+ pub(crate) fn compute_bounds(
+ &self,
+ param_ty: Ty<'tcx>,
+ ast_bounds: &[hir::GenericBound<'_>],
+ ) -> Bounds<'tcx> {
+ self.compute_bounds_inner(param_ty, ast_bounds)
+ }
+
+ /// Convert the bounds in `ast_bounds` that refer to traits which define an associated type
+ /// named `assoc_name` into ty::Bounds. Ignore the rest.
+ pub(crate) fn compute_bounds_that_match_assoc_type(
+ &self,
+ param_ty: Ty<'tcx>,
+ ast_bounds: &[hir::GenericBound<'_>],
+ assoc_name: Ident,
+ ) -> Bounds<'tcx> {
+ let mut result = Vec::new();
+
+ for ast_bound in ast_bounds {
+ if let Some(trait_ref) = ast_bound.trait_ref()
+ && let Some(trait_did) = trait_ref.trait_def_id()
+ && self.tcx().trait_may_define_assoc_type(trait_did, assoc_name)
+ {
+ result.push(ast_bound.clone());
+ }
+ }
+
+ self.compute_bounds_inner(param_ty, &result)
+ }
+
+ fn compute_bounds_inner(
+ &self,
+ param_ty: Ty<'tcx>,
+ ast_bounds: &[hir::GenericBound<'_>],
+ ) -> Bounds<'tcx> {
+ let mut bounds = Bounds::default();
+
+ self.add_bounds(param_ty, ast_bounds.iter(), &mut bounds, ty::List::empty());
+ debug!(?bounds);
+
+ bounds
+ }
+
+ /// Given an HIR binding like `Item = Foo` or `Item: Foo`, pushes the corresponding predicates
+ /// onto `bounds`.
+ ///
+ /// **A note on binders:** given something like `T: for<'a> Iterator<Item = &'a u32>`, the
+ /// `trait_ref` here will be `for<'a> T: Iterator`. The `binding` data however is from *inside*
+ /// the binder (e.g., `&'a u32`) and hence may reference bound regions.
+ #[instrument(level = "debug", skip(self, bounds, speculative, dup_bindings, path_span))]
+ fn add_predicates_for_ast_type_binding(
+ &self,
+ hir_ref_id: hir::HirId,
+ trait_ref: ty::PolyTraitRef<'tcx>,
+ binding: &ConvertedBinding<'_, 'tcx>,
+ bounds: &mut Bounds<'tcx>,
+ speculative: bool,
+ dup_bindings: &mut FxHashMap<DefId, Span>,
+ path_span: Span,
+ constness: ty::BoundConstness,
+ ) -> Result<(), ErrorGuaranteed> {
+ // Given something like `U: SomeTrait<T = X>`, we want to produce a
+ // predicate like `<U as SomeTrait>::T = X`. This is somewhat
+ // subtle in the event that `T` is defined in a supertrait of
+ // `SomeTrait`, because in that case we need to upcast.
+ //
+ // That is, consider this case:
+ //
+ // ```
+ // trait SubTrait: SuperTrait<i32> { }
+ // trait SuperTrait<A> { type T; }
+ //
+ // ... B: SubTrait<T = foo> ...
+ // ```
+ //
+ // We want to produce `<B as SuperTrait<i32>>::T == foo`.
+
+ let tcx = self.tcx();
+
+ let candidate =
+ if self.trait_defines_associated_type_named(trait_ref.def_id(), binding.item_name) {
+ // Simple case: X is defined in the current trait.
+ trait_ref
+ } else {
+ // Otherwise, we have to walk through the supertraits to find
+ // those that do.
+ self.one_bound_for_assoc_type(
+ || traits::supertraits(tcx, trait_ref),
+ || trait_ref.print_only_trait_path().to_string(),
+ binding.item_name,
+ path_span,
+ || match binding.kind {
+ ConvertedBindingKind::Equality(ty) => Some(ty.to_string()),
+ _ => None,
+ },
+ )?
+ };
+
+ let (assoc_ident, def_scope) =
+ tcx.adjust_ident_and_get_scope(binding.item_name, candidate.def_id(), hir_ref_id);
+
+ // We have already adjusted the item name above, so compare with `ident.normalize_to_macros_2_0()` instead
+ // of calling `filter_by_name_and_kind`.
+ let find_item_of_kind = |kind| {
+ tcx.associated_items(candidate.def_id())
+ .filter_by_name_unhygienic(assoc_ident.name)
+ .find(|i| i.kind == kind && i.ident(tcx).normalize_to_macros_2_0() == assoc_ident)
+ };
+ let assoc_item = find_item_of_kind(ty::AssocKind::Type)
+ .or_else(|| find_item_of_kind(ty::AssocKind::Const))
+ .expect("missing associated type");
+
+ if !assoc_item.visibility(tcx).is_accessible_from(def_scope, tcx) {
+ tcx.sess
+ .struct_span_err(
+ binding.span,
+ &format!("{} `{}` is private", assoc_item.kind, binding.item_name),
+ )
+ .span_label(binding.span, &format!("private {}", assoc_item.kind))
+ .emit();
+ }
+ tcx.check_stability(assoc_item.def_id, Some(hir_ref_id), binding.span, None);
+
+ if !speculative {
+ dup_bindings
+ .entry(assoc_item.def_id)
+ .and_modify(|prev_span| {
+ self.tcx().sess.emit_err(ValueOfAssociatedStructAlreadySpecified {
+ span: binding.span,
+ prev_span: *prev_span,
+ item_name: binding.item_name,
+ def_path: tcx.def_path_str(assoc_item.container_id(tcx)),
+ });
+ })
+ .or_insert(binding.span);
+ }
+
+ // Include substitutions for generic parameters of associated types
+ let projection_ty = candidate.map_bound(|trait_ref| {
+ let ident = Ident::new(assoc_item.name, binding.item_name.span);
+ let item_segment = hir::PathSegment {
+ ident,
+ hir_id: binding.hir_id,
+ res: Res::Err,
+ args: Some(binding.gen_args),
+ infer_args: false,
+ };
+
+ let substs_trait_ref_and_assoc_item = self.create_substs_for_associated_item(
+ path_span,
+ assoc_item.def_id,
+ &item_segment,
+ trait_ref.substs,
+ );
+
+ debug!(?substs_trait_ref_and_assoc_item);
+
+ ty::ProjectionTy {
+ item_def_id: assoc_item.def_id,
+ substs: substs_trait_ref_and_assoc_item,
+ }
+ });
+
+ if !speculative {
+ // Find any late-bound regions declared in `ty` that are not
+ // declared in the trait-ref or assoc_item. These are not well-formed.
+ //
+ // Example:
+ //
+ // for<'a> <T as Iterator>::Item = &'a str // <-- 'a is bad
+ // for<'a> <T as FnMut<(&'a u32,)>>::Output = &'a str // <-- 'a is ok
+ if let ConvertedBindingKind::Equality(ty) = binding.kind {
+ let late_bound_in_trait_ref =
+ tcx.collect_constrained_late_bound_regions(&projection_ty);
+ let late_bound_in_ty =
+ tcx.collect_referenced_late_bound_regions(&trait_ref.rebind(ty));
+ debug!(?late_bound_in_trait_ref);
+ debug!(?late_bound_in_ty);
+
+ // FIXME: point at the type params that don't have appropriate lifetimes:
+ // struct S1<F: for<'a> Fn(&i32, &i32) -> &'a i32>(F);
+ // ---- ---- ^^^^^^^
+ self.validate_late_bound_regions(
+ late_bound_in_trait_ref,
+ late_bound_in_ty,
+ |br_name| {
+ struct_span_err!(
+ tcx.sess,
+ binding.span,
+ E0582,
+ "binding for associated type `{}` references {}, \
+ which does not appear in the trait input types",
+ binding.item_name,
+ br_name
+ )
+ },
+ );
+ }
+ }
+
+ match binding.kind {
+ ConvertedBindingKind::Equality(mut term) => {
+ // "Desugar" a constraint like `T: Iterator<Item = u32>` this to
+ // the "projection predicate" for:
+ //
+ // `<T as Iterator>::Item = u32`
+ let assoc_item_def_id = projection_ty.skip_binder().item_def_id;
+ let def_kind = tcx.def_kind(assoc_item_def_id);
+ match (def_kind, term.unpack()) {
+ (hir::def::DefKind::AssocTy, ty::TermKind::Ty(_))
+ | (hir::def::DefKind::AssocConst, ty::TermKind::Const(_)) => (),
+ (_, _) => {
+ let got = if let Some(_) = term.ty() { "type" } else { "constant" };
+ let expected = def_kind.descr(assoc_item_def_id);
+ tcx.sess
+ .struct_span_err(
+ binding.span,
+ &format!("expected {expected} bound, found {got}"),
+ )
+ .span_note(
+ tcx.def_span(assoc_item_def_id),
+ &format!("{expected} defined here"),
+ )
+ .emit();
+ term = match def_kind {
+ hir::def::DefKind::AssocTy => tcx.ty_error().into(),
+ hir::def::DefKind::AssocConst => tcx
+ .const_error(
+ tcx.bound_type_of(assoc_item_def_id)
+ .subst(tcx, projection_ty.skip_binder().substs),
+ )
+ .into(),
+ _ => unreachable!(),
+ };
+ }
+ }
+ bounds.projection_bounds.push((
+ projection_ty.map_bound(|projection_ty| ty::ProjectionPredicate {
+ projection_ty,
+ term: term,
+ }),
+ binding.span,
+ ));
+ }
+ ConvertedBindingKind::Constraint(ast_bounds) => {
+ // "Desugar" a constraint like `T: Iterator<Item: Debug>` to
+ //
+ // `<T as Iterator>::Item: Debug`
+ //
+ // Calling `skip_binder` is okay, because `add_bounds` expects the `param_ty`
+ // parameter to have a skipped binder.
+ let param_ty = tcx.mk_ty(ty::Projection(projection_ty.skip_binder()));
+ self.add_bounds(param_ty, ast_bounds.iter(), bounds, candidate.bound_vars());
+ }
+ }
+ Ok(())
+ }
+
+ fn ast_path_to_ty(
+ &self,
+ span: Span,
+ did: DefId,
+ item_segment: &hir::PathSegment<'_>,
+ ) -> Ty<'tcx> {
+ let substs = self.ast_path_substs_for_ty(span, did, item_segment);
+ self.normalize_ty(
+ span,
+ EarlyBinder(self.tcx().at(span).type_of(did)).subst(self.tcx(), substs),
+ )
+ }
+
+ fn conv_object_ty_poly_trait_ref(
+ &self,
+ span: Span,
+ trait_bounds: &[hir::PolyTraitRef<'_>],
+ lifetime: &hir::Lifetime,
+ borrowed: bool,
+ representation: DynKind,
+ ) -> Ty<'tcx> {
+ let tcx = self.tcx();
+
+ let mut bounds = Bounds::default();
+ let mut potential_assoc_types = Vec::new();
+ let dummy_self = self.tcx().types.trait_object_dummy_self;
+ for trait_bound in trait_bounds.iter().rev() {
+ if let GenericArgCountResult {
+ correct:
+ Err(GenericArgCountMismatch { invalid_args: cur_potential_assoc_types, .. }),
+ ..
+ } = self.instantiate_poly_trait_ref(
+ &trait_bound.trait_ref,
+ trait_bound.span,
+ ty::BoundConstness::NotConst,
+ dummy_self,
+ &mut bounds,
+ false,
+ ) {
+ potential_assoc_types.extend(cur_potential_assoc_types);
+ }
+ }
+
+ // Expand trait aliases recursively and check that only one regular (non-auto) trait
+ // is used and no 'maybe' bounds are used.
+ let expanded_traits =
+ traits::expand_trait_aliases(tcx, bounds.trait_bounds.iter().map(|&(a, b, _)| (a, b)));
+ let (mut auto_traits, regular_traits): (Vec<_>, Vec<_>) = expanded_traits
+ .filter(|i| i.trait_ref().self_ty().skip_binder() == dummy_self)
+ .partition(|i| tcx.trait_is_auto(i.trait_ref().def_id()));
+ if regular_traits.len() > 1 {
+ let first_trait = &regular_traits[0];
+ let additional_trait = &regular_traits[1];
+ let mut err = struct_span_err!(
+ tcx.sess,
+ additional_trait.bottom().1,
+ E0225,
+ "only auto traits can be used as additional traits in a trait object"
+ );
+ additional_trait.label_with_exp_info(
+ &mut err,
+ "additional non-auto trait",
+ "additional use",
+ );
+ first_trait.label_with_exp_info(&mut err, "first non-auto trait", "first use");
+ err.help(&format!(
+ "consider creating a new trait with all of these as supertraits and using that \
+ trait here instead: `trait NewTrait: {} {{}}`",
+ regular_traits
+ .iter()
+ .map(|t| t.trait_ref().print_only_trait_path().to_string())
+ .collect::<Vec<_>>()
+ .join(" + "),
+ ));
+ err.note(
+ "auto-traits like `Send` and `Sync` are traits that have special properties; \
+ for more information on them, visit \
+ <https://doc.rust-lang.org/reference/special-types-and-traits.html#auto-traits>",
+ );
+ err.emit();
+ }
+
+ if regular_traits.is_empty() && auto_traits.is_empty() {
+ let trait_alias_span = bounds
+ .trait_bounds
+ .iter()
+ .map(|&(trait_ref, _, _)| trait_ref.def_id())
+ .find(|&trait_ref| tcx.is_trait_alias(trait_ref))
+ .map(|trait_ref| tcx.def_span(trait_ref));
+ tcx.sess.emit_err(TraitObjectDeclaredWithNoTraits { span, trait_alias_span });
+ return tcx.ty_error();
+ }
+
+ // Check that there are no gross object safety violations;
+ // most importantly, that the supertraits don't contain `Self`,
+ // to avoid ICEs.
+ for item in &regular_traits {
+ let object_safety_violations =
+ astconv_object_safety_violations(tcx, item.trait_ref().def_id());
+ if !object_safety_violations.is_empty() {
+ report_object_safety_error(
+ tcx,
+ span,
+ item.trait_ref().def_id(),
+ &object_safety_violations,
+ )
+ .emit();
+ return tcx.ty_error();
+ }
+ }
+
+ // Use a `BTreeSet` to keep output in a more consistent order.
+ let mut associated_types: FxHashMap<Span, BTreeSet<DefId>> = FxHashMap::default();
+
+ let regular_traits_refs_spans = bounds
+ .trait_bounds
+ .into_iter()
+ .filter(|(trait_ref, _, _)| !tcx.trait_is_auto(trait_ref.def_id()));
+
+ for (base_trait_ref, span, constness) in regular_traits_refs_spans {
+ assert_eq!(constness, ty::BoundConstness::NotConst);
+
+ for obligation in traits::elaborate_trait_ref(tcx, base_trait_ref) {
+ debug!(
+ "conv_object_ty_poly_trait_ref: observing object predicate `{:?}`",
+ obligation.predicate
+ );
+
+ let bound_predicate = obligation.predicate.kind();
+ match bound_predicate.skip_binder() {
+ ty::PredicateKind::Trait(pred) => {
+ let pred = bound_predicate.rebind(pred);
+ associated_types.entry(span).or_default().extend(
+ tcx.associated_items(pred.def_id())
+ .in_definition_order()
+ .filter(|item| item.kind == ty::AssocKind::Type)
+ .map(|item| item.def_id),
+ );
+ }
+ ty::PredicateKind::Projection(pred) => {
+ let pred = bound_predicate.rebind(pred);
+ // A `Self` within the original bound will be substituted with a
+ // `trait_object_dummy_self`, so check for that.
+ let references_self = match pred.skip_binder().term.unpack() {
+ ty::TermKind::Ty(ty) => ty.walk().any(|arg| arg == dummy_self.into()),
+ ty::TermKind::Const(c) => {
+ c.ty().walk().any(|arg| arg == dummy_self.into())
+ }
+ };
+
+ // If the projection output contains `Self`, force the user to
+ // elaborate it explicitly to avoid a lot of complexity.
+ //
+ // The "classically useful" case is the following:
+ // ```
+ // trait MyTrait: FnMut() -> <Self as MyTrait>::MyOutput {
+ // type MyOutput;
+ // }
+ // ```
+ //
+ // Here, the user could theoretically write `dyn MyTrait<Output = X>`,
+ // but actually supporting that would "expand" to an infinitely-long type
+ // `fix $ τ → dyn MyTrait<MyOutput = X, Output = <τ as MyTrait>::MyOutput`.
+ //
+ // Instead, we force the user to write
+ // `dyn MyTrait<MyOutput = X, Output = X>`, which is uglier but works. See
+ // the discussion in #56288 for alternatives.
+ if !references_self {
+ // Include projections defined on supertraits.
+ bounds.projection_bounds.push((pred, span));
+ }
+ }
+ _ => (),
+ }
+ }
+ }
+
+ for (projection_bound, _) in &bounds.projection_bounds {
+ for def_ids in associated_types.values_mut() {
+ def_ids.remove(&projection_bound.projection_def_id());
+ }
+ }
+
+ self.complain_about_missing_associated_types(
+ associated_types,
+ potential_assoc_types,
+ trait_bounds,
+ );
+
+ // De-duplicate auto traits so that, e.g., `dyn Trait + Send + Send` is the same as
+ // `dyn Trait + Send`.
+ // We remove duplicates by inserting into a `FxHashSet` to avoid re-ordering
+ // the bounds
+ let mut duplicates = FxHashSet::default();
+ auto_traits.retain(|i| duplicates.insert(i.trait_ref().def_id()));
+ debug!("regular_traits: {:?}", regular_traits);
+ debug!("auto_traits: {:?}", auto_traits);
+
+ // Erase the `dummy_self` (`trait_object_dummy_self`) used above.
+ let existential_trait_refs = regular_traits.iter().map(|i| {
+ i.trait_ref().map_bound(|trait_ref: ty::TraitRef<'tcx>| {
+ assert_eq!(trait_ref.self_ty(), dummy_self);
+
+ // Verify that `dummy_self` did not leak inside default type parameters. This
+ // could not be done at path creation, since we need to see through trait aliases.
+ let mut missing_type_params = vec![];
+ let mut references_self = false;
+ let generics = tcx.generics_of(trait_ref.def_id);
+ let substs: Vec<_> = trait_ref
+ .substs
+ .iter()
+ .enumerate()
+ .skip(1) // Remove `Self` for `ExistentialPredicate`.
+ .map(|(index, arg)| {
+ if arg == dummy_self.into() {
+ let param = &generics.params[index];
+ missing_type_params.push(param.name);
+ return tcx.ty_error().into();
+ } else if arg.walk().any(|arg| arg == dummy_self.into()) {
+ references_self = true;
+ return tcx.ty_error().into();
+ }
+ arg
+ })
+ .collect();
+ let substs = tcx.intern_substs(&substs[..]);
+
+ let span = i.bottom().1;
+ let empty_generic_args = trait_bounds.iter().any(|hir_bound| {
+ hir_bound.trait_ref.path.res == Res::Def(DefKind::Trait, trait_ref.def_id)
+ && hir_bound.span.contains(span)
+ });
+ self.complain_about_missing_type_params(
+ missing_type_params,
+ trait_ref.def_id,
+ span,
+ empty_generic_args,
+ );
+
+ if references_self {
+ let def_id = i.bottom().0.def_id();
+ let mut err = struct_span_err!(
+ tcx.sess,
+ i.bottom().1,
+ E0038,
+ "the {} `{}` cannot be made into an object",
+ tcx.def_kind(def_id).descr(def_id),
+ tcx.item_name(def_id),
+ );
+ err.note(
+ rustc_middle::traits::ObjectSafetyViolation::SupertraitSelf(smallvec![])
+ .error_msg(),
+ );
+ err.emit();
+ }
+
+ ty::ExistentialTraitRef { def_id: trait_ref.def_id, substs }
+ })
+ });
+
+ let existential_projections = bounds.projection_bounds.iter().map(|(bound, _)| {
+ bound.map_bound(|mut b| {
+ assert_eq!(b.projection_ty.self_ty(), dummy_self);
+
+ // Like for trait refs, verify that `dummy_self` did not leak inside default type
+ // parameters.
+ let references_self = b.projection_ty.substs.iter().skip(1).any(|arg| {
+ if arg.walk().any(|arg| arg == dummy_self.into()) {
+ return true;
+ }
+ false
+ });
+ if references_self {
+ tcx.sess
+ .delay_span_bug(span, "trait object projection bounds reference `Self`");
+ let substs: Vec<_> = b
+ .projection_ty
+ .substs
+ .iter()
+ .map(|arg| {
+ if arg.walk().any(|arg| arg == dummy_self.into()) {
+ return tcx.ty_error().into();
+ }
+ arg
+ })
+ .collect();
+ b.projection_ty.substs = tcx.intern_substs(&substs[..]);
+ }
+
+ ty::ExistentialProjection::erase_self_ty(tcx, b)
+ })
+ });
+
+ let regular_trait_predicates = existential_trait_refs
+ .map(|trait_ref| trait_ref.map_bound(ty::ExistentialPredicate::Trait));
+ let auto_trait_predicates = auto_traits.into_iter().map(|trait_ref| {
+ ty::Binder::dummy(ty::ExistentialPredicate::AutoTrait(trait_ref.trait_ref().def_id()))
+ });
+ // N.b. principal, projections, auto traits
+ // FIXME: This is actually wrong with multiple principals in regards to symbol mangling
+ let mut v = regular_trait_predicates
+ .chain(
+ existential_projections.map(|x| x.map_bound(ty::ExistentialPredicate::Projection)),
+ )
+ .chain(auto_trait_predicates)
+ .collect::<SmallVec<[_; 8]>>();
+ v.sort_by(|a, b| a.skip_binder().stable_cmp(tcx, &b.skip_binder()));
+ v.dedup();
+ let existential_predicates = tcx.mk_poly_existential_predicates(v.into_iter());
+
+ // Use explicitly-specified region bound.
+ let region_bound = if !lifetime.is_elided() {
+ self.ast_region_to_region(lifetime, None)
+ } else {
+ self.compute_object_lifetime_bound(span, existential_predicates).unwrap_or_else(|| {
+ if tcx.named_region(lifetime.hir_id).is_some() {
+ self.ast_region_to_region(lifetime, None)
+ } else {
+ self.re_infer(None, span).unwrap_or_else(|| {
+ let mut err = struct_span_err!(
+ tcx.sess,
+ span,
+ E0228,
+ "the lifetime bound for this object type cannot be deduced \
+ from context; please supply an explicit bound"
+ );
+ if borrowed {
+ // We will have already emitted an error E0106 complaining about a
+ // missing named lifetime in `&dyn Trait`, so we elide this one.
+ err.delay_as_bug();
+ } else {
+ err.emit();
+ }
+ tcx.lifetimes.re_static
+ })
+ }
+ })
+ };
+ debug!("region_bound: {:?}", region_bound);
+
+ let ty = tcx.mk_dynamic(existential_predicates, region_bound, representation);
+ debug!("trait_object_type: {:?}", ty);
+ ty
+ }
+
+ fn report_ambiguous_associated_type(
+ &self,
+ span: Span,
+ type_str: &str,
+ trait_str: &str,
+ name: Symbol,
+ ) -> ErrorGuaranteed {
+ let mut err = struct_span_err!(self.tcx().sess, span, E0223, "ambiguous associated type");
+ if self
+ .tcx()
+ .resolutions(())
+ .confused_type_with_std_module
+ .keys()
+ .any(|full_span| full_span.contains(span))
+ {
+ err.span_suggestion(
+ span.shrink_to_lo(),
+ "you are looking for the module in `std`, not the primitive type",
+ "std::",
+ Applicability::MachineApplicable,
+ );
+ } else {
+ err.span_suggestion(
+ span,
+ "use fully-qualified syntax",
+ format!("<{} as {}>::{}", type_str, trait_str, name),
+ Applicability::HasPlaceholders,
+ );
+ }
+ err.emit()
+ }
+
+ // Search for a bound on a type parameter which includes the associated item
+ // given by `assoc_name`. `ty_param_def_id` is the `DefId` of the type parameter
+ // This function will fail if there are no suitable bounds or there is
+ // any ambiguity.
+ fn find_bound_for_assoc_item(
+ &self,
+ ty_param_def_id: LocalDefId,
+ assoc_name: Ident,
+ span: Span,
+ ) -> Result<ty::PolyTraitRef<'tcx>, ErrorGuaranteed> {
+ let tcx = self.tcx();
+
+ debug!(
+ "find_bound_for_assoc_item(ty_param_def_id={:?}, assoc_name={:?}, span={:?})",
+ ty_param_def_id, assoc_name, span,
+ );
+
+ let predicates = &self
+ .get_type_parameter_bounds(span, ty_param_def_id.to_def_id(), assoc_name)
+ .predicates;
+
+ debug!("find_bound_for_assoc_item: predicates={:#?}", predicates);
+
+ let param_name = tcx.hir().ty_param_name(ty_param_def_id);
+ self.one_bound_for_assoc_type(
+ || {
+ traits::transitive_bounds_that_define_assoc_type(
+ tcx,
+ predicates.iter().filter_map(|(p, _)| {
+ Some(p.to_opt_poly_trait_pred()?.map_bound(|t| t.trait_ref))
+ }),
+ assoc_name,
+ )
+ },
+ || param_name.to_string(),
+ assoc_name,
+ span,
+ || None,
+ )
+ }
+
+ // Checks that `bounds` contains exactly one element and reports appropriate
+ // errors otherwise.
+ #[instrument(level = "debug", skip(self, all_candidates, ty_param_name, is_equality), ret)]
+ fn one_bound_for_assoc_type<I>(
+ &self,
+ all_candidates: impl Fn() -> I,
+ ty_param_name: impl Fn() -> String,
+ assoc_name: Ident,
+ span: Span,
+ is_equality: impl Fn() -> Option<String>,
+ ) -> Result<ty::PolyTraitRef<'tcx>, ErrorGuaranteed>
+ where
+ I: Iterator<Item = ty::PolyTraitRef<'tcx>>,
+ {
+ let mut matching_candidates = all_candidates()
+ .filter(|r| self.trait_defines_associated_type_named(r.def_id(), assoc_name));
+ let mut const_candidates = all_candidates()
+ .filter(|r| self.trait_defines_associated_const_named(r.def_id(), assoc_name));
+
+ let (bound, next_cand) = match (matching_candidates.next(), const_candidates.next()) {
+ (Some(bound), _) => (bound, matching_candidates.next()),
+ (None, Some(bound)) => (bound, const_candidates.next()),
+ (None, None) => {
+ let reported = self.complain_about_assoc_type_not_found(
+ all_candidates,
+ &ty_param_name(),
+ assoc_name,
+ span,
+ );
+ return Err(reported);
+ }
+ };
+ debug!(?bound);
+
+ if let Some(bound2) = next_cand {
+ debug!(?bound2);
+
+ let is_equality = is_equality();
+ let bounds = IntoIterator::into_iter([bound, bound2]).chain(matching_candidates);
+ let mut err = if is_equality.is_some() {
+ // More specific Error Index entry.
+ struct_span_err!(
+ self.tcx().sess,
+ span,
+ E0222,
+ "ambiguous associated type `{}` in bounds of `{}`",
+ assoc_name,
+ ty_param_name()
+ )
+ } else {
+ struct_span_err!(
+ self.tcx().sess,
+ span,
+ E0221,
+ "ambiguous associated type `{}` in bounds of `{}`",
+ assoc_name,
+ ty_param_name()
+ )
+ };
+ err.span_label(span, format!("ambiguous associated type `{}`", assoc_name));
+
+ let mut where_bounds = vec![];
+ for bound in bounds {
+ let bound_id = bound.def_id();
+ let bound_span = self
+ .tcx()
+ .associated_items(bound_id)
+ .find_by_name_and_kind(self.tcx(), assoc_name, ty::AssocKind::Type, bound_id)
+ .and_then(|item| self.tcx().hir().span_if_local(item.def_id));
+
+ if let Some(bound_span) = bound_span {
+ err.span_label(
+ bound_span,
+ format!(
+ "ambiguous `{}` from `{}`",
+ assoc_name,
+ bound.print_only_trait_path(),
+ ),
+ );
+ if let Some(constraint) = &is_equality {
+ where_bounds.push(format!(
+ " T: {trait}::{assoc} = {constraint}",
+ trait=bound.print_only_trait_path(),
+ assoc=assoc_name,
+ constraint=constraint,
+ ));
+ } else {
+ err.span_suggestion_verbose(
+ span.with_hi(assoc_name.span.lo()),
+ "use fully qualified syntax to disambiguate",
+ format!(
+ "<{} as {}>::",
+ ty_param_name(),
+ bound.print_only_trait_path(),
+ ),
+ Applicability::MaybeIncorrect,
+ );
+ }
+ } else {
+ err.note(&format!(
+ "associated type `{}` could derive from `{}`",
+ ty_param_name(),
+ bound.print_only_trait_path(),
+ ));
+ }
+ }
+ if !where_bounds.is_empty() {
+ err.help(&format!(
+ "consider introducing a new type parameter `T` and adding `where` constraints:\
+ \n where\n T: {},\n{}",
+ ty_param_name(),
+ where_bounds.join(",\n"),
+ ));
+ }
+ let reported = err.emit();
+ if !where_bounds.is_empty() {
+ return Err(reported);
+ }
+ }
+
+ Ok(bound)
+ }
+
+ // Create a type from a path to an associated type.
+ // For a path `A::B::C::D`, `qself_ty` and `qself_def` are the type and def for `A::B::C`
+ // and item_segment is the path segment for `D`. We return a type and a def for
+ // the whole path.
+ // Will fail except for `T::A` and `Self::A`; i.e., if `qself_ty`/`qself_def` are not a type
+ // parameter or `Self`.
+ // NOTE: When this function starts resolving `Trait::AssocTy` successfully
+ // it should also start reporting the `BARE_TRAIT_OBJECTS` lint.
+ #[instrument(level = "debug", skip(self, hir_ref_id, span, qself, assoc_segment), fields(assoc_ident=?assoc_segment.ident), ret)]
+ pub fn associated_path_to_ty(
+ &self,
+ hir_ref_id: hir::HirId,
+ span: Span,
+ qself_ty: Ty<'tcx>,
+ qself: &hir::Ty<'_>,
+ assoc_segment: &hir::PathSegment<'_>,
+ permit_variants: bool,
+ ) -> Result<(Ty<'tcx>, DefKind, DefId), ErrorGuaranteed> {
+ let tcx = self.tcx();
+ let assoc_ident = assoc_segment.ident;
+ let qself_res = if let hir::TyKind::Path(hir::QPath::Resolved(_, ref path)) = qself.kind {
+ path.res
+ } else {
+ Res::Err
+ };
+
+ // Check if we have an enum variant.
+ let mut variant_resolution = None;
+ if let ty::Adt(adt_def, _) = qself_ty.kind() {
+ if adt_def.is_enum() {
+ let variant_def = adt_def
+ .variants()
+ .iter()
+ .find(|vd| tcx.hygienic_eq(assoc_ident, vd.ident(tcx), adt_def.did()));
+ if let Some(variant_def) = variant_def {
+ if permit_variants {
+ tcx.check_stability(variant_def.def_id, Some(hir_ref_id), span, None);
+ self.prohibit_generics(slice::from_ref(assoc_segment).iter(), |err| {
+ err.note("enum variants can't have type parameters");
+ let type_name = tcx.item_name(adt_def.did());
+ let msg = format!(
+ "you might have meant to specity type parameters on enum \
+ `{type_name}`"
+ );
+ let Some(args) = assoc_segment.args else { return; };
+ // Get the span of the generics args *including* the leading `::`.
+ let args_span = assoc_segment.ident.span.shrink_to_hi().to(args.span_ext);
+ if tcx.generics_of(adt_def.did()).count() == 0 {
+ // FIXME(estebank): we could also verify that the arguments being
+ // work for the `enum`, instead of just looking if it takes *any*.
+ err.span_suggestion_verbose(
+ args_span,
+ &format!("{type_name} doesn't have generic parameters"),
+ "",
+ Applicability::MachineApplicable,
+ );
+ return;
+ }
+ let Ok(snippet) = tcx.sess.source_map().span_to_snippet(args_span) else {
+ err.note(&msg);
+ return;
+ };
+ let (qself_sugg_span, is_self) = if let hir::TyKind::Path(
+ hir::QPath::Resolved(_, ref path)
+ ) = qself.kind {
+ // If the path segment already has type params, we want to overwrite
+ // them.
+ match &path.segments[..] {
+ // `segment` is the previous to last element on the path,
+ // which would normally be the `enum` itself, while the last
+ // `_` `PathSegment` corresponds to the variant.
+ [.., hir::PathSegment {
+ ident,
+ args,
+ res: Res::Def(DefKind::Enum, _),
+ ..
+ }, _] => (
+ // We need to include the `::` in `Type::Variant::<Args>`
+ // to point the span to `::<Args>`, not just `<Args>`.
+ ident.span.shrink_to_hi().to(args.map_or(
+ ident.span.shrink_to_hi(),
+ |a| a.span_ext)),
+ false,
+ ),
+ [segment] => (
+ // We need to include the `::` in `Type::Variant::<Args>`
+ // to point the span to `::<Args>`, not just `<Args>`.
+ segment.ident.span.shrink_to_hi().to(segment.args.map_or(
+ segment.ident.span.shrink_to_hi(),
+ |a| a.span_ext)),
+ kw::SelfUpper == segment.ident.name,
+ ),
+ _ => {
+ err.note(&msg);
+ return;
+ }
+ }
+ } else {
+ err.note(&msg);
+ return;
+ };
+ let suggestion = vec![
+ if is_self {
+ // Account for people writing `Self::Variant::<Args>`, where
+ // `Self` is the enum, and suggest replacing `Self` with the
+ // appropriate type: `Type::<Args>::Variant`.
+ (qself.span, format!("{type_name}{snippet}"))
+ } else {
+ (qself_sugg_span, snippet)
+ },
+ (args_span, String::new()),
+ ];
+ err.multipart_suggestion_verbose(
+ &msg,
+ suggestion,
+ Applicability::MaybeIncorrect,
+ );
+ });
+ return Ok((qself_ty, DefKind::Variant, variant_def.def_id));
+ } else {
+ variant_resolution = Some(variant_def.def_id);
+ }
+ }
+ }
+ }
+
+ // Find the type of the associated item, and the trait where the associated
+ // item is declared.
+ let bound = match (&qself_ty.kind(), qself_res) {
+ (_, Res::SelfTyAlias { alias_to: impl_def_id, is_trait_impl: true, .. }) => {
+ // `Self` in an impl of a trait -- we have a concrete self type and a
+ // trait reference.
+ let Some(trait_ref) = tcx.impl_trait_ref(impl_def_id) else {
+ // A cycle error occurred, most likely.
+ let guar = tcx.sess.delay_span_bug(span, "expected cycle error");
+ return Err(guar);
+ };
+
+ self.one_bound_for_assoc_type(
+ || traits::supertraits(tcx, ty::Binder::dummy(trait_ref)),
+ || "Self".to_string(),
+ assoc_ident,
+ span,
+ || None,
+ )?
+ }
+ (
+ &ty::Param(_),
+ Res::SelfTyParam { trait_: param_did } | Res::Def(DefKind::TyParam, param_did),
+ ) => self.find_bound_for_assoc_item(param_did.expect_local(), assoc_ident, span)?,
+ _ => {
+ let reported = if variant_resolution.is_some() {
+ // Variant in type position
+ let msg = format!("expected type, found variant `{}`", assoc_ident);
+ tcx.sess.span_err(span, &msg)
+ } else if qself_ty.is_enum() {
+ let mut err = struct_span_err!(
+ tcx.sess,
+ assoc_ident.span,
+ E0599,
+ "no variant named `{}` found for enum `{}`",
+ assoc_ident,
+ qself_ty,
+ );
+
+ let adt_def = qself_ty.ty_adt_def().expect("enum is not an ADT");
+ if let Some(suggested_name) = find_best_match_for_name(
+ &adt_def
+ .variants()
+ .iter()
+ .map(|variant| variant.name)
+ .collect::<Vec<Symbol>>(),
+ assoc_ident.name,
+ None,
+ ) {
+ err.span_suggestion(
+ assoc_ident.span,
+ "there is a variant with a similar name",
+ suggested_name,
+ Applicability::MaybeIncorrect,
+ );
+ } else {
+ err.span_label(
+ assoc_ident.span,
+ format!("variant not found in `{}`", qself_ty),
+ );
+ }
+
+ if let Some(sp) = tcx.hir().span_if_local(adt_def.did()) {
+ err.span_label(sp, format!("variant `{}` not found here", assoc_ident));
+ }
+
+ err.emit()
+ } else if let Some(reported) = qself_ty.error_reported() {
+ reported
+ } else {
+ // Don't print `TyErr` to the user.
+ self.report_ambiguous_associated_type(
+ span,
+ &qself_ty.to_string(),
+ "Trait",
+ assoc_ident.name,
+ )
+ };
+ return Err(reported);
+ }
+ };
+
+ let trait_did = bound.def_id();
+ let (assoc_ident, def_scope) =
+ tcx.adjust_ident_and_get_scope(assoc_ident, trait_did, hir_ref_id);
+
+ // We have already adjusted the item name above, so compare with `ident.normalize_to_macros_2_0()` instead
+ // of calling `filter_by_name_and_kind`.
+ let item = tcx.associated_items(trait_did).in_definition_order().find(|i| {
+ i.kind.namespace() == Namespace::TypeNS
+ && i.ident(tcx).normalize_to_macros_2_0() == assoc_ident
+ });
+ // Assume that if it's not matched, there must be a const defined with the same name
+ // but it was used in a type position.
+ let Some(item) = item else {
+ let msg = format!("found associated const `{assoc_ident}` when type was expected");
+ let guar = tcx.sess.struct_span_err(span, &msg).emit();
+ return Err(guar);
+ };
+
+ let ty = self.projected_ty_from_poly_trait_ref(span, item.def_id, assoc_segment, bound);
+ let ty = self.normalize_ty(span, ty);
+
+ let kind = DefKind::AssocTy;
+ if !item.visibility(tcx).is_accessible_from(def_scope, tcx) {
+ let kind = kind.descr(item.def_id);
+ let msg = format!("{} `{}` is private", kind, assoc_ident);
+ tcx.sess
+ .struct_span_err(span, &msg)
+ .span_label(span, &format!("private {}", kind))
+ .emit();
+ }
+ tcx.check_stability(item.def_id, Some(hir_ref_id), span, None);
+
+ if let Some(variant_def_id) = variant_resolution {
+ tcx.struct_span_lint_hir(
+ AMBIGUOUS_ASSOCIATED_ITEMS,
+ hir_ref_id,
+ span,
+ "ambiguous associated item",
+ |lint| {
+ let mut could_refer_to = |kind: DefKind, def_id, also| {
+ let note_msg = format!(
+ "`{}` could{} refer to the {} defined here",
+ assoc_ident,
+ also,
+ kind.descr(def_id)
+ );
+ lint.span_note(tcx.def_span(def_id), &note_msg);
+ };
+
+ could_refer_to(DefKind::Variant, variant_def_id, "");
+ could_refer_to(kind, item.def_id, " also");
+
+ lint.span_suggestion(
+ span,
+ "use fully-qualified syntax",
+ format!("<{} as {}>::{}", qself_ty, tcx.item_name(trait_did), assoc_ident),
+ Applicability::MachineApplicable,
+ );
+
+ lint
+ },
+ );
+ }
+ Ok((ty, kind, item.def_id))
+ }
+
+ fn qpath_to_ty(
+ &self,
+ span: Span,
+ opt_self_ty: Option<Ty<'tcx>>,
+ item_def_id: DefId,
+ trait_segment: &hir::PathSegment<'_>,
+ item_segment: &hir::PathSegment<'_>,
+ constness: ty::BoundConstness,
+ ) -> Ty<'tcx> {
+ let tcx = self.tcx();
+
+ let trait_def_id = tcx.parent(item_def_id);
+
+ debug!("qpath_to_ty: trait_def_id={:?}", trait_def_id);
+
+ let Some(self_ty) = opt_self_ty else {
+ let path_str = tcx.def_path_str(trait_def_id);
+
+ let def_id = self.item_def_id();
+
+ debug!("qpath_to_ty: self.item_def_id()={:?}", def_id);
+
+ let parent_def_id = def_id
+ .and_then(|def_id| {
+ def_id.as_local().map(|def_id| tcx.hir().local_def_id_to_hir_id(def_id))
+ })
+ .map(|hir_id| tcx.hir().get_parent_item(hir_id).to_def_id());
+
+ debug!("qpath_to_ty: parent_def_id={:?}", parent_def_id);
+
+ // If the trait in segment is the same as the trait defining the item,
+ // use the `<Self as ..>` syntax in the error.
+ let is_part_of_self_trait_constraints = def_id == Some(trait_def_id);
+ let is_part_of_fn_in_self_trait = parent_def_id == Some(trait_def_id);
+
+ let type_name = if is_part_of_self_trait_constraints || is_part_of_fn_in_self_trait {
+ "Self"
+ } else {
+ "Type"
+ };
+
+ self.report_ambiguous_associated_type(
+ span,
+ type_name,
+ &path_str,
+ item_segment.ident.name,
+ );
+ return tcx.ty_error();
+ };
+
+ debug!("qpath_to_ty: self_type={:?}", self_ty);
+
+ let trait_ref = self.ast_path_to_mono_trait_ref(
+ span,
+ trait_def_id,
+ self_ty,
+ trait_segment,
+ false,
+ Some(constness),
+ );
+
+ let item_substs = self.create_substs_for_associated_item(
+ span,
+ item_def_id,
+ item_segment,
+ trait_ref.substs,
+ );
+
+ debug!("qpath_to_ty: trait_ref={:?}", trait_ref);
+
+ self.normalize_ty(span, tcx.mk_projection(item_def_id, item_substs))
+ }
+
+ pub fn prohibit_generics<'a>(
+ &self,
+ segments: impl Iterator<Item = &'a hir::PathSegment<'a>> + Clone,
+ extend: impl Fn(&mut Diagnostic),
+ ) -> bool {
+ let args = segments.clone().flat_map(|segment| segment.args().args);
+
+ let (lt, ty, ct, inf) =
+ args.clone().fold((false, false, false, false), |(lt, ty, ct, inf), arg| match arg {
+ hir::GenericArg::Lifetime(_) => (true, ty, ct, inf),
+ hir::GenericArg::Type(_) => (lt, true, ct, inf),
+ hir::GenericArg::Const(_) => (lt, ty, true, inf),
+ hir::GenericArg::Infer(_) => (lt, ty, ct, true),
+ });
+ let mut emitted = false;
+ if lt || ty || ct || inf {
+ let types_and_spans: Vec<_> = segments
+ .clone()
+ .flat_map(|segment| {
+ if segment.args().args.is_empty() {
+ None
+ } else {
+ Some((
+ match segment.res {
+ Res::PrimTy(ty) => format!("{} `{}`", segment.res.descr(), ty.name()),
+ Res::Def(_, def_id)
+ if let Some(name) = self.tcx().opt_item_name(def_id) => {
+ format!("{} `{name}`", segment.res.descr())
+ }
+ Res::Err => "this type".to_string(),
+ _ => segment.res.descr().to_string(),
+ },
+ segment.ident.span,
+ ))
+ }
+ })
+ .collect();
+ let this_type = match &types_and_spans[..] {
+ [.., _, (last, _)] => format!(
+ "{} and {last}",
+ types_and_spans[..types_and_spans.len() - 1]
+ .iter()
+ .map(|(x, _)| x.as_str())
+ .intersperse(&", ")
+ .collect::<String>()
+ ),
+ [(only, _)] => only.to_string(),
+ [] => "this type".to_string(),
+ };
+
+ let arg_spans: Vec<Span> = args.map(|arg| arg.span()).collect();
+
+ let mut kinds = Vec::with_capacity(4);
+ if lt {
+ kinds.push("lifetime");
+ }
+ if ty {
+ kinds.push("type");
+ }
+ if ct {
+ kinds.push("const");
+ }
+ if inf {
+ kinds.push("generic");
+ }
+ let (kind, s) = match kinds[..] {
+ [.., _, last] => (
+ format!(
+ "{} and {last}",
+ kinds[..kinds.len() - 1]
+ .iter()
+ .map(|&x| x)
+ .intersperse(", ")
+ .collect::<String>()
+ ),
+ "s",
+ ),
+ [only] => (format!("{only}"), ""),
+ [] => unreachable!(),
+ };
+ let last_span = *arg_spans.last().unwrap();
+ let span: MultiSpan = arg_spans.into();
+ let mut err = struct_span_err!(
+ self.tcx().sess,
+ span,
+ E0109,
+ "{kind} arguments are not allowed on {this_type}",
+ );
+ err.span_label(last_span, format!("{kind} argument{s} not allowed"));
+ for (what, span) in types_and_spans {
+ err.span_label(span, format!("not allowed on {what}"));
+ }
+ extend(&mut err);
+ err.emit();
+ emitted = true;
+ }
+
+ for segment in segments {
+ // Only emit the first error to avoid overloading the user with error messages.
+ if let Some(b) = segment.args().bindings.first() {
+ Self::prohibit_assoc_ty_binding(self.tcx(), b.span);
+ return true;
+ }
+ }
+ emitted
+ }
+
+ // FIXME(eddyb, varkor) handle type paths here too, not just value ones.
+ pub fn def_ids_for_value_path_segments(
+ &self,
+ segments: &[hir::PathSegment<'_>],
+ self_ty: Option<Ty<'tcx>>,
+ kind: DefKind,
+ def_id: DefId,
+ ) -> Vec<PathSeg> {
+ // We need to extract the type parameters supplied by the user in
+ // the path `path`. Due to the current setup, this is a bit of a
+ // tricky-process; the problem is that resolve only tells us the
+ // end-point of the path resolution, and not the intermediate steps.
+ // Luckily, we can (at least for now) deduce the intermediate steps
+ // just from the end-point.
+ //
+ // There are basically five cases to consider:
+ //
+ // 1. Reference to a constructor of a struct:
+ //
+ // struct Foo<T>(...)
+ //
+ // In this case, the parameters are declared in the type space.
+ //
+ // 2. Reference to a constructor of an enum variant:
+ //
+ // enum E<T> { Foo(...) }
+ //
+ // In this case, the parameters are defined in the type space,
+ // but may be specified either on the type or the variant.
+ //
+ // 3. Reference to a fn item or a free constant:
+ //
+ // fn foo<T>() { }
+ //
+ // In this case, the path will again always have the form
+ // `a::b::foo::<T>` where only the final segment should have
+ // type parameters. However, in this case, those parameters are
+ // declared on a value, and hence are in the `FnSpace`.
+ //
+ // 4. Reference to a method or an associated constant:
+ //
+ // impl<A> SomeStruct<A> {
+ // fn foo<B>(...)
+ // }
+ //
+ // Here we can have a path like
+ // `a::b::SomeStruct::<A>::foo::<B>`, in which case parameters
+ // may appear in two places. The penultimate segment,
+ // `SomeStruct::<A>`, contains parameters in TypeSpace, and the
+ // final segment, `foo::<B>` contains parameters in fn space.
+ //
+ // The first step then is to categorize the segments appropriately.
+
+ let tcx = self.tcx();
+
+ assert!(!segments.is_empty());
+ let last = segments.len() - 1;
+
+ let mut path_segs = vec![];
+
+ match kind {
+ // Case 1. Reference to a struct constructor.
+ DefKind::Ctor(CtorOf::Struct, ..) => {
+ // Everything but the final segment should have no
+ // parameters at all.
+ let generics = tcx.generics_of(def_id);
+ // Variant and struct constructors use the
+ // generics of their parent type definition.
+ let generics_def_id = generics.parent.unwrap_or(def_id);
+ path_segs.push(PathSeg(generics_def_id, last));
+ }
+
+ // Case 2. Reference to a variant constructor.
+ DefKind::Ctor(CtorOf::Variant, ..) | DefKind::Variant => {
+ let adt_def = self_ty.map(|t| t.ty_adt_def().unwrap());
+ let (generics_def_id, index) = if let Some(adt_def) = adt_def {
+ debug_assert!(adt_def.is_enum());
+ (adt_def.did(), last)
+ } else if last >= 1 && segments[last - 1].args.is_some() {
+ // Everything but the penultimate segment should have no
+ // parameters at all.
+ let mut def_id = def_id;
+
+ // `DefKind::Ctor` -> `DefKind::Variant`
+ if let DefKind::Ctor(..) = kind {
+ def_id = tcx.parent(def_id);
+ }
+
+ // `DefKind::Variant` -> `DefKind::Enum`
+ let enum_def_id = tcx.parent(def_id);
+ (enum_def_id, last - 1)
+ } else {
+ // FIXME: lint here recommending `Enum::<...>::Variant` form
+ // instead of `Enum::Variant::<...>` form.
+
+ // Everything but the final segment should have no
+ // parameters at all.
+ let generics = tcx.generics_of(def_id);
+ // Variant and struct constructors use the
+ // generics of their parent type definition.
+ (generics.parent.unwrap_or(def_id), last)
+ };
+ path_segs.push(PathSeg(generics_def_id, index));
+ }
+
+ // Case 3. Reference to a top-level value.
+ DefKind::Fn | DefKind::Const | DefKind::ConstParam | DefKind::Static(_) => {
+ path_segs.push(PathSeg(def_id, last));
+ }
+
+ // Case 4. Reference to a method or associated const.
+ DefKind::AssocFn | DefKind::AssocConst => {
+ if segments.len() >= 2 {
+ let generics = tcx.generics_of(def_id);
+ path_segs.push(PathSeg(generics.parent.unwrap(), last - 1));
+ }
+ path_segs.push(PathSeg(def_id, last));
+ }
+
+ kind => bug!("unexpected definition kind {:?} for {:?}", kind, def_id),
+ }
+
+ debug!("path_segs = {:?}", path_segs);
+
+ path_segs
+ }
+
+ // Check a type `Path` and convert it to a `Ty`.
+ pub fn res_to_ty(
+ &self,
+ opt_self_ty: Option<Ty<'tcx>>,
+ path: &hir::Path<'_>,
+ permit_variants: bool,
+ ) -> Ty<'tcx> {
+ let tcx = self.tcx();
+
+ debug!(
+ "res_to_ty(res={:?}, opt_self_ty={:?}, path_segments={:?})",
+ path.res, opt_self_ty, path.segments
+ );
+
+ let span = path.span;
+ match path.res {
+ Res::Def(DefKind::OpaqueTy | DefKind::ImplTraitPlaceholder, did) => {
+ // Check for desugared `impl Trait`.
+ assert!(ty::is_impl_trait_defn(tcx, did).is_none());
+ let item_segment = path.segments.split_last().unwrap();
+ self.prohibit_generics(item_segment.1.iter(), |err| {
+ err.note("`impl Trait` types can't have type parameters");
+ });
+ let substs = self.ast_path_substs_for_ty(span, did, item_segment.0);
+ self.normalize_ty(span, tcx.mk_opaque(did, substs))
+ }
+ Res::Def(
+ DefKind::Enum
+ | DefKind::TyAlias
+ | DefKind::Struct
+ | DefKind::Union
+ | DefKind::ForeignTy,
+ did,
+ ) => {
+ assert_eq!(opt_self_ty, None);
+ self.prohibit_generics(path.segments.split_last().unwrap().1.iter(), |_| {});
+ self.ast_path_to_ty(span, did, path.segments.last().unwrap())
+ }
+ Res::Def(kind @ DefKind::Variant, def_id) if permit_variants => {
+ // Convert "variant type" as if it were a real type.
+ // The resulting `Ty` is type of the variant's enum for now.
+ assert_eq!(opt_self_ty, None);
+
+ let path_segs =
+ self.def_ids_for_value_path_segments(path.segments, None, kind, def_id);
+ let generic_segs: FxHashSet<_> =
+ path_segs.iter().map(|PathSeg(_, index)| index).collect();
+ self.prohibit_generics(
+ path.segments.iter().enumerate().filter_map(|(index, seg)| {
+ if !generic_segs.contains(&index) { Some(seg) } else { None }
+ }),
+ |err| {
+ err.note("enum variants can't have type parameters");
+ },
+ );
+
+ let PathSeg(def_id, index) = path_segs.last().unwrap();
+ self.ast_path_to_ty(span, *def_id, &path.segments[*index])
+ }
+ Res::Def(DefKind::TyParam, def_id) => {
+ assert_eq!(opt_self_ty, None);
+ self.prohibit_generics(path.segments.iter(), |err| {
+ if let Some(span) = tcx.def_ident_span(def_id) {
+ let name = tcx.item_name(def_id);
+ err.span_note(span, &format!("type parameter `{name}` defined here"));
+ }
+ });
+
+ let def_id = def_id.expect_local();
+ let item_def_id = tcx.hir().ty_param_owner(def_id);
+ let generics = tcx.generics_of(item_def_id);
+ let index = generics.param_def_id_to_index[&def_id.to_def_id()];
+ tcx.mk_ty_param(index, tcx.hir().ty_param_name(def_id))
+ }
+ Res::SelfTyParam { .. } => {
+ // `Self` in trait or type alias.
+ assert_eq!(opt_self_ty, None);
+ self.prohibit_generics(path.segments.iter(), |err| {
+ if let [hir::PathSegment { args: Some(args), ident, .. }] = &path.segments[..] {
+ err.span_suggestion_verbose(
+ ident.span.shrink_to_hi().to(args.span_ext),
+ "the `Self` type doesn't accept type parameters",
+ "",
+ Applicability::MaybeIncorrect,
+ );
+ }
+ });
+ tcx.types.self_param
+ }
+ Res::SelfTyAlias { alias_to: def_id, forbid_generic, .. } => {
+ // `Self` in impl (we know the concrete type).
+ assert_eq!(opt_self_ty, None);
+ // Try to evaluate any array length constants.
+ let ty = tcx.at(span).type_of(def_id);
+ let span_of_impl = tcx.span_of_impl(def_id);
+ self.prohibit_generics(path.segments.iter(), |err| {
+ let def_id = match *ty.kind() {
+ ty::Adt(self_def, _) => self_def.did(),
+ _ => return,
+ };
+
+ let type_name = tcx.item_name(def_id);
+ let span_of_ty = tcx.def_ident_span(def_id);
+ let generics = tcx.generics_of(def_id).count();
+
+ let msg = format!("`Self` is of type `{ty}`");
+ if let (Ok(i_sp), Some(t_sp)) = (span_of_impl, span_of_ty) {
+ let mut span: MultiSpan = vec![t_sp].into();
+ span.push_span_label(
+ i_sp,
+ &format!("`Self` is on type `{type_name}` in this `impl`"),
+ );
+ let mut postfix = "";
+ if generics == 0 {
+ postfix = ", which doesn't have generic parameters";
+ }
+ span.push_span_label(
+ t_sp,
+ &format!("`Self` corresponds to this type{postfix}"),
+ );
+ err.span_note(span, &msg);
+ } else {
+ err.note(&msg);
+ }
+ for segment in path.segments {
+ if let Some(args) = segment.args && segment.ident.name == kw::SelfUpper {
+ if generics == 0 {
+ // FIXME(estebank): we could also verify that the arguments being
+ // work for the `enum`, instead of just looking if it takes *any*.
+ err.span_suggestion_verbose(
+ segment.ident.span.shrink_to_hi().to(args.span_ext),
+ "the `Self` type doesn't accept type parameters",
+ "",
+ Applicability::MachineApplicable,
+ );
+ return;
+ } else {
+ err.span_suggestion_verbose(
+ segment.ident.span,
+ format!(
+ "the `Self` type doesn't accept type parameters, use the \
+ concrete type's name `{type_name}` instead if you want to \
+ specify its type parameters"
+ ),
+ type_name,
+ Applicability::MaybeIncorrect,
+ );
+ }
+ }
+ }
+ });
+ // HACK(min_const_generics): Forbid generic `Self` types
+ // here as we can't easily do that during nameres.
+ //
+ // We do this before normalization as we otherwise allow
+ // ```rust
+ // trait AlwaysApplicable { type Assoc; }
+ // impl<T: ?Sized> AlwaysApplicable for T { type Assoc = usize; }
+ //
+ // trait BindsParam<T> {
+ // type ArrayTy;
+ // }
+ // impl<T> BindsParam<T> for <T as AlwaysApplicable>::Assoc {
+ // type ArrayTy = [u8; Self::MAX];
+ // }
+ // ```
+ // Note that the normalization happens in the param env of
+ // the anon const, which is empty. This is why the
+ // `AlwaysApplicable` impl needs a `T: ?Sized` bound for
+ // this to compile if we were to normalize here.
+ if forbid_generic && ty.needs_subst() {
+ let mut err = tcx.sess.struct_span_err(
+ path.span,
+ "generic `Self` types are currently not permitted in anonymous constants",
+ );
+ if let Some(hir::Node::Item(&hir::Item {
+ kind: hir::ItemKind::Impl(ref impl_),
+ ..
+ })) = tcx.hir().get_if_local(def_id)
+ {
+ err.span_note(impl_.self_ty.span, "not a concrete type");
+ }
+ err.emit();
+ tcx.ty_error()
+ } else {
+ self.normalize_ty(span, ty)
+ }
+ }
+ Res::Def(DefKind::AssocTy, def_id) => {
+ debug_assert!(path.segments.len() >= 2);
+ self.prohibit_generics(path.segments[..path.segments.len() - 2].iter(), |_| {});
+ // HACK: until we support `<Type as ~const Trait>`, assume all of them are.
+ let constness = if tcx.has_attr(tcx.parent(def_id), sym::const_trait) {
+ ty::BoundConstness::ConstIfConst
+ } else {
+ ty::BoundConstness::NotConst
+ };
+ self.qpath_to_ty(
+ span,
+ opt_self_ty,
+ def_id,
+ &path.segments[path.segments.len() - 2],
+ path.segments.last().unwrap(),
+ constness,
+ )
+ }
+ Res::PrimTy(prim_ty) => {
+ assert_eq!(opt_self_ty, None);
+ self.prohibit_generics(path.segments.iter(), |err| {
+ let name = prim_ty.name_str();
+ for segment in path.segments {
+ if let Some(args) = segment.args {
+ err.span_suggestion_verbose(
+ segment.ident.span.shrink_to_hi().to(args.span_ext),
+ &format!("primitive type `{name}` doesn't have generic parameters"),
+ "",
+ Applicability::MaybeIncorrect,
+ );
+ }
+ }
+ });
+ match prim_ty {
+ hir::PrimTy::Bool => tcx.types.bool,
+ hir::PrimTy::Char => tcx.types.char,
+ hir::PrimTy::Int(it) => tcx.mk_mach_int(ty::int_ty(it)),
+ hir::PrimTy::Uint(uit) => tcx.mk_mach_uint(ty::uint_ty(uit)),
+ hir::PrimTy::Float(ft) => tcx.mk_mach_float(ty::float_ty(ft)),
+ hir::PrimTy::Str => tcx.types.str_,
+ }
+ }
+ Res::Err => {
+ self.set_tainted_by_errors();
+ self.tcx().ty_error()
+ }
+ _ => span_bug!(span, "unexpected resolution: {:?}", path.res),
+ }
+ }
+
+ /// Parses the programmer's textual representation of a type into our
+ /// internal notion of a type.
+ pub fn ast_ty_to_ty(&self, ast_ty: &hir::Ty<'_>) -> Ty<'tcx> {
+ self.ast_ty_to_ty_inner(ast_ty, false, false)
+ }
+
+ /// Parses the programmer's textual representation of a type into our
+ /// internal notion of a type. This is meant to be used within a path.
+ pub fn ast_ty_to_ty_in_path(&self, ast_ty: &hir::Ty<'_>) -> Ty<'tcx> {
+ self.ast_ty_to_ty_inner(ast_ty, false, true)
+ }
+
+ /// Turns a `hir::Ty` into a `Ty`. For diagnostics' purposes we keep track of whether trait
+ /// objects are borrowed like `&dyn Trait` to avoid emitting redundant errors.
+ #[instrument(level = "debug", skip(self), ret)]
+ fn ast_ty_to_ty_inner(&self, ast_ty: &hir::Ty<'_>, borrowed: bool, in_path: bool) -> Ty<'tcx> {
+ let tcx = self.tcx();
+
+ let result_ty = match ast_ty.kind {
+ hir::TyKind::Slice(ref ty) => tcx.mk_slice(self.ast_ty_to_ty(ty)),
+ hir::TyKind::Ptr(ref mt) => {
+ tcx.mk_ptr(ty::TypeAndMut { ty: self.ast_ty_to_ty(mt.ty), mutbl: mt.mutbl })
+ }
+ hir::TyKind::Rptr(ref region, ref mt) => {
+ let r = self.ast_region_to_region(region, None);
+ debug!(?r);
+ let t = self.ast_ty_to_ty_inner(mt.ty, true, false);
+ tcx.mk_ref(r, ty::TypeAndMut { ty: t, mutbl: mt.mutbl })
+ }
+ hir::TyKind::Never => tcx.types.never,
+ hir::TyKind::Tup(fields) => tcx.mk_tup(fields.iter().map(|t| self.ast_ty_to_ty(t))),
+ hir::TyKind::BareFn(bf) => {
+ require_c_abi_if_c_variadic(tcx, bf.decl, bf.abi, ast_ty.span);
+
+ tcx.mk_fn_ptr(self.ty_of_fn(
+ ast_ty.hir_id,
+ bf.unsafety,
+ bf.abi,
+ bf.decl,
+ None,
+ Some(ast_ty),
+ ))
+ }
+ hir::TyKind::TraitObject(bounds, ref lifetime, repr) => {
+ self.maybe_lint_bare_trait(ast_ty, in_path);
+ let repr = match repr {
+ TraitObjectSyntax::Dyn | TraitObjectSyntax::None => ty::Dyn,
+ TraitObjectSyntax::DynStar => ty::DynStar,
+ };
+ self.conv_object_ty_poly_trait_ref(ast_ty.span, bounds, lifetime, borrowed, repr)
+ }
+ hir::TyKind::Path(hir::QPath::Resolved(ref maybe_qself, ref path)) => {
+ debug!(?maybe_qself, ?path);
+ let opt_self_ty = maybe_qself.as_ref().map(|qself| self.ast_ty_to_ty(qself));
+ self.res_to_ty(opt_self_ty, path, false)
+ }
+ hir::TyKind::OpaqueDef(item_id, lifetimes, in_trait) => {
+ let opaque_ty = tcx.hir().item(item_id);
+ let def_id = item_id.owner_id.to_def_id();
+
+ match opaque_ty.kind {
+ hir::ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) => {
+ self.impl_trait_ty_to_ty(def_id, lifetimes, origin, in_trait)
+ }
+ ref i => bug!("`impl Trait` pointed to non-opaque type?? {:#?}", i),
+ }
+ }
+ hir::TyKind::Path(hir::QPath::TypeRelative(ref qself, ref segment)) => {
+ debug!(?qself, ?segment);
+ let ty = self.ast_ty_to_ty_inner(qself, false, true);
+ self.associated_path_to_ty(ast_ty.hir_id, ast_ty.span, ty, qself, segment, false)
+ .map(|(ty, _, _)| ty)
+ .unwrap_or_else(|_| tcx.ty_error())
+ }
+ hir::TyKind::Path(hir::QPath::LangItem(lang_item, span, _)) => {
+ let def_id = tcx.require_lang_item(lang_item, Some(span));
+ let (substs, _) = self.create_substs_for_ast_path(
+ span,
+ def_id,
+ &[],
+ &hir::PathSegment::invalid(),
+ &GenericArgs::none(),
+ true,
+ None,
+ None,
+ );
+ EarlyBinder(self.normalize_ty(span, tcx.at(span).type_of(def_id)))
+ .subst(tcx, substs)
+ }
+ hir::TyKind::Array(ref ty, ref length) => {
+ let length = match length {
+ &hir::ArrayLen::Infer(_, span) => self.ct_infer(tcx.types.usize, None, span),
+ hir::ArrayLen::Body(constant) => {
+ let length_def_id = tcx.hir().local_def_id(constant.hir_id);
+ ty::Const::from_anon_const(tcx, length_def_id)
+ }
+ };
+
+ let array_ty = tcx.mk_ty(ty::Array(self.ast_ty_to_ty(ty), length));
+ self.normalize_ty(ast_ty.span, array_ty)
+ }
+ hir::TyKind::Typeof(ref e) => {
+ let ty_erased = tcx.type_of(tcx.hir().local_def_id(e.hir_id));
+ let ty = tcx.fold_regions(ty_erased, |r, _| {
+ if r.is_erased() { tcx.lifetimes.re_static } else { r }
+ });
+ let span = ast_ty.span;
+ tcx.sess.emit_err(TypeofReservedKeywordUsed {
+ span,
+ ty,
+ opt_sugg: Some((span, Applicability::MachineApplicable))
+ .filter(|_| ty.is_suggestable(tcx, false)),
+ });
+
+ ty
+ }
+ hir::TyKind::Infer => {
+ // Infer also appears as the type of arguments or return
+ // values in an ExprKind::Closure, or as
+ // the type of local variables. Both of these cases are
+ // handled specially and will not descend into this routine.
+ self.ty_infer(None, ast_ty.span)
+ }
+ hir::TyKind::Err => tcx.ty_error(),
+ };
+
+ self.record_ty(ast_ty.hir_id, result_ty, ast_ty.span);
+ result_ty
+ }
+
+ #[instrument(level = "debug", skip(self), ret)]
+ fn impl_trait_ty_to_ty(
+ &self,
+ def_id: DefId,
+ lifetimes: &[hir::GenericArg<'_>],
+ origin: OpaqueTyOrigin,
+ in_trait: bool,
+ ) -> Ty<'tcx> {
+ debug!("impl_trait_ty_to_ty(def_id={:?}, lifetimes={:?})", def_id, lifetimes);
+ let tcx = self.tcx();
+
+ let generics = tcx.generics_of(def_id);
+
+ debug!("impl_trait_ty_to_ty: generics={:?}", generics);
+ let substs = InternalSubsts::for_item(tcx, def_id, |param, _| {
+ if let Some(i) = (param.index as usize).checked_sub(generics.parent_count) {
+ // Our own parameters are the resolved lifetimes.
+ if let GenericParamDefKind::Lifetime = param.kind {
+ if let hir::GenericArg::Lifetime(lifetime) = &lifetimes[i] {
+ self.ast_region_to_region(lifetime, None).into()
+ } else {
+ bug!()
+ }
+ } else {
+ bug!()
+ }
+ } else {
+ match param.kind {
+ // For RPIT (return position impl trait), only lifetimes
+ // mentioned in the impl Trait predicate are captured by
+ // the opaque type, so the lifetime parameters from the
+ // parent item need to be replaced with `'static`.
+ //
+ // For `impl Trait` in the types of statics, constants,
+ // locals and type aliases. These capture all parent
+ // lifetimes, so they can use their identity subst.
+ GenericParamDefKind::Lifetime
+ if matches!(
+ origin,
+ hir::OpaqueTyOrigin::FnReturn(..) | hir::OpaqueTyOrigin::AsyncFn(..)
+ ) =>
+ {
+ tcx.lifetimes.re_static.into()
+ }
+ _ => tcx.mk_param_from_def(param),
+ }
+ }
+ });
+ debug!("impl_trait_ty_to_ty: substs={:?}", substs);
+
+ if in_trait { tcx.mk_projection(def_id, substs) } else { tcx.mk_opaque(def_id, substs) }
+ }
+
+ pub fn ty_of_arg(&self, ty: &hir::Ty<'_>, expected_ty: Option<Ty<'tcx>>) -> Ty<'tcx> {
+ match ty.kind {
+ hir::TyKind::Infer if expected_ty.is_some() => {
+ self.record_ty(ty.hir_id, expected_ty.unwrap(), ty.span);
+ expected_ty.unwrap()
+ }
+ _ => self.ast_ty_to_ty(ty),
+ }
+ }
+
+ #[instrument(level = "debug", skip(self, hir_id, unsafety, abi, decl, generics, hir_ty), ret)]
+ pub fn ty_of_fn(
+ &self,
+ hir_id: hir::HirId,
+ unsafety: hir::Unsafety,
+ abi: abi::Abi,
+ decl: &hir::FnDecl<'_>,
+ generics: Option<&hir::Generics<'_>>,
+ hir_ty: Option<&hir::Ty<'_>>,
+ ) -> ty::PolyFnSig<'tcx> {
+ let tcx = self.tcx();
+ let bound_vars = tcx.late_bound_vars(hir_id);
+ debug!(?bound_vars);
+
+ // We proactively collect all the inferred type params to emit a single error per fn def.
+ let mut visitor = HirPlaceholderCollector::default();
+ let mut infer_replacements = vec![];
+
+ if let Some(generics) = generics {
+ walk_generics(&mut visitor, generics);
+ }
+
+ let input_tys: Vec<_> = decl
+ .inputs
+ .iter()
+ .enumerate()
+ .map(|(i, a)| {
+ if let hir::TyKind::Infer = a.kind && !self.allow_ty_infer() {
+ if let Some(suggested_ty) =
+ self.suggest_trait_fn_ty_for_impl_fn_infer(hir_id, Some(i))
+ {
+ infer_replacements.push((a.span, suggested_ty.to_string()));
+ return suggested_ty;
+ }
+ }
+
+ // Only visit the type looking for `_` if we didn't fix the type above
+ visitor.visit_ty(a);
+ self.ty_of_arg(a, None)
+ })
+ .collect();
+
+ let output_ty = match decl.output {
+ hir::FnRetTy::Return(output) => {
+ if let hir::TyKind::Infer = output.kind
+ && !self.allow_ty_infer()
+ && let Some(suggested_ty) =
+ self.suggest_trait_fn_ty_for_impl_fn_infer(hir_id, None)
+ {
+ infer_replacements.push((output.span, suggested_ty.to_string()));
+ suggested_ty
+ } else {
+ visitor.visit_ty(output);
+ self.ast_ty_to_ty(output)
+ }
+ }
+ hir::FnRetTy::DefaultReturn(..) => tcx.mk_unit(),
+ };
+
+ debug!(?output_ty);
+
+ let fn_ty = tcx.mk_fn_sig(input_tys.into_iter(), output_ty, decl.c_variadic, unsafety, abi);
+ let bare_fn_ty = ty::Binder::bind_with_vars(fn_ty, bound_vars);
+
+ if !self.allow_ty_infer() && !(visitor.0.is_empty() && infer_replacements.is_empty()) {
+ // We always collect the spans for placeholder types when evaluating `fn`s, but we
+ // only want to emit an error complaining about them if infer types (`_`) are not
+ // allowed. `allow_ty_infer` gates this behavior. We check for the presence of
+ // `ident_span` to not emit an error twice when we have `fn foo(_: fn() -> _)`.
+
+ let mut diag = crate::collect::placeholder_type_error_diag(
+ tcx,
+ generics,
+ visitor.0,
+ infer_replacements.iter().map(|(s, _)| *s).collect(),
+ true,
+ hir_ty,
+ "function",
+ );
+
+ if !infer_replacements.is_empty() {
+ diag.multipart_suggestion(
+ &format!(
+ "try replacing `_` with the type{} in the corresponding trait method signature",
+ rustc_errors::pluralize!(infer_replacements.len()),
+ ),
+ infer_replacements,
+ Applicability::MachineApplicable,
+ );
+ }
+
+ diag.emit();
+ }
+
+ // Find any late-bound regions declared in return type that do
+ // not appear in the arguments. These are not well-formed.
+ //
+ // Example:
+ // for<'a> fn() -> &'a str <-- 'a is bad
+ // for<'a> fn(&'a String) -> &'a str <-- 'a is ok
+ let inputs = bare_fn_ty.inputs();
+ let late_bound_in_args =
+ tcx.collect_constrained_late_bound_regions(&inputs.map_bound(|i| i.to_owned()));
+ let output = bare_fn_ty.output();
+ let late_bound_in_ret = tcx.collect_referenced_late_bound_regions(&output);
+
+ self.validate_late_bound_regions(late_bound_in_args, late_bound_in_ret, |br_name| {
+ struct_span_err!(
+ tcx.sess,
+ decl.output.span(),
+ E0581,
+ "return type references {}, which is not constrained by the fn input types",
+ br_name
+ )
+ });
+
+ bare_fn_ty
+ }
+
+ /// Given a fn_hir_id for a impl function, suggest the type that is found on the
+ /// corresponding function in the trait that the impl implements, if it exists.
+ /// If arg_idx is Some, then it corresponds to an input type index, otherwise it
+ /// corresponds to the return type.
+ fn suggest_trait_fn_ty_for_impl_fn_infer(
+ &self,
+ fn_hir_id: hir::HirId,
+ arg_idx: Option<usize>,
+ ) -> Option<Ty<'tcx>> {
+ let tcx = self.tcx();
+ let hir = tcx.hir();
+
+ let hir::Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Fn(..), ident, .. }) =
+ hir.get(fn_hir_id) else { return None };
+ let hir::Node::Item(hir::Item { kind: hir::ItemKind::Impl(i), .. }) =
+ hir.get(hir.get_parent_node(fn_hir_id)) else { bug!("ImplItem should have Impl parent") };
+
+ let trait_ref = self.instantiate_mono_trait_ref(
+ i.of_trait.as_ref()?,
+ self.ast_ty_to_ty(i.self_ty),
+ ty::BoundConstness::NotConst,
+ );
+
+ let assoc = tcx.associated_items(trait_ref.def_id).find_by_name_and_kind(
+ tcx,
+ *ident,
+ ty::AssocKind::Fn,
+ trait_ref.def_id,
+ )?;
+
+ let fn_sig = tcx.bound_fn_sig(assoc.def_id).subst(
+ tcx,
+ trait_ref.substs.extend_to(tcx, assoc.def_id, |param, _| tcx.mk_param_from_def(param)),
+ );
+
+ let ty = if let Some(arg_idx) = arg_idx { fn_sig.input(arg_idx) } else { fn_sig.output() };
+
+ Some(tcx.liberate_late_bound_regions(fn_hir_id.expect_owner().to_def_id(), ty))
+ }
+
+ fn validate_late_bound_regions(
+ &self,
+ constrained_regions: FxHashSet<ty::BoundRegionKind>,
+ referenced_regions: FxHashSet<ty::BoundRegionKind>,
+ generate_err: impl Fn(&str) -> DiagnosticBuilder<'tcx, ErrorGuaranteed>,
+ ) {
+ for br in referenced_regions.difference(&constrained_regions) {
+ let br_name = match *br {
+ ty::BrNamed(_, kw::UnderscoreLifetime) | ty::BrAnon(_) | ty::BrEnv => {
+ "an anonymous lifetime".to_string()
+ }
+ ty::BrNamed(_, name) => format!("lifetime `{}`", name),
+ };
+
+ let mut err = generate_err(&br_name);
+
+ if let ty::BrNamed(_, kw::UnderscoreLifetime) | ty::BrAnon(_) = *br {
+ // The only way for an anonymous lifetime to wind up
+ // in the return type but **also** be unconstrained is
+ // if it only appears in "associated types" in the
+ // input. See #47511 and #62200 for examples. In this case,
+ // though we can easily give a hint that ought to be
+ // relevant.
+ err.note(
+ "lifetimes appearing in an associated or opaque type are not considered constrained",
+ );
+ err.note("consider introducing a named lifetime parameter");
+ }
+
+ err.emit();
+ }
+ }
+
+ /// Given the bounds on an object, determines what single region bound (if any) we can
+ /// use to summarize this type. The basic idea is that we will use the bound the user
+ /// provided, if they provided one, and otherwise search the supertypes of trait bounds
+ /// for region bounds. It may be that we can derive no bound at all, in which case
+ /// we return `None`.
+ fn compute_object_lifetime_bound(
+ &self,
+ span: Span,
+ existential_predicates: &'tcx ty::List<ty::Binder<'tcx, ty::ExistentialPredicate<'tcx>>>,
+ ) -> Option<ty::Region<'tcx>> // if None, use the default
+ {
+ let tcx = self.tcx();
+
+ debug!("compute_opt_region_bound(existential_predicates={:?})", existential_predicates);
+
+ // No explicit region bound specified. Therefore, examine trait
+ // bounds and see if we can derive region bounds from those.
+ let derived_region_bounds = object_region_bounds(tcx, existential_predicates);
+
+ // If there are no derived region bounds, then report back that we
+ // can find no region bound. The caller will use the default.
+ if derived_region_bounds.is_empty() {
+ return None;
+ }
+
+ // If any of the derived region bounds are 'static, that is always
+ // the best choice.
+ if derived_region_bounds.iter().any(|r| r.is_static()) {
+ return Some(tcx.lifetimes.re_static);
+ }
+
+ // Determine whether there is exactly one unique region in the set
+ // of derived region bounds. If so, use that. Otherwise, report an
+ // error.
+ let r = derived_region_bounds[0];
+ if derived_region_bounds[1..].iter().any(|r1| r != *r1) {
+ tcx.sess.emit_err(AmbiguousLifetimeBound { span });
+ }
+ Some(r)
+ }
+
+ /// Make sure that we are in the condition to suggest the blanket implementation.
+ fn maybe_lint_blanket_trait_impl(&self, self_ty: &hir::Ty<'_>, diag: &mut Diagnostic) {
+ let tcx = self.tcx();
+ let parent_id = tcx.hir().get_parent_item(self_ty.hir_id).def_id;
+ if let hir::Node::Item(hir::Item {
+ kind:
+ hir::ItemKind::Impl(hir::Impl {
+ self_ty: impl_self_ty, of_trait: Some(of_trait_ref), generics, ..
+ }),
+ ..
+ }) = tcx.hir().get_by_def_id(parent_id) && self_ty.hir_id == impl_self_ty.hir_id
+ {
+ if !of_trait_ref.trait_def_id().map_or(false, |def_id| def_id.is_local()) {
+ return;
+ }
+ let of_trait_span = of_trait_ref.path.span;
+ // make sure that we are not calling unwrap to abort during the compilation
+ let Ok(impl_trait_name) = tcx.sess.source_map().span_to_snippet(self_ty.span) else { return; };
+ let Ok(of_trait_name) = tcx.sess.source_map().span_to_snippet(of_trait_span) else { return; };
+ // check if the trait has generics, to make a correct suggestion
+ let param_name = generics.params.next_type_param_name(None);
+
+ let add_generic_sugg = if let Some(span) = generics.span_for_param_suggestion() {
+ (span, format!(", {}: {}", param_name, impl_trait_name))
+ } else {
+ (generics.span, format!("<{}: {}>", param_name, impl_trait_name))
+ };
+ diag.multipart_suggestion(
+ format!("alternatively use a blanket \
+ implementation to implement `{of_trait_name}` for \
+ all types that also implement `{impl_trait_name}`"),
+ vec![
+ (self_ty.span, param_name),
+ add_generic_sugg,
+ ],
+ Applicability::MaybeIncorrect,
+ );
+ }
+ }
+
+ fn maybe_lint_bare_trait(&self, self_ty: &hir::Ty<'_>, in_path: bool) {
+ let tcx = self.tcx();
+ if let hir::TyKind::TraitObject([poly_trait_ref, ..], _, TraitObjectSyntax::None) =
+ self_ty.kind
+ {
+ let needs_bracket = in_path
+ && !tcx
+ .sess
+ .source_map()
+ .span_to_prev_source(self_ty.span)
+ .ok()
+ .map_or(false, |s| s.trim_end().ends_with('<'));
+
+ let is_global = poly_trait_ref.trait_ref.path.is_global();
+
+ let mut sugg = Vec::from_iter([(
+ self_ty.span.shrink_to_lo(),
+ format!(
+ "{}dyn {}",
+ if needs_bracket { "<" } else { "" },
+ if is_global { "(" } else { "" },
+ ),
+ )]);
+
+ if is_global || needs_bracket {
+ sugg.push((
+ self_ty.span.shrink_to_hi(),
+ format!(
+ "{}{}",
+ if is_global { ")" } else { "" },
+ if needs_bracket { ">" } else { "" },
+ ),
+ ));
+ }
+
+ if self_ty.span.edition() >= Edition::Edition2021 {
+ let msg = "trait objects must include the `dyn` keyword";
+ let label = "add `dyn` keyword before this trait";
+ let mut diag =
+ rustc_errors::struct_span_err!(tcx.sess, self_ty.span, E0782, "{}", msg);
+ diag.multipart_suggestion_verbose(label, sugg, Applicability::MachineApplicable);
+ // check if the impl trait that we are considering is a impl of a local trait
+ self.maybe_lint_blanket_trait_impl(&self_ty, &mut diag);
+ diag.emit();
+ } else {
+ let msg = "trait objects without an explicit `dyn` are deprecated";
+ tcx.struct_span_lint_hir(
+ BARE_TRAIT_OBJECTS,
+ self_ty.hir_id,
+ self_ty.span,
+ msg,
+ |lint| {
+ lint.multipart_suggestion_verbose(
+ "use `dyn`",
+ sugg,
+ Applicability::MachineApplicable,
+ );
+ self.maybe_lint_blanket_trait_impl(&self_ty, lint);
+ lint
+ },
+ );
+ }
+ }
+ }
+}
diff --git a/compiler/rustc_typeck/src/bounds.rs b/compiler/rustc_hir_analysis/src/bounds.rs
index 6a28bb16a..6a28bb16a 100644
--- a/compiler/rustc_typeck/src/bounds.rs
+++ b/compiler/rustc_hir_analysis/src/bounds.rs
diff --git a/compiler/rustc_hir_analysis/src/check/check.rs b/compiler/rustc_hir_analysis/src/check/check.rs
new file mode 100644
index 000000000..b70ac0205
--- /dev/null
+++ b/compiler/rustc_hir_analysis/src/check/check.rs
@@ -0,0 +1,1443 @@
+use crate::check::intrinsicck::InlineAsmCtxt;
+
+use super::compare_method::check_type_bounds;
+use super::compare_method::{compare_impl_method, compare_ty_impl};
+use super::*;
+use rustc_attr as attr;
+use rustc_errors::{Applicability, ErrorGuaranteed, MultiSpan};
+use rustc_hir as hir;
+use rustc_hir::def::{DefKind, Res};
+use rustc_hir::def_id::{DefId, LocalDefId};
+use rustc_hir::intravisit::Visitor;
+use rustc_hir::{ItemKind, Node, PathSegment};
+use rustc_infer::infer::outlives::env::OutlivesEnvironment;
+use rustc_infer::infer::{DefiningAnchor, RegionVariableOrigin, TyCtxtInferExt};
+use rustc_infer::traits::Obligation;
+use rustc_lint::builtin::REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS;
+use rustc_middle::hir::nested_filter;
+use rustc_middle::middle::stability::EvalResult;
+use rustc_middle::ty::layout::{LayoutError, MAX_SIMD_LANES};
+use rustc_middle::ty::subst::GenericArgKind;
+use rustc_middle::ty::util::{Discr, IntTypeExt};
+use rustc_middle::ty::{
+ self, ParamEnv, ToPredicate, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable,
+};
+use rustc_session::lint::builtin::{UNINHABITED_STATIC, UNSUPPORTED_CALLING_CONVENTIONS};
+use rustc_span::symbol::sym;
+use rustc_span::{self, Span};
+use rustc_target::spec::abi::Abi;
+use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _;
+use rustc_trait_selection::traits::{self, ObligationCtxt};
+
+use std::ops::ControlFlow;
+
+pub fn check_abi(tcx: TyCtxt<'_>, hir_id: hir::HirId, span: Span, abi: Abi) {
+ match tcx.sess.target.is_abi_supported(abi) {
+ Some(true) => (),
+ Some(false) => {
+ struct_span_err!(
+ tcx.sess,
+ span,
+ E0570,
+ "`{abi}` is not a supported ABI for the current target",
+ )
+ .emit();
+ }
+ None => {
+ tcx.struct_span_lint_hir(
+ UNSUPPORTED_CALLING_CONVENTIONS,
+ hir_id,
+ span,
+ "use of calling convention not supported on this target",
+ |lint| lint,
+ );
+ }
+ }
+
+ // This ABI is only allowed on function pointers
+ if abi == Abi::CCmseNonSecureCall {
+ struct_span_err!(
+ tcx.sess,
+ span,
+ E0781,
+ "the `\"C-cmse-nonsecure-call\"` ABI is only allowed on function pointers"
+ )
+ .emit();
+ }
+}
+
+fn check_struct(tcx: TyCtxt<'_>, def_id: LocalDefId) {
+ let def = tcx.adt_def(def_id);
+ let span = tcx.def_span(def_id);
+ def.destructor(tcx); // force the destructor to be evaluated
+
+ if def.repr().simd() {
+ check_simd(tcx, span, def_id);
+ }
+
+ check_transparent(tcx, span, def);
+ check_packed(tcx, span, def);
+}
+
+fn check_union(tcx: TyCtxt<'_>, def_id: LocalDefId) {
+ let def = tcx.adt_def(def_id);
+ let span = tcx.def_span(def_id);
+ def.destructor(tcx); // force the destructor to be evaluated
+ check_transparent(tcx, span, def);
+ check_union_fields(tcx, span, def_id);
+ check_packed(tcx, span, def);
+}
+
+/// Check that the fields of the `union` do not need dropping.
+fn check_union_fields(tcx: TyCtxt<'_>, span: Span, item_def_id: LocalDefId) -> bool {
+ let item_type = tcx.type_of(item_def_id);
+ if let ty::Adt(def, substs) = item_type.kind() {
+ assert!(def.is_union());
+
+ fn allowed_union_field<'tcx>(
+ ty: Ty<'tcx>,
+ tcx: TyCtxt<'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+ span: Span,
+ ) -> bool {
+ // We don't just accept all !needs_drop fields, due to semver concerns.
+ match ty.kind() {
+ ty::Ref(..) => true, // references never drop (even mutable refs, which are non-Copy and hence fail the later check)
+ ty::Tuple(tys) => {
+ // allow tuples of allowed types
+ tys.iter().all(|ty| allowed_union_field(ty, tcx, param_env, span))
+ }
+ ty::Array(elem, _len) => {
+ // Like `Copy`, we do *not* special-case length 0.
+ allowed_union_field(*elem, tcx, param_env, span)
+ }
+ _ => {
+ // Fallback case: allow `ManuallyDrop` and things that are `Copy`.
+ ty.ty_adt_def().is_some_and(|adt_def| adt_def.is_manually_drop())
+ || ty.is_copy_modulo_regions(tcx, param_env)
+ }
+ }
+ }
+
+ let param_env = tcx.param_env(item_def_id);
+ for field in &def.non_enum_variant().fields {
+ let field_ty = field.ty(tcx, substs);
+
+ if !allowed_union_field(field_ty, tcx, param_env, span) {
+ let (field_span, ty_span) = match tcx.hir().get_if_local(field.did) {
+ // We are currently checking the type this field came from, so it must be local.
+ Some(Node::Field(field)) => (field.span, field.ty.span),
+ _ => unreachable!("mir field has to correspond to hir field"),
+ };
+ struct_span_err!(
+ tcx.sess,
+ field_span,
+ E0740,
+ "unions cannot contain fields that may need dropping"
+ )
+ .note(
+ "a type is guaranteed not to need dropping \
+ when it implements `Copy`, or when it is the special `ManuallyDrop<_>` type",
+ )
+ .multipart_suggestion_verbose(
+ "when the type does not implement `Copy`, \
+ wrap it inside a `ManuallyDrop<_>` and ensure it is manually dropped",
+ vec![
+ (ty_span.shrink_to_lo(), "std::mem::ManuallyDrop<".into()),
+ (ty_span.shrink_to_hi(), ">".into()),
+ ],
+ Applicability::MaybeIncorrect,
+ )
+ .emit();
+ return false;
+ } else if field_ty.needs_drop(tcx, param_env) {
+ // This should never happen. But we can get here e.g. in case of name resolution errors.
+ tcx.sess.delay_span_bug(span, "we should never accept maybe-dropping union fields");
+ }
+ }
+ } else {
+ span_bug!(span, "unions must be ty::Adt, but got {:?}", item_type.kind());
+ }
+ true
+}
+
+/// Check that a `static` is inhabited.
+fn check_static_inhabited<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) {
+ // Make sure statics are inhabited.
+ // Other parts of the compiler assume that there are no uninhabited places. In principle it
+ // would be enough to check this for `extern` statics, as statics with an initializer will
+ // have UB during initialization if they are uninhabited, but there also seems to be no good
+ // reason to allow any statics to be uninhabited.
+ let ty = tcx.type_of(def_id);
+ let span = tcx.def_span(def_id);
+ let layout = match tcx.layout_of(ParamEnv::reveal_all().and(ty)) {
+ Ok(l) => l,
+ // Foreign statics that overflow their allowed size should emit an error
+ Err(LayoutError::SizeOverflow(_))
+ if {
+ let node = tcx.hir().get_by_def_id(def_id);
+ matches!(
+ node,
+ hir::Node::ForeignItem(hir::ForeignItem {
+ kind: hir::ForeignItemKind::Static(..),
+ ..
+ })
+ )
+ } =>
+ {
+ tcx.sess
+ .struct_span_err(span, "extern static is too large for the current architecture")
+ .emit();
+ return;
+ }
+ // Generic statics are rejected, but we still reach this case.
+ Err(e) => {
+ tcx.sess.delay_span_bug(span, &e.to_string());
+ return;
+ }
+ };
+ if layout.abi.is_uninhabited() {
+ tcx.struct_span_lint_hir(
+ UNINHABITED_STATIC,
+ tcx.hir().local_def_id_to_hir_id(def_id),
+ span,
+ "static of uninhabited type",
+ |lint| {
+ lint
+ .note("uninhabited statics cannot be initialized, and any access would be an immediate error")
+ },
+ );
+ }
+}
+
+/// Checks that an opaque type does not contain cycles and does not use `Self` or `T::Foo`
+/// projections that would result in "inheriting lifetimes".
+fn check_opaque<'tcx>(tcx: TyCtxt<'tcx>, id: hir::ItemId) {
+ let item = tcx.hir().item(id);
+ let hir::ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) = item.kind else {
+ tcx.sess.delay_span_bug(tcx.hir().span(id.hir_id()), "expected opaque item");
+ return;
+ };
+
+ // HACK(jynelson): trying to infer the type of `impl trait` breaks documenting
+ // `async-std` (and `pub async fn` in general).
+ // Since rustdoc doesn't care about the concrete type behind `impl Trait`, just don't look at it!
+ // See https://github.com/rust-lang/rust/issues/75100
+ if tcx.sess.opts.actually_rustdoc {
+ return;
+ }
+
+ let substs = InternalSubsts::identity_for_item(tcx, item.owner_id.to_def_id());
+ let span = tcx.def_span(item.owner_id.def_id);
+
+ check_opaque_for_inheriting_lifetimes(tcx, item.owner_id.def_id, span);
+ if tcx.type_of(item.owner_id.def_id).references_error() {
+ return;
+ }
+ if check_opaque_for_cycles(tcx, item.owner_id.def_id, substs, span, &origin).is_err() {
+ return;
+ }
+ check_opaque_meets_bounds(tcx, item.owner_id.def_id, substs, span, &origin);
+}
+/// Checks that an opaque type does not use `Self` or `T::Foo` projections that would result
+/// in "inheriting lifetimes".
+#[instrument(level = "debug", skip(tcx, span))]
+pub(super) fn check_opaque_for_inheriting_lifetimes<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ def_id: LocalDefId,
+ span: Span,
+) {
+ let item = tcx.hir().expect_item(def_id);
+ debug!(?item, ?span);
+
+ struct FoundParentLifetime;
+ struct FindParentLifetimeVisitor<'tcx>(&'tcx ty::Generics);
+ impl<'tcx> ty::visit::TypeVisitor<'tcx> for FindParentLifetimeVisitor<'tcx> {
+ type BreakTy = FoundParentLifetime;
+
+ fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> {
+ debug!("FindParentLifetimeVisitor: r={:?}", r);
+ if let ty::ReEarlyBound(ty::EarlyBoundRegion { index, .. }) = *r {
+ if index < self.0.parent_count as u32 {
+ return ControlFlow::Break(FoundParentLifetime);
+ } else {
+ return ControlFlow::CONTINUE;
+ }
+ }
+
+ r.super_visit_with(self)
+ }
+
+ fn visit_const(&mut self, c: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> {
+ if let ty::ConstKind::Unevaluated(..) = c.kind() {
+ // FIXME(#72219) We currently don't detect lifetimes within substs
+ // which would violate this check. Even though the particular substitution is not used
+ // within the const, this should still be fixed.
+ return ControlFlow::CONTINUE;
+ }
+ c.super_visit_with(self)
+ }
+ }
+
+ struct ProhibitOpaqueVisitor<'tcx> {
+ tcx: TyCtxt<'tcx>,
+ opaque_identity_ty: Ty<'tcx>,
+ generics: &'tcx ty::Generics,
+ selftys: Vec<(Span, Option<String>)>,
+ }
+
+ impl<'tcx> ty::visit::TypeVisitor<'tcx> for ProhibitOpaqueVisitor<'tcx> {
+ type BreakTy = Ty<'tcx>;
+
+ fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
+ debug!("check_opaque_for_inheriting_lifetimes: (visit_ty) t={:?}", t);
+ if t == self.opaque_identity_ty {
+ ControlFlow::CONTINUE
+ } else {
+ t.super_visit_with(&mut FindParentLifetimeVisitor(self.generics))
+ .map_break(|FoundParentLifetime| t)
+ }
+ }
+ }
+
+ impl<'tcx> Visitor<'tcx> for ProhibitOpaqueVisitor<'tcx> {
+ type NestedFilter = nested_filter::OnlyBodies;
+
+ fn nested_visit_map(&mut self) -> Self::Map {
+ self.tcx.hir()
+ }
+
+ fn visit_ty(&mut self, arg: &'tcx hir::Ty<'tcx>) {
+ match arg.kind {
+ hir::TyKind::Path(hir::QPath::Resolved(None, path)) => match &path.segments {
+ [PathSegment { res: Res::SelfTyParam { .. }, .. }] => {
+ let impl_ty_name = None;
+ self.selftys.push((path.span, impl_ty_name));
+ }
+ [PathSegment { res: Res::SelfTyAlias { alias_to: def_id, .. }, .. }] => {
+ let impl_ty_name = Some(self.tcx.def_path_str(*def_id));
+ self.selftys.push((path.span, impl_ty_name));
+ }
+ _ => {}
+ },
+ _ => {}
+ }
+ hir::intravisit::walk_ty(self, arg);
+ }
+ }
+
+ if let ItemKind::OpaqueTy(hir::OpaqueTy {
+ origin: hir::OpaqueTyOrigin::AsyncFn(..) | hir::OpaqueTyOrigin::FnReturn(..),
+ ..
+ }) = item.kind
+ {
+ let mut visitor = ProhibitOpaqueVisitor {
+ opaque_identity_ty: tcx.mk_opaque(
+ def_id.to_def_id(),
+ InternalSubsts::identity_for_item(tcx, def_id.to_def_id()),
+ ),
+ generics: tcx.generics_of(def_id),
+ tcx,
+ selftys: vec![],
+ };
+ let prohibit_opaque = tcx
+ .explicit_item_bounds(def_id)
+ .iter()
+ .try_for_each(|(predicate, _)| predicate.visit_with(&mut visitor));
+ debug!(
+ "check_opaque_for_inheriting_lifetimes: prohibit_opaque={:?}, visitor.opaque_identity_ty={:?}, visitor.generics={:?}",
+ prohibit_opaque, visitor.opaque_identity_ty, visitor.generics
+ );
+
+ if let Some(ty) = prohibit_opaque.break_value() {
+ visitor.visit_item(&item);
+ let is_async = match item.kind {
+ ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) => {
+ matches!(origin, hir::OpaqueTyOrigin::AsyncFn(..))
+ }
+ _ => unreachable!(),
+ };
+
+ let mut err = struct_span_err!(
+ tcx.sess,
+ span,
+ E0760,
+ "`{}` return type cannot contain a projection or `Self` that references lifetimes from \
+ a parent scope",
+ if is_async { "async fn" } else { "impl Trait" },
+ );
+
+ for (span, name) in visitor.selftys {
+ err.span_suggestion(
+ span,
+ "consider spelling out the type instead",
+ name.unwrap_or_else(|| format!("{:?}", ty)),
+ Applicability::MaybeIncorrect,
+ );
+ }
+ err.emit();
+ }
+ }
+}
+
+/// Checks that an opaque type does not contain cycles.
+pub(super) fn check_opaque_for_cycles<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ def_id: LocalDefId,
+ substs: SubstsRef<'tcx>,
+ span: Span,
+ origin: &hir::OpaqueTyOrigin,
+) -> Result<(), ErrorGuaranteed> {
+ if tcx.try_expand_impl_trait_type(def_id.to_def_id(), substs).is_err() {
+ let reported = match origin {
+ hir::OpaqueTyOrigin::AsyncFn(..) => async_opaque_type_cycle_error(tcx, span),
+ _ => opaque_type_cycle_error(tcx, def_id, span),
+ };
+ Err(reported)
+ } else {
+ Ok(())
+ }
+}
+
+/// Check that the concrete type behind `impl Trait` actually implements `Trait`.
+///
+/// This is mostly checked at the places that specify the opaque type, but we
+/// check those cases in the `param_env` of that function, which may have
+/// bounds not on this opaque type:
+///
+/// ```ignore (illustrative)
+/// type X<T> = impl Clone;
+/// fn f<T: Clone>(t: T) -> X<T> {
+/// t
+/// }
+/// ```
+///
+/// Without this check the above code is incorrectly accepted: we would ICE if
+/// some tried, for example, to clone an `Option<X<&mut ()>>`.
+#[instrument(level = "debug", skip(tcx))]
+fn check_opaque_meets_bounds<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ def_id: LocalDefId,
+ substs: SubstsRef<'tcx>,
+ span: Span,
+ origin: &hir::OpaqueTyOrigin,
+) {
+ let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
+ let defining_use_anchor = match *origin {
+ hir::OpaqueTyOrigin::FnReturn(did) | hir::OpaqueTyOrigin::AsyncFn(did) => did,
+ hir::OpaqueTyOrigin::TyAlias => def_id,
+ };
+ let param_env = tcx.param_env(defining_use_anchor);
+
+ let infcx = tcx
+ .infer_ctxt()
+ .with_opaque_type_inference(DefiningAnchor::Bind(defining_use_anchor))
+ .build();
+ let ocx = ObligationCtxt::new(&infcx);
+ let opaque_ty = tcx.mk_opaque(def_id.to_def_id(), substs);
+
+ // `ReErased` regions appear in the "parent_substs" of closures/generators.
+ // We're ignoring them here and replacing them with fresh region variables.
+ // See tests in ui/type-alias-impl-trait/closure_{parent_substs,wf_outlives}.rs.
+ //
+ // FIXME: Consider wrapping the hidden type in an existential `Binder` and instantiating it
+ // here rather than using ReErased.
+ let hidden_ty = tcx.bound_type_of(def_id.to_def_id()).subst(tcx, substs);
+ let hidden_ty = tcx.fold_regions(hidden_ty, |re, _dbi| match re.kind() {
+ ty::ReErased => infcx.next_region_var(RegionVariableOrigin::MiscVariable(span)),
+ _ => re,
+ });
+
+ let misc_cause = traits::ObligationCause::misc(span, hir_id);
+
+ match infcx.at(&misc_cause, param_env).eq(opaque_ty, hidden_ty) {
+ Ok(infer_ok) => ocx.register_infer_ok_obligations(infer_ok),
+ Err(ty_err) => {
+ tcx.sess.delay_span_bug(
+ span,
+ &format!("could not unify `{hidden_ty}` with revealed type:\n{ty_err}"),
+ );
+ }
+ }
+
+ // Additionally require the hidden type to be well-formed with only the generics of the opaque type.
+ // Defining use functions may have more bounds than the opaque type, which is ok, as long as the
+ // hidden type is well formed even without those bounds.
+ let predicate =
+ ty::Binder::dummy(ty::PredicateKind::WellFormed(hidden_ty.into())).to_predicate(tcx);
+ ocx.register_obligation(Obligation::new(misc_cause, param_env, predicate));
+
+ // Check that all obligations are satisfied by the implementation's
+ // version.
+ let errors = ocx.select_all_or_error();
+ if !errors.is_empty() {
+ infcx.err_ctxt().report_fulfillment_errors(&errors, None, false);
+ }
+ match origin {
+ // Checked when type checking the function containing them.
+ hir::OpaqueTyOrigin::FnReturn(..) | hir::OpaqueTyOrigin::AsyncFn(..) => {}
+ // Can have different predicates to their defining use
+ hir::OpaqueTyOrigin::TyAlias => {
+ let outlives_environment = OutlivesEnvironment::new(param_env);
+ infcx.check_region_obligations_and_report_errors(
+ defining_use_anchor,
+ &outlives_environment,
+ );
+ }
+ }
+ // Clean up after ourselves
+ let _ = infcx.inner.borrow_mut().opaque_type_storage.take_opaque_types();
+}
+
+fn check_item_type<'tcx>(tcx: TyCtxt<'tcx>, id: hir::ItemId) {
+ debug!(
+ "check_item_type(it.def_id={:?}, it.name={})",
+ id.owner_id,
+ tcx.def_path_str(id.owner_id.to_def_id())
+ );
+ let _indenter = indenter();
+ match tcx.def_kind(id.owner_id) {
+ DefKind::Static(..) => {
+ tcx.ensure().typeck(id.owner_id.def_id);
+ maybe_check_static_with_link_section(tcx, id.owner_id.def_id);
+ check_static_inhabited(tcx, id.owner_id.def_id);
+ }
+ DefKind::Const => {
+ tcx.ensure().typeck(id.owner_id.def_id);
+ }
+ DefKind::Enum => {
+ let item = tcx.hir().item(id);
+ let hir::ItemKind::Enum(ref enum_definition, _) = item.kind else {
+ return;
+ };
+ check_enum(tcx, &enum_definition.variants, item.owner_id.def_id);
+ }
+ DefKind::Fn => {} // entirely within check_item_body
+ DefKind::Impl => {
+ let it = tcx.hir().item(id);
+ let hir::ItemKind::Impl(ref impl_) = it.kind else {
+ return;
+ };
+ debug!("ItemKind::Impl {} with id {:?}", it.ident, it.owner_id);
+ if let Some(impl_trait_ref) = tcx.impl_trait_ref(it.owner_id) {
+ check_impl_items_against_trait(
+ tcx,
+ it.span,
+ it.owner_id.def_id,
+ impl_trait_ref,
+ &impl_.items,
+ );
+ check_on_unimplemented(tcx, it);
+ }
+ }
+ DefKind::Trait => {
+ let it = tcx.hir().item(id);
+ let hir::ItemKind::Trait(_, _, _, _, ref items) = it.kind else {
+ return;
+ };
+ check_on_unimplemented(tcx, it);
+
+ for item in items.iter() {
+ let item = tcx.hir().trait_item(item.id);
+ match item.kind {
+ hir::TraitItemKind::Fn(ref sig, _) => {
+ let abi = sig.header.abi;
+ fn_maybe_err(tcx, item.ident.span, abi);
+ }
+ hir::TraitItemKind::Type(.., Some(default)) => {
+ let assoc_item = tcx.associated_item(item.owner_id);
+ let trait_substs =
+ InternalSubsts::identity_for_item(tcx, it.owner_id.to_def_id());
+ let _: Result<_, rustc_errors::ErrorGuaranteed> = check_type_bounds(
+ tcx,
+ assoc_item,
+ assoc_item,
+ default.span,
+ ty::TraitRef { def_id: it.owner_id.to_def_id(), substs: trait_substs },
+ );
+ }
+ _ => {}
+ }
+ }
+ }
+ DefKind::Struct => {
+ check_struct(tcx, id.owner_id.def_id);
+ }
+ DefKind::Union => {
+ check_union(tcx, id.owner_id.def_id);
+ }
+ DefKind::OpaqueTy => {
+ check_opaque(tcx, id);
+ }
+ DefKind::ImplTraitPlaceholder => {
+ let parent = tcx.impl_trait_in_trait_parent(id.owner_id.to_def_id());
+ // Only check the validity of this opaque type if the function has a default body
+ if let hir::Node::TraitItem(hir::TraitItem {
+ kind: hir::TraitItemKind::Fn(_, hir::TraitFn::Provided(_)),
+ ..
+ }) = tcx.hir().get_by_def_id(parent.expect_local())
+ {
+ check_opaque(tcx, id);
+ }
+ }
+ DefKind::TyAlias => {
+ let pty_ty = tcx.type_of(id.owner_id);
+ let generics = tcx.generics_of(id.owner_id);
+ check_type_params_are_used(tcx, &generics, pty_ty);
+ }
+ DefKind::ForeignMod => {
+ let it = tcx.hir().item(id);
+ let hir::ItemKind::ForeignMod { abi, items } = it.kind else {
+ return;
+ };
+ check_abi(tcx, it.hir_id(), it.span, abi);
+
+ if abi == Abi::RustIntrinsic {
+ for item in items {
+ let item = tcx.hir().foreign_item(item.id);
+ intrinsic::check_intrinsic_type(tcx, item);
+ }
+ } else if abi == Abi::PlatformIntrinsic {
+ for item in items {
+ let item = tcx.hir().foreign_item(item.id);
+ intrinsic::check_platform_intrinsic_type(tcx, item);
+ }
+ } else {
+ for item in items {
+ let def_id = item.id.owner_id.def_id;
+ let generics = tcx.generics_of(def_id);
+ let own_counts = generics.own_counts();
+ if generics.params.len() - own_counts.lifetimes != 0 {
+ let (kinds, kinds_pl, egs) = match (own_counts.types, own_counts.consts) {
+ (_, 0) => ("type", "types", Some("u32")),
+ // We don't specify an example value, because we can't generate
+ // a valid value for any type.
+ (0, _) => ("const", "consts", None),
+ _ => ("type or const", "types or consts", None),
+ };
+ struct_span_err!(
+ tcx.sess,
+ item.span,
+ E0044,
+ "foreign items may not have {kinds} parameters",
+ )
+ .span_label(item.span, &format!("can't have {kinds} parameters"))
+ .help(
+ // FIXME: once we start storing spans for type arguments, turn this
+ // into a suggestion.
+ &format!(
+ "replace the {} parameters with concrete {}{}",
+ kinds,
+ kinds_pl,
+ egs.map(|egs| format!(" like `{}`", egs)).unwrap_or_default(),
+ ),
+ )
+ .emit();
+ }
+
+ let item = tcx.hir().foreign_item(item.id);
+ match item.kind {
+ hir::ForeignItemKind::Fn(ref fn_decl, _, _) => {
+ require_c_abi_if_c_variadic(tcx, fn_decl, abi, item.span);
+ }
+ hir::ForeignItemKind::Static(..) => {
+ check_static_inhabited(tcx, def_id);
+ }
+ _ => {}
+ }
+ }
+ }
+ }
+ DefKind::GlobalAsm => {
+ let it = tcx.hir().item(id);
+ let hir::ItemKind::GlobalAsm(asm) = it.kind else { span_bug!(it.span, "DefKind::GlobalAsm but got {:#?}", it) };
+ InlineAsmCtxt::new_global_asm(tcx).check_asm(asm, id.hir_id());
+ }
+ _ => {}
+ }
+}
+
+pub(super) fn check_on_unimplemented(tcx: TyCtxt<'_>, item: &hir::Item<'_>) {
+ // an error would be reported if this fails.
+ let _ = traits::OnUnimplementedDirective::of_item(tcx, item.owner_id.to_def_id());
+}
+
+pub(super) fn check_specialization_validity<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ trait_def: &ty::TraitDef,
+ trait_item: &ty::AssocItem,
+ impl_id: DefId,
+ impl_item: &hir::ImplItemRef,
+) {
+ let Ok(ancestors) = trait_def.ancestors(tcx, impl_id) else { return };
+ let mut ancestor_impls = ancestors.skip(1).filter_map(|parent| {
+ if parent.is_from_trait() {
+ None
+ } else {
+ Some((parent, parent.item(tcx, trait_item.def_id)))
+ }
+ });
+
+ let opt_result = ancestor_impls.find_map(|(parent_impl, parent_item)| {
+ match parent_item {
+ // Parent impl exists, and contains the parent item we're trying to specialize, but
+ // doesn't mark it `default`.
+ Some(parent_item) if traits::impl_item_is_final(tcx, &parent_item) => {
+ Some(Err(parent_impl.def_id()))
+ }
+
+ // Parent impl contains item and makes it specializable.
+ Some(_) => Some(Ok(())),
+
+ // Parent impl doesn't mention the item. This means it's inherited from the
+ // grandparent. In that case, if parent is a `default impl`, inherited items use the
+ // "defaultness" from the grandparent, else they are final.
+ None => {
+ if tcx.impl_defaultness(parent_impl.def_id()).is_default() {
+ None
+ } else {
+ Some(Err(parent_impl.def_id()))
+ }
+ }
+ }
+ });
+
+ // If `opt_result` is `None`, we have only encountered `default impl`s that don't contain the
+ // item. This is allowed, the item isn't actually getting specialized here.
+ let result = opt_result.unwrap_or(Ok(()));
+
+ if let Err(parent_impl) = result {
+ report_forbidden_specialization(tcx, impl_item, parent_impl);
+ }
+}
+
+fn check_impl_items_against_trait<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ full_impl_span: Span,
+ impl_id: LocalDefId,
+ impl_trait_ref: ty::TraitRef<'tcx>,
+ impl_item_refs: &[hir::ImplItemRef],
+) {
+ // If the trait reference itself is erroneous (so the compilation is going
+ // to fail), skip checking the items here -- the `impl_item` table in `tcx`
+ // isn't populated for such impls.
+ if impl_trait_ref.references_error() {
+ return;
+ }
+
+ // Negative impls are not expected to have any items
+ match tcx.impl_polarity(impl_id) {
+ ty::ImplPolarity::Reservation | ty::ImplPolarity::Positive => {}
+ ty::ImplPolarity::Negative => {
+ if let [first_item_ref, ..] = impl_item_refs {
+ let first_item_span = tcx.hir().impl_item(first_item_ref.id).span;
+ struct_span_err!(
+ tcx.sess,
+ first_item_span,
+ E0749,
+ "negative impls cannot have any items"
+ )
+ .emit();
+ }
+ return;
+ }
+ }
+
+ let trait_def = tcx.trait_def(impl_trait_ref.def_id);
+
+ for impl_item in impl_item_refs {
+ let ty_impl_item = tcx.associated_item(impl_item.id.owner_id);
+ let ty_trait_item = if let Some(trait_item_id) = ty_impl_item.trait_item_def_id {
+ tcx.associated_item(trait_item_id)
+ } else {
+ // Checked in `associated_item`.
+ tcx.sess.delay_span_bug(impl_item.span, "missing associated item in trait");
+ continue;
+ };
+ let impl_item_full = tcx.hir().impl_item(impl_item.id);
+ match impl_item_full.kind {
+ hir::ImplItemKind::Const(..) => {
+ let _ = tcx.compare_assoc_const_impl_item_with_trait_item((
+ impl_item.id.owner_id.def_id,
+ ty_impl_item.trait_item_def_id.unwrap(),
+ ));
+ }
+ hir::ImplItemKind::Fn(..) => {
+ let opt_trait_span = tcx.hir().span_if_local(ty_trait_item.def_id);
+ compare_impl_method(
+ tcx,
+ &ty_impl_item,
+ &ty_trait_item,
+ impl_trait_ref,
+ opt_trait_span,
+ );
+ }
+ hir::ImplItemKind::Type(impl_ty) => {
+ let opt_trait_span = tcx.hir().span_if_local(ty_trait_item.def_id);
+ compare_ty_impl(
+ tcx,
+ &ty_impl_item,
+ impl_ty.span,
+ &ty_trait_item,
+ impl_trait_ref,
+ opt_trait_span,
+ );
+ }
+ }
+
+ check_specialization_validity(
+ tcx,
+ trait_def,
+ &ty_trait_item,
+ impl_id.to_def_id(),
+ impl_item,
+ );
+ }
+
+ if let Ok(ancestors) = trait_def.ancestors(tcx, impl_id.to_def_id()) {
+ // Check for missing items from trait
+ let mut missing_items = Vec::new();
+
+ let mut must_implement_one_of: Option<&[Ident]> =
+ trait_def.must_implement_one_of.as_deref();
+
+ for &trait_item_id in tcx.associated_item_def_ids(impl_trait_ref.def_id) {
+ let is_implemented = ancestors
+ .leaf_def(tcx, trait_item_id)
+ .map_or(false, |node_item| node_item.item.defaultness(tcx).has_value());
+
+ if !is_implemented && tcx.impl_defaultness(impl_id).is_final() {
+ missing_items.push(tcx.associated_item(trait_item_id));
+ }
+
+ // true if this item is specifically implemented in this impl
+ let is_implemented_here = ancestors
+ .leaf_def(tcx, trait_item_id)
+ .map_or(false, |node_item| !node_item.defining_node.is_from_trait());
+
+ if !is_implemented_here {
+ match tcx.eval_default_body_stability(trait_item_id, full_impl_span) {
+ EvalResult::Deny { feature, reason, issue, .. } => default_body_is_unstable(
+ tcx,
+ full_impl_span,
+ trait_item_id,
+ feature,
+ reason,
+ issue,
+ ),
+
+ // Unmarked default bodies are considered stable (at least for now).
+ EvalResult::Allow | EvalResult::Unmarked => {}
+ }
+ }
+
+ if let Some(required_items) = &must_implement_one_of {
+ if is_implemented_here {
+ let trait_item = tcx.associated_item(trait_item_id);
+ if required_items.contains(&trait_item.ident(tcx)) {
+ must_implement_one_of = None;
+ }
+ }
+ }
+ }
+
+ if !missing_items.is_empty() {
+ missing_items_err(tcx, tcx.def_span(impl_id), &missing_items, full_impl_span);
+ }
+
+ if let Some(missing_items) = must_implement_one_of {
+ let attr_span = tcx
+ .get_attr(impl_trait_ref.def_id, sym::rustc_must_implement_one_of)
+ .map(|attr| attr.span);
+
+ missing_items_must_implement_one_of_err(
+ tcx,
+ tcx.def_span(impl_id),
+ missing_items,
+ attr_span,
+ );
+ }
+ }
+}
+
+pub fn check_simd(tcx: TyCtxt<'_>, sp: Span, def_id: LocalDefId) {
+ let t = tcx.type_of(def_id);
+ if let ty::Adt(def, substs) = t.kind()
+ && def.is_struct()
+ {
+ let fields = &def.non_enum_variant().fields;
+ if fields.is_empty() {
+ struct_span_err!(tcx.sess, sp, E0075, "SIMD vector cannot be empty").emit();
+ return;
+ }
+ let e = fields[0].ty(tcx, substs);
+ if !fields.iter().all(|f| f.ty(tcx, substs) == e) {
+ struct_span_err!(tcx.sess, sp, E0076, "SIMD vector should be homogeneous")
+ .span_label(sp, "SIMD elements must have the same type")
+ .emit();
+ return;
+ }
+
+ let len = if let ty::Array(_ty, c) = e.kind() {
+ c.try_eval_usize(tcx, tcx.param_env(def.did()))
+ } else {
+ Some(fields.len() as u64)
+ };
+ if let Some(len) = len {
+ if len == 0 {
+ struct_span_err!(tcx.sess, sp, E0075, "SIMD vector cannot be empty").emit();
+ return;
+ } else if len > MAX_SIMD_LANES {
+ struct_span_err!(
+ tcx.sess,
+ sp,
+ E0075,
+ "SIMD vector cannot have more than {MAX_SIMD_LANES} elements",
+ )
+ .emit();
+ return;
+ }
+ }
+
+ // Check that we use types valid for use in the lanes of a SIMD "vector register"
+ // These are scalar types which directly match a "machine" type
+ // Yes: Integers, floats, "thin" pointers
+ // No: char, "fat" pointers, compound types
+ match e.kind() {
+ ty::Param(_) => (), // pass struct<T>(T, T, T, T) through, let monomorphization catch errors
+ ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::RawPtr(_) => (), // struct(u8, u8, u8, u8) is ok
+ ty::Array(t, _) if matches!(t.kind(), ty::Param(_)) => (), // pass struct<T>([T; N]) through, let monomorphization catch errors
+ ty::Array(t, _clen)
+ if matches!(
+ t.kind(),
+ ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::RawPtr(_)
+ ) =>
+ { /* struct([f32; 4]) is ok */ }
+ _ => {
+ struct_span_err!(
+ tcx.sess,
+ sp,
+ E0077,
+ "SIMD vector element type should be a \
+ primitive scalar (integer/float/pointer) type"
+ )
+ .emit();
+ return;
+ }
+ }
+ }
+}
+
+pub(super) fn check_packed(tcx: TyCtxt<'_>, sp: Span, def: ty::AdtDef<'_>) {
+ let repr = def.repr();
+ if repr.packed() {
+ for attr in tcx.get_attrs(def.did(), sym::repr) {
+ for r in attr::parse_repr_attr(&tcx.sess, attr) {
+ if let attr::ReprPacked(pack) = r
+ && let Some(repr_pack) = repr.pack
+ && pack as u64 != repr_pack.bytes()
+ {
+ struct_span_err!(
+ tcx.sess,
+ sp,
+ E0634,
+ "type has conflicting packed representation hints"
+ )
+ .emit();
+ }
+ }
+ }
+ if repr.align.is_some() {
+ struct_span_err!(
+ tcx.sess,
+ sp,
+ E0587,
+ "type has conflicting packed and align representation hints"
+ )
+ .emit();
+ } else {
+ if let Some(def_spans) = check_packed_inner(tcx, def.did(), &mut vec![]) {
+ let mut err = struct_span_err!(
+ tcx.sess,
+ sp,
+ E0588,
+ "packed type cannot transitively contain a `#[repr(align)]` type"
+ );
+
+ err.span_note(
+ tcx.def_span(def_spans[0].0),
+ &format!(
+ "`{}` has a `#[repr(align)]` attribute",
+ tcx.item_name(def_spans[0].0)
+ ),
+ );
+
+ if def_spans.len() > 2 {
+ let mut first = true;
+ for (adt_def, span) in def_spans.iter().skip(1).rev() {
+ let ident = tcx.item_name(*adt_def);
+ err.span_note(
+ *span,
+ &if first {
+ format!(
+ "`{}` contains a field of type `{}`",
+ tcx.type_of(def.did()),
+ ident
+ )
+ } else {
+ format!("...which contains a field of type `{ident}`")
+ },
+ );
+ first = false;
+ }
+ }
+
+ err.emit();
+ }
+ }
+ }
+}
+
+pub(super) fn check_packed_inner(
+ tcx: TyCtxt<'_>,
+ def_id: DefId,
+ stack: &mut Vec<DefId>,
+) -> Option<Vec<(DefId, Span)>> {
+ if let ty::Adt(def, substs) = tcx.type_of(def_id).kind() {
+ if def.is_struct() || def.is_union() {
+ if def.repr().align.is_some() {
+ return Some(vec![(def.did(), DUMMY_SP)]);
+ }
+
+ stack.push(def_id);
+ for field in &def.non_enum_variant().fields {
+ if let ty::Adt(def, _) = field.ty(tcx, substs).kind()
+ && !stack.contains(&def.did())
+ && let Some(mut defs) = check_packed_inner(tcx, def.did(), stack)
+ {
+ defs.push((def.did(), field.ident(tcx).span));
+ return Some(defs);
+ }
+ }
+ stack.pop();
+ }
+ }
+
+ None
+}
+
+pub(super) fn check_transparent<'tcx>(tcx: TyCtxt<'tcx>, sp: Span, adt: ty::AdtDef<'tcx>) {
+ if !adt.repr().transparent() {
+ return;
+ }
+
+ if adt.is_union() && !tcx.features().transparent_unions {
+ feature_err(
+ &tcx.sess.parse_sess,
+ sym::transparent_unions,
+ sp,
+ "transparent unions are unstable",
+ )
+ .emit();
+ }
+
+ if adt.variants().len() != 1 {
+ bad_variant_count(tcx, adt, sp, adt.did());
+ if adt.variants().is_empty() {
+ // Don't bother checking the fields. No variants (and thus no fields) exist.
+ return;
+ }
+ }
+
+ // For each field, figure out if it's known to be a ZST and align(1), with "known"
+ // respecting #[non_exhaustive] attributes.
+ let field_infos = adt.all_fields().map(|field| {
+ let ty = field.ty(tcx, InternalSubsts::identity_for_item(tcx, field.did));
+ let param_env = tcx.param_env(field.did);
+ let layout = tcx.layout_of(param_env.and(ty));
+ // We are currently checking the type this field came from, so it must be local
+ let span = tcx.hir().span_if_local(field.did).unwrap();
+ let zst = layout.map_or(false, |layout| layout.is_zst());
+ let align1 = layout.map_or(false, |layout| layout.align.abi.bytes() == 1);
+ if !zst {
+ return (span, zst, align1, None);
+ }
+
+ fn check_non_exhaustive<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ t: Ty<'tcx>,
+ ) -> ControlFlow<(&'static str, DefId, SubstsRef<'tcx>, bool)> {
+ match t.kind() {
+ ty::Tuple(list) => list.iter().try_for_each(|t| check_non_exhaustive(tcx, t)),
+ ty::Array(ty, _) => check_non_exhaustive(tcx, *ty),
+ ty::Adt(def, subst) => {
+ if !def.did().is_local() {
+ let non_exhaustive = def.is_variant_list_non_exhaustive()
+ || def
+ .variants()
+ .iter()
+ .any(ty::VariantDef::is_field_list_non_exhaustive);
+ let has_priv = def.all_fields().any(|f| !f.vis.is_public());
+ if non_exhaustive || has_priv {
+ return ControlFlow::Break((
+ def.descr(),
+ def.did(),
+ subst,
+ non_exhaustive,
+ ));
+ }
+ }
+ def.all_fields()
+ .map(|field| field.ty(tcx, subst))
+ .try_for_each(|t| check_non_exhaustive(tcx, t))
+ }
+ _ => ControlFlow::Continue(()),
+ }
+ }
+
+ (span, zst, align1, check_non_exhaustive(tcx, ty).break_value())
+ });
+
+ let non_zst_fields = field_infos
+ .clone()
+ .filter_map(|(span, zst, _align1, _non_exhaustive)| if !zst { Some(span) } else { None });
+ let non_zst_count = non_zst_fields.clone().count();
+ if non_zst_count >= 2 {
+ bad_non_zero_sized_fields(tcx, adt, non_zst_count, non_zst_fields, sp);
+ }
+ let incompatible_zst_fields =
+ field_infos.clone().filter(|(_, _, _, opt)| opt.is_some()).count();
+ let incompat = incompatible_zst_fields + non_zst_count >= 2 && non_zst_count < 2;
+ for (span, zst, align1, non_exhaustive) in field_infos {
+ if zst && !align1 {
+ struct_span_err!(
+ tcx.sess,
+ span,
+ E0691,
+ "zero-sized field in transparent {} has alignment larger than 1",
+ adt.descr(),
+ )
+ .span_label(span, "has alignment larger than 1")
+ .emit();
+ }
+ if incompat && let Some((descr, def_id, substs, non_exhaustive)) = non_exhaustive {
+ tcx.struct_span_lint_hir(
+ REPR_TRANSPARENT_EXTERNAL_PRIVATE_FIELDS,
+ tcx.hir().local_def_id_to_hir_id(adt.did().expect_local()),
+ span,
+ "zero-sized fields in `repr(transparent)` cannot contain external non-exhaustive types",
+ |lint| {
+ let note = if non_exhaustive {
+ "is marked with `#[non_exhaustive]`"
+ } else {
+ "contains private fields"
+ };
+ let field_ty = tcx.def_path_str_with_substs(def_id, substs);
+ lint
+ .note(format!("this {descr} contains `{field_ty}`, which {note}, \
+ and makes it not a breaking change to become non-zero-sized in the future."))
+ },
+ )
+ }
+ }
+}
+
+#[allow(trivial_numeric_casts)]
+fn check_enum<'tcx>(tcx: TyCtxt<'tcx>, vs: &'tcx [hir::Variant<'tcx>], def_id: LocalDefId) {
+ let def = tcx.adt_def(def_id);
+ let sp = tcx.def_span(def_id);
+ def.destructor(tcx); // force the destructor to be evaluated
+
+ if vs.is_empty() {
+ if let Some(attr) = tcx.get_attrs(def_id.to_def_id(), sym::repr).next() {
+ struct_span_err!(
+ tcx.sess,
+ attr.span,
+ E0084,
+ "unsupported representation for zero-variant enum"
+ )
+ .span_label(sp, "zero-variant enum")
+ .emit();
+ }
+ }
+
+ let repr_type_ty = def.repr().discr_type().to_ty(tcx);
+ if repr_type_ty == tcx.types.i128 || repr_type_ty == tcx.types.u128 {
+ if !tcx.features().repr128 {
+ feature_err(
+ &tcx.sess.parse_sess,
+ sym::repr128,
+ sp,
+ "repr with 128-bit type is unstable",
+ )
+ .emit();
+ }
+ }
+
+ for v in vs {
+ if let Some(ref e) = v.disr_expr {
+ tcx.ensure().typeck(tcx.hir().local_def_id(e.hir_id));
+ }
+ }
+
+ if tcx.adt_def(def_id).repr().int.is_none() {
+ let is_unit = |var: &hir::Variant<'_>| matches!(var.data, hir::VariantData::Unit(..));
+
+ let has_disr = |var: &hir::Variant<'_>| var.disr_expr.is_some();
+ let has_non_units = vs.iter().any(|var| !is_unit(var));
+ let disr_units = vs.iter().any(|var| is_unit(&var) && has_disr(&var));
+ let disr_non_unit = vs.iter().any(|var| !is_unit(&var) && has_disr(&var));
+
+ if disr_non_unit || (disr_units && has_non_units) {
+ let mut err =
+ struct_span_err!(tcx.sess, sp, E0732, "`#[repr(inttype)]` must be specified");
+ err.emit();
+ }
+ }
+
+ detect_discriminant_duplicate(tcx, def.discriminants(tcx).collect(), vs, sp);
+
+ check_transparent(tcx, sp, def);
+}
+
+/// Part of enum check. Given the discriminants of an enum, errors if two or more discriminants are equal
+fn detect_discriminant_duplicate<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ mut discrs: Vec<(VariantIdx, Discr<'tcx>)>,
+ vs: &'tcx [hir::Variant<'tcx>],
+ self_span: Span,
+) {
+ // Helper closure to reduce duplicate code. This gets called everytime we detect a duplicate.
+ // Here `idx` refers to the order of which the discriminant appears, and its index in `vs`
+ let report = |dis: Discr<'tcx>, idx: usize, err: &mut Diagnostic| {
+ let var = &vs[idx]; // HIR for the duplicate discriminant
+ let (span, display_discr) = match var.disr_expr {
+ Some(ref expr) => {
+ // In the case the discriminant is both a duplicate and overflowed, let the user know
+ if let hir::ExprKind::Lit(lit) = &tcx.hir().body(expr.body).value.kind
+ && let rustc_ast::LitKind::Int(lit_value, _int_kind) = &lit.node
+ && *lit_value != dis.val
+ {
+ (tcx.hir().span(expr.hir_id), format!("`{dis}` (overflowed from `{lit_value}`)"))
+ // Otherwise, format the value as-is
+ } else {
+ (tcx.hir().span(expr.hir_id), format!("`{dis}`"))
+ }
+ }
+ None => {
+ // At this point we know this discriminant is a duplicate, and was not explicitly
+ // assigned by the user. Here we iterate backwards to fetch the HIR for the last
+ // explicitly assigned discriminant, and letting the user know that this was the
+ // increment startpoint, and how many steps from there leading to the duplicate
+ if let Some((n, hir::Variant { span, ident, .. })) =
+ vs[..idx].iter().rev().enumerate().find(|v| v.1.disr_expr.is_some())
+ {
+ let ve_ident = var.ident;
+ let n = n + 1;
+ let sp = if n > 1 { "variants" } else { "variant" };
+
+ err.span_label(
+ *span,
+ format!("discriminant for `{ve_ident}` incremented from this startpoint (`{ident}` + {n} {sp} later => `{ve_ident}` = {dis})"),
+ );
+ }
+
+ (vs[idx].span, format!("`{dis}`"))
+ }
+ };
+
+ err.span_label(span, format!("{display_discr} assigned here"));
+ };
+
+ // Here we loop through the discriminants, comparing each discriminant to another.
+ // When a duplicate is detected, we instantiate an error and point to both
+ // initial and duplicate value. The duplicate discriminant is then discarded by swapping
+ // it with the last element and decrementing the `vec.len` (which is why we have to evaluate
+ // `discrs.len()` anew every iteration, and why this could be tricky to do in a functional
+ // style as we are mutating `discrs` on the fly).
+ let mut i = 0;
+ while i < discrs.len() {
+ let hir_var_i_idx = discrs[i].0.index();
+ let mut error: Option<DiagnosticBuilder<'_, _>> = None;
+
+ let mut o = i + 1;
+ while o < discrs.len() {
+ let hir_var_o_idx = discrs[o].0.index();
+
+ if discrs[i].1.val == discrs[o].1.val {
+ let err = error.get_or_insert_with(|| {
+ let mut ret = struct_span_err!(
+ tcx.sess,
+ self_span,
+ E0081,
+ "discriminant value `{}` assigned more than once",
+ discrs[i].1,
+ );
+
+ report(discrs[i].1, hir_var_i_idx, &mut ret);
+
+ ret
+ });
+
+ report(discrs[o].1, hir_var_o_idx, err);
+
+ // Safe to unwrap here, as we wouldn't reach this point if `discrs` was empty
+ discrs[o] = *discrs.last().unwrap();
+ discrs.pop();
+ } else {
+ o += 1;
+ }
+ }
+
+ if let Some(mut e) = error {
+ e.emit();
+ }
+
+ i += 1;
+ }
+}
+
+pub(super) fn check_type_params_are_used<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ generics: &ty::Generics,
+ ty: Ty<'tcx>,
+) {
+ debug!("check_type_params_are_used(generics={:?}, ty={:?})", generics, ty);
+
+ assert_eq!(generics.parent, None);
+
+ if generics.own_counts().types == 0 {
+ return;
+ }
+
+ let mut params_used = BitSet::new_empty(generics.params.len());
+
+ if ty.references_error() {
+ // If there is already another error, do not emit
+ // an error for not using a type parameter.
+ assert!(tcx.sess.has_errors().is_some());
+ return;
+ }
+
+ for leaf in ty.walk() {
+ if let GenericArgKind::Type(leaf_ty) = leaf.unpack()
+ && let ty::Param(param) = leaf_ty.kind()
+ {
+ debug!("found use of ty param {:?}", param);
+ params_used.insert(param.index);
+ }
+ }
+
+ for param in &generics.params {
+ if !params_used.contains(param.index)
+ && let ty::GenericParamDefKind::Type { .. } = param.kind
+ {
+ let span = tcx.def_span(param.def_id);
+ struct_span_err!(
+ tcx.sess,
+ span,
+ E0091,
+ "type parameter `{}` is unused",
+ param.name,
+ )
+ .span_label(span, "unused type parameter")
+ .emit();
+ }
+ }
+}
+
+pub(super) fn check_mod_item_types(tcx: TyCtxt<'_>, module_def_id: LocalDefId) {
+ let module = tcx.hir_module_items(module_def_id);
+ for id in module.items() {
+ check_item_type(tcx, id);
+ }
+}
+
+fn async_opaque_type_cycle_error(tcx: TyCtxt<'_>, span: Span) -> ErrorGuaranteed {
+ struct_span_err!(tcx.sess, span, E0733, "recursion in an `async fn` requires boxing")
+ .span_label(span, "recursive `async fn`")
+ .note("a recursive `async fn` must be rewritten to return a boxed `dyn Future`")
+ .note(
+ "consider using the `async_recursion` crate: https://crates.io/crates/async_recursion",
+ )
+ .emit()
+}
+
+/// Emit an error for recursive opaque types.
+///
+/// If this is a return `impl Trait`, find the item's return expressions and point at them. For
+/// direct recursion this is enough, but for indirect recursion also point at the last intermediary
+/// `impl Trait`.
+///
+/// If all the return expressions evaluate to `!`, then we explain that the error will go away
+/// after changing it. This can happen when a user uses `panic!()` or similar as a placeholder.
+fn opaque_type_cycle_error(tcx: TyCtxt<'_>, def_id: LocalDefId, span: Span) -> ErrorGuaranteed {
+ let mut err = struct_span_err!(tcx.sess, span, E0720, "cannot resolve opaque type");
+
+ let mut label = false;
+ if let Some((def_id, visitor)) = get_owner_return_paths(tcx, def_id) {
+ let typeck_results = tcx.typeck(def_id);
+ if visitor
+ .returns
+ .iter()
+ .filter_map(|expr| typeck_results.node_type_opt(expr.hir_id))
+ .all(|ty| matches!(ty.kind(), ty::Never))
+ {
+ let spans = visitor
+ .returns
+ .iter()
+ .filter(|expr| typeck_results.node_type_opt(expr.hir_id).is_some())
+ .map(|expr| expr.span)
+ .collect::<Vec<Span>>();
+ let span_len = spans.len();
+ if span_len == 1 {
+ err.span_label(spans[0], "this returned value is of `!` type");
+ } else {
+ let mut multispan: MultiSpan = spans.clone().into();
+ for span in spans {
+ multispan.push_span_label(span, "this returned value is of `!` type");
+ }
+ err.span_note(multispan, "these returned values have a concrete \"never\" type");
+ }
+ err.help("this error will resolve once the item's body returns a concrete type");
+ } else {
+ let mut seen = FxHashSet::default();
+ seen.insert(span);
+ err.span_label(span, "recursive opaque type");
+ label = true;
+ for (sp, ty) in visitor
+ .returns
+ .iter()
+ .filter_map(|e| typeck_results.node_type_opt(e.hir_id).map(|t| (e.span, t)))
+ .filter(|(_, ty)| !matches!(ty.kind(), ty::Never))
+ {
+ struct OpaqueTypeCollector(Vec<DefId>);
+ impl<'tcx> ty::visit::TypeVisitor<'tcx> for OpaqueTypeCollector {
+ fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
+ match *t.kind() {
+ ty::Opaque(def, _) => {
+ self.0.push(def);
+ ControlFlow::CONTINUE
+ }
+ _ => t.super_visit_with(self),
+ }
+ }
+ }
+ let mut visitor = OpaqueTypeCollector(vec![]);
+ ty.visit_with(&mut visitor);
+ for def_id in visitor.0 {
+ let ty_span = tcx.def_span(def_id);
+ if !seen.contains(&ty_span) {
+ err.span_label(ty_span, &format!("returning this opaque type `{ty}`"));
+ seen.insert(ty_span);
+ }
+ err.span_label(sp, &format!("returning here with type `{ty}`"));
+ }
+ }
+ }
+ }
+ if !label {
+ err.span_label(span, "cannot resolve opaque type");
+ }
+ err.emit()
+}
diff --git a/compiler/rustc_hir_analysis/src/check/compare_method.rs b/compiler/rustc_hir_analysis/src/check/compare_method.rs
new file mode 100644
index 000000000..32f66b06f
--- /dev/null
+++ b/compiler/rustc_hir_analysis/src/check/compare_method.rs
@@ -0,0 +1,1825 @@
+use super::potentially_plural_count;
+use crate::errors::LifetimesOrBoundsMismatchOnTrait;
+use hir::def_id::{DefId, LocalDefId};
+use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticId, ErrorGuaranteed};
+use rustc_hir as hir;
+use rustc_hir::def::{DefKind, Res};
+use rustc_hir::intravisit;
+use rustc_hir::{GenericParamKind, ImplItemKind, TraitItemKind};
+use rustc_infer::infer::outlives::env::OutlivesEnvironment;
+use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
+use rustc_infer::infer::{self, TyCtxtInferExt};
+use rustc_infer::traits::util;
+use rustc_middle::ty::error::{ExpectedFound, TypeError};
+use rustc_middle::ty::util::ExplicitSelf;
+use rustc_middle::ty::InternalSubsts;
+use rustc_middle::ty::{
+ self, AssocItem, DefIdTree, Ty, TypeFoldable, TypeFolder, TypeSuperFoldable, TypeVisitable,
+};
+use rustc_middle::ty::{GenericParamDefKind, ToPredicate, TyCtxt};
+use rustc_span::Span;
+use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt;
+use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _;
+use rustc_trait_selection::traits::{
+ self, ObligationCause, ObligationCauseCode, ObligationCtxt, Reveal,
+};
+use std::iter;
+
+/// Checks that a method from an impl conforms to the signature of
+/// the same method as declared in the trait.
+///
+/// # Parameters
+///
+/// - `impl_m`: type of the method we are checking
+/// - `impl_m_span`: span to use for reporting errors
+/// - `trait_m`: the method in the trait
+/// - `impl_trait_ref`: the TraitRef corresponding to the trait implementation
+pub(crate) fn compare_impl_method<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ impl_m: &ty::AssocItem,
+ trait_m: &ty::AssocItem,
+ impl_trait_ref: ty::TraitRef<'tcx>,
+ trait_item_span: Option<Span>,
+) {
+ debug!("compare_impl_method(impl_trait_ref={:?})", impl_trait_ref);
+
+ let impl_m_span = tcx.def_span(impl_m.def_id);
+
+ if let Err(_) = compare_self_type(tcx, impl_m, impl_m_span, trait_m, impl_trait_ref) {
+ return;
+ }
+
+ if let Err(_) = compare_number_of_generics(tcx, impl_m, impl_m_span, trait_m, trait_item_span) {
+ return;
+ }
+
+ if let Err(_) = compare_generic_param_kinds(tcx, impl_m, trait_m) {
+ return;
+ }
+
+ if let Err(_) =
+ compare_number_of_method_arguments(tcx, impl_m, impl_m_span, trait_m, trait_item_span)
+ {
+ return;
+ }
+
+ if let Err(_) = compare_synthetic_generics(tcx, impl_m, trait_m) {
+ return;
+ }
+
+ if let Err(_) = compare_predicate_entailment(tcx, impl_m, impl_m_span, trait_m, impl_trait_ref)
+ {
+ return;
+ }
+}
+
+/// This function is best explained by example. Consider a trait:
+///
+/// trait Trait<'t, T> {
+/// // `trait_m`
+/// fn method<'a, M>(t: &'t T, m: &'a M) -> Self;
+/// }
+///
+/// And an impl:
+///
+/// impl<'i, 'j, U> Trait<'j, &'i U> for Foo {
+/// // `impl_m`
+/// fn method<'b, N>(t: &'j &'i U, m: &'b N) -> Foo;
+/// }
+///
+/// We wish to decide if those two method types are compatible.
+/// For this we have to show that, assuming the bounds of the impl hold, the
+/// bounds of `trait_m` imply the bounds of `impl_m`.
+///
+/// We start out with `trait_to_impl_substs`, that maps the trait
+/// type parameters to impl type parameters. This is taken from the
+/// impl trait reference:
+///
+/// trait_to_impl_substs = {'t => 'j, T => &'i U, Self => Foo}
+///
+/// We create a mapping `dummy_substs` that maps from the impl type
+/// parameters to fresh types and regions. For type parameters,
+/// this is the identity transform, but we could as well use any
+/// placeholder types. For regions, we convert from bound to free
+/// regions (Note: but only early-bound regions, i.e., those
+/// declared on the impl or used in type parameter bounds).
+///
+/// impl_to_placeholder_substs = {'i => 'i0, U => U0, N => N0 }
+///
+/// Now we can apply `placeholder_substs` to the type of the impl method
+/// to yield a new function type in terms of our fresh, placeholder
+/// types:
+///
+/// <'b> fn(t: &'i0 U0, m: &'b) -> Foo
+///
+/// We now want to extract and substitute the type of the *trait*
+/// method and compare it. To do so, we must create a compound
+/// substitution by combining `trait_to_impl_substs` and
+/// `impl_to_placeholder_substs`, and also adding a mapping for the method
+/// type parameters. We extend the mapping to also include
+/// the method parameters.
+///
+/// trait_to_placeholder_substs = { T => &'i0 U0, Self => Foo, M => N0 }
+///
+/// Applying this to the trait method type yields:
+///
+/// <'a> fn(t: &'i0 U0, m: &'a) -> Foo
+///
+/// This type is also the same but the name of the bound region (`'a`
+/// vs `'b`). However, the normal subtyping rules on fn types handle
+/// this kind of equivalency just fine.
+///
+/// We now use these substitutions to ensure that all declared bounds are
+/// satisfied by the implementation's method.
+///
+/// We do this by creating a parameter environment which contains a
+/// substitution corresponding to `impl_to_placeholder_substs`. We then build
+/// `trait_to_placeholder_substs` and use it to convert the predicates contained
+/// in the `trait_m` generics to the placeholder form.
+///
+/// Finally we register each of these predicates as an obligation and check that
+/// they hold.
+#[instrument(level = "debug", skip(tcx, impl_m_span, impl_trait_ref))]
+fn compare_predicate_entailment<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ impl_m: &AssocItem,
+ impl_m_span: Span,
+ trait_m: &AssocItem,
+ impl_trait_ref: ty::TraitRef<'tcx>,
+) -> Result<(), ErrorGuaranteed> {
+ let trait_to_impl_substs = impl_trait_ref.substs;
+
+ // This node-id should be used for the `body_id` field on each
+ // `ObligationCause` (and the `FnCtxt`).
+ //
+ // FIXME(@lcnr): remove that after removing `cause.body_id` from
+ // obligations.
+ let impl_m_hir_id = tcx.hir().local_def_id_to_hir_id(impl_m.def_id.expect_local());
+ // We sometimes modify the span further down.
+ let mut cause = ObligationCause::new(
+ impl_m_span,
+ impl_m_hir_id,
+ ObligationCauseCode::CompareImplItemObligation {
+ impl_item_def_id: impl_m.def_id.expect_local(),
+ trait_item_def_id: trait_m.def_id,
+ kind: impl_m.kind,
+ },
+ );
+
+ // Create mapping from impl to placeholder.
+ let impl_to_placeholder_substs = InternalSubsts::identity_for_item(tcx, impl_m.def_id);
+
+ // Create mapping from trait to placeholder.
+ let trait_to_placeholder_substs =
+ impl_to_placeholder_substs.rebase_onto(tcx, impl_m.container_id(tcx), trait_to_impl_substs);
+ debug!("compare_impl_method: trait_to_placeholder_substs={:?}", trait_to_placeholder_substs);
+
+ let impl_m_generics = tcx.generics_of(impl_m.def_id);
+ let trait_m_generics = tcx.generics_of(trait_m.def_id);
+ let impl_m_predicates = tcx.predicates_of(impl_m.def_id);
+ let trait_m_predicates = tcx.predicates_of(trait_m.def_id);
+
+ // Check region bounds.
+ check_region_bounds_on_impl_item(tcx, impl_m, trait_m, &trait_m_generics, &impl_m_generics)?;
+
+ // Create obligations for each predicate declared by the impl
+ // definition in the context of the trait's parameter
+ // environment. We can't just use `impl_env.caller_bounds`,
+ // however, because we want to replace all late-bound regions with
+ // region variables.
+ let impl_predicates = tcx.predicates_of(impl_m_predicates.parent.unwrap());
+ let mut hybrid_preds = impl_predicates.instantiate_identity(tcx);
+
+ debug!("compare_impl_method: impl_bounds={:?}", hybrid_preds);
+
+ // This is the only tricky bit of the new way we check implementation methods
+ // We need to build a set of predicates where only the method-level bounds
+ // are from the trait and we assume all other bounds from the implementation
+ // to be previously satisfied.
+ //
+ // We then register the obligations from the impl_m and check to see
+ // if all constraints hold.
+ hybrid_preds
+ .predicates
+ .extend(trait_m_predicates.instantiate_own(tcx, trait_to_placeholder_substs).predicates);
+
+ // Construct trait parameter environment and then shift it into the placeholder viewpoint.
+ // The key step here is to update the caller_bounds's predicates to be
+ // the new hybrid bounds we computed.
+ let normalize_cause = traits::ObligationCause::misc(impl_m_span, impl_m_hir_id);
+ let param_env = ty::ParamEnv::new(
+ tcx.intern_predicates(&hybrid_preds.predicates),
+ Reveal::UserFacing,
+ hir::Constness::NotConst,
+ );
+ let param_env = traits::normalize_param_env_or_error(tcx, param_env, normalize_cause);
+
+ let infcx = &tcx.infer_ctxt().build();
+ let ocx = ObligationCtxt::new(infcx);
+
+ debug!("compare_impl_method: caller_bounds={:?}", param_env.caller_bounds());
+
+ let mut selcx = traits::SelectionContext::new(&infcx);
+ let impl_m_own_bounds = impl_m_predicates.instantiate_own(tcx, impl_to_placeholder_substs);
+ for (predicate, span) in iter::zip(impl_m_own_bounds.predicates, impl_m_own_bounds.spans) {
+ let normalize_cause = traits::ObligationCause::misc(span, impl_m_hir_id);
+ let traits::Normalized { value: predicate, obligations } =
+ traits::normalize(&mut selcx, param_env, normalize_cause, predicate);
+
+ ocx.register_obligations(obligations);
+ let cause = ObligationCause::new(
+ span,
+ impl_m_hir_id,
+ ObligationCauseCode::CompareImplItemObligation {
+ impl_item_def_id: impl_m.def_id.expect_local(),
+ trait_item_def_id: trait_m.def_id,
+ kind: impl_m.kind,
+ },
+ );
+ ocx.register_obligation(traits::Obligation::new(cause, param_env, predicate));
+ }
+
+ // We now need to check that the signature of the impl method is
+ // compatible with that of the trait method. We do this by
+ // checking that `impl_fty <: trait_fty`.
+ //
+ // FIXME. Unfortunately, this doesn't quite work right now because
+ // associated type normalization is not integrated into subtype
+ // checks. For the comparison to be valid, we need to
+ // normalize the associated types in the impl/trait methods
+ // first. However, because function types bind regions, just
+ // calling `normalize_associated_types_in` would have no effect on
+ // any associated types appearing in the fn arguments or return
+ // type.
+
+ // Compute placeholder form of impl and trait method tys.
+ let tcx = infcx.tcx;
+
+ let mut wf_tys = FxHashSet::default();
+
+ let impl_sig = infcx.replace_bound_vars_with_fresh_vars(
+ impl_m_span,
+ infer::HigherRankedType,
+ tcx.fn_sig(impl_m.def_id),
+ );
+
+ let norm_cause = ObligationCause::misc(impl_m_span, impl_m_hir_id);
+ let impl_sig = ocx.normalize(norm_cause.clone(), param_env, impl_sig);
+ let impl_fty = tcx.mk_fn_ptr(ty::Binder::dummy(impl_sig));
+ debug!("compare_impl_method: impl_fty={:?}", impl_fty);
+
+ let trait_sig = tcx.bound_fn_sig(trait_m.def_id).subst(tcx, trait_to_placeholder_substs);
+ let trait_sig = tcx.liberate_late_bound_regions(impl_m.def_id, trait_sig);
+
+ // Next, add all inputs and output as well-formed tys. Importantly,
+ // we have to do this before normalization, since the normalized ty may
+ // not contain the input parameters. See issue #87748.
+ wf_tys.extend(trait_sig.inputs_and_output.iter());
+ let trait_sig = ocx.normalize(norm_cause, param_env, trait_sig);
+ // We also have to add the normalized trait signature
+ // as we don't normalize during implied bounds computation.
+ wf_tys.extend(trait_sig.inputs_and_output.iter());
+ let trait_fty = tcx.mk_fn_ptr(ty::Binder::dummy(trait_sig));
+
+ debug!("compare_impl_method: trait_fty={:?}", trait_fty);
+
+ // FIXME: We'd want to keep more accurate spans than "the method signature" when
+ // processing the comparison between the trait and impl fn, but we sadly lose them
+ // and point at the whole signature when a trait bound or specific input or output
+ // type would be more appropriate. In other places we have a `Vec<Span>`
+ // corresponding to their `Vec<Predicate>`, but we don't have that here.
+ // Fixing this would improve the output of test `issue-83765.rs`.
+ let mut result = infcx
+ .at(&cause, param_env)
+ .sup(trait_fty, impl_fty)
+ .map(|infer_ok| ocx.register_infer_ok_obligations(infer_ok));
+
+ // HACK(RPITIT): #101614. When we are trying to infer the hidden types for
+ // RPITITs, we need to equate the output tys instead of just subtyping. If
+ // we just use `sup` above, we'll end up `&'static str <: _#1t`, which causes
+ // us to infer `_#1t = #'_#2r str`, where `'_#2r` is unconstrained, which gets
+ // fixed up to `ReEmpty`, and which is certainly not what we want.
+ if trait_fty.has_infer_types() {
+ result = result.and_then(|()| {
+ infcx
+ .at(&cause, param_env)
+ .eq(trait_sig.output(), impl_sig.output())
+ .map(|infer_ok| ocx.register_infer_ok_obligations(infer_ok))
+ });
+ }
+
+ if let Err(terr) = result {
+ debug!("sub_types failed: impl ty {:?}, trait ty {:?}", impl_fty, trait_fty);
+
+ let (impl_err_span, trait_err_span) =
+ extract_spans_for_error_reporting(&infcx, terr, &cause, impl_m, trait_m);
+
+ cause.span = impl_err_span;
+
+ let mut diag = struct_span_err!(
+ tcx.sess,
+ cause.span(),
+ E0053,
+ "method `{}` has an incompatible type for trait",
+ trait_m.name
+ );
+ match &terr {
+ TypeError::ArgumentMutability(0) | TypeError::ArgumentSorts(_, 0)
+ if trait_m.fn_has_self_parameter =>
+ {
+ let ty = trait_sig.inputs()[0];
+ let sugg = match ExplicitSelf::determine(ty, |_| ty == impl_trait_ref.self_ty()) {
+ ExplicitSelf::ByValue => "self".to_owned(),
+ ExplicitSelf::ByReference(_, hir::Mutability::Not) => "&self".to_owned(),
+ ExplicitSelf::ByReference(_, hir::Mutability::Mut) => "&mut self".to_owned(),
+ _ => format!("self: {ty}"),
+ };
+
+ // When the `impl` receiver is an arbitrary self type, like `self: Box<Self>`, the
+ // span points only at the type `Box<Self`>, but we want to cover the whole
+ // argument pattern and type.
+ let span = match tcx.hir().expect_impl_item(impl_m.def_id.expect_local()).kind {
+ ImplItemKind::Fn(ref sig, body) => tcx
+ .hir()
+ .body_param_names(body)
+ .zip(sig.decl.inputs.iter())
+ .map(|(param, ty)| param.span.to(ty.span))
+ .next()
+ .unwrap_or(impl_err_span),
+ _ => bug!("{:?} is not a method", impl_m),
+ };
+
+ diag.span_suggestion(
+ span,
+ "change the self-receiver type to match the trait",
+ sugg,
+ Applicability::MachineApplicable,
+ );
+ }
+ TypeError::ArgumentMutability(i) | TypeError::ArgumentSorts(_, i) => {
+ if trait_sig.inputs().len() == *i {
+ // Suggestion to change output type. We do not suggest in `async` functions
+ // to avoid complex logic or incorrect output.
+ match tcx.hir().expect_impl_item(impl_m.def_id.expect_local()).kind {
+ ImplItemKind::Fn(ref sig, _)
+ if sig.header.asyncness == hir::IsAsync::NotAsync =>
+ {
+ let msg = "change the output type to match the trait";
+ let ap = Applicability::MachineApplicable;
+ match sig.decl.output {
+ hir::FnRetTy::DefaultReturn(sp) => {
+ let sugg = format!("-> {} ", trait_sig.output());
+ diag.span_suggestion_verbose(sp, msg, sugg, ap);
+ }
+ hir::FnRetTy::Return(hir_ty) => {
+ let sugg = trait_sig.output();
+ diag.span_suggestion(hir_ty.span, msg, sugg, ap);
+ }
+ };
+ }
+ _ => {}
+ };
+ } else if let Some(trait_ty) = trait_sig.inputs().get(*i) {
+ diag.span_suggestion(
+ impl_err_span,
+ "change the parameter type to match the trait",
+ trait_ty,
+ Applicability::MachineApplicable,
+ );
+ }
+ }
+ _ => {}
+ }
+
+ infcx.err_ctxt().note_type_err(
+ &mut diag,
+ &cause,
+ trait_err_span.map(|sp| (sp, "type in trait".to_owned())),
+ Some(infer::ValuePairs::Terms(ExpectedFound {
+ expected: trait_fty.into(),
+ found: impl_fty.into(),
+ })),
+ terr,
+ false,
+ false,
+ );
+
+ return Err(diag.emit());
+ }
+
+ // Check that all obligations are satisfied by the implementation's
+ // version.
+ let errors = ocx.select_all_or_error();
+ if !errors.is_empty() {
+ let reported = infcx.err_ctxt().report_fulfillment_errors(&errors, None, false);
+ return Err(reported);
+ }
+
+ // Finally, resolve all regions. This catches wily misuses of
+ // lifetime parameters.
+ let outlives_environment = OutlivesEnvironment::with_bounds(
+ param_env,
+ Some(infcx),
+ infcx.implied_bounds_tys(param_env, impl_m_hir_id, wf_tys),
+ );
+ infcx.check_region_obligations_and_report_errors(
+ impl_m.def_id.expect_local(),
+ &outlives_environment,
+ );
+
+ Ok(())
+}
+
+pub fn collect_trait_impl_trait_tys<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ def_id: DefId,
+) -> Result<&'tcx FxHashMap<DefId, Ty<'tcx>>, ErrorGuaranteed> {
+ let impl_m = tcx.opt_associated_item(def_id).unwrap();
+ let trait_m = tcx.opt_associated_item(impl_m.trait_item_def_id.unwrap()).unwrap();
+ let impl_trait_ref = tcx.impl_trait_ref(impl_m.impl_container(tcx).unwrap()).unwrap();
+ let param_env = tcx.param_env(def_id);
+
+ let trait_to_impl_substs = impl_trait_ref.substs;
+
+ let impl_m_hir_id = tcx.hir().local_def_id_to_hir_id(impl_m.def_id.expect_local());
+ let return_span = tcx.hir().fn_decl_by_hir_id(impl_m_hir_id).unwrap().output.span();
+ let cause = ObligationCause::new(
+ return_span,
+ impl_m_hir_id,
+ ObligationCauseCode::CompareImplItemObligation {
+ impl_item_def_id: impl_m.def_id.expect_local(),
+ trait_item_def_id: trait_m.def_id,
+ kind: impl_m.kind,
+ },
+ );
+
+ // Create mapping from impl to placeholder.
+ let impl_to_placeholder_substs = InternalSubsts::identity_for_item(tcx, impl_m.def_id);
+
+ // Create mapping from trait to placeholder.
+ let trait_to_placeholder_substs =
+ impl_to_placeholder_substs.rebase_onto(tcx, impl_m.container_id(tcx), trait_to_impl_substs);
+
+ let infcx = &tcx.infer_ctxt().build();
+ let ocx = ObligationCtxt::new(infcx);
+
+ let norm_cause = ObligationCause::misc(return_span, impl_m_hir_id);
+ let impl_sig = ocx.normalize(
+ norm_cause.clone(),
+ param_env,
+ infcx.replace_bound_vars_with_fresh_vars(
+ return_span,
+ infer::HigherRankedType,
+ tcx.fn_sig(impl_m.def_id),
+ ),
+ );
+ let impl_return_ty = impl_sig.output();
+
+ let mut collector = ImplTraitInTraitCollector::new(&ocx, return_span, param_env, impl_m_hir_id);
+ let unnormalized_trait_sig = tcx
+ .liberate_late_bound_regions(
+ impl_m.def_id,
+ tcx.bound_fn_sig(trait_m.def_id).subst(tcx, trait_to_placeholder_substs),
+ )
+ .fold_with(&mut collector);
+ let trait_sig = ocx.normalize(norm_cause.clone(), param_env, unnormalized_trait_sig);
+ let trait_return_ty = trait_sig.output();
+
+ let wf_tys = FxHashSet::from_iter(
+ unnormalized_trait_sig.inputs_and_output.iter().chain(trait_sig.inputs_and_output.iter()),
+ );
+
+ match infcx.at(&cause, param_env).eq(trait_return_ty, impl_return_ty) {
+ Ok(infer::InferOk { value: (), obligations }) => {
+ ocx.register_obligations(obligations);
+ }
+ Err(terr) => {
+ let mut diag = struct_span_err!(
+ tcx.sess,
+ cause.span(),
+ E0053,
+ "method `{}` has an incompatible return type for trait",
+ trait_m.name
+ );
+ let hir = tcx.hir();
+ infcx.err_ctxt().note_type_err(
+ &mut diag,
+ &cause,
+ hir.get_if_local(impl_m.def_id)
+ .and_then(|node| node.fn_decl())
+ .map(|decl| (decl.output.span(), "return type in trait".to_owned())),
+ Some(infer::ValuePairs::Terms(ExpectedFound {
+ expected: trait_return_ty.into(),
+ found: impl_return_ty.into(),
+ })),
+ terr,
+ false,
+ false,
+ );
+ return Err(diag.emit());
+ }
+ }
+
+ // Unify the whole function signature. We need to do this to fully infer
+ // the lifetimes of the return type, but do this after unifying just the
+ // return types, since we want to avoid duplicating errors from
+ // `compare_predicate_entailment`.
+ match infcx
+ .at(&cause, param_env)
+ .eq(tcx.mk_fn_ptr(ty::Binder::dummy(trait_sig)), tcx.mk_fn_ptr(ty::Binder::dummy(impl_sig)))
+ {
+ Ok(infer::InferOk { value: (), obligations }) => {
+ ocx.register_obligations(obligations);
+ }
+ Err(terr) => {
+ let guar = tcx.sess.delay_span_bug(
+ return_span,
+ format!("could not unify `{trait_sig}` and `{impl_sig}`: {terr:?}"),
+ );
+ return Err(guar);
+ }
+ }
+
+ // Check that all obligations are satisfied by the implementation's
+ // RPITs.
+ let errors = ocx.select_all_or_error();
+ if !errors.is_empty() {
+ let reported = infcx.err_ctxt().report_fulfillment_errors(&errors, None, false);
+ return Err(reported);
+ }
+
+ // Finally, resolve all regions. This catches wily misuses of
+ // lifetime parameters.
+ let outlives_environment = OutlivesEnvironment::with_bounds(
+ param_env,
+ Some(infcx),
+ infcx.implied_bounds_tys(param_env, impl_m_hir_id, wf_tys),
+ );
+ infcx.check_region_obligations_and_report_errors(
+ impl_m.def_id.expect_local(),
+ &outlives_environment,
+ );
+
+ let mut collected_tys = FxHashMap::default();
+ for (def_id, (ty, substs)) in collector.types {
+ match infcx.fully_resolve(ty) {
+ Ok(ty) => {
+ // `ty` contains free regions that we created earlier while liberating the
+ // trait fn signature. However, projection normalization expects `ty` to
+ // contains `def_id`'s early-bound regions.
+ let id_substs = InternalSubsts::identity_for_item(tcx, def_id);
+ debug!(?id_substs, ?substs);
+ let map: FxHashMap<ty::GenericArg<'tcx>, ty::GenericArg<'tcx>> =
+ std::iter::zip(substs, id_substs).collect();
+ debug!(?map);
+
+ // NOTE(compiler-errors): RPITITs, like all other RPITs, have early-bound
+ // region substs that are synthesized during AST lowering. These are substs
+ // that are appended to the parent substs (trait and trait method). However,
+ // we're trying to infer the unsubstituted type value of the RPITIT inside
+ // the *impl*, so we can later use the impl's method substs to normalize
+ // an RPITIT to a concrete type (`confirm_impl_trait_in_trait_candidate`).
+ //
+ // Due to the design of RPITITs, during AST lowering, we have no idea that
+ // an impl method corresponds to a trait method with RPITITs in it. Therefore,
+ // we don't have a list of early-bound region substs for the RPITIT in the impl.
+ // Since early region parameters are index-based, we can't just rebase these
+ // (trait method) early-bound region substs onto the impl, and there's no
+ // guarantee that the indices from the trait substs and impl substs line up.
+ // So to fix this, we subtract the number of trait substs and add the number of
+ // impl substs to *renumber* these early-bound regions to their corresponding
+ // indices in the impl's substitutions list.
+ //
+ // Also, we only need to account for a difference in trait and impl substs,
+ // since we previously enforce that the trait method and impl method have the
+ // same generics.
+ let num_trait_substs = trait_to_impl_substs.len();
+ let num_impl_substs = tcx.generics_of(impl_m.container_id(tcx)).params.len();
+ let ty = tcx.fold_regions(ty, |region, _| {
+ let (ty::ReFree(_) | ty::ReEarlyBound(_)) = region.kind() else { return region; };
+ let Some(ty::ReEarlyBound(e)) = map.get(&region.into()).map(|r| r.expect_region().kind())
+ else {
+ tcx
+ .sess
+ .delay_span_bug(
+ return_span,
+ "expected ReFree to map to ReEarlyBound"
+ );
+ return tcx.lifetimes.re_static;
+ };
+ tcx.mk_region(ty::ReEarlyBound(ty::EarlyBoundRegion {
+ def_id: e.def_id,
+ name: e.name,
+ index: (e.index as usize - num_trait_substs + num_impl_substs) as u32,
+ }))
+ });
+ debug!(%ty);
+ collected_tys.insert(def_id, ty);
+ }
+ Err(err) => {
+ tcx.sess.delay_span_bug(
+ return_span,
+ format!("could not fully resolve: {ty} => {err:?}"),
+ );
+ collected_tys.insert(def_id, tcx.ty_error());
+ }
+ }
+ }
+
+ Ok(&*tcx.arena.alloc(collected_tys))
+}
+
+struct ImplTraitInTraitCollector<'a, 'tcx> {
+ ocx: &'a ObligationCtxt<'a, 'tcx>,
+ types: FxHashMap<DefId, (Ty<'tcx>, ty::SubstsRef<'tcx>)>,
+ span: Span,
+ param_env: ty::ParamEnv<'tcx>,
+ body_id: hir::HirId,
+}
+
+impl<'a, 'tcx> ImplTraitInTraitCollector<'a, 'tcx> {
+ fn new(
+ ocx: &'a ObligationCtxt<'a, 'tcx>,
+ span: Span,
+ param_env: ty::ParamEnv<'tcx>,
+ body_id: hir::HirId,
+ ) -> Self {
+ ImplTraitInTraitCollector { ocx, types: FxHashMap::default(), span, param_env, body_id }
+ }
+}
+
+impl<'tcx> TypeFolder<'tcx> for ImplTraitInTraitCollector<'_, 'tcx> {
+ fn tcx<'a>(&'a self) -> TyCtxt<'tcx> {
+ self.ocx.infcx.tcx
+ }
+
+ fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
+ if let ty::Projection(proj) = ty.kind()
+ && self.tcx().def_kind(proj.item_def_id) == DefKind::ImplTraitPlaceholder
+ {
+ if let Some((ty, _)) = self.types.get(&proj.item_def_id) {
+ return *ty;
+ }
+ //FIXME(RPITIT): Deny nested RPITIT in substs too
+ if proj.substs.has_escaping_bound_vars() {
+ bug!("FIXME(RPITIT): error here");
+ }
+ // Replace with infer var
+ let infer_ty = self.ocx.infcx.next_ty_var(TypeVariableOrigin {
+ span: self.span,
+ kind: TypeVariableOriginKind::MiscVariable,
+ });
+ self.types.insert(proj.item_def_id, (infer_ty, proj.substs));
+ // Recurse into bounds
+ for (pred, pred_span) in self.tcx().bound_explicit_item_bounds(proj.item_def_id).subst_iter_copied(self.tcx(), proj.substs) {
+ let pred = pred.fold_with(self);
+ let pred = self.ocx.normalize(
+ ObligationCause::misc(self.span, self.body_id),
+ self.param_env,
+ pred,
+ );
+
+ self.ocx.register_obligation(traits::Obligation::new(
+ ObligationCause::new(
+ self.span,
+ self.body_id,
+ ObligationCauseCode::BindingObligation(proj.item_def_id, pred_span),
+ ),
+ self.param_env,
+ pred,
+ ));
+ }
+ infer_ty
+ } else {
+ ty.super_fold_with(self)
+ }
+ }
+}
+
+fn check_region_bounds_on_impl_item<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ impl_m: &ty::AssocItem,
+ trait_m: &ty::AssocItem,
+ trait_generics: &ty::Generics,
+ impl_generics: &ty::Generics,
+) -> Result<(), ErrorGuaranteed> {
+ let trait_params = trait_generics.own_counts().lifetimes;
+ let impl_params = impl_generics.own_counts().lifetimes;
+
+ debug!(
+ "check_region_bounds_on_impl_item: \
+ trait_generics={:?} \
+ impl_generics={:?}",
+ trait_generics, impl_generics
+ );
+
+ // Must have same number of early-bound lifetime parameters.
+ // Unfortunately, if the user screws up the bounds, then this
+ // will change classification between early and late. E.g.,
+ // if in trait we have `<'a,'b:'a>`, and in impl we just have
+ // `<'a,'b>`, then we have 2 early-bound lifetime parameters
+ // in trait but 0 in the impl. But if we report "expected 2
+ // but found 0" it's confusing, because it looks like there
+ // are zero. Since I don't quite know how to phrase things at
+ // the moment, give a kind of vague error message.
+ if trait_params != impl_params {
+ let span = tcx
+ .hir()
+ .get_generics(impl_m.def_id.expect_local())
+ .expect("expected impl item to have generics or else we can't compare them")
+ .span;
+ let generics_span = if let Some(local_def_id) = trait_m.def_id.as_local() {
+ Some(
+ tcx.hir()
+ .get_generics(local_def_id)
+ .expect("expected trait item to have generics or else we can't compare them")
+ .span,
+ )
+ } else {
+ None
+ };
+
+ let reported = tcx.sess.emit_err(LifetimesOrBoundsMismatchOnTrait {
+ span,
+ item_kind: assoc_item_kind_str(impl_m),
+ ident: impl_m.ident(tcx),
+ generics_span,
+ });
+ return Err(reported);
+ }
+
+ Ok(())
+}
+
+#[instrument(level = "debug", skip(infcx))]
+fn extract_spans_for_error_reporting<'tcx>(
+ infcx: &infer::InferCtxt<'tcx>,
+ terr: TypeError<'_>,
+ cause: &ObligationCause<'tcx>,
+ impl_m: &ty::AssocItem,
+ trait_m: &ty::AssocItem,
+) -> (Span, Option<Span>) {
+ let tcx = infcx.tcx;
+ let mut impl_args = match tcx.hir().expect_impl_item(impl_m.def_id.expect_local()).kind {
+ ImplItemKind::Fn(ref sig, _) => {
+ sig.decl.inputs.iter().map(|t| t.span).chain(iter::once(sig.decl.output.span()))
+ }
+ _ => bug!("{:?} is not a method", impl_m),
+ };
+ let trait_args =
+ trait_m.def_id.as_local().map(|def_id| match tcx.hir().expect_trait_item(def_id).kind {
+ TraitItemKind::Fn(ref sig, _) => {
+ sig.decl.inputs.iter().map(|t| t.span).chain(iter::once(sig.decl.output.span()))
+ }
+ _ => bug!("{:?} is not a TraitItemKind::Fn", trait_m),
+ });
+
+ match terr {
+ TypeError::ArgumentMutability(i) => {
+ (impl_args.nth(i).unwrap(), trait_args.and_then(|mut args| args.nth(i)))
+ }
+ TypeError::ArgumentSorts(ExpectedFound { .. }, i) => {
+ (impl_args.nth(i).unwrap(), trait_args.and_then(|mut args| args.nth(i)))
+ }
+ _ => (cause.span(), tcx.hir().span_if_local(trait_m.def_id)),
+ }
+}
+
+fn compare_self_type<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ impl_m: &ty::AssocItem,
+ impl_m_span: Span,
+ trait_m: &ty::AssocItem,
+ impl_trait_ref: ty::TraitRef<'tcx>,
+) -> Result<(), ErrorGuaranteed> {
+ // Try to give more informative error messages about self typing
+ // mismatches. Note that any mismatch will also be detected
+ // below, where we construct a canonical function type that
+ // includes the self parameter as a normal parameter. It's just
+ // that the error messages you get out of this code are a bit more
+ // inscrutable, particularly for cases where one method has no
+ // self.
+
+ let self_string = |method: &ty::AssocItem| {
+ let untransformed_self_ty = match method.container {
+ ty::ImplContainer => impl_trait_ref.self_ty(),
+ ty::TraitContainer => tcx.types.self_param,
+ };
+ let self_arg_ty = tcx.fn_sig(method.def_id).input(0);
+ let param_env = ty::ParamEnv::reveal_all();
+
+ let infcx = tcx.infer_ctxt().build();
+ let self_arg_ty = tcx.liberate_late_bound_regions(method.def_id, self_arg_ty);
+ let can_eq_self = |ty| infcx.can_eq(param_env, untransformed_self_ty, ty).is_ok();
+ match ExplicitSelf::determine(self_arg_ty, can_eq_self) {
+ ExplicitSelf::ByValue => "self".to_owned(),
+ ExplicitSelf::ByReference(_, hir::Mutability::Not) => "&self".to_owned(),
+ ExplicitSelf::ByReference(_, hir::Mutability::Mut) => "&mut self".to_owned(),
+ _ => format!("self: {self_arg_ty}"),
+ }
+ };
+
+ match (trait_m.fn_has_self_parameter, impl_m.fn_has_self_parameter) {
+ (false, false) | (true, true) => {}
+
+ (false, true) => {
+ let self_descr = self_string(impl_m);
+ let mut err = struct_span_err!(
+ tcx.sess,
+ impl_m_span,
+ E0185,
+ "method `{}` has a `{}` declaration in the impl, but not in the trait",
+ trait_m.name,
+ self_descr
+ );
+ err.span_label(impl_m_span, format!("`{self_descr}` used in impl"));
+ if let Some(span) = tcx.hir().span_if_local(trait_m.def_id) {
+ err.span_label(span, format!("trait method declared without `{self_descr}`"));
+ } else {
+ err.note_trait_signature(trait_m.name, trait_m.signature(tcx));
+ }
+ let reported = err.emit();
+ return Err(reported);
+ }
+
+ (true, false) => {
+ let self_descr = self_string(trait_m);
+ let mut err = struct_span_err!(
+ tcx.sess,
+ impl_m_span,
+ E0186,
+ "method `{}` has a `{}` declaration in the trait, but not in the impl",
+ trait_m.name,
+ self_descr
+ );
+ err.span_label(impl_m_span, format!("expected `{self_descr}` in impl"));
+ if let Some(span) = tcx.hir().span_if_local(trait_m.def_id) {
+ err.span_label(span, format!("`{self_descr}` used in trait"));
+ } else {
+ err.note_trait_signature(trait_m.name, trait_m.signature(tcx));
+ }
+ let reported = err.emit();
+ return Err(reported);
+ }
+ }
+
+ Ok(())
+}
+
+/// Checks that the number of generics on a given assoc item in a trait impl is the same
+/// as the number of generics on the respective assoc item in the trait definition.
+///
+/// For example this code emits the errors in the following code:
+/// ```
+/// trait Trait {
+/// fn foo();
+/// type Assoc<T>;
+/// }
+///
+/// impl Trait for () {
+/// fn foo<T>() {}
+/// //~^ error
+/// type Assoc = u32;
+/// //~^ error
+/// }
+/// ```
+///
+/// Notably this does not error on `foo<T>` implemented as `foo<const N: u8>` or
+/// `foo<const N: u8>` implemented as `foo<const N: u32>`. This is handled in
+/// [`compare_generic_param_kinds`]. This function also does not handle lifetime parameters
+fn compare_number_of_generics<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ impl_: &ty::AssocItem,
+ _impl_span: Span,
+ trait_: &ty::AssocItem,
+ trait_span: Option<Span>,
+) -> Result<(), ErrorGuaranteed> {
+ let trait_own_counts = tcx.generics_of(trait_.def_id).own_counts();
+ let impl_own_counts = tcx.generics_of(impl_.def_id).own_counts();
+
+ // This avoids us erroring on `foo<T>` implemented as `foo<const N: u8>` as this is implemented
+ // in `compare_generic_param_kinds` which will give a nicer error message than something like:
+ // "expected 1 type parameter, found 0 type parameters"
+ if (trait_own_counts.types + trait_own_counts.consts)
+ == (impl_own_counts.types + impl_own_counts.consts)
+ {
+ return Ok(());
+ }
+
+ let matchings = [
+ ("type", trait_own_counts.types, impl_own_counts.types),
+ ("const", trait_own_counts.consts, impl_own_counts.consts),
+ ];
+
+ let item_kind = assoc_item_kind_str(impl_);
+
+ let mut err_occurred = None;
+ for (kind, trait_count, impl_count) in matchings {
+ if impl_count != trait_count {
+ let arg_spans = |kind: ty::AssocKind, generics: &hir::Generics<'_>| {
+ let mut spans = generics
+ .params
+ .iter()
+ .filter(|p| match p.kind {
+ hir::GenericParamKind::Lifetime {
+ kind: hir::LifetimeParamKind::Elided,
+ } => {
+ // A fn can have an arbitrary number of extra elided lifetimes for the
+ // same signature.
+ !matches!(kind, ty::AssocKind::Fn)
+ }
+ _ => true,
+ })
+ .map(|p| p.span)
+ .collect::<Vec<Span>>();
+ if spans.is_empty() {
+ spans = vec![generics.span]
+ }
+ spans
+ };
+ let (trait_spans, impl_trait_spans) = if let Some(def_id) = trait_.def_id.as_local() {
+ let trait_item = tcx.hir().expect_trait_item(def_id);
+ let arg_spans: Vec<Span> = arg_spans(trait_.kind, trait_item.generics);
+ let impl_trait_spans: Vec<Span> = trait_item
+ .generics
+ .params
+ .iter()
+ .filter_map(|p| match p.kind {
+ GenericParamKind::Type { synthetic: true, .. } => Some(p.span),
+ _ => None,
+ })
+ .collect();
+ (Some(arg_spans), impl_trait_spans)
+ } else {
+ (trait_span.map(|s| vec![s]), vec![])
+ };
+
+ let impl_item = tcx.hir().expect_impl_item(impl_.def_id.expect_local());
+ let impl_item_impl_trait_spans: Vec<Span> = impl_item
+ .generics
+ .params
+ .iter()
+ .filter_map(|p| match p.kind {
+ GenericParamKind::Type { synthetic: true, .. } => Some(p.span),
+ _ => None,
+ })
+ .collect();
+ let spans = arg_spans(impl_.kind, impl_item.generics);
+ let span = spans.first().copied();
+
+ let mut err = tcx.sess.struct_span_err_with_code(
+ spans,
+ &format!(
+ "{} `{}` has {} {kind} parameter{} but its trait \
+ declaration has {} {kind} parameter{}",
+ item_kind,
+ trait_.name,
+ impl_count,
+ pluralize!(impl_count),
+ trait_count,
+ pluralize!(trait_count),
+ kind = kind,
+ ),
+ DiagnosticId::Error("E0049".into()),
+ );
+
+ let mut suffix = None;
+
+ if let Some(spans) = trait_spans {
+ let mut spans = spans.iter();
+ if let Some(span) = spans.next() {
+ err.span_label(
+ *span,
+ format!(
+ "expected {} {} parameter{}",
+ trait_count,
+ kind,
+ pluralize!(trait_count),
+ ),
+ );
+ }
+ for span in spans {
+ err.span_label(*span, "");
+ }
+ } else {
+ suffix = Some(format!(", expected {trait_count}"));
+ }
+
+ if let Some(span) = span {
+ err.span_label(
+ span,
+ format!(
+ "found {} {} parameter{}{}",
+ impl_count,
+ kind,
+ pluralize!(impl_count),
+ suffix.unwrap_or_else(String::new),
+ ),
+ );
+ }
+
+ for span in impl_trait_spans.iter().chain(impl_item_impl_trait_spans.iter()) {
+ err.span_label(*span, "`impl Trait` introduces an implicit type parameter");
+ }
+
+ let reported = err.emit();
+ err_occurred = Some(reported);
+ }
+ }
+
+ if let Some(reported) = err_occurred { Err(reported) } else { Ok(()) }
+}
+
+fn compare_number_of_method_arguments<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ impl_m: &ty::AssocItem,
+ impl_m_span: Span,
+ trait_m: &ty::AssocItem,
+ trait_item_span: Option<Span>,
+) -> Result<(), ErrorGuaranteed> {
+ let impl_m_fty = tcx.fn_sig(impl_m.def_id);
+ let trait_m_fty = tcx.fn_sig(trait_m.def_id);
+ let trait_number_args = trait_m_fty.inputs().skip_binder().len();
+ let impl_number_args = impl_m_fty.inputs().skip_binder().len();
+ if trait_number_args != impl_number_args {
+ let trait_span = if let Some(def_id) = trait_m.def_id.as_local() {
+ match tcx.hir().expect_trait_item(def_id).kind {
+ TraitItemKind::Fn(ref trait_m_sig, _) => {
+ let pos = if trait_number_args > 0 { trait_number_args - 1 } else { 0 };
+ if let Some(arg) = trait_m_sig.decl.inputs.get(pos) {
+ Some(if pos == 0 {
+ arg.span
+ } else {
+ arg.span.with_lo(trait_m_sig.decl.inputs[0].span.lo())
+ })
+ } else {
+ trait_item_span
+ }
+ }
+ _ => bug!("{:?} is not a method", impl_m),
+ }
+ } else {
+ trait_item_span
+ };
+ let impl_span = match tcx.hir().expect_impl_item(impl_m.def_id.expect_local()).kind {
+ ImplItemKind::Fn(ref impl_m_sig, _) => {
+ let pos = if impl_number_args > 0 { impl_number_args - 1 } else { 0 };
+ if let Some(arg) = impl_m_sig.decl.inputs.get(pos) {
+ if pos == 0 {
+ arg.span
+ } else {
+ arg.span.with_lo(impl_m_sig.decl.inputs[0].span.lo())
+ }
+ } else {
+ impl_m_span
+ }
+ }
+ _ => bug!("{:?} is not a method", impl_m),
+ };
+ let mut err = struct_span_err!(
+ tcx.sess,
+ impl_span,
+ E0050,
+ "method `{}` has {} but the declaration in trait `{}` has {}",
+ trait_m.name,
+ potentially_plural_count(impl_number_args, "parameter"),
+ tcx.def_path_str(trait_m.def_id),
+ trait_number_args
+ );
+ if let Some(trait_span) = trait_span {
+ err.span_label(
+ trait_span,
+ format!(
+ "trait requires {}",
+ potentially_plural_count(trait_number_args, "parameter")
+ ),
+ );
+ } else {
+ err.note_trait_signature(trait_m.name, trait_m.signature(tcx));
+ }
+ err.span_label(
+ impl_span,
+ format!(
+ "expected {}, found {}",
+ potentially_plural_count(trait_number_args, "parameter"),
+ impl_number_args
+ ),
+ );
+ let reported = err.emit();
+ return Err(reported);
+ }
+
+ Ok(())
+}
+
+fn compare_synthetic_generics<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ impl_m: &ty::AssocItem,
+ trait_m: &ty::AssocItem,
+) -> Result<(), ErrorGuaranteed> {
+ // FIXME(chrisvittal) Clean up this function, list of FIXME items:
+ // 1. Better messages for the span labels
+ // 2. Explanation as to what is going on
+ // If we get here, we already have the same number of generics, so the zip will
+ // be okay.
+ let mut error_found = None;
+ let impl_m_generics = tcx.generics_of(impl_m.def_id);
+ let trait_m_generics = tcx.generics_of(trait_m.def_id);
+ let impl_m_type_params = impl_m_generics.params.iter().filter_map(|param| match param.kind {
+ GenericParamDefKind::Type { synthetic, .. } => Some((param.def_id, synthetic)),
+ GenericParamDefKind::Lifetime | GenericParamDefKind::Const { .. } => None,
+ });
+ let trait_m_type_params = trait_m_generics.params.iter().filter_map(|param| match param.kind {
+ GenericParamDefKind::Type { synthetic, .. } => Some((param.def_id, synthetic)),
+ GenericParamDefKind::Lifetime | GenericParamDefKind::Const { .. } => None,
+ });
+ for ((impl_def_id, impl_synthetic), (trait_def_id, trait_synthetic)) in
+ iter::zip(impl_m_type_params, trait_m_type_params)
+ {
+ if impl_synthetic != trait_synthetic {
+ let impl_def_id = impl_def_id.expect_local();
+ let impl_span = tcx.def_span(impl_def_id);
+ let trait_span = tcx.def_span(trait_def_id);
+ let mut err = struct_span_err!(
+ tcx.sess,
+ impl_span,
+ E0643,
+ "method `{}` has incompatible signature for trait",
+ trait_m.name
+ );
+ err.span_label(trait_span, "declaration in trait here");
+ match (impl_synthetic, trait_synthetic) {
+ // The case where the impl method uses `impl Trait` but the trait method uses
+ // explicit generics
+ (true, false) => {
+ err.span_label(impl_span, "expected generic parameter, found `impl Trait`");
+ (|| {
+ // try taking the name from the trait impl
+ // FIXME: this is obviously suboptimal since the name can already be used
+ // as another generic argument
+ let new_name = tcx.opt_item_name(trait_def_id)?;
+ let trait_m = trait_m.def_id.as_local()?;
+ let trait_m = tcx.hir().expect_trait_item(trait_m);
+
+ let impl_m = impl_m.def_id.as_local()?;
+ let impl_m = tcx.hir().expect_impl_item(impl_m);
+
+ // in case there are no generics, take the spot between the function name
+ // and the opening paren of the argument list
+ let new_generics_span = tcx.def_ident_span(impl_def_id)?.shrink_to_hi();
+ // in case there are generics, just replace them
+ let generics_span =
+ impl_m.generics.span.substitute_dummy(new_generics_span);
+ // replace with the generics from the trait
+ let new_generics =
+ tcx.sess.source_map().span_to_snippet(trait_m.generics.span).ok()?;
+
+ err.multipart_suggestion(
+ "try changing the `impl Trait` argument to a generic parameter",
+ vec![
+ // replace `impl Trait` with `T`
+ (impl_span, new_name.to_string()),
+ // replace impl method generics with trait method generics
+ // This isn't quite right, as users might have changed the names
+ // of the generics, but it works for the common case
+ (generics_span, new_generics),
+ ],
+ Applicability::MaybeIncorrect,
+ );
+ Some(())
+ })();
+ }
+ // The case where the trait method uses `impl Trait`, but the impl method uses
+ // explicit generics.
+ (false, true) => {
+ err.span_label(impl_span, "expected `impl Trait`, found generic parameter");
+ (|| {
+ let impl_m = impl_m.def_id.as_local()?;
+ let impl_m = tcx.hir().expect_impl_item(impl_m);
+ let input_tys = match impl_m.kind {
+ hir::ImplItemKind::Fn(ref sig, _) => sig.decl.inputs,
+ _ => unreachable!(),
+ };
+ struct Visitor(Option<Span>, hir::def_id::LocalDefId);
+ impl<'v> intravisit::Visitor<'v> for Visitor {
+ fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) {
+ intravisit::walk_ty(self, ty);
+ if let hir::TyKind::Path(hir::QPath::Resolved(None, ref path)) =
+ ty.kind
+ && let Res::Def(DefKind::TyParam, def_id) = path.res
+ && def_id == self.1.to_def_id()
+ {
+ self.0 = Some(ty.span);
+ }
+ }
+ }
+ let mut visitor = Visitor(None, impl_def_id);
+ for ty in input_tys {
+ intravisit::Visitor::visit_ty(&mut visitor, ty);
+ }
+ let span = visitor.0?;
+
+ let bounds = impl_m.generics.bounds_for_param(impl_def_id).next()?.bounds;
+ let bounds = bounds.first()?.span().to(bounds.last()?.span());
+ let bounds = tcx.sess.source_map().span_to_snippet(bounds).ok()?;
+
+ err.multipart_suggestion(
+ "try removing the generic parameter and using `impl Trait` instead",
+ vec![
+ // delete generic parameters
+ (impl_m.generics.span, String::new()),
+ // replace param usage with `impl Trait`
+ (span, format!("impl {bounds}")),
+ ],
+ Applicability::MaybeIncorrect,
+ );
+ Some(())
+ })();
+ }
+ _ => unreachable!(),
+ }
+ let reported = err.emit();
+ error_found = Some(reported);
+ }
+ }
+ if let Some(reported) = error_found { Err(reported) } else { Ok(()) }
+}
+
+/// Checks that all parameters in the generics of a given assoc item in a trait impl have
+/// the same kind as the respective generic parameter in the trait def.
+///
+/// For example all 4 errors in the following code are emitted here:
+/// ```
+/// trait Foo {
+/// fn foo<const N: u8>();
+/// type bar<const N: u8>;
+/// fn baz<const N: u32>();
+/// type blah<T>;
+/// }
+///
+/// impl Foo for () {
+/// fn foo<const N: u64>() {}
+/// //~^ error
+/// type bar<const N: u64> {}
+/// //~^ error
+/// fn baz<T>() {}
+/// //~^ error
+/// type blah<const N: i64> = u32;
+/// //~^ error
+/// }
+/// ```
+///
+/// This function does not handle lifetime parameters
+fn compare_generic_param_kinds<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ impl_item: &ty::AssocItem,
+ trait_item: &ty::AssocItem,
+) -> Result<(), ErrorGuaranteed> {
+ assert_eq!(impl_item.kind, trait_item.kind);
+
+ let ty_const_params_of = |def_id| {
+ tcx.generics_of(def_id).params.iter().filter(|param| {
+ matches!(
+ param.kind,
+ GenericParamDefKind::Const { .. } | GenericParamDefKind::Type { .. }
+ )
+ })
+ };
+
+ for (param_impl, param_trait) in
+ iter::zip(ty_const_params_of(impl_item.def_id), ty_const_params_of(trait_item.def_id))
+ {
+ use GenericParamDefKind::*;
+ if match (&param_impl.kind, &param_trait.kind) {
+ (Const { .. }, Const { .. })
+ if tcx.type_of(param_impl.def_id) != tcx.type_of(param_trait.def_id) =>
+ {
+ true
+ }
+ (Const { .. }, Type { .. }) | (Type { .. }, Const { .. }) => true,
+ // this is exhaustive so that anyone adding new generic param kinds knows
+ // to make sure this error is reported for them.
+ (Const { .. }, Const { .. }) | (Type { .. }, Type { .. }) => false,
+ (Lifetime { .. }, _) | (_, Lifetime { .. }) => unreachable!(),
+ } {
+ let param_impl_span = tcx.def_span(param_impl.def_id);
+ let param_trait_span = tcx.def_span(param_trait.def_id);
+
+ let mut err = struct_span_err!(
+ tcx.sess,
+ param_impl_span,
+ E0053,
+ "{} `{}` has an incompatible generic parameter for trait `{}`",
+ assoc_item_kind_str(&impl_item),
+ trait_item.name,
+ &tcx.def_path_str(tcx.parent(trait_item.def_id))
+ );
+
+ let make_param_message = |prefix: &str, param: &ty::GenericParamDef| match param.kind {
+ Const { .. } => {
+ format!("{} const parameter of type `{}`", prefix, tcx.type_of(param.def_id))
+ }
+ Type { .. } => format!("{} type parameter", prefix),
+ Lifetime { .. } => unreachable!(),
+ };
+
+ let trait_header_span = tcx.def_ident_span(tcx.parent(trait_item.def_id)).unwrap();
+ err.span_label(trait_header_span, "");
+ err.span_label(param_trait_span, make_param_message("expected", param_trait));
+
+ let impl_header_span = tcx.def_span(tcx.parent(impl_item.def_id));
+ err.span_label(impl_header_span, "");
+ err.span_label(param_impl_span, make_param_message("found", param_impl));
+
+ let reported = err.emit();
+ return Err(reported);
+ }
+ }
+
+ Ok(())
+}
+
+/// Use `tcx.compare_assoc_const_impl_item_with_trait_item` instead
+pub(crate) fn raw_compare_const_impl<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ (impl_const_item_def, trait_const_item_def): (LocalDefId, DefId),
+) -> Result<(), ErrorGuaranteed> {
+ let impl_const_item = tcx.associated_item(impl_const_item_def);
+ let trait_const_item = tcx.associated_item(trait_const_item_def);
+ let impl_trait_ref = tcx.impl_trait_ref(impl_const_item.container_id(tcx)).unwrap();
+ debug!("compare_const_impl(impl_trait_ref={:?})", impl_trait_ref);
+
+ let impl_c_span = tcx.def_span(impl_const_item_def.to_def_id());
+
+ let infcx = tcx.infer_ctxt().build();
+ let param_env = tcx.param_env(impl_const_item_def.to_def_id());
+ let ocx = ObligationCtxt::new(&infcx);
+
+ // The below is for the most part highly similar to the procedure
+ // for methods above. It is simpler in many respects, especially
+ // because we shouldn't really have to deal with lifetimes or
+ // predicates. In fact some of this should probably be put into
+ // shared functions because of DRY violations...
+ let trait_to_impl_substs = impl_trait_ref.substs;
+
+ // Create a parameter environment that represents the implementation's
+ // method.
+ let impl_c_hir_id = tcx.hir().local_def_id_to_hir_id(impl_const_item_def);
+
+ // Compute placeholder form of impl and trait const tys.
+ let impl_ty = tcx.type_of(impl_const_item_def.to_def_id());
+ let trait_ty = tcx.bound_type_of(trait_const_item_def).subst(tcx, trait_to_impl_substs);
+ let mut cause = ObligationCause::new(
+ impl_c_span,
+ impl_c_hir_id,
+ ObligationCauseCode::CompareImplItemObligation {
+ impl_item_def_id: impl_const_item_def,
+ trait_item_def_id: trait_const_item_def,
+ kind: impl_const_item.kind,
+ },
+ );
+
+ // There is no "body" here, so just pass dummy id.
+ let impl_ty = ocx.normalize(cause.clone(), param_env, impl_ty);
+
+ debug!("compare_const_impl: impl_ty={:?}", impl_ty);
+
+ let trait_ty = ocx.normalize(cause.clone(), param_env, trait_ty);
+
+ debug!("compare_const_impl: trait_ty={:?}", trait_ty);
+
+ let err = infcx
+ .at(&cause, param_env)
+ .sup(trait_ty, impl_ty)
+ .map(|ok| ocx.register_infer_ok_obligations(ok));
+
+ if let Err(terr) = err {
+ debug!(
+ "checking associated const for compatibility: impl ty {:?}, trait ty {:?}",
+ impl_ty, trait_ty
+ );
+
+ // Locate the Span containing just the type of the offending impl
+ match tcx.hir().expect_impl_item(impl_const_item_def).kind {
+ ImplItemKind::Const(ref ty, _) => cause.span = ty.span,
+ _ => bug!("{:?} is not a impl const", impl_const_item),
+ }
+
+ let mut diag = struct_span_err!(
+ tcx.sess,
+ cause.span,
+ E0326,
+ "implemented const `{}` has an incompatible type for trait",
+ trait_const_item.name
+ );
+
+ let trait_c_span = trait_const_item_def.as_local().map(|trait_c_def_id| {
+ // Add a label to the Span containing just the type of the const
+ match tcx.hir().expect_trait_item(trait_c_def_id).kind {
+ TraitItemKind::Const(ref ty, _) => ty.span,
+ _ => bug!("{:?} is not a trait const", trait_const_item),
+ }
+ });
+
+ infcx.err_ctxt().note_type_err(
+ &mut diag,
+ &cause,
+ trait_c_span.map(|span| (span, "type in trait".to_owned())),
+ Some(infer::ValuePairs::Terms(ExpectedFound {
+ expected: trait_ty.into(),
+ found: impl_ty.into(),
+ })),
+ terr,
+ false,
+ false,
+ );
+ return Err(diag.emit());
+ };
+
+ // Check that all obligations are satisfied by the implementation's
+ // version.
+ let errors = ocx.select_all_or_error();
+ if !errors.is_empty() {
+ return Err(infcx.err_ctxt().report_fulfillment_errors(&errors, None, false));
+ }
+
+ // FIXME return `ErrorReported` if region obligations error?
+ let outlives_environment = OutlivesEnvironment::new(param_env);
+ infcx.check_region_obligations_and_report_errors(impl_const_item_def, &outlives_environment);
+ Ok(())
+}
+
+pub(crate) fn compare_ty_impl<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ impl_ty: &ty::AssocItem,
+ impl_ty_span: Span,
+ trait_ty: &ty::AssocItem,
+ impl_trait_ref: ty::TraitRef<'tcx>,
+ trait_item_span: Option<Span>,
+) {
+ debug!("compare_impl_type(impl_trait_ref={:?})", impl_trait_ref);
+
+ let _: Result<(), ErrorGuaranteed> = (|| {
+ compare_number_of_generics(tcx, impl_ty, impl_ty_span, trait_ty, trait_item_span)?;
+
+ compare_generic_param_kinds(tcx, impl_ty, trait_ty)?;
+
+ let sp = tcx.def_span(impl_ty.def_id);
+ compare_type_predicate_entailment(tcx, impl_ty, sp, trait_ty, impl_trait_ref)?;
+
+ check_type_bounds(tcx, trait_ty, impl_ty, impl_ty_span, impl_trait_ref)
+ })();
+}
+
+/// The equivalent of [compare_predicate_entailment], but for associated types
+/// instead of associated functions.
+fn compare_type_predicate_entailment<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ impl_ty: &ty::AssocItem,
+ impl_ty_span: Span,
+ trait_ty: &ty::AssocItem,
+ impl_trait_ref: ty::TraitRef<'tcx>,
+) -> Result<(), ErrorGuaranteed> {
+ let impl_substs = InternalSubsts::identity_for_item(tcx, impl_ty.def_id);
+ let trait_to_impl_substs =
+ impl_substs.rebase_onto(tcx, impl_ty.container_id(tcx), impl_trait_ref.substs);
+
+ let impl_ty_generics = tcx.generics_of(impl_ty.def_id);
+ let trait_ty_generics = tcx.generics_of(trait_ty.def_id);
+ let impl_ty_predicates = tcx.predicates_of(impl_ty.def_id);
+ let trait_ty_predicates = tcx.predicates_of(trait_ty.def_id);
+
+ check_region_bounds_on_impl_item(
+ tcx,
+ impl_ty,
+ trait_ty,
+ &trait_ty_generics,
+ &impl_ty_generics,
+ )?;
+
+ let impl_ty_own_bounds = impl_ty_predicates.instantiate_own(tcx, impl_substs);
+
+ if impl_ty_own_bounds.is_empty() {
+ // Nothing to check.
+ return Ok(());
+ }
+
+ // This `HirId` should be used for the `body_id` field on each
+ // `ObligationCause` (and the `FnCtxt`). This is what
+ // `regionck_item` expects.
+ let impl_ty_hir_id = tcx.hir().local_def_id_to_hir_id(impl_ty.def_id.expect_local());
+ debug!("compare_type_predicate_entailment: trait_to_impl_substs={:?}", trait_to_impl_substs);
+
+ // The predicates declared by the impl definition, the trait and the
+ // associated type in the trait are assumed.
+ let impl_predicates = tcx.predicates_of(impl_ty_predicates.parent.unwrap());
+ let mut hybrid_preds = impl_predicates.instantiate_identity(tcx);
+ hybrid_preds
+ .predicates
+ .extend(trait_ty_predicates.instantiate_own(tcx, trait_to_impl_substs).predicates);
+
+ debug!("compare_type_predicate_entailment: bounds={:?}", hybrid_preds);
+
+ let normalize_cause = traits::ObligationCause::misc(impl_ty_span, impl_ty_hir_id);
+ let param_env = ty::ParamEnv::new(
+ tcx.intern_predicates(&hybrid_preds.predicates),
+ Reveal::UserFacing,
+ hir::Constness::NotConst,
+ );
+ let param_env = traits::normalize_param_env_or_error(tcx, param_env, normalize_cause);
+ let infcx = tcx.infer_ctxt().build();
+ let ocx = ObligationCtxt::new(&infcx);
+
+ debug!("compare_type_predicate_entailment: caller_bounds={:?}", param_env.caller_bounds());
+
+ let mut selcx = traits::SelectionContext::new(&infcx);
+
+ assert_eq!(impl_ty_own_bounds.predicates.len(), impl_ty_own_bounds.spans.len());
+ for (span, predicate) in std::iter::zip(impl_ty_own_bounds.spans, impl_ty_own_bounds.predicates)
+ {
+ let cause = ObligationCause::misc(span, impl_ty_hir_id);
+ let traits::Normalized { value: predicate, obligations } =
+ traits::normalize(&mut selcx, param_env, cause, predicate);
+
+ let cause = ObligationCause::new(
+ span,
+ impl_ty_hir_id,
+ ObligationCauseCode::CompareImplItemObligation {
+ impl_item_def_id: impl_ty.def_id.expect_local(),
+ trait_item_def_id: trait_ty.def_id,
+ kind: impl_ty.kind,
+ },
+ );
+ ocx.register_obligations(obligations);
+ ocx.register_obligation(traits::Obligation::new(cause, param_env, predicate));
+ }
+
+ // Check that all obligations are satisfied by the implementation's
+ // version.
+ let errors = ocx.select_all_or_error();
+ if !errors.is_empty() {
+ let reported = infcx.err_ctxt().report_fulfillment_errors(&errors, None, false);
+ return Err(reported);
+ }
+
+ // Finally, resolve all regions. This catches wily misuses of
+ // lifetime parameters.
+ let outlives_environment = OutlivesEnvironment::new(param_env);
+ infcx.check_region_obligations_and_report_errors(
+ impl_ty.def_id.expect_local(),
+ &outlives_environment,
+ );
+
+ Ok(())
+}
+
+/// Validate that `ProjectionCandidate`s created for this associated type will
+/// be valid.
+///
+/// Usually given
+///
+/// trait X { type Y: Copy } impl X for T { type Y = S; }
+///
+/// We are able to normalize `<T as X>::U` to `S`, and so when we check the
+/// impl is well-formed we have to prove `S: Copy`.
+///
+/// For default associated types the normalization is not possible (the value
+/// from the impl could be overridden). We also can't normalize generic
+/// associated types (yet) because they contain bound parameters.
+#[instrument(level = "debug", skip(tcx))]
+pub fn check_type_bounds<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ trait_ty: &ty::AssocItem,
+ impl_ty: &ty::AssocItem,
+ impl_ty_span: Span,
+ impl_trait_ref: ty::TraitRef<'tcx>,
+) -> Result<(), ErrorGuaranteed> {
+ // Given
+ //
+ // impl<A, B> Foo<u32> for (A, B) {
+ // type Bar<C> =...
+ // }
+ //
+ // - `impl_trait_ref` would be `<(A, B) as Foo<u32>>
+ // - `impl_ty_substs` would be `[A, B, ^0.0]` (`^0.0` here is the bound var with db 0 and index 0)
+ // - `rebased_substs` would be `[(A, B), u32, ^0.0]`, combining the substs from
+ // the *trait* with the generic associated type parameters (as bound vars).
+ //
+ // A note regarding the use of bound vars here:
+ // Imagine as an example
+ // ```
+ // trait Family {
+ // type Member<C: Eq>;
+ // }
+ //
+ // impl Family for VecFamily {
+ // type Member<C: Eq> = i32;
+ // }
+ // ```
+ // Here, we would generate
+ // ```notrust
+ // forall<C> { Normalize(<VecFamily as Family>::Member<C> => i32) }
+ // ```
+ // when we really would like to generate
+ // ```notrust
+ // forall<C> { Normalize(<VecFamily as Family>::Member<C> => i32) :- Implemented(C: Eq) }
+ // ```
+ // But, this is probably fine, because although the first clause can be used with types C that
+ // do not implement Eq, for it to cause some kind of problem, there would have to be a
+ // VecFamily::Member<X> for some type X where !(X: Eq), that appears in the value of type
+ // Member<C: Eq> = .... That type would fail a well-formedness check that we ought to be doing
+ // elsewhere, which would check that any <T as Family>::Member<X> meets the bounds declared in
+ // the trait (notably, that X: Eq and T: Family).
+ let defs: &ty::Generics = tcx.generics_of(impl_ty.def_id);
+ let mut substs = smallvec::SmallVec::with_capacity(defs.count());
+ if let Some(def_id) = defs.parent {
+ let parent_defs = tcx.generics_of(def_id);
+ InternalSubsts::fill_item(&mut substs, tcx, parent_defs, &mut |param, _| {
+ tcx.mk_param_from_def(param)
+ });
+ }
+ let mut bound_vars: smallvec::SmallVec<[ty::BoundVariableKind; 8]> =
+ smallvec::SmallVec::with_capacity(defs.count());
+ InternalSubsts::fill_single(&mut substs, defs, &mut |param, _| match param.kind {
+ GenericParamDefKind::Type { .. } => {
+ let kind = ty::BoundTyKind::Param(param.name);
+ let bound_var = ty::BoundVariableKind::Ty(kind);
+ bound_vars.push(bound_var);
+ tcx.mk_ty(ty::Bound(
+ ty::INNERMOST,
+ ty::BoundTy { var: ty::BoundVar::from_usize(bound_vars.len() - 1), kind },
+ ))
+ .into()
+ }
+ GenericParamDefKind::Lifetime => {
+ let kind = ty::BoundRegionKind::BrNamed(param.def_id, param.name);
+ let bound_var = ty::BoundVariableKind::Region(kind);
+ bound_vars.push(bound_var);
+ tcx.mk_region(ty::ReLateBound(
+ ty::INNERMOST,
+ ty::BoundRegion { var: ty::BoundVar::from_usize(bound_vars.len() - 1), kind },
+ ))
+ .into()
+ }
+ GenericParamDefKind::Const { .. } => {
+ let bound_var = ty::BoundVariableKind::Const;
+ bound_vars.push(bound_var);
+ tcx.mk_const(ty::ConstS {
+ ty: tcx.type_of(param.def_id),
+ kind: ty::ConstKind::Bound(
+ ty::INNERMOST,
+ ty::BoundVar::from_usize(bound_vars.len() - 1),
+ ),
+ })
+ .into()
+ }
+ });
+ let bound_vars = tcx.mk_bound_variable_kinds(bound_vars.into_iter());
+ let impl_ty_substs = tcx.intern_substs(&substs);
+ let container_id = impl_ty.container_id(tcx);
+
+ let rebased_substs = impl_ty_substs.rebase_onto(tcx, container_id, impl_trait_ref.substs);
+ let impl_ty_value = tcx.type_of(impl_ty.def_id);
+
+ let param_env = tcx.param_env(impl_ty.def_id);
+
+ // When checking something like
+ //
+ // trait X { type Y: PartialEq<<Self as X>::Y> }
+ // impl X for T { default type Y = S; }
+ //
+ // We will have to prove the bound S: PartialEq<<T as X>::Y>. In this case
+ // we want <T as X>::Y to normalize to S. This is valid because we are
+ // checking the default value specifically here. Add this equality to the
+ // ParamEnv for normalization specifically.
+ let normalize_param_env = {
+ let mut predicates = param_env.caller_bounds().iter().collect::<Vec<_>>();
+ match impl_ty_value.kind() {
+ ty::Projection(proj)
+ if proj.item_def_id == trait_ty.def_id && proj.substs == rebased_substs =>
+ {
+ // Don't include this predicate if the projected type is
+ // exactly the same as the projection. This can occur in
+ // (somewhat dubious) code like this:
+ //
+ // impl<T> X for T where T: X { type Y = <T as X>::Y; }
+ }
+ _ => predicates.push(
+ ty::Binder::bind_with_vars(
+ ty::ProjectionPredicate {
+ projection_ty: ty::ProjectionTy {
+ item_def_id: trait_ty.def_id,
+ substs: rebased_substs,
+ },
+ term: impl_ty_value.into(),
+ },
+ bound_vars,
+ )
+ .to_predicate(tcx),
+ ),
+ };
+ ty::ParamEnv::new(
+ tcx.intern_predicates(&predicates),
+ Reveal::UserFacing,
+ param_env.constness(),
+ )
+ };
+ debug!(?normalize_param_env);
+
+ let impl_ty_hir_id = tcx.hir().local_def_id_to_hir_id(impl_ty.def_id.expect_local());
+ let impl_ty_substs = InternalSubsts::identity_for_item(tcx, impl_ty.def_id);
+ let rebased_substs = impl_ty_substs.rebase_onto(tcx, container_id, impl_trait_ref.substs);
+
+ let infcx = tcx.infer_ctxt().build();
+ let ocx = ObligationCtxt::new(&infcx);
+
+ let assumed_wf_types =
+ ocx.assumed_wf_types(param_env, impl_ty_span, impl_ty.def_id.expect_local());
+
+ let mut selcx = traits::SelectionContext::new(&infcx);
+ let normalize_cause = ObligationCause::new(
+ impl_ty_span,
+ impl_ty_hir_id,
+ ObligationCauseCode::CheckAssociatedTypeBounds {
+ impl_item_def_id: impl_ty.def_id.expect_local(),
+ trait_item_def_id: trait_ty.def_id,
+ },
+ );
+ let mk_cause = |span: Span| {
+ let code = if span.is_dummy() {
+ traits::ItemObligation(trait_ty.def_id)
+ } else {
+ traits::BindingObligation(trait_ty.def_id, span)
+ };
+ ObligationCause::new(impl_ty_span, impl_ty_hir_id, code)
+ };
+
+ let obligations = tcx
+ .bound_explicit_item_bounds(trait_ty.def_id)
+ .subst_iter_copied(tcx, rebased_substs)
+ .map(|(concrete_ty_bound, span)| {
+ debug!("check_type_bounds: concrete_ty_bound = {:?}", concrete_ty_bound);
+ traits::Obligation::new(mk_cause(span), param_env, concrete_ty_bound)
+ })
+ .collect();
+ debug!("check_type_bounds: item_bounds={:?}", obligations);
+
+ for mut obligation in util::elaborate_obligations(tcx, obligations) {
+ let traits::Normalized { value: normalized_predicate, obligations } = traits::normalize(
+ &mut selcx,
+ normalize_param_env,
+ normalize_cause.clone(),
+ obligation.predicate,
+ );
+ debug!("compare_projection_bounds: normalized predicate = {:?}", normalized_predicate);
+ obligation.predicate = normalized_predicate;
+
+ ocx.register_obligations(obligations);
+ ocx.register_obligation(obligation);
+ }
+ // Check that all obligations are satisfied by the implementation's
+ // version.
+ let errors = ocx.select_all_or_error();
+ if !errors.is_empty() {
+ let reported = infcx.err_ctxt().report_fulfillment_errors(&errors, None, false);
+ return Err(reported);
+ }
+
+ // Finally, resolve all regions. This catches wily misuses of
+ // lifetime parameters.
+ let implied_bounds = infcx.implied_bounds_tys(param_env, impl_ty_hir_id, assumed_wf_types);
+ let outlives_environment =
+ OutlivesEnvironment::with_bounds(param_env, Some(&infcx), implied_bounds);
+
+ infcx.check_region_obligations_and_report_errors(
+ impl_ty.def_id.expect_local(),
+ &outlives_environment,
+ );
+
+ let constraints = infcx.inner.borrow_mut().opaque_type_storage.take_opaque_types();
+ for (key, value) in constraints {
+ infcx
+ .err_ctxt()
+ .report_mismatched_types(
+ &ObligationCause::misc(
+ value.hidden_type.span,
+ tcx.hir().local_def_id_to_hir_id(impl_ty.def_id.expect_local()),
+ ),
+ tcx.mk_opaque(key.def_id.to_def_id(), key.substs),
+ value.hidden_type.ty,
+ TypeError::Mismatch,
+ )
+ .emit();
+ }
+
+ Ok(())
+}
+
+fn assoc_item_kind_str(impl_item: &ty::AssocItem) -> &'static str {
+ match impl_item.kind {
+ ty::AssocKind::Const => "const",
+ ty::AssocKind::Fn => "method",
+ ty::AssocKind::Type => "type",
+ }
+}
diff --git a/compiler/rustc_typeck/src/check/dropck.rs b/compiler/rustc_hir_analysis/src/check/dropck.rs
index 321064ec0..a74016e22 100644
--- a/compiler/rustc_typeck/src/check/dropck.rs
+++ b/compiler/rustc_hir_analysis/src/check/dropck.rs
@@ -1,4 +1,4 @@
-// FIXME(@lcnr): Move this module out of `rustc_typeck`.
+// FIXME(@lcnr): Move this module out of `rustc_hir_analysis`.
//
// We don't do any drop checking during hir typeck.
use crate::hir::def_id::{DefId, LocalDefId};
@@ -144,6 +144,8 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>(
let assumptions_in_impl_context = generic_assumptions.instantiate(tcx, &self_to_impl_substs);
let assumptions_in_impl_context = assumptions_in_impl_context.predicates;
+ debug!(?assumptions_in_impl_context, ?dtor_predicates.predicates);
+
let self_param_env = tcx.param_env(self_type_did);
// An earlier version of this code attempted to do this checking
@@ -182,13 +184,7 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>(
let p = p.kind();
match (predicate.skip_binder(), p.skip_binder()) {
(ty::PredicateKind::Trait(a), ty::PredicateKind::Trait(b)) => {
- // Since struct predicates cannot have ~const, project the impl predicate
- // onto one that ignores the constness. This is equivalent to saying that
- // we match a `Trait` bound on the struct with a `Trait` or `~const Trait`
- // in the impl.
- let non_const_a =
- ty::TraitPredicate { constness: ty::BoundConstness::NotConst, ..a };
- relator.relate(predicate.rebind(non_const_a), p.rebind(b)).is_ok()
+ relator.relate(predicate.rebind(a), p.rebind(b)).is_ok()
}
(ty::PredicateKind::Projection(a), ty::PredicateKind::Projection(b)) => {
relator.relate(predicate.rebind(a), p.rebind(b)).is_ok()
@@ -196,7 +192,7 @@ fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>(
(
ty::PredicateKind::ConstEvaluatable(a),
ty::PredicateKind::ConstEvaluatable(b),
- ) => tcx.try_unify_abstract_consts(self_param_env.and((a, b))),
+ ) => relator.relate(predicate.rebind(a), predicate.rebind(b)).is_ok(),
(
ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(ty_a, lt_a)),
ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(ty_b, lt_b)),
diff --git a/compiler/rustc_typeck/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs
index 3f2a0da8d..609095c9c 100644
--- a/compiler/rustc_typeck/src/check/intrinsic.rs
+++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs
@@ -7,10 +7,10 @@ use crate::errors::{
};
use crate::require_same_types;
-use rustc_errors::struct_span_err;
+use hir::def_id::DefId;
+use rustc_errors::{struct_span_err, DiagnosticMessage};
use rustc_hir as hir;
use rustc_middle::traits::{ObligationCause, ObligationCauseCode};
-use rustc_middle::ty::subst::Subst;
use rustc_middle::ty::{self, TyCtxt};
use rustc_span::symbol::{kw, sym, Symbol};
use rustc_target::spec::abi::Abi;
@@ -26,7 +26,7 @@ fn equate_intrinsic_type<'tcx>(
) {
let (own_counts, span) = match &it.kind {
hir::ForeignItemKind::Fn(.., generics) => {
- let own_counts = tcx.generics_of(it.def_id.to_def_id()).own_counts();
+ let own_counts = tcx.generics_of(it.owner_id.to_def_id()).own_counts();
(own_counts, generics.span)
}
_ => {
@@ -57,18 +57,25 @@ fn equate_intrinsic_type<'tcx>(
{
let fty = tcx.mk_fn_ptr(sig);
let cause = ObligationCause::new(it.span, it.hir_id(), ObligationCauseCode::IntrinsicType);
- require_same_types(tcx, &cause, tcx.mk_fn_ptr(tcx.fn_sig(it.def_id)), fty);
+ require_same_types(tcx, &cause, tcx.mk_fn_ptr(tcx.fn_sig(it.owner_id)), fty);
}
}
/// Returns the unsafety of the given intrinsic.
-pub fn intrinsic_operation_unsafety(intrinsic: Symbol) -> hir::Unsafety {
- match intrinsic {
+pub fn intrinsic_operation_unsafety(tcx: TyCtxt<'_>, intrinsic_id: DefId) -> hir::Unsafety {
+ let has_safe_attr = match tcx.has_attr(intrinsic_id, sym::rustc_safe_intrinsic) {
+ true => hir::Unsafety::Normal,
+ false => hir::Unsafety::Unsafe,
+ };
+ let is_in_list = match tcx.item_name(intrinsic_id) {
// When adding a new intrinsic to this list,
// it's usually worth updating that intrinsic's documentation
// to note that it's safe to call, since
// safe extern fns are otherwise unprecedented.
sym::abort
+ | sym::assert_inhabited
+ | sym::assert_zero_valid
+ | sym::assert_uninit_valid
| sym::size_of
| sym::min_align_of
| sym::needs_drop
@@ -92,8 +99,7 @@ pub fn intrinsic_operation_unsafety(intrinsic: Symbol) -> hir::Unsafety {
| sym::type_id
| sym::likely
| sym::unlikely
- | sym::ptr_guaranteed_eq
- | sym::ptr_guaranteed_ne
+ | sym::ptr_guaranteed_cmp
| sym::minnumf32
| sym::minnumf64
| sym::maxnumf32
@@ -102,16 +108,29 @@ pub fn intrinsic_operation_unsafety(intrinsic: Symbol) -> hir::Unsafety {
| sym::type_name
| sym::forget
| sym::black_box
- | sym::variant_count => hir::Unsafety::Normal,
+ | sym::variant_count
+ | sym::ptr_mask => hir::Unsafety::Normal,
_ => hir::Unsafety::Unsafe,
+ };
+
+ if has_safe_attr != is_in_list {
+ tcx.sess.struct_span_err(
+ tcx.def_span(intrinsic_id),
+ DiagnosticMessage::Str(format!(
+ "intrinsic safety mismatch between list of intrinsics within the compiler and core library intrinsics for intrinsic `{}`",
+ tcx.item_name(intrinsic_id)
+ ))).emit();
}
+
+ is_in_list
}
/// Remember to add all intrinsics here, in `compiler/rustc_codegen_llvm/src/intrinsic.rs`,
/// and in `library/core/src/intrinsics.rs`.
pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) {
let param = |n| tcx.mk_ty_param(n, Symbol::intern(&format!("P{}", n)));
- let intrinsic_name = tcx.item_name(it.def_id.to_def_id());
+ let intrinsic_id = it.owner_id.to_def_id();
+ let intrinsic_name = tcx.item_name(intrinsic_id);
let name_str = intrinsic_name.as_str();
let bound_vars = tcx.mk_bound_variable_kinds(
@@ -158,7 +177,7 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) {
};
(n_tps, 0, inputs, output, hir::Unsafety::Unsafe)
} else {
- let unsafety = intrinsic_operation_unsafety(intrinsic_name);
+ let unsafety = intrinsic_operation_unsafety(tcx, intrinsic_id);
let (n_tps, inputs, output) = match intrinsic_name {
sym::abort => (0, Vec::new(), tcx.types.never),
sym::unreachable => (0, Vec::new(), tcx.types.never),
@@ -200,6 +219,15 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) {
],
tcx.mk_ptr(ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Not }),
),
+ sym::ptr_mask => (
+ 1,
+ vec![
+ tcx.mk_ptr(ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Not }),
+ tcx.types.usize,
+ ],
+ tcx.mk_ptr(ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Not }),
+ ),
+
sym::copy | sym::copy_nonoverlapping => (
1,
vec![
@@ -289,8 +317,8 @@ pub fn check_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) {
(1, vec![param(0), param(0)], tcx.intern_tup(&[param(0), tcx.types.bool]))
}
- sym::ptr_guaranteed_eq | sym::ptr_guaranteed_ne => {
- (1, vec![tcx.mk_imm_ptr(param(0)), tcx.mk_imm_ptr(param(0))], tcx.types.bool)
+ sym::ptr_guaranteed_cmp => {
+ (1, vec![tcx.mk_imm_ptr(param(0)), tcx.mk_imm_ptr(param(0))], tcx.types.u8)
}
sym::const_allocate => {
@@ -465,7 +493,11 @@ pub fn check_platform_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>)
sym::simd_scatter => (3, vec![param(0), param(1), param(2)], tcx.mk_unit()),
sym::simd_insert => (2, vec![param(0), tcx.types.u32, param(1)], param(0)),
sym::simd_extract => (2, vec![param(0), tcx.types.u32], param(1)),
- sym::simd_cast | sym::simd_as => (2, vec![param(0)], param(1)),
+ sym::simd_cast
+ | sym::simd_as
+ | sym::simd_cast_ptr
+ | sym::simd_expose_addr
+ | sym::simd_from_exposed_addr => (2, vec![param(0)], param(1)),
sym::simd_bitmask => (2, vec![param(0)], param(1)),
sym::simd_select | sym::simd_select_bitmask => {
(2, vec![param(0), param(1), param(1)], param(1))
diff --git a/compiler/rustc_hir_analysis/src/check/intrinsicck.rs b/compiler/rustc_hir_analysis/src/check/intrinsicck.rs
new file mode 100644
index 000000000..17c4d0d48
--- /dev/null
+++ b/compiler/rustc_hir_analysis/src/check/intrinsicck.rs
@@ -0,0 +1,437 @@
+use rustc_ast::InlineAsmTemplatePiece;
+use rustc_data_structures::fx::FxHashSet;
+use rustc_hir as hir;
+use rustc_middle::ty::{self, Article, FloatTy, IntTy, Ty, TyCtxt, TypeVisitable, UintTy};
+use rustc_session::lint;
+use rustc_span::{Symbol, DUMMY_SP};
+use rustc_target::asm::{InlineAsmReg, InlineAsmRegClass, InlineAsmRegOrRegClass, InlineAsmType};
+
+pub struct InlineAsmCtxt<'a, 'tcx> {
+ tcx: TyCtxt<'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+ get_operand_ty: Box<dyn Fn(&'tcx hir::Expr<'tcx>) -> Ty<'tcx> + 'a>,
+}
+
+impl<'a, 'tcx> InlineAsmCtxt<'a, 'tcx> {
+ pub fn new_global_asm(tcx: TyCtxt<'tcx>) -> Self {
+ InlineAsmCtxt {
+ tcx,
+ param_env: ty::ParamEnv::empty(),
+ get_operand_ty: Box::new(|e| bug!("asm operand in global asm: {e:?}")),
+ }
+ }
+
+ pub fn new_in_fn(
+ tcx: TyCtxt<'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+ get_operand_ty: impl Fn(&'tcx hir::Expr<'tcx>) -> Ty<'tcx> + 'a,
+ ) -> Self {
+ InlineAsmCtxt { tcx, param_env, get_operand_ty: Box::new(get_operand_ty) }
+ }
+
+ // FIXME(compiler-errors): This could use `<$ty as Pointee>::Metadata == ()`
+ fn is_thin_ptr_ty(&self, ty: Ty<'tcx>) -> bool {
+ // Type still may have region variables, but `Sized` does not depend
+ // on those, so just erase them before querying.
+ if ty.is_sized(self.tcx, self.param_env) {
+ return true;
+ }
+ if let ty::Foreign(..) = ty.kind() {
+ return true;
+ }
+ false
+ }
+
+ fn check_asm_operand_type(
+ &self,
+ idx: usize,
+ reg: InlineAsmRegOrRegClass,
+ expr: &'tcx hir::Expr<'tcx>,
+ template: &[InlineAsmTemplatePiece],
+ is_input: bool,
+ tied_input: Option<(&'tcx hir::Expr<'tcx>, Option<InlineAsmType>)>,
+ target_features: &FxHashSet<Symbol>,
+ ) -> Option<InlineAsmType> {
+ let ty = (self.get_operand_ty)(expr);
+ if ty.has_non_region_infer() {
+ bug!("inference variable in asm operand ty: {:?} {:?}", expr, ty);
+ }
+ let asm_ty_isize = match self.tcx.sess.target.pointer_width {
+ 16 => InlineAsmType::I16,
+ 32 => InlineAsmType::I32,
+ 64 => InlineAsmType::I64,
+ _ => unreachable!(),
+ };
+
+ let asm_ty = match *ty.kind() {
+ // `!` is allowed for input but not for output (issue #87802)
+ ty::Never if is_input => return None,
+ ty::Error(_) => return None,
+ ty::Int(IntTy::I8) | ty::Uint(UintTy::U8) => Some(InlineAsmType::I8),
+ ty::Int(IntTy::I16) | ty::Uint(UintTy::U16) => Some(InlineAsmType::I16),
+ ty::Int(IntTy::I32) | ty::Uint(UintTy::U32) => Some(InlineAsmType::I32),
+ ty::Int(IntTy::I64) | ty::Uint(UintTy::U64) => Some(InlineAsmType::I64),
+ ty::Int(IntTy::I128) | ty::Uint(UintTy::U128) => Some(InlineAsmType::I128),
+ ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize) => Some(asm_ty_isize),
+ ty::Float(FloatTy::F32) => Some(InlineAsmType::F32),
+ ty::Float(FloatTy::F64) => Some(InlineAsmType::F64),
+ ty::FnPtr(_) => Some(asm_ty_isize),
+ ty::RawPtr(ty::TypeAndMut { ty, mutbl: _ }) if self.is_thin_ptr_ty(ty) => {
+ Some(asm_ty_isize)
+ }
+ ty::Adt(adt, substs) if adt.repr().simd() => {
+ let fields = &adt.non_enum_variant().fields;
+ let elem_ty = fields[0].ty(self.tcx, substs);
+ match elem_ty.kind() {
+ ty::Never | ty::Error(_) => return None,
+ ty::Int(IntTy::I8) | ty::Uint(UintTy::U8) => {
+ Some(InlineAsmType::VecI8(fields.len() as u64))
+ }
+ ty::Int(IntTy::I16) | ty::Uint(UintTy::U16) => {
+ Some(InlineAsmType::VecI16(fields.len() as u64))
+ }
+ ty::Int(IntTy::I32) | ty::Uint(UintTy::U32) => {
+ Some(InlineAsmType::VecI32(fields.len() as u64))
+ }
+ ty::Int(IntTy::I64) | ty::Uint(UintTy::U64) => {
+ Some(InlineAsmType::VecI64(fields.len() as u64))
+ }
+ ty::Int(IntTy::I128) | ty::Uint(UintTy::U128) => {
+ Some(InlineAsmType::VecI128(fields.len() as u64))
+ }
+ ty::Int(IntTy::Isize) | ty::Uint(UintTy::Usize) => {
+ Some(match self.tcx.sess.target.pointer_width {
+ 16 => InlineAsmType::VecI16(fields.len() as u64),
+ 32 => InlineAsmType::VecI32(fields.len() as u64),
+ 64 => InlineAsmType::VecI64(fields.len() as u64),
+ _ => unreachable!(),
+ })
+ }
+ ty::Float(FloatTy::F32) => Some(InlineAsmType::VecF32(fields.len() as u64)),
+ ty::Float(FloatTy::F64) => Some(InlineAsmType::VecF64(fields.len() as u64)),
+ _ => None,
+ }
+ }
+ ty::Infer(_) => unreachable!(),
+ _ => None,
+ };
+ let Some(asm_ty) = asm_ty else {
+ let msg = &format!("cannot use value of type `{ty}` for inline assembly");
+ let mut err = self.tcx.sess.struct_span_err(expr.span, msg);
+ err.note(
+ "only integers, floats, SIMD vectors, pointers and function pointers \
+ can be used as arguments for inline assembly",
+ );
+ err.emit();
+ return None;
+ };
+
+ // Check that the type implements Copy. The only case where this can
+ // possibly fail is for SIMD types which don't #[derive(Copy)].
+ if !ty.is_copy_modulo_regions(self.tcx, self.param_env) {
+ let msg = "arguments for inline assembly must be copyable";
+ let mut err = self.tcx.sess.struct_span_err(expr.span, msg);
+ err.note(&format!("`{ty}` does not implement the Copy trait"));
+ err.emit();
+ }
+
+ // Ideally we wouldn't need to do this, but LLVM's register allocator
+ // really doesn't like it when tied operands have different types.
+ //
+ // This is purely an LLVM limitation, but we have to live with it since
+ // there is no way to hide this with implicit conversions.
+ //
+ // For the purposes of this check we only look at the `InlineAsmType`,
+ // which means that pointers and integers are treated as identical (modulo
+ // size).
+ if let Some((in_expr, Some(in_asm_ty))) = tied_input {
+ if in_asm_ty != asm_ty {
+ let msg = "incompatible types for asm inout argument";
+ let mut err = self.tcx.sess.struct_span_err(vec![in_expr.span, expr.span], msg);
+
+ let in_expr_ty = (self.get_operand_ty)(in_expr);
+ err.span_label(in_expr.span, &format!("type `{in_expr_ty}`"));
+ err.span_label(expr.span, &format!("type `{ty}`"));
+ err.note(
+ "asm inout arguments must have the same type, \
+ unless they are both pointers or integers of the same size",
+ );
+ err.emit();
+ }
+
+ // All of the later checks have already been done on the input, so
+ // let's not emit errors and warnings twice.
+ return Some(asm_ty);
+ }
+
+ // Check the type against the list of types supported by the selected
+ // register class.
+ let asm_arch = self.tcx.sess.asm_arch.unwrap();
+ let reg_class = reg.reg_class();
+ let supported_tys = reg_class.supported_types(asm_arch);
+ let Some((_, feature)) = supported_tys.iter().find(|&&(t, _)| t == asm_ty) else {
+ let msg = &format!("type `{ty}` cannot be used with this register class");
+ let mut err = self.tcx.sess.struct_span_err(expr.span, msg);
+ let supported_tys: Vec<_> =
+ supported_tys.iter().map(|(t, _)| t.to_string()).collect();
+ err.note(&format!(
+ "register class `{}` supports these types: {}",
+ reg_class.name(),
+ supported_tys.join(", "),
+ ));
+ if let Some(suggest) = reg_class.suggest_class(asm_arch, asm_ty) {
+ err.help(&format!(
+ "consider using the `{}` register class instead",
+ suggest.name()
+ ));
+ }
+ err.emit();
+ return Some(asm_ty);
+ };
+
+ // Check whether the selected type requires a target feature. Note that
+ // this is different from the feature check we did earlier. While the
+ // previous check checked that this register class is usable at all
+ // with the currently enabled features, some types may only be usable
+ // with a register class when a certain feature is enabled. We check
+ // this here since it depends on the results of typeck.
+ //
+ // Also note that this check isn't run when the operand type is never
+ // (!). In that case we still need the earlier check to verify that the
+ // register class is usable at all.
+ if let Some(feature) = feature {
+ if !target_features.contains(&feature) {
+ let msg = &format!("`{}` target feature is not enabled", feature);
+ let mut err = self.tcx.sess.struct_span_err(expr.span, msg);
+ err.note(&format!(
+ "this is required to use type `{}` with register class `{}`",
+ ty,
+ reg_class.name(),
+ ));
+ err.emit();
+ return Some(asm_ty);
+ }
+ }
+
+ // Check whether a modifier is suggested for using this type.
+ if let Some((suggested_modifier, suggested_result)) =
+ reg_class.suggest_modifier(asm_arch, asm_ty)
+ {
+ // Search for any use of this operand without a modifier and emit
+ // the suggestion for them.
+ let mut spans = vec![];
+ for piece in template {
+ if let &InlineAsmTemplatePiece::Placeholder { operand_idx, modifier, span } = piece
+ {
+ if operand_idx == idx && modifier.is_none() {
+ spans.push(span);
+ }
+ }
+ }
+ if !spans.is_empty() {
+ let (default_modifier, default_result) =
+ reg_class.default_modifier(asm_arch).unwrap();
+ self.tcx.struct_span_lint_hir(
+ lint::builtin::ASM_SUB_REGISTER,
+ expr.hir_id,
+ spans,
+ "formatting may not be suitable for sub-register argument",
+ |lint| {
+ lint.span_label(expr.span, "for this argument");
+ lint.help(&format!(
+ "use `{{{idx}:{suggested_modifier}}}` to have the register formatted as `{suggested_result}`",
+ ));
+ lint.help(&format!(
+ "or use `{{{idx}:{default_modifier}}}` to keep the default formatting of `{default_result}`",
+ ));
+ lint
+ },
+ );
+ }
+ }
+
+ Some(asm_ty)
+ }
+
+ pub fn check_asm(&self, asm: &hir::InlineAsm<'tcx>, enclosing_id: hir::HirId) {
+ let hir = self.tcx.hir();
+ let enclosing_def_id = hir.local_def_id(enclosing_id).to_def_id();
+ let target_features = self.tcx.asm_target_features(enclosing_def_id);
+ let Some(asm_arch) = self.tcx.sess.asm_arch else {
+ self.tcx.sess.delay_span_bug(DUMMY_SP, "target architecture does not support asm");
+ return;
+ };
+ for (idx, (op, op_sp)) in asm.operands.iter().enumerate() {
+ // Validate register classes against currently enabled target
+ // features. We check that at least one type is available for
+ // the enabled features.
+ //
+ // We ignore target feature requirements for clobbers: if the
+ // feature is disabled then the compiler doesn't care what we
+ // do with the registers.
+ //
+ // Note that this is only possible for explicit register
+ // operands, which cannot be used in the asm string.
+ if let Some(reg) = op.reg() {
+ // Some explicit registers cannot be used depending on the
+ // target. Reject those here.
+ if let InlineAsmRegOrRegClass::Reg(reg) = reg {
+ if let InlineAsmReg::Err = reg {
+ // `validate` will panic on `Err`, as an error must
+ // already have been reported.
+ continue;
+ }
+ if let Err(msg) = reg.validate(
+ asm_arch,
+ self.tcx.sess.relocation_model(),
+ &target_features,
+ &self.tcx.sess.target,
+ op.is_clobber(),
+ ) {
+ let msg = format!("cannot use register `{}`: {}", reg.name(), msg);
+ self.tcx.sess.struct_span_err(*op_sp, &msg).emit();
+ continue;
+ }
+ }
+
+ if !op.is_clobber() {
+ let mut missing_required_features = vec![];
+ let reg_class = reg.reg_class();
+ if let InlineAsmRegClass::Err = reg_class {
+ continue;
+ }
+ for &(_, feature) in reg_class.supported_types(asm_arch) {
+ match feature {
+ Some(feature) => {
+ if target_features.contains(&feature) {
+ missing_required_features.clear();
+ break;
+ } else {
+ missing_required_features.push(feature);
+ }
+ }
+ None => {
+ missing_required_features.clear();
+ break;
+ }
+ }
+ }
+
+ // We are sorting primitive strs here and can use unstable sort here
+ missing_required_features.sort_unstable();
+ missing_required_features.dedup();
+ match &missing_required_features[..] {
+ [] => {}
+ [feature] => {
+ let msg = format!(
+ "register class `{}` requires the `{}` target feature",
+ reg_class.name(),
+ feature
+ );
+ self.tcx.sess.struct_span_err(*op_sp, &msg).emit();
+ // register isn't enabled, don't do more checks
+ continue;
+ }
+ features => {
+ let msg = format!(
+ "register class `{}` requires at least one of the following target features: {}",
+ reg_class.name(),
+ features
+ .iter()
+ .map(|f| f.as_str())
+ .intersperse(", ")
+ .collect::<String>(),
+ );
+ self.tcx.sess.struct_span_err(*op_sp, &msg).emit();
+ // register isn't enabled, don't do more checks
+ continue;
+ }
+ }
+ }
+ }
+
+ match *op {
+ hir::InlineAsmOperand::In { reg, ref expr } => {
+ self.check_asm_operand_type(
+ idx,
+ reg,
+ expr,
+ asm.template,
+ true,
+ None,
+ &target_features,
+ );
+ }
+ hir::InlineAsmOperand::Out { reg, late: _, ref expr } => {
+ if let Some(expr) = expr {
+ self.check_asm_operand_type(
+ idx,
+ reg,
+ expr,
+ asm.template,
+ false,
+ None,
+ &target_features,
+ );
+ }
+ }
+ hir::InlineAsmOperand::InOut { reg, late: _, ref expr } => {
+ self.check_asm_operand_type(
+ idx,
+ reg,
+ expr,
+ asm.template,
+ false,
+ None,
+ &target_features,
+ );
+ }
+ hir::InlineAsmOperand::SplitInOut { reg, late: _, ref in_expr, ref out_expr } => {
+ let in_ty = self.check_asm_operand_type(
+ idx,
+ reg,
+ in_expr,
+ asm.template,
+ true,
+ None,
+ &target_features,
+ );
+ if let Some(out_expr) = out_expr {
+ self.check_asm_operand_type(
+ idx,
+ reg,
+ out_expr,
+ asm.template,
+ false,
+ Some((in_expr, in_ty)),
+ &target_features,
+ );
+ }
+ }
+ // No special checking is needed for these:
+ // - Typeck has checked that Const operands are integers.
+ // - AST lowering guarantees that SymStatic points to a static.
+ hir::InlineAsmOperand::Const { .. } | hir::InlineAsmOperand::SymStatic { .. } => {}
+ // Check that sym actually points to a function. Later passes
+ // depend on this.
+ hir::InlineAsmOperand::SymFn { anon_const } => {
+ let ty = self.tcx.typeck_body(anon_const.body).node_type(anon_const.hir_id);
+ match ty.kind() {
+ ty::Never | ty::Error(_) => {}
+ ty::FnDef(..) => {}
+ _ => {
+ let mut err =
+ self.tcx.sess.struct_span_err(*op_sp, "invalid `sym` operand");
+ err.span_label(
+ self.tcx.hir().span(anon_const.body.hir_id),
+ &format!("is {} `{}`", ty.kind().article(), ty),
+ );
+ err.help("`sym` operands must refer to either a function or a static");
+ err.emit();
+ }
+ };
+ }
+ }
+ }
+ }
+}
diff --git a/compiler/rustc_hir_analysis/src/check/mod.rs b/compiler/rustc_hir_analysis/src/check/mod.rs
new file mode 100644
index 000000000..2e7b10257
--- /dev/null
+++ b/compiler/rustc_hir_analysis/src/check/mod.rs
@@ -0,0 +1,515 @@
+/*!
+
+# typeck: check phase
+
+Within the check phase of type check, we check each item one at a time
+(bodies of function expressions are checked as part of the containing
+function). Inference is used to supply types wherever they are unknown.
+
+By far the most complex case is checking the body of a function. This
+can be broken down into several distinct phases:
+
+- gather: creates type variables to represent the type of each local
+ variable and pattern binding.
+
+- main: the main pass does the lion's share of the work: it
+ determines the types of all expressions, resolves
+ methods, checks for most invalid conditions, and so forth. In
+ some cases, where a type is unknown, it may create a type or region
+ variable and use that as the type of an expression.
+
+ In the process of checking, various constraints will be placed on
+ these type variables through the subtyping relationships requested
+ through the `demand` module. The `infer` module is in charge
+ of resolving those constraints.
+
+- regionck: after main is complete, the regionck pass goes over all
+ types looking for regions and making sure that they did not escape
+ into places where they are not in scope. This may also influence the
+ final assignments of the various region variables if there is some
+ flexibility.
+
+- writeback: writes the final types within a function body, replacing
+ type variables with their final inferred types. These final types
+ are written into the `tcx.node_types` table, which should *never* contain
+ any reference to a type variable.
+
+## Intermediate types
+
+While type checking a function, the intermediate types for the
+expressions, blocks, and so forth contained within the function are
+stored in `fcx.node_types` and `fcx.node_substs`. These types
+may contain unresolved type variables. After type checking is
+complete, the functions in the writeback module are used to take the
+types from this table, resolve them, and then write them into their
+permanent home in the type context `tcx`.
+
+This means that during inferencing you should use `fcx.write_ty()`
+and `fcx.expr_ty()` / `fcx.node_ty()` to write/obtain the types of
+nodes within the function.
+
+The types of top-level items, which never contain unbound type
+variables, are stored directly into the `tcx` typeck_results.
+
+N.B., a type variable is not the same thing as a type parameter. A
+type variable is an instance of a type parameter. That is,
+given a generic function `fn foo<T>(t: T)`, while checking the
+function `foo`, the type `ty_param(0)` refers to the type `T`, which
+is treated in abstract. However, when `foo()` is called, `T` will be
+substituted for a fresh type variable `N`. This variable will
+eventually be resolved to some concrete type (which might itself be
+a type parameter).
+
+*/
+
+mod check;
+mod compare_method;
+pub mod dropck;
+pub mod intrinsic;
+pub mod intrinsicck;
+mod region;
+pub mod wfcheck;
+
+pub use check::check_abi;
+
+use check::check_mod_item_types;
+use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_errors::{pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder};
+use rustc_hir as hir;
+use rustc_hir::def_id::{DefId, LocalDefId};
+use rustc_hir::intravisit::Visitor;
+use rustc_index::bit_set::BitSet;
+use rustc_middle::ty::query::Providers;
+use rustc_middle::ty::{self, Ty, TyCtxt};
+use rustc_middle::ty::{InternalSubsts, SubstsRef};
+use rustc_session::parse::feature_err;
+use rustc_span::source_map::DUMMY_SP;
+use rustc_span::symbol::{kw, Ident};
+use rustc_span::{self, BytePos, Span, Symbol};
+use rustc_target::abi::VariantIdx;
+use rustc_target::spec::abi::Abi;
+use rustc_trait_selection::traits::error_reporting::suggestions::ReturnsVisitor;
+use std::num::NonZeroU32;
+
+use crate::require_c_abi_if_c_variadic;
+use crate::util::common::indenter;
+
+use self::compare_method::collect_trait_impl_trait_tys;
+use self::region::region_scope_tree;
+
+pub fn provide(providers: &mut Providers) {
+ wfcheck::provide(providers);
+ *providers = Providers {
+ adt_destructor,
+ check_mod_item_types,
+ region_scope_tree,
+ collect_trait_impl_trait_tys,
+ compare_assoc_const_impl_item_with_trait_item: compare_method::raw_compare_const_impl,
+ ..*providers
+ };
+}
+
+fn adt_destructor(tcx: TyCtxt<'_>, def_id: DefId) -> Option<ty::Destructor> {
+ tcx.calculate_dtor(def_id, dropck::check_drop_impl)
+}
+
+/// Given a `DefId` for an opaque type in return position, find its parent item's return
+/// expressions.
+fn get_owner_return_paths<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ def_id: LocalDefId,
+) -> Option<(LocalDefId, ReturnsVisitor<'tcx>)> {
+ let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
+ let parent_id = tcx.hir().get_parent_item(hir_id).def_id;
+ tcx.hir().find_by_def_id(parent_id).and_then(|node| node.body_id()).map(|body_id| {
+ let body = tcx.hir().body(body_id);
+ let mut visitor = ReturnsVisitor::default();
+ visitor.visit_body(body);
+ (parent_id, visitor)
+ })
+}
+
+/// Forbid defining intrinsics in Rust code,
+/// as they must always be defined by the compiler.
+// FIXME: Move this to a more appropriate place.
+pub fn fn_maybe_err(tcx: TyCtxt<'_>, sp: Span, abi: Abi) {
+ if let Abi::RustIntrinsic | Abi::PlatformIntrinsic = abi {
+ tcx.sess.span_err(sp, "intrinsic must be in `extern \"rust-intrinsic\" { ... }` block");
+ }
+}
+
+fn maybe_check_static_with_link_section(tcx: TyCtxt<'_>, id: LocalDefId) {
+ // Only restricted on wasm target for now
+ if !tcx.sess.target.is_like_wasm {
+ return;
+ }
+
+ // If `#[link_section]` is missing, then nothing to verify
+ let attrs = tcx.codegen_fn_attrs(id);
+ if attrs.link_section.is_none() {
+ return;
+ }
+
+ // For the wasm32 target statics with `#[link_section]` are placed into custom
+ // sections of the final output file, but this isn't link custom sections of
+ // other executable formats. Namely we can only embed a list of bytes,
+ // nothing with provenance (pointers to anything else). If any provenance
+ // show up, reject it here.
+ // `#[link_section]` may contain arbitrary, or even undefined bytes, but it is
+ // the consumer's responsibility to ensure all bytes that have been read
+ // have defined values.
+ if let Ok(alloc) = tcx.eval_static_initializer(id.to_def_id())
+ && alloc.inner().provenance().len() != 0
+ {
+ let msg = "statics with a custom `#[link_section]` must be a \
+ simple list of bytes on the wasm target with no \
+ extra levels of indirection such as references";
+ tcx.sess.span_err(tcx.def_span(id), msg);
+ }
+}
+
+fn report_forbidden_specialization(
+ tcx: TyCtxt<'_>,
+ impl_item: &hir::ImplItemRef,
+ parent_impl: DefId,
+) {
+ let mut err = struct_span_err!(
+ tcx.sess,
+ impl_item.span,
+ E0520,
+ "`{}` specializes an item from a parent `impl`, but \
+ that item is not marked `default`",
+ impl_item.ident
+ );
+ err.span_label(impl_item.span, format!("cannot specialize default item `{}`", impl_item.ident));
+
+ match tcx.span_of_impl(parent_impl) {
+ Ok(span) => {
+ err.span_label(span, "parent `impl` is here");
+ err.note(&format!(
+ "to specialize, `{}` in the parent `impl` must be marked `default`",
+ impl_item.ident
+ ));
+ }
+ Err(cname) => {
+ err.note(&format!("parent implementation is in crate `{cname}`"));
+ }
+ }
+
+ err.emit();
+}
+
+fn missing_items_err(
+ tcx: TyCtxt<'_>,
+ impl_span: Span,
+ missing_items: &[&ty::AssocItem],
+ full_impl_span: Span,
+) {
+ let missing_items_msg = missing_items
+ .iter()
+ .map(|trait_item| trait_item.name.to_string())
+ .collect::<Vec<_>>()
+ .join("`, `");
+
+ let mut err = struct_span_err!(
+ tcx.sess,
+ impl_span,
+ E0046,
+ "not all trait items implemented, missing: `{missing_items_msg}`",
+ );
+ err.span_label(impl_span, format!("missing `{missing_items_msg}` in implementation"));
+
+ // `Span` before impl block closing brace.
+ let hi = full_impl_span.hi() - BytePos(1);
+ // Point at the place right before the closing brace of the relevant `impl` to suggest
+ // adding the associated item at the end of its body.
+ let sugg_sp = full_impl_span.with_lo(hi).with_hi(hi);
+ // Obtain the level of indentation ending in `sugg_sp`.
+ let padding =
+ tcx.sess.source_map().indentation_before(sugg_sp).unwrap_or_else(|| String::new());
+
+ for trait_item in missing_items {
+ let snippet = suggestion_signature(trait_item, tcx);
+ let code = format!("{}{}\n{}", padding, snippet, padding);
+ let msg = format!("implement the missing item: `{snippet}`");
+ let appl = Applicability::HasPlaceholders;
+ if let Some(span) = tcx.hir().span_if_local(trait_item.def_id) {
+ err.span_label(span, format!("`{}` from trait", trait_item.name));
+ err.tool_only_span_suggestion(sugg_sp, &msg, code, appl);
+ } else {
+ err.span_suggestion_hidden(sugg_sp, &msg, code, appl);
+ }
+ }
+ err.emit();
+}
+
+fn missing_items_must_implement_one_of_err(
+ tcx: TyCtxt<'_>,
+ impl_span: Span,
+ missing_items: &[Ident],
+ annotation_span: Option<Span>,
+) {
+ let missing_items_msg =
+ missing_items.iter().map(Ident::to_string).collect::<Vec<_>>().join("`, `");
+
+ let mut err = struct_span_err!(
+ tcx.sess,
+ impl_span,
+ E0046,
+ "not all trait items implemented, missing one of: `{missing_items_msg}`",
+ );
+ err.span_label(impl_span, format!("missing one of `{missing_items_msg}` in implementation"));
+
+ if let Some(annotation_span) = annotation_span {
+ err.span_note(annotation_span, "required because of this annotation");
+ }
+
+ err.emit();
+}
+
+fn default_body_is_unstable(
+ tcx: TyCtxt<'_>,
+ impl_span: Span,
+ item_did: DefId,
+ feature: Symbol,
+ reason: Option<Symbol>,
+ issue: Option<NonZeroU32>,
+) {
+ let missing_item_name = &tcx.associated_item(item_did).name;
+ let use_of_unstable_library_feature_note = match reason {
+ Some(r) => format!("use of unstable library feature '{feature}': {r}"),
+ None => format!("use of unstable library feature '{feature}'"),
+ };
+
+ let mut err = struct_span_err!(
+ tcx.sess,
+ impl_span,
+ E0046,
+ "not all trait items implemented, missing: `{missing_item_name}`",
+ );
+ err.note(format!("default implementation of `{missing_item_name}` is unstable"));
+ err.note(use_of_unstable_library_feature_note);
+ rustc_session::parse::add_feature_diagnostics_for_issue(
+ &mut err,
+ &tcx.sess.parse_sess,
+ feature,
+ rustc_feature::GateIssue::Library(issue),
+ );
+ err.emit();
+}
+
+/// Re-sugar `ty::GenericPredicates` in a way suitable to be used in structured suggestions.
+fn bounds_from_generic_predicates<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ predicates: ty::GenericPredicates<'tcx>,
+) -> (String, String) {
+ let mut types: FxHashMap<Ty<'tcx>, Vec<DefId>> = FxHashMap::default();
+ let mut projections = vec![];
+ for (predicate, _) in predicates.predicates {
+ debug!("predicate {:?}", predicate);
+ let bound_predicate = predicate.kind();
+ match bound_predicate.skip_binder() {
+ ty::PredicateKind::Trait(trait_predicate) => {
+ let entry = types.entry(trait_predicate.self_ty()).or_default();
+ let def_id = trait_predicate.def_id();
+ if Some(def_id) != tcx.lang_items().sized_trait() {
+ // Type params are `Sized` by default, do not add that restriction to the list
+ // if it is a positive requirement.
+ entry.push(trait_predicate.def_id());
+ }
+ }
+ ty::PredicateKind::Projection(projection_pred) => {
+ projections.push(bound_predicate.rebind(projection_pred));
+ }
+ _ => {}
+ }
+ }
+ let generics = if types.is_empty() {
+ "".to_string()
+ } else {
+ format!(
+ "<{}>",
+ types
+ .keys()
+ .filter_map(|t| match t.kind() {
+ ty::Param(_) => Some(t.to_string()),
+ // Avoid suggesting the following:
+ // fn foo<T, <T as Trait>::Bar>(_: T) where T: Trait, <T as Trait>::Bar: Other {}
+ _ => None,
+ })
+ .collect::<Vec<_>>()
+ .join(", ")
+ )
+ };
+ let mut where_clauses = vec![];
+ for (ty, bounds) in types {
+ where_clauses
+ .extend(bounds.into_iter().map(|bound| format!("{}: {}", ty, tcx.def_path_str(bound))));
+ }
+ for projection in &projections {
+ let p = projection.skip_binder();
+ // FIXME: this is not currently supported syntax, we should be looking at the `types` and
+ // insert the associated types where they correspond, but for now let's be "lazy" and
+ // propose this instead of the following valid resugaring:
+ // `T: Trait, Trait::Assoc = K` → `T: Trait<Assoc = K>`
+ where_clauses.push(format!(
+ "{} = {}",
+ tcx.def_path_str(p.projection_ty.item_def_id),
+ p.term,
+ ));
+ }
+ let where_clauses = if where_clauses.is_empty() {
+ String::new()
+ } else {
+ format!(" where {}", where_clauses.join(", "))
+ };
+ (generics, where_clauses)
+}
+
+/// Return placeholder code for the given function.
+fn fn_sig_suggestion<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ sig: ty::FnSig<'tcx>,
+ ident: Ident,
+ predicates: ty::GenericPredicates<'tcx>,
+ assoc: &ty::AssocItem,
+) -> String {
+ let args = sig
+ .inputs()
+ .iter()
+ .enumerate()
+ .map(|(i, ty)| {
+ Some(match ty.kind() {
+ ty::Param(_) if assoc.fn_has_self_parameter && i == 0 => "self".to_string(),
+ ty::Ref(reg, ref_ty, mutability) if i == 0 => {
+ let reg = format!("{reg} ");
+ let reg = match &reg[..] {
+ "'_ " | " " => "",
+ reg => reg,
+ };
+ if assoc.fn_has_self_parameter {
+ match ref_ty.kind() {
+ ty::Param(param) if param.name == kw::SelfUpper => {
+ format!("&{}{}self", reg, mutability.prefix_str())
+ }
+
+ _ => format!("self: {ty}"),
+ }
+ } else {
+ format!("_: {ty}")
+ }
+ }
+ _ => {
+ if assoc.fn_has_self_parameter && i == 0 {
+ format!("self: {ty}")
+ } else {
+ format!("_: {ty}")
+ }
+ }
+ })
+ })
+ .chain(std::iter::once(if sig.c_variadic { Some("...".to_string()) } else { None }))
+ .flatten()
+ .collect::<Vec<String>>()
+ .join(", ");
+ let output = sig.output();
+ let output = if !output.is_unit() { format!(" -> {output}") } else { String::new() };
+
+ let unsafety = sig.unsafety.prefix_str();
+ let (generics, where_clauses) = bounds_from_generic_predicates(tcx, predicates);
+
+ // FIXME: this is not entirely correct, as the lifetimes from borrowed params will
+ // not be present in the `fn` definition, not will we account for renamed
+ // lifetimes between the `impl` and the `trait`, but this should be good enough to
+ // fill in a significant portion of the missing code, and other subsequent
+ // suggestions can help the user fix the code.
+ format!("{unsafety}fn {ident}{generics}({args}){output}{where_clauses} {{ todo!() }}")
+}
+
+pub fn ty_kind_suggestion(ty: Ty<'_>) -> Option<&'static str> {
+ Some(match ty.kind() {
+ ty::Bool => "true",
+ ty::Char => "'a'",
+ ty::Int(_) | ty::Uint(_) => "42",
+ ty::Float(_) => "3.14159",
+ ty::Error(_) | ty::Never => return None,
+ _ => "value",
+ })
+}
+
+/// Return placeholder code for the given associated item.
+/// Similar to `ty::AssocItem::suggestion`, but appropriate for use as the code snippet of a
+/// structured suggestion.
+fn suggestion_signature(assoc: &ty::AssocItem, tcx: TyCtxt<'_>) -> String {
+ match assoc.kind {
+ ty::AssocKind::Fn => {
+ // We skip the binder here because the binder would deanonymize all
+ // late-bound regions, and we don't want method signatures to show up
+ // `as for<'r> fn(&'r MyType)`. Pretty-printing handles late-bound
+ // regions just fine, showing `fn(&MyType)`.
+ fn_sig_suggestion(
+ tcx,
+ tcx.fn_sig(assoc.def_id).skip_binder(),
+ assoc.ident(tcx),
+ tcx.predicates_of(assoc.def_id),
+ assoc,
+ )
+ }
+ ty::AssocKind::Type => format!("type {} = Type;", assoc.name),
+ ty::AssocKind::Const => {
+ let ty = tcx.type_of(assoc.def_id);
+ let val = ty_kind_suggestion(ty).unwrap_or("value");
+ format!("const {}: {} = {};", assoc.name, ty, val)
+ }
+ }
+}
+
+/// Emit an error when encountering two or more variants in a transparent enum.
+fn bad_variant_count<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>, sp: Span, did: DefId) {
+ let variant_spans: Vec<_> = adt
+ .variants()
+ .iter()
+ .map(|variant| tcx.hir().span_if_local(variant.def_id).unwrap())
+ .collect();
+ let msg = format!("needs exactly one variant, but has {}", adt.variants().len(),);
+ let mut err = struct_span_err!(tcx.sess, sp, E0731, "transparent enum {msg}");
+ err.span_label(sp, &msg);
+ if let [start @ .., end] = &*variant_spans {
+ for variant_span in start {
+ err.span_label(*variant_span, "");
+ }
+ err.span_label(*end, &format!("too many variants in `{}`", tcx.def_path_str(did)));
+ }
+ err.emit();
+}
+
+/// Emit an error when encountering two or more non-zero-sized fields in a transparent
+/// enum.
+fn bad_non_zero_sized_fields<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ adt: ty::AdtDef<'tcx>,
+ field_count: usize,
+ field_spans: impl Iterator<Item = Span>,
+ sp: Span,
+) {
+ let msg = format!("needs at most one non-zero-sized field, but has {field_count}");
+ let mut err = struct_span_err!(
+ tcx.sess,
+ sp,
+ E0690,
+ "{}transparent {} {}",
+ if adt.is_enum() { "the variant of a " } else { "" },
+ adt.descr(),
+ msg,
+ );
+ err.span_label(sp, &msg);
+ for sp in field_spans {
+ err.span_label(sp, "this field is non-zero-sized");
+ }
+ err.emit();
+}
+
+// FIXME: Consider moving this method to a more fitting place.
+pub fn potentially_plural_count(count: usize, word: &str) -> String {
+ format!("{} {}{}", count, word, pluralize!(count))
+}
diff --git a/compiler/rustc_typeck/src/check/region.rs b/compiler/rustc_hir_analysis/src/check/region.rs
index 0081e9049..ff32329e4 100644
--- a/compiler/rustc_typeck/src/check/region.rs
+++ b/compiler/rustc_hir_analysis/src/check/region.rs
@@ -126,6 +126,29 @@ fn resolve_block<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, blk: &'tcx h
for (i, statement) in blk.stmts.iter().enumerate() {
match statement.kind {
+ hir::StmtKind::Local(hir::Local { els: Some(els), .. }) => {
+ // Let-else has a special lexical structure for variables.
+ // First we take a checkpoint of the current scope context here.
+ let mut prev_cx = visitor.cx;
+
+ visitor.enter_scope(Scope {
+ id: blk.hir_id.local_id,
+ data: ScopeData::Remainder(FirstStatementIndex::new(i)),
+ });
+ visitor.cx.var_parent = visitor.cx.parent;
+ visitor.visit_stmt(statement);
+ // We need to back out temporarily to the last enclosing scope
+ // for the `else` block, so that even the temporaries receiving
+ // extended lifetime will be dropped inside this block.
+ // We are visiting the `else` block in this order so that
+ // the sequence of visits agree with the order in the default
+ // `hir::intravisit` visitor.
+ mem::swap(&mut prev_cx, &mut visitor.cx);
+ visitor.terminating_scopes.insert(els.hir_id.local_id);
+ visitor.visit_block(els);
+ // From now on, we continue normally.
+ visitor.cx = prev_cx;
+ }
hir::StmtKind::Local(..) | hir::StmtKind::Item(..) => {
// Each declaration introduces a subscope for bindings
// introduced by the declaration; this subscope covers a
@@ -138,10 +161,10 @@ fn resolve_block<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, blk: &'tcx h
data: ScopeData::Remainder(FirstStatementIndex::new(i)),
});
visitor.cx.var_parent = visitor.cx.parent;
+ visitor.visit_stmt(statement)
}
- hir::StmtKind::Expr(..) | hir::StmtKind::Semi(..) => {}
+ hir::StmtKind::Expr(..) | hir::StmtKind::Semi(..) => visitor.visit_stmt(statement),
}
- visitor.visit_stmt(statement)
}
walk_list!(visitor, visit_expr, &blk.expr);
}
@@ -229,9 +252,13 @@ fn resolve_expr<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, expr: &'tcx h
) => {
// For shortcircuiting operators, mark the RHS as a terminating
// scope since it only executes conditionally.
- terminating(r.hir_id.local_id);
- }
+ // `Let` expressions (in a let-chain) shouldn't be terminating, as their temporaries
+ // should live beyond the immediate expression
+ if !matches!(r.kind, hir::ExprKind::Let(_)) {
+ terminating(r.hir_id.local_id);
+ }
+ }
hir::ExprKind::If(_, ref then, Some(ref otherwise)) => {
terminating(then.hir_id.local_id);
terminating(otherwise.hir_id.local_id);
@@ -460,7 +487,6 @@ fn resolve_local<'tcx>(
visitor: &mut RegionResolutionVisitor<'tcx>,
pat: Option<&'tcx hir::Pat<'tcx>>,
init: Option<&'tcx hir::Expr<'tcx>>,
- els: Option<&'tcx hir::Block<'tcx>>,
) {
debug!("resolve_local(pat={:?}, init={:?})", pat, init);
@@ -547,9 +573,6 @@ fn resolve_local<'tcx>(
if let Some(pat) = pat {
visitor.visit_pat(pat);
}
- if let Some(els) = els {
- visitor.visit_block(els);
- }
/// Returns `true` if `pat` match the `P&` non-terminal.
///
@@ -587,8 +610,7 @@ fn resolve_local<'tcx>(
// & expression, and its lifetime would be extended to the end of the block (due
// to a different rule, not the below code).
match pat.kind {
- PatKind::Binding(hir::BindingAnnotation::Ref, ..)
- | PatKind::Binding(hir::BindingAnnotation::RefMut, ..) => true,
+ PatKind::Binding(hir::BindingAnnotation(hir::ByRef::Yes, _), ..) => true,
PatKind::Struct(_, ref field_pats, _) => {
field_pats.iter().any(|fp| is_binding_pat(&fp.pat))
@@ -607,10 +629,7 @@ fn resolve_local<'tcx>(
PatKind::Box(ref subpat) => is_binding_pat(&subpat),
PatKind::Ref(_, _)
- | PatKind::Binding(
- hir::BindingAnnotation::Unannotated | hir::BindingAnnotation::Mutable,
- ..,
- )
+ | PatKind::Binding(hir::BindingAnnotation(hir::ByRef::No, _), ..)
| PatKind::Wild
| PatKind::Path(_)
| PatKind::Lit(_)
@@ -770,7 +789,7 @@ impl<'tcx> Visitor<'tcx> for RegionResolutionVisitor<'tcx> {
// (i.e., `'static`), which means that after `g` returns, it drops,
// and all the associated destruction scope rules apply.
self.cx.var_parent = None;
- resolve_local(self, None, Some(&body.value), None);
+ resolve_local(self, None, Some(&body.value));
}
if body.generator_kind.is_some() {
@@ -797,7 +816,7 @@ impl<'tcx> Visitor<'tcx> for RegionResolutionVisitor<'tcx> {
resolve_expr(self, ex);
}
fn visit_local(&mut self, l: &'tcx Local<'tcx>) {
- resolve_local(self, Some(&l.pat), l.init, l.els)
+ resolve_local(self, Some(&l.pat), l.init)
}
}
diff --git a/compiler/rustc_typeck/src/check/wfcheck.rs b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
index d0334cd0d..a23575004 100644
--- a/compiler/rustc_typeck/src/check/wfcheck.rs
+++ b/compiler/rustc_hir_analysis/src/check/wfcheck.rs
@@ -1,5 +1,5 @@
-use crate::check::regionck::OutlivesEnvironmentExt;
use crate::constrained_generic_params::{identify_constrained_generic_params, Parameter};
+use hir::def::DefKind;
use rustc_ast as ast;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed};
@@ -10,22 +10,21 @@ use rustc_hir::ItemKind;
use rustc_infer::infer::outlives::env::{OutlivesEnvironment, RegionBoundPairs};
use rustc_infer::infer::outlives::obligations::TypeOutlives;
use rustc_infer::infer::{self, InferCtxt, TyCtxtInferExt};
-use rustc_infer::traits::Normalized;
+use rustc_middle::mir::ConstraintCategory;
use rustc_middle::ty::query::Providers;
-use rustc_middle::ty::subst::{GenericArgKind, InternalSubsts, Subst};
use rustc_middle::ty::trait_def::TraitSpecializationKind;
use rustc_middle::ty::{
self, AdtKind, DefIdTree, GenericParamDefKind, ToPredicate, Ty, TyCtxt, TypeFoldable,
TypeSuperVisitable, TypeVisitable, TypeVisitor,
};
+use rustc_middle::ty::{GenericArgKind, InternalSubsts};
use rustc_session::parse::feature_err;
use rustc_span::symbol::{sym, Ident, Symbol};
use rustc_span::{Span, DUMMY_SP};
use rustc_trait_selection::autoderef::Autoderef;
-use rustc_trait_selection::traits::error_reporting::InferCtxtExt;
+use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt;
+use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _;
use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _;
-use rustc_trait_selection::traits::query::normalize::AtExt;
-use rustc_trait_selection::traits::query::NoSolution;
use rustc_trait_selection::traits::{
self, ObligationCause, ObligationCauseCode, ObligationCtxt, WellFormedLoc,
};
@@ -72,9 +71,11 @@ impl<'tcx> WfCheckingCtxt<'_, 'tcx> {
) {
let cause =
traits::ObligationCause::new(span, self.body_id, ObligationCauseCode::WellFormed(loc));
+ // for a type to be WF, we do not need to check if const trait predicates satisfy.
+ let param_env = self.param_env.without_const();
self.ocx.register_obligation(traits::Obligation::new(
cause,
- self.param_env,
+ param_env,
ty::Binder::dummy(ty::PredicateKind::WellFormed(arg)).to_predicate(self.tcx()),
));
}
@@ -86,31 +87,35 @@ pub(super) fn enter_wf_checking_ctxt<'tcx, F>(
body_def_id: LocalDefId,
f: F,
) where
- F: for<'a> FnOnce(&WfCheckingCtxt<'a, 'tcx>) -> FxHashSet<Ty<'tcx>>,
+ F: for<'a> FnOnce(&WfCheckingCtxt<'a, 'tcx>),
{
let param_env = tcx.param_env(body_def_id);
let body_id = tcx.hir().local_def_id_to_hir_id(body_def_id);
- tcx.infer_ctxt().enter(|ref infcx| {
- let ocx = ObligationCtxt::new(infcx);
- let mut wfcx = WfCheckingCtxt { ocx, span, body_id, param_env };
+ let infcx = &tcx.infer_ctxt().build();
+ let ocx = ObligationCtxt::new(infcx);
- if !tcx.features().trivial_bounds {
- wfcx.check_false_global_bounds()
- }
- let wf_tys = f(&mut wfcx);
- let errors = wfcx.select_all_or_error();
- if !errors.is_empty() {
- infcx.report_fulfillment_errors(&errors, None, false);
- return;
- }
+ let assumed_wf_types = ocx.assumed_wf_types(param_env, span, body_def_id);
- let mut outlives_environment = OutlivesEnvironment::new(param_env);
- outlives_environment.add_implied_bounds(infcx, wf_tys, body_id);
- infcx.check_region_obligations_and_report_errors(body_def_id, &outlives_environment);
- })
+ let mut wfcx = WfCheckingCtxt { ocx, span, body_id, param_env };
+
+ if !tcx.features().trivial_bounds {
+ wfcx.check_false_global_bounds()
+ }
+ f(&mut wfcx);
+ let errors = wfcx.select_all_or_error();
+ if !errors.is_empty() {
+ infcx.err_ctxt().report_fulfillment_errors(&errors, None, false);
+ return;
+ }
+
+ let implied_bounds = infcx.implied_bounds_tys(param_env, body_id, assumed_wf_types);
+ let outlives_environment =
+ OutlivesEnvironment::with_bounds(param_env, Some(infcx), implied_bounds);
+
+ infcx.check_region_obligations_and_report_errors(body_def_id, &outlives_environment);
}
-fn check_well_formed(tcx: TyCtxt<'_>, def_id: LocalDefId) {
+fn check_well_formed(tcx: TyCtxt<'_>, def_id: hir::OwnerId) {
let node = tcx.hir().expect_owner(def_id);
match node {
hir::OwnerNode::Crate(_) => {}
@@ -142,10 +147,10 @@ fn check_well_formed(tcx: TyCtxt<'_>, def_id: LocalDefId) {
/// the types first.
#[instrument(skip(tcx), level = "debug")]
fn check_item<'tcx>(tcx: TyCtxt<'tcx>, item: &'tcx hir::Item<'tcx>) {
- let def_id = item.def_id;
+ let def_id = item.owner_id.def_id;
debug!(
- ?item.def_id,
+ ?item.owner_id,
item.name = ? tcx.def_path_str(def_id.to_def_id())
);
@@ -169,7 +174,7 @@ fn check_item<'tcx>(tcx: TyCtxt<'tcx>, item: &'tcx hir::Item<'tcx>) {
// for `T`
hir::ItemKind::Impl(ref impl_) => {
let is_auto = tcx
- .impl_trait_ref(item.def_id)
+ .impl_trait_ref(def_id)
.map_or(false, |trait_ref| tcx.trait_is_auto(trait_ref.def_id));
if let (hir::Defaultness::Default { .. }, true) = (impl_.defaultness, is_auto) {
let sp = impl_.of_trait.as_ref().map_or(item.span, |t| t.path.span);
@@ -205,13 +210,13 @@ fn check_item<'tcx>(tcx: TyCtxt<'tcx>, item: &'tcx hir::Item<'tcx>) {
}
}
hir::ItemKind::Fn(ref sig, ..) => {
- check_item_fn(tcx, item.def_id, item.ident, item.span, sig.decl);
+ check_item_fn(tcx, def_id, item.ident, item.span, sig.decl);
}
hir::ItemKind::Static(ty, ..) => {
- check_item_type(tcx, item.def_id, ty.span, false);
+ check_item_type(tcx, def_id, ty.span, false);
}
hir::ItemKind::Const(ty, ..) => {
- check_item_type(tcx, item.def_id, ty.span, false);
+ check_item_type(tcx, def_id, ty.span, false);
}
hir::ItemKind::Struct(ref struct_def, ref ast_generics) => {
check_type_defn(tcx, item, false, |wfcx| vec![wfcx.non_enum_variant(struct_def)]);
@@ -241,24 +246,24 @@ fn check_item<'tcx>(tcx: TyCtxt<'tcx>, item: &'tcx hir::Item<'tcx>) {
}
fn check_foreign_item(tcx: TyCtxt<'_>, item: &hir::ForeignItem<'_>) {
- let def_id = item.def_id;
+ let def_id = item.owner_id.def_id;
debug!(
- ?item.def_id,
+ ?item.owner_id,
item.name = ? tcx.def_path_str(def_id.to_def_id())
);
match item.kind {
hir::ForeignItemKind::Fn(decl, ..) => {
- check_item_fn(tcx, item.def_id, item.ident, item.span, decl)
+ check_item_fn(tcx, def_id, item.ident, item.span, decl)
}
- hir::ForeignItemKind::Static(ty, ..) => check_item_type(tcx, item.def_id, ty.span, true),
+ hir::ForeignItemKind::Static(ty, ..) => check_item_type(tcx, def_id, ty.span, true),
hir::ForeignItemKind::Type => (),
}
}
fn check_trait_item(tcx: TyCtxt<'_>, trait_item: &hir::TraitItem<'_>) {
- let def_id = trait_item.def_id;
+ let def_id = trait_item.owner_id.def_id;
let (method_sig, span) = match trait_item.kind {
hir::TraitItemKind::Fn(ref sig, _) => (Some(sig), trait_item.span),
@@ -266,11 +271,11 @@ fn check_trait_item(tcx: TyCtxt<'_>, trait_item: &hir::TraitItem<'_>) {
_ => (None, trait_item.span),
};
check_object_unsafe_self_trait_by_name(tcx, trait_item);
- check_associated_item(tcx, trait_item.def_id, span, method_sig);
+ check_associated_item(tcx, def_id, span, method_sig);
let encl_trait_def_id = tcx.local_parent(def_id);
let encl_trait = tcx.hir().expect_item(encl_trait_def_id);
- let encl_trait_def_id = encl_trait.def_id.to_def_id();
+ let encl_trait_def_id = encl_trait.owner_id.to_def_id();
let fn_lang_item_name = if Some(encl_trait_def_id) == tcx.lang_items().fn_trait() {
Some("fn")
} else if Some(encl_trait_def_id) == tcx.lang_items().fn_mut_trait() {
@@ -343,7 +348,7 @@ fn check_gat_where_clauses(tcx: TyCtxt<'_>, associated_items: &[hir::TraitItemRe
loop {
let mut should_continue = false;
for gat_item in associated_items {
- let gat_def_id = gat_item.id.def_id;
+ let gat_def_id = gat_item.id.owner_id;
let gat_item = tcx.associated_item(gat_def_id);
// If this item is not an assoc ty, or has no substs, then it's not a GAT
if gat_item.kind != ty::AssocKind::Type {
@@ -360,7 +365,7 @@ fn check_gat_where_clauses(tcx: TyCtxt<'_>, associated_items: &[hir::TraitItemRe
// constrains the GAT with individually.
let mut new_required_bounds: Option<FxHashSet<ty::Predicate<'_>>> = None;
for item in associated_items {
- let item_def_id = item.id.def_id;
+ let item_def_id = item.id.owner_id;
// Skip our own GAT, since it does not constrain itself at all.
if item_def_id == gat_def_id {
continue;
@@ -383,11 +388,11 @@ fn check_gat_where_clauses(tcx: TyCtxt<'_>, associated_items: &[hir::TraitItemRe
tcx,
param_env,
item_hir_id,
- sig.output(),
+ sig.inputs_and_output,
// We also assume that all of the function signature's parameter types
// are well formed.
&sig.inputs().iter().copied().collect(),
- gat_def_id,
+ gat_def_id.def_id,
gat_generics,
)
}
@@ -410,7 +415,7 @@ fn check_gat_where_clauses(tcx: TyCtxt<'_>, associated_items: &[hir::TraitItemRe
.copied()
.collect::<Vec<_>>(),
&FxHashSet::default(),
- gat_def_id,
+ gat_def_id.def_id,
gat_generics,
)
}
@@ -450,7 +455,7 @@ fn check_gat_where_clauses(tcx: TyCtxt<'_>, associated_items: &[hir::TraitItemRe
}
for (gat_def_id, required_bounds) in required_bounds_by_item {
- let gat_item_hir = tcx.hir().expect_trait_item(gat_def_id);
+ let gat_item_hir = tcx.hir().expect_trait_item(gat_def_id.def_id);
debug!(?required_bounds);
let param_env = tcx.param_env(gat_def_id);
let gat_hir = gat_item_hir.hir_id();
@@ -658,7 +663,7 @@ fn ty_known_to_outlive<'tcx>(
resolve_regions_with_wf_tys(tcx, id, param_env, &wf_tys, |infcx, region_bound_pairs| {
let origin = infer::RelateParamBound(DUMMY_SP, ty, None);
let outlives = &mut TypeOutlives::new(infcx, tcx, region_bound_pairs, None, param_env);
- outlives.type_must_outlive(origin, ty, region);
+ outlives.type_must_outlive(origin, ty, region, ConstraintCategory::BoringNoLocation);
})
}
@@ -676,7 +681,12 @@ fn region_known_to_outlive<'tcx>(
use rustc_infer::infer::outlives::obligations::TypeOutlivesDelegate;
let origin = infer::RelateRegionParamBound(DUMMY_SP);
// `region_a: region_b` -> `region_b <= region_a`
- infcx.push_sub_region_constraint(origin, region_b, region_a);
+ infcx.push_sub_region_constraint(
+ origin,
+ region_b,
+ region_a,
+ ConstraintCategory::BoringNoLocation,
+ );
})
}
@@ -688,26 +698,32 @@ fn resolve_regions_with_wf_tys<'tcx>(
id: hir::HirId,
param_env: ty::ParamEnv<'tcx>,
wf_tys: &FxHashSet<Ty<'tcx>>,
- add_constraints: impl for<'a> FnOnce(&'a InferCtxt<'a, 'tcx>, &'a RegionBoundPairs<'tcx>),
+ add_constraints: impl for<'a> FnOnce(&'a InferCtxt<'tcx>, &'a RegionBoundPairs<'tcx>),
) -> bool {
// Unfortunately, we have to use a new `InferCtxt` each call, because
// region constraints get added and solved there and we need to test each
// call individually.
- tcx.infer_ctxt().enter(|infcx| {
- let mut outlives_environment = OutlivesEnvironment::new(param_env);
- outlives_environment.add_implied_bounds(&infcx, wf_tys.clone(), id);
- let region_bound_pairs = outlives_environment.region_bound_pairs();
+ let infcx = tcx.infer_ctxt().build();
+ let outlives_environment = OutlivesEnvironment::with_bounds(
+ param_env,
+ Some(&infcx),
+ infcx.implied_bounds_tys(param_env, id, wf_tys.clone()),
+ );
+ let region_bound_pairs = outlives_environment.region_bound_pairs();
- add_constraints(&infcx, region_bound_pairs);
+ add_constraints(&infcx, region_bound_pairs);
- let errors = infcx.resolve_regions(&outlives_environment);
+ infcx.process_registered_region_obligations(
+ outlives_environment.region_bound_pairs(),
+ param_env,
+ );
+ let errors = infcx.resolve_regions(&outlives_environment);
- debug!(?errors, "errors");
+ debug!(?errors, "errors");
- // If we were able to prove that the type outlives the region without
- // an error, it must be because of the implied or explicit bounds...
- errors.is_empty()
- })
+ // If we were able to prove that the type outlives the region without
+ // an error, it must be because of the implied or explicit bounds...
+ errors.is_empty()
}
/// TypeVisitor that looks for uses of GATs like
@@ -761,7 +777,7 @@ impl<'tcx> TypeVisitor<'tcx> for GATSubstCollector<'tcx> {
fn could_be_self(trait_def_id: LocalDefId, ty: &hir::Ty<'_>) -> bool {
match ty.kind {
hir::TyKind::TraitObject([trait_ref], ..) => match trait_ref.trait_ref.path.segments {
- [s] => s.res.and_then(|r| r.opt_def_id()) == Some(trait_def_id.to_def_id()),
+ [s] => s.res.opt_def_id() == Some(trait_def_id.to_def_id()),
_ => false,
},
_ => false,
@@ -772,9 +788,9 @@ fn could_be_self(trait_def_id: LocalDefId, ty: &hir::Ty<'_>) -> bool {
/// When this is done, suggest using `Self` instead.
fn check_object_unsafe_self_trait_by_name(tcx: TyCtxt<'_>, item: &hir::TraitItem<'_>) {
let (trait_name, trait_def_id) =
- match tcx.hir().get_by_def_id(tcx.hir().get_parent_item(item.hir_id())) {
+ match tcx.hir().get_by_def_id(tcx.hir().get_parent_item(item.hir_id()).def_id) {
hir::Node::Item(item) => match item.kind {
- hir::ItemKind::Trait(..) => (item.ident, item.def_id),
+ hir::ItemKind::Trait(..) => (item.ident, item.owner_id),
_ => return,
},
_ => return,
@@ -782,18 +798,18 @@ fn check_object_unsafe_self_trait_by_name(tcx: TyCtxt<'_>, item: &hir::TraitItem
let mut trait_should_be_self = vec![];
match &item.kind {
hir::TraitItemKind::Const(ty, _) | hir::TraitItemKind::Type(_, Some(ty))
- if could_be_self(trait_def_id, ty) =>
+ if could_be_self(trait_def_id.def_id, ty) =>
{
trait_should_be_self.push(ty.span)
}
hir::TraitItemKind::Fn(sig, _) => {
for ty in sig.decl.inputs {
- if could_be_self(trait_def_id, ty) {
+ if could_be_self(trait_def_id.def_id, ty) {
trait_should_be_self.push(ty.span);
}
}
match sig.decl.output {
- hir::FnRetTy::Return(ty) if could_be_self(trait_def_id, ty) => {
+ hir::FnRetTy::Return(ty) if could_be_self(trait_def_id.def_id, ty) => {
trait_should_be_self.push(ty.span);
}
_ => {}
@@ -822,16 +838,14 @@ fn check_object_unsafe_self_trait_by_name(tcx: TyCtxt<'_>, item: &hir::TraitItem
}
fn check_impl_item(tcx: TyCtxt<'_>, impl_item: &hir::ImplItem<'_>) {
- let def_id = impl_item.def_id;
-
let (method_sig, span) = match impl_item.kind {
hir::ImplItemKind::Fn(ref sig, _) => (Some(sig), impl_item.span),
// Constrain binding and overflow error spans to `<Ty>` in `type foo = <Ty>`.
- hir::ImplItemKind::TyAlias(ty) if ty.span != DUMMY_SP => (None, ty.span),
+ hir::ImplItemKind::Type(ty) if ty.span != DUMMY_SP => (None, ty.span),
_ => (None, impl_item.span),
};
- check_associated_item(tcx, def_id, span, method_sig);
+ check_associated_item(tcx, impl_item.owner_id.def_id, span, method_sig);
}
fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) {
@@ -965,7 +979,7 @@ fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) {
}
}
-#[tracing::instrument(level = "debug", skip(tcx, span, sig_if_method))]
+#[instrument(level = "debug", skip(tcx, span, sig_if_method))]
fn check_associated_item(
tcx: TyCtxt<'_>,
item_id: LocalDefId,
@@ -976,15 +990,9 @@ fn check_associated_item(
enter_wf_checking_ctxt(tcx, span, item_id, |wfcx| {
let item = tcx.associated_item(item_id);
- let (mut implied_bounds, self_ty) = match item.container {
- ty::TraitContainer => (FxHashSet::default(), tcx.types.self_param),
- ty::ImplContainer => {
- let def_id = item.container_id(tcx);
- (
- impl_implied_bounds(tcx, wfcx.param_env, def_id.expect_local(), span),
- tcx.type_of(def_id),
- )
- }
+ let self_ty = match item.container {
+ ty::TraitContainer => tcx.types.self_param,
+ ty::ImplContainer => tcx.type_of(item.container_id(tcx)),
};
match item.kind {
@@ -1002,7 +1010,6 @@ fn check_associated_item(
sig,
hir_sig.decl,
item.def_id.expect_local(),
- &mut implied_bounds,
);
check_method_receiver(wfcx, hir_sig, item, self_ty);
}
@@ -1017,8 +1024,6 @@ fn check_associated_item(
}
}
}
-
- implied_bounds
})
}
@@ -1040,9 +1045,11 @@ fn check_type_defn<'tcx, F>(
) where
F: FnMut(&WfCheckingCtxt<'_, 'tcx>) -> Vec<AdtVariant<'tcx>>,
{
- enter_wf_checking_ctxt(tcx, item.span, item.def_id, |wfcx| {
+ let _ = tcx.representability(item.owner_id.def_id);
+
+ enter_wf_checking_ctxt(tcx, item.span, item.owner_id.def_id, |wfcx| {
let variants = lookup_fields(wfcx);
- let packed = tcx.adt_def(item.def_id).repr().packed();
+ let packed = tcx.adt_def(item.owner_id).repr().packed();
for variant in &variants {
// All field types must be well-formed.
@@ -1066,7 +1073,7 @@ fn check_type_defn<'tcx, F>(
// Just treat unresolved type expression as if it needs drop.
true
} else {
- ty.needs_drop(tcx, tcx.param_env(item.def_id))
+ ty.needs_drop(tcx, tcx.param_env(item.owner_id))
}
}
};
@@ -1098,8 +1105,6 @@ fn check_type_defn<'tcx, F>(
// Explicit `enum` discriminant values must const-evaluate successfully.
if let Some(discr_def_id) = variant.explicit_discr {
- let discr_substs = InternalSubsts::identity_for_item(tcx, discr_def_id.to_def_id());
-
let cause = traits::ObligationCause::new(
tcx.def_span(discr_def_id),
wfcx.body_id,
@@ -1108,31 +1113,28 @@ fn check_type_defn<'tcx, F>(
wfcx.register_obligation(traits::Obligation::new(
cause,
wfcx.param_env,
- ty::Binder::dummy(ty::PredicateKind::ConstEvaluatable(ty::Unevaluated::new(
- ty::WithOptConstParam::unknown(discr_def_id.to_def_id()),
- discr_substs,
- )))
+ ty::Binder::dummy(ty::PredicateKind::ConstEvaluatable(
+ ty::Const::from_anon_const(tcx, discr_def_id),
+ ))
.to_predicate(tcx),
));
}
}
- check_where_clauses(wfcx, item.span, item.def_id);
-
- // No implied bounds in a struct definition.
- FxHashSet::default()
+ check_where_clauses(wfcx, item.span, item.owner_id.def_id);
});
}
#[instrument(skip(tcx, item))]
fn check_trait(tcx: TyCtxt<'_>, item: &hir::Item<'_>) {
- debug!(?item.def_id);
+ debug!(?item.owner_id);
- let trait_def = tcx.trait_def(item.def_id);
+ let def_id = item.owner_id.def_id;
+ let trait_def = tcx.trait_def(def_id);
if trait_def.is_marker
|| matches!(trait_def.specialization_kind, TraitSpecializationKind::Marker)
{
- for associated_def_id in &*tcx.associated_item_def_ids(item.def_id) {
+ for associated_def_id in &*tcx.associated_item_def_ids(def_id) {
struct_span_err!(
tcx.sess,
tcx.def_span(*associated_def_id),
@@ -1143,10 +1145,8 @@ fn check_trait(tcx: TyCtxt<'_>, item: &hir::Item<'_>) {
}
}
- enter_wf_checking_ctxt(tcx, item.span, item.def_id, |wfcx| {
- check_where_clauses(wfcx, item.span, item.def_id);
-
- FxHashSet::default()
+ enter_wf_checking_ctxt(tcx, item.span, def_id, |wfcx| {
+ check_where_clauses(wfcx, item.span, def_id)
});
// Only check traits, don't check trait aliases
@@ -1186,9 +1186,7 @@ fn check_item_fn(
) {
enter_wf_checking_ctxt(tcx, span, def_id, |wfcx| {
let sig = tcx.fn_sig(def_id);
- let mut implied_bounds = FxHashSet::default();
- check_fn_or_method(wfcx, ident.span, sig, decl, def_id, &mut implied_bounds);
- implied_bounds
+ check_fn_or_method(wfcx, ident.span, sig, decl, def_id);
})
}
@@ -1231,13 +1229,10 @@ fn check_item_type(tcx: TyCtxt<'_>, item_id: LocalDefId, ty_span: Span, allow_fo
tcx.require_lang_item(LangItem::Sync, Some(ty_span)),
);
}
-
- // No implied bounds in a const, etc.
- FxHashSet::default()
});
}
-#[tracing::instrument(level = "debug", skip(tcx, ast_self_ty, ast_trait_ref))]
+#[instrument(level = "debug", skip(tcx, ast_self_ty, ast_trait_ref))]
fn check_impl<'tcx>(
tcx: TyCtxt<'tcx>,
item: &'tcx hir::Item<'tcx>,
@@ -1245,13 +1240,13 @@ fn check_impl<'tcx>(
ast_trait_ref: &Option<hir::TraitRef<'_>>,
constness: hir::Constness,
) {
- enter_wf_checking_ctxt(tcx, item.span, item.def_id, |wfcx| {
+ enter_wf_checking_ctxt(tcx, item.span, item.owner_id.def_id, |wfcx| {
match *ast_trait_ref {
Some(ref ast_trait_ref) => {
// `#[rustc_reservation_impl]` impls are not real impls and
// therefore don't need to be WF (the trait's `Self: Trait` predicate
// won't hold).
- let trait_ref = tcx.impl_trait_ref(item.def_id).unwrap();
+ let trait_ref = tcx.impl_trait_ref(item.owner_id).unwrap();
let trait_ref = wfcx.normalize(ast_trait_ref.path.span, None, trait_ref);
let trait_pred = ty::TraitPredicate {
trait_ref,
@@ -1273,19 +1268,21 @@ fn check_impl<'tcx>(
wfcx.register_obligations(obligations);
}
None => {
- let self_ty = tcx.type_of(item.def_id);
- let self_ty = wfcx.normalize(item.span, None, self_ty);
+ let self_ty = tcx.type_of(item.owner_id);
+ let self_ty = wfcx.normalize(
+ item.span,
+ Some(WellFormedLoc::Ty(item.hir_id().expect_owner().def_id)),
+ self_ty,
+ );
wfcx.register_wf_obligation(
ast_self_ty.span,
- Some(WellFormedLoc::Ty(item.hir_id().expect_owner())),
+ Some(WellFormedLoc::Ty(item.hir_id().expect_owner().def_id)),
self_ty.into(),
);
}
}
- check_where_clauses(wfcx, item.span, item.def_id);
-
- impl_implied_bounds(tcx, wfcx.param_env, item.def_id, item.span)
+ check_where_clauses(wfcx, item.span, item.owner_id.def_id);
});
}
@@ -1321,7 +1318,11 @@ fn check_where_clauses<'tcx>(wfcx: &WfCheckingCtxt<'_, 'tcx>, span: Span, def_id
// parameter includes another (e.g., `<T, U = T>`). In those cases, we can't
// be sure if it will error or not as user might always specify the other.
if !ty.needs_subst() {
- wfcx.register_wf_obligation(tcx.def_span(param.def_id), None, ty.into());
+ wfcx.register_wf_obligation(
+ tcx.def_span(param.def_id),
+ Some(WellFormedLoc::Ty(param.def_id.expect_local())),
+ ty.into(),
+ );
}
}
}
@@ -1426,9 +1427,7 @@ fn check_where_clauses<'tcx>(wfcx: &WfCheckingCtxt<'_, 'tcx>, span: Span, def_id
let substituted_pred = predicates.rebind(pred).subst(tcx, substs);
// Don't check non-defaulted params, dependent defaults (including lifetimes)
// or preds with multiple params.
- if substituted_pred.has_param_types_or_consts()
- || param_count.params.len() > 1
- || has_region
+ if substituted_pred.has_non_region_param() || param_count.params.len() > 1 || has_region
{
None
} else if predicates.0.predicates.iter().any(|&(p, _)| p == substituted_pred) {
@@ -1465,21 +1464,26 @@ fn check_where_clauses<'tcx>(wfcx: &WfCheckingCtxt<'_, 'tcx>, span: Span, def_id
assert_eq!(predicates.predicates.len(), predicates.spans.len());
let wf_obligations =
iter::zip(&predicates.predicates, &predicates.spans).flat_map(|(&p, &sp)| {
- traits::wf::predicate_obligations(infcx, wfcx.param_env, wfcx.body_id, p, sp)
+ traits::wf::predicate_obligations(
+ infcx,
+ wfcx.param_env.without_const(),
+ wfcx.body_id,
+ p,
+ sp,
+ )
});
let obligations: Vec<_> = wf_obligations.chain(default_obligations).collect();
wfcx.register_obligations(obligations);
}
-#[tracing::instrument(level = "debug", skip(wfcx, span, hir_decl))]
+#[instrument(level = "debug", skip(wfcx, span, hir_decl))]
fn check_fn_or_method<'tcx>(
wfcx: &WfCheckingCtxt<'_, 'tcx>,
span: Span,
sig: ty::PolyFnSig<'tcx>,
hir_decl: &hir::FnDecl<'_>,
def_id: LocalDefId,
- implied_bounds: &mut FxHashSet<Ty<'tcx>>,
) {
let tcx = wfcx.tcx();
let sig = tcx.liberate_late_bound_regions(def_id.to_def_id(), sig);
@@ -1521,23 +1525,66 @@ fn check_fn_or_method<'tcx>(
);
}
- implied_bounds.extend(sig.inputs());
-
- wfcx.register_wf_obligation(hir_decl.output.span(), None, sig.output().into());
+ wfcx.register_wf_obligation(
+ hir_decl.output.span(),
+ Some(WellFormedLoc::Param {
+ function: def_id,
+ param_idx: sig.inputs().len().try_into().unwrap(),
+ }),
+ sig.output().into(),
+ );
- // FIXME(#27579) return types should not be implied bounds
- implied_bounds.insert(sig.output());
+ check_where_clauses(wfcx, span, def_id);
- debug!(?implied_bounds);
+ check_return_position_impl_trait_in_trait_bounds(
+ tcx,
+ wfcx,
+ def_id,
+ sig.output(),
+ hir_decl.output.span(),
+ );
+}
- check_where_clauses(wfcx, span, def_id);
+/// Basically `check_associated_type_bounds`, but separated for now and should be
+/// deduplicated when RPITITs get lowered into real associated items.
+fn check_return_position_impl_trait_in_trait_bounds<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ wfcx: &WfCheckingCtxt<'_, 'tcx>,
+ fn_def_id: LocalDefId,
+ fn_output: Ty<'tcx>,
+ span: Span,
+) {
+ if let Some(assoc_item) = tcx.opt_associated_item(fn_def_id.to_def_id())
+ && assoc_item.container == ty::AssocItemContainer::TraitContainer
+ {
+ for arg in fn_output.walk() {
+ if let ty::GenericArgKind::Type(ty) = arg.unpack()
+ && let ty::Projection(proj) = ty.kind()
+ && tcx.def_kind(proj.item_def_id) == DefKind::ImplTraitPlaceholder
+ && tcx.impl_trait_in_trait_parent(proj.item_def_id) == fn_def_id.to_def_id()
+ {
+ let bounds = wfcx.tcx().explicit_item_bounds(proj.item_def_id);
+ let wf_obligations = bounds.iter().flat_map(|&(bound, bound_span)| {
+ let normalized_bound = wfcx.normalize(span, None, bound);
+ traits::wf::predicate_obligations(
+ wfcx.infcx,
+ wfcx.param_env,
+ wfcx.body_id,
+ normalized_bound,
+ bound_span,
+ )
+ });
+ wfcx.register_obligations(wf_obligations);
+ }
+ }
+ }
}
const HELP_FOR_SELF_TYPE: &str = "consider changing to `self`, `&self`, `&mut self`, `self: Box<Self>`, \
`self: Rc<Self>`, `self: Arc<Self>`, or `self: Pin<P>` (where P is one \
of the previous types except `Self`)";
-#[tracing::instrument(level = "debug", skip(wfcx))]
+#[instrument(level = "debug", skip(wfcx))]
fn check_method_receiver<'tcx>(
wfcx: &WfCheckingCtxt<'_, 'tcx>,
fn_sig: &hir::FnSig<'_>,
@@ -1629,7 +1676,7 @@ fn receiver_is_valid<'tcx>(
// `self: Self` is always valid.
if can_eq_self(receiver_ty) {
if let Err(err) = wfcx.equate_types(&cause, wfcx.param_env, self_ty, receiver_ty) {
- infcx.report_mismatched_types(&cause, self_ty, receiver_ty, err).emit();
+ infcx.err_ctxt().report_mismatched_types(&cause, self_ty, receiver_ty, err).emit();
}
return true;
}
@@ -1661,7 +1708,10 @@ fn receiver_is_valid<'tcx>(
if let Err(err) =
wfcx.equate_types(&cause, wfcx.param_env, self_ty, potential_self_ty)
{
- infcx.report_mismatched_types(&cause, self_ty, potential_self_ty, err).emit();
+ infcx
+ .err_ctxt()
+ .report_mismatched_types(&cause, self_ty, potential_self_ty, err)
+ .emit();
}
break;
@@ -1728,14 +1778,14 @@ fn check_variances_for_type_defn<'tcx>(
item: &hir::Item<'tcx>,
hir_generics: &hir::Generics<'_>,
) {
- let ty = tcx.type_of(item.def_id);
+ let ty = tcx.type_of(item.owner_id);
if tcx.has_error_field(ty) {
return;
}
- let ty_predicates = tcx.predicates_of(item.def_id);
+ let ty_predicates = tcx.predicates_of(item.owner_id);
assert_eq!(ty_predicates.parent, None);
- let variances = tcx.variances_of(item.def_id);
+ let variances = tcx.variances_of(item.owner_id);
let mut constrained_parameters: FxHashSet<_> = variances
.iter()
@@ -1748,7 +1798,7 @@ fn check_variances_for_type_defn<'tcx>(
// Lazily calculated because it is only needed in case of an error.
let explicitly_bounded_params = LazyCell::new(|| {
- let icx = crate::collect::ItemCtxt::new(tcx, item.def_id.to_def_id());
+ let icx = crate::collect::ItemCtxt::new(tcx, item.owner_id.to_def_id());
hir_generics
.predicates
.iter()
@@ -1817,6 +1867,7 @@ fn report_bivariance(
impl<'tcx> WfCheckingCtxt<'_, 'tcx> {
/// Feature gates RFC 2056 -- trivial bounds, checking for global bounds that
/// aren't true.
+ #[instrument(level = "debug", skip(self))]
fn check_false_global_bounds(&mut self) {
let tcx = self.ocx.infcx.tcx;
let mut span = self.span;
@@ -1868,10 +1919,10 @@ impl<'tcx> WfCheckingCtxt<'_, 'tcx> {
fn check_mod_type_wf(tcx: TyCtxt<'_>, module: LocalDefId) {
let items = tcx.hir_module_items(module);
- items.par_items(|item| tcx.ensure().check_well_formed(item.def_id));
- items.par_impl_items(|item| tcx.ensure().check_well_formed(item.def_id));
- items.par_trait_items(|item| tcx.ensure().check_well_formed(item.def_id));
- items.par_foreign_items(|item| tcx.ensure().check_well_formed(item.def_id));
+ items.par_items(|item| tcx.ensure().check_well_formed(item.owner_id));
+ items.par_impl_items(|item| tcx.ensure().check_well_formed(item.owner_id));
+ items.par_trait_items(|item| tcx.ensure().check_well_formed(item.owner_id));
+ items.par_foreign_items(|item| tcx.ensure().check_well_formed(item.owner_id));
}
///////////////////////////////////////////////////////////////////////////
@@ -1924,40 +1975,6 @@ impl<'a, 'tcx> WfCheckingCtxt<'a, 'tcx> {
}
}
-pub fn impl_implied_bounds<'tcx>(
- tcx: TyCtxt<'tcx>,
- param_env: ty::ParamEnv<'tcx>,
- impl_def_id: LocalDefId,
- span: Span,
-) -> FxHashSet<Ty<'tcx>> {
- // We completely ignore any obligations caused by normalizing the types
- // we assume to be well formed. Considering that the user of the implied
- // bounds will also normalize them, we leave it to them to emit errors
- // which should result in better causes and spans.
- tcx.infer_ctxt().enter(|infcx| {
- let cause = ObligationCause::misc(span, tcx.hir().local_def_id_to_hir_id(impl_def_id));
- match tcx.impl_trait_ref(impl_def_id) {
- Some(trait_ref) => {
- // Trait impl: take implied bounds from all types that
- // appear in the trait reference.
- match infcx.at(&cause, param_env).normalize(trait_ref) {
- Ok(Normalized { value, obligations: _ }) => value.substs.types().collect(),
- Err(NoSolution) => FxHashSet::default(),
- }
- }
-
- None => {
- // Inherent impl: take implied bounds from the `self` type.
- let self_ty = tcx.type_of(impl_def_id);
- match infcx.at(&cause, param_env).normalize(self_ty) {
- Ok(Normalized { value, obligations: _ }) => FxHashSet::from_iter([value]),
- Err(NoSolution) => FxHashSet::default(),
- }
- }
- }
- })
-}
-
fn error_392(
tcx: TyCtxt<'_>,
span: Span,
diff --git a/compiler/rustc_hir_analysis/src/check_unused.rs b/compiler/rustc_hir_analysis/src/check_unused.rs
new file mode 100644
index 000000000..d0c317334
--- /dev/null
+++ b/compiler/rustc_hir_analysis/src/check_unused.rs
@@ -0,0 +1,192 @@
+use crate::errors::{ExternCrateNotIdiomatic, UnusedExternCrate};
+use rustc_data_structures::fx::FxHashMap;
+use rustc_data_structures::unord::UnordSet;
+use rustc_hir as hir;
+use rustc_hir::def::DefKind;
+use rustc_hir::def_id::{DefId, LocalDefId};
+use rustc_middle::ty::TyCtxt;
+use rustc_session::lint;
+use rustc_span::{Span, Symbol};
+
+pub fn check_crate(tcx: TyCtxt<'_>) {
+ let mut used_trait_imports: UnordSet<LocalDefId> = Default::default();
+
+ for item_def_id in tcx.hir().body_owners() {
+ let imports = tcx.used_trait_imports(item_def_id);
+ debug!("GatherVisitor: item_def_id={:?} with imports {:#?}", item_def_id, imports);
+ used_trait_imports.extend(imports.items().copied());
+ }
+
+ for &id in tcx.maybe_unused_trait_imports(()) {
+ debug_assert_eq!(tcx.def_kind(id), DefKind::Use);
+ if tcx.visibility(id).is_public() {
+ continue;
+ }
+ if used_trait_imports.contains(&id) {
+ continue;
+ }
+ let item = tcx.hir().expect_item(id);
+ if item.span.is_dummy() {
+ continue;
+ }
+ let hir::ItemKind::Use(path, _) = item.kind else { unreachable!() };
+ let msg = if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(path.span) {
+ format!("unused import: `{}`", snippet)
+ } else {
+ "unused import".to_owned()
+ };
+ tcx.struct_span_lint_hir(
+ lint::builtin::UNUSED_IMPORTS,
+ item.hir_id(),
+ path.span,
+ msg,
+ |lint| lint,
+ );
+ }
+
+ unused_crates_lint(tcx);
+}
+
+fn unused_crates_lint(tcx: TyCtxt<'_>) {
+ let lint = lint::builtin::UNUSED_EXTERN_CRATES;
+
+ // Collect first the crates that are completely unused. These we
+ // can always suggest removing (no matter which edition we are
+ // in).
+ let unused_extern_crates: FxHashMap<LocalDefId, Span> = tcx
+ .maybe_unused_extern_crates(())
+ .iter()
+ .filter(|&&(def_id, _)| {
+ // The `def_id` here actually was calculated during resolution (at least
+ // at the time of this writing) and is being shipped to us via a side
+ // channel of the tcx. There may have been extra expansion phases,
+ // however, which ended up removing the `def_id` *after* expansion.
+ //
+ // As a result we need to verify that `def_id` is indeed still valid for
+ // our AST and actually present in the HIR map. If it's not there then
+ // there's safely nothing to warn about, and otherwise we carry on with
+ // our execution.
+ //
+ // Note that if we carry through to the `extern_mod_stmt_cnum` query
+ // below it'll cause a panic because `def_id` is actually bogus at this
+ // point in time otherwise.
+ if tcx.hir().find(tcx.hir().local_def_id_to_hir_id(def_id)).is_none() {
+ return false;
+ }
+ true
+ })
+ .filter(|&&(def_id, _)| {
+ tcx.extern_mod_stmt_cnum(def_id).map_or(true, |cnum| {
+ !tcx.is_compiler_builtins(cnum)
+ && !tcx.is_panic_runtime(cnum)
+ && !tcx.has_global_allocator(cnum)
+ && !tcx.has_panic_handler(cnum)
+ })
+ })
+ .cloned()
+ .collect();
+
+ // Collect all the extern crates (in a reliable order).
+ let mut crates_to_lint = vec![];
+
+ for id in tcx.hir().items() {
+ if matches!(tcx.def_kind(id.owner_id), DefKind::ExternCrate) {
+ let item = tcx.hir().item(id);
+ if let hir::ItemKind::ExternCrate(orig_name) = item.kind {
+ crates_to_lint.push(ExternCrateToLint {
+ def_id: item.owner_id.to_def_id(),
+ span: item.span,
+ orig_name,
+ warn_if_unused: !item.ident.as_str().starts_with('_'),
+ });
+ }
+ }
+ }
+
+ let extern_prelude = &tcx.resolutions(()).extern_prelude;
+
+ for extern_crate in &crates_to_lint {
+ let def_id = extern_crate.def_id.expect_local();
+ let item = tcx.hir().expect_item(def_id);
+
+ // If the crate is fully unused, we suggest removing it altogether.
+ // We do this in any edition.
+ if extern_crate.warn_if_unused {
+ if let Some(&span) = unused_extern_crates.get(&def_id) {
+ // Removal suggestion span needs to include attributes (Issue #54400)
+ let id = tcx.hir().local_def_id_to_hir_id(def_id);
+ let span_with_attrs = tcx
+ .hir()
+ .attrs(id)
+ .iter()
+ .map(|attr| attr.span)
+ .fold(span, |acc, attr_span| acc.to(attr_span));
+
+ tcx.emit_spanned_lint(lint, id, span, UnusedExternCrate { span: span_with_attrs });
+ continue;
+ }
+ }
+
+ // If we are not in Rust 2018 edition, then we don't make any further
+ // suggestions.
+ if !tcx.sess.rust_2018() {
+ continue;
+ }
+
+ // If the extern crate isn't in the extern prelude,
+ // there is no way it can be written as a `use`.
+ let orig_name = extern_crate.orig_name.unwrap_or(item.ident.name);
+ if !extern_prelude.get(&orig_name).map_or(false, |from_item| !from_item) {
+ continue;
+ }
+
+ // If the extern crate is renamed, then we cannot suggest replacing it with a use as this
+ // would not insert the new name into the prelude, where other imports in the crate may be
+ // expecting it.
+ if extern_crate.orig_name.is_some() {
+ continue;
+ }
+
+ let id = tcx.hir().local_def_id_to_hir_id(def_id);
+ // If the extern crate has any attributes, they may have funky
+ // semantics we can't faithfully represent using `use` (most
+ // notably `#[macro_use]`). Ignore it.
+ if !tcx.hir().attrs(id).is_empty() {
+ continue;
+ }
+
+ let base_replacement = match extern_crate.orig_name {
+ Some(orig_name) => format!("use {} as {};", orig_name, item.ident.name),
+ None => format!("use {};", item.ident.name),
+ };
+ let vis = tcx.sess.source_map().span_to_snippet(item.vis_span).unwrap_or_default();
+ let add_vis = |to| if vis.is_empty() { to } else { format!("{} {}", vis, to) };
+ tcx.emit_spanned_lint(
+ lint,
+ id,
+ extern_crate.span,
+ ExternCrateNotIdiomatic {
+ span: extern_crate.span,
+ msg_code: add_vis("use".to_string()),
+ suggestion_code: add_vis(base_replacement),
+ },
+ );
+ }
+}
+
+struct ExternCrateToLint {
+ /// `DefId` of the extern crate
+ def_id: DefId,
+
+ /// span from the item
+ span: Span,
+
+ /// if `Some`, then this is renamed (`extern crate orig_name as
+ /// crate_name`), and -- perhaps surprisingly -- this stores the
+ /// *original* name (`item.name` will contain the new name)
+ orig_name: Option<Symbol>,
+
+ /// if `false`, the original name started with `_`, so we shouldn't lint
+ /// about it going unused (but we should still emit idiom lints).
+ warn_if_unused: bool,
+}
diff --git a/compiler/rustc_hir_analysis/src/coherence/builtin.rs b/compiler/rustc_hir_analysis/src/coherence/builtin.rs
new file mode 100644
index 000000000..b6c91d425
--- /dev/null
+++ b/compiler/rustc_hir_analysis/src/coherence/builtin.rs
@@ -0,0 +1,572 @@
+//! Check properties that are required by built-in traits and set
+//! up data structures required by type-checking/codegen.
+
+use crate::errors::{CopyImplOnNonAdt, CopyImplOnTypeWithDtor, DropImplOnWrongItem};
+use rustc_errors::{struct_span_err, MultiSpan};
+use rustc_hir as hir;
+use rustc_hir::def_id::{DefId, LocalDefId};
+use rustc_hir::lang_items::LangItem;
+use rustc_hir::ItemKind;
+use rustc_infer::infer;
+use rustc_infer::infer::outlives::env::OutlivesEnvironment;
+use rustc_infer::infer::TyCtxtInferExt;
+use rustc_middle::ty::adjustment::CoerceUnsizedInfo;
+use rustc_middle::ty::{self, suggest_constraining_type_params, Ty, TyCtxt, TypeVisitable};
+use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt;
+use rustc_trait_selection::traits::misc::{can_type_implement_copy, CopyImplementationError};
+use rustc_trait_selection::traits::predicate_for_trait_def;
+use rustc_trait_selection::traits::{self, ObligationCause};
+use std::collections::BTreeMap;
+
+pub fn check_trait(tcx: TyCtxt<'_>, trait_def_id: DefId) {
+ let lang_items = tcx.lang_items();
+ Checker { tcx, trait_def_id }
+ .check(lang_items.drop_trait(), visit_implementation_of_drop)
+ .check(lang_items.copy_trait(), visit_implementation_of_copy)
+ .check(lang_items.coerce_unsized_trait(), visit_implementation_of_coerce_unsized)
+ .check(lang_items.dispatch_from_dyn_trait(), visit_implementation_of_dispatch_from_dyn);
+}
+
+struct Checker<'tcx> {
+ tcx: TyCtxt<'tcx>,
+ trait_def_id: DefId,
+}
+
+impl<'tcx> Checker<'tcx> {
+ fn check<F>(&self, trait_def_id: Option<DefId>, mut f: F) -> &Self
+ where
+ F: FnMut(TyCtxt<'tcx>, LocalDefId),
+ {
+ if Some(self.trait_def_id) == trait_def_id {
+ for &impl_def_id in self.tcx.hir().trait_impls(self.trait_def_id) {
+ f(self.tcx, impl_def_id);
+ }
+ }
+ self
+ }
+}
+
+fn visit_implementation_of_drop(tcx: TyCtxt<'_>, impl_did: LocalDefId) {
+ // Destructors only work on local ADT types.
+ match tcx.type_of(impl_did).kind() {
+ ty::Adt(def, _) if def.did().is_local() => return,
+ ty::Error(_) => return,
+ _ => {}
+ }
+
+ let sp = match tcx.hir().expect_item(impl_did).kind {
+ ItemKind::Impl(ref impl_) => impl_.self_ty.span,
+ _ => bug!("expected Drop impl item"),
+ };
+
+ tcx.sess.emit_err(DropImplOnWrongItem { span: sp });
+}
+
+fn visit_implementation_of_copy(tcx: TyCtxt<'_>, impl_did: LocalDefId) {
+ debug!("visit_implementation_of_copy: impl_did={:?}", impl_did);
+
+ let impl_hir_id = tcx.hir().local_def_id_to_hir_id(impl_did);
+
+ let self_type = tcx.type_of(impl_did);
+ debug!("visit_implementation_of_copy: self_type={:?} (bound)", self_type);
+
+ let param_env = tcx.param_env(impl_did);
+ assert!(!self_type.has_escaping_bound_vars());
+
+ debug!("visit_implementation_of_copy: self_type={:?} (free)", self_type);
+
+ let span = match tcx.hir().expect_item(impl_did).kind {
+ ItemKind::Impl(hir::Impl { polarity: hir::ImplPolarity::Negative(_), .. }) => return,
+ ItemKind::Impl(impl_) => impl_.self_ty.span,
+ _ => bug!("expected Copy impl item"),
+ };
+
+ let cause = traits::ObligationCause::misc(span, impl_hir_id);
+ match can_type_implement_copy(tcx, param_env, self_type, cause) {
+ Ok(()) => {}
+ Err(CopyImplementationError::InfrigingFields(fields)) => {
+ let mut err = struct_span_err!(
+ tcx.sess,
+ span,
+ E0204,
+ "the trait `Copy` may not be implemented for this type"
+ );
+
+ // We'll try to suggest constraining type parameters to fulfill the requirements of
+ // their `Copy` implementation.
+ let mut errors: BTreeMap<_, Vec<_>> = Default::default();
+ let mut bounds = vec![];
+
+ for (field, ty) in fields {
+ let field_span = tcx.def_span(field.did);
+ let field_ty_span = match tcx.hir().get_if_local(field.did) {
+ Some(hir::Node::Field(field_def)) => field_def.ty.span,
+ _ => field_span,
+ };
+ err.span_label(field_span, "this field does not implement `Copy`");
+ // Spin up a new FulfillmentContext, so we can get the _precise_ reason
+ // why this field does not implement Copy. This is useful because sometimes
+ // it is not immediately clear why Copy is not implemented for a field, since
+ // all we point at is the field itself.
+ let infcx = tcx.infer_ctxt().ignoring_regions().build();
+ for error in traits::fully_solve_bound(
+ &infcx,
+ traits::ObligationCause::dummy_with_span(field_ty_span),
+ param_env,
+ ty,
+ tcx.lang_items().copy_trait().unwrap(),
+ ) {
+ let error_predicate = error.obligation.predicate;
+ // Only note if it's not the root obligation, otherwise it's trivial and
+ // should be self-explanatory (i.e. a field literally doesn't implement Copy).
+
+ // FIXME: This error could be more descriptive, especially if the error_predicate
+ // contains a foreign type or if it's a deeply nested type...
+ if error_predicate != error.root_obligation.predicate {
+ errors
+ .entry((ty.to_string(), error_predicate.to_string()))
+ .or_default()
+ .push(error.obligation.cause.span);
+ }
+ if let ty::PredicateKind::Trait(ty::TraitPredicate {
+ trait_ref,
+ polarity: ty::ImplPolarity::Positive,
+ ..
+ }) = error_predicate.kind().skip_binder()
+ {
+ let ty = trait_ref.self_ty();
+ if let ty::Param(_) = ty.kind() {
+ bounds.push((
+ format!("{ty}"),
+ trait_ref.print_only_trait_path().to_string(),
+ Some(trait_ref.def_id),
+ ));
+ }
+ }
+ }
+ }
+ for ((ty, error_predicate), spans) in errors {
+ let span: MultiSpan = spans.into();
+ err.span_note(
+ span,
+ &format!("the `Copy` impl for `{}` requires that `{}`", ty, error_predicate),
+ );
+ }
+ suggest_constraining_type_params(
+ tcx,
+ tcx.hir().get_generics(impl_did).expect("impls always have generics"),
+ &mut err,
+ bounds.iter().map(|(param, constraint, def_id)| {
+ (param.as_str(), constraint.as_str(), *def_id)
+ }),
+ );
+ err.emit();
+ }
+ Err(CopyImplementationError::NotAnAdt) => {
+ tcx.sess.emit_err(CopyImplOnNonAdt { span });
+ }
+ Err(CopyImplementationError::HasDestructor) => {
+ tcx.sess.emit_err(CopyImplOnTypeWithDtor { span });
+ }
+ }
+}
+
+fn visit_implementation_of_coerce_unsized<'tcx>(tcx: TyCtxt<'tcx>, impl_did: LocalDefId) {
+ debug!("visit_implementation_of_coerce_unsized: impl_did={:?}", impl_did);
+
+ // Just compute this for the side-effects, in particular reporting
+ // errors; other parts of the code may demand it for the info of
+ // course.
+ let span = tcx.def_span(impl_did);
+ tcx.at(span).coerce_unsized_info(impl_did);
+}
+
+fn visit_implementation_of_dispatch_from_dyn<'tcx>(tcx: TyCtxt<'tcx>, impl_did: LocalDefId) {
+ debug!("visit_implementation_of_dispatch_from_dyn: impl_did={:?}", impl_did);
+
+ let impl_hir_id = tcx.hir().local_def_id_to_hir_id(impl_did);
+ let span = tcx.hir().span(impl_hir_id);
+
+ let dispatch_from_dyn_trait = tcx.require_lang_item(LangItem::DispatchFromDyn, Some(span));
+
+ let source = tcx.type_of(impl_did);
+ assert!(!source.has_escaping_bound_vars());
+ let target = {
+ let trait_ref = tcx.impl_trait_ref(impl_did).unwrap();
+ assert_eq!(trait_ref.def_id, dispatch_from_dyn_trait);
+
+ trait_ref.substs.type_at(1)
+ };
+
+ debug!("visit_implementation_of_dispatch_from_dyn: {:?} -> {:?}", source, target);
+
+ let param_env = tcx.param_env(impl_did);
+
+ let create_err = |msg: &str| struct_span_err!(tcx.sess, span, E0378, "{}", msg);
+
+ let infcx = tcx.infer_ctxt().build();
+ let cause = ObligationCause::misc(span, impl_hir_id);
+
+ use rustc_type_ir::sty::TyKind::*;
+ match (source.kind(), target.kind()) {
+ (&Ref(r_a, _, mutbl_a), Ref(r_b, _, mutbl_b))
+ if infcx.at(&cause, param_env).eq(r_a, *r_b).is_ok() && mutbl_a == *mutbl_b => {}
+ (&RawPtr(tm_a), &RawPtr(tm_b)) if tm_a.mutbl == tm_b.mutbl => (),
+ (&Adt(def_a, substs_a), &Adt(def_b, substs_b))
+ if def_a.is_struct() && def_b.is_struct() =>
+ {
+ if def_a != def_b {
+ let source_path = tcx.def_path_str(def_a.did());
+ let target_path = tcx.def_path_str(def_b.did());
+
+ create_err(&format!(
+ "the trait `DispatchFromDyn` may only be implemented \
+ for a coercion between structures with the same \
+ definition; expected `{}`, found `{}`",
+ source_path, target_path,
+ ))
+ .emit();
+
+ return;
+ }
+
+ if def_a.repr().c() || def_a.repr().packed() {
+ create_err(
+ "structs implementing `DispatchFromDyn` may not have \
+ `#[repr(packed)]` or `#[repr(C)]`",
+ )
+ .emit();
+ }
+
+ let fields = &def_a.non_enum_variant().fields;
+
+ let coerced_fields = fields
+ .iter()
+ .filter(|field| {
+ let ty_a = field.ty(tcx, substs_a);
+ let ty_b = field.ty(tcx, substs_b);
+
+ if let Ok(layout) = tcx.layout_of(param_env.and(ty_a)) {
+ if layout.is_zst() && layout.align.abi.bytes() == 1 {
+ // ignore ZST fields with alignment of 1 byte
+ return false;
+ }
+ }
+
+ if let Ok(ok) = infcx.at(&cause, param_env).eq(ty_a, ty_b) {
+ if ok.obligations.is_empty() {
+ create_err(
+ "the trait `DispatchFromDyn` may only be implemented \
+ for structs containing the field being coerced, \
+ ZST fields with 1 byte alignment, and nothing else",
+ )
+ .note(&format!(
+ "extra field `{}` of type `{}` is not allowed",
+ field.name, ty_a,
+ ))
+ .emit();
+
+ return false;
+ }
+ }
+
+ return true;
+ })
+ .collect::<Vec<_>>();
+
+ if coerced_fields.is_empty() {
+ create_err(
+ "the trait `DispatchFromDyn` may only be implemented \
+ for a coercion between structures with a single field \
+ being coerced, none found",
+ )
+ .emit();
+ } else if coerced_fields.len() > 1 {
+ create_err("implementing the `DispatchFromDyn` trait requires multiple coercions")
+ .note(
+ "the trait `DispatchFromDyn` may only be implemented \
+ for a coercion between structures with a single field \
+ being coerced",
+ )
+ .note(&format!(
+ "currently, {} fields need coercions: {}",
+ coerced_fields.len(),
+ coerced_fields
+ .iter()
+ .map(|field| {
+ format!(
+ "`{}` (`{}` to `{}`)",
+ field.name,
+ field.ty(tcx, substs_a),
+ field.ty(tcx, substs_b),
+ )
+ })
+ .collect::<Vec<_>>()
+ .join(", ")
+ ))
+ .emit();
+ } else {
+ let errors = traits::fully_solve_obligations(
+ &infcx,
+ coerced_fields.into_iter().map(|field| {
+ predicate_for_trait_def(
+ tcx,
+ param_env,
+ cause.clone(),
+ dispatch_from_dyn_trait,
+ 0,
+ field.ty(tcx, substs_a),
+ &[field.ty(tcx, substs_b).into()],
+ )
+ }),
+ );
+ if !errors.is_empty() {
+ infcx.err_ctxt().report_fulfillment_errors(&errors, None, false);
+ }
+
+ // Finally, resolve all regions.
+ let outlives_env = OutlivesEnvironment::new(param_env);
+ infcx.check_region_obligations_and_report_errors(impl_did, &outlives_env);
+ }
+ }
+ _ => {
+ create_err(
+ "the trait `DispatchFromDyn` may only be implemented \
+ for a coercion between structures",
+ )
+ .emit();
+ }
+ }
+}
+
+pub fn coerce_unsized_info<'tcx>(tcx: TyCtxt<'tcx>, impl_did: DefId) -> CoerceUnsizedInfo {
+ debug!("compute_coerce_unsized_info(impl_did={:?})", impl_did);
+
+ // this provider should only get invoked for local def-ids
+ let impl_did = impl_did.expect_local();
+ let span = tcx.def_span(impl_did);
+
+ let coerce_unsized_trait = tcx.require_lang_item(LangItem::CoerceUnsized, Some(span));
+
+ let unsize_trait = tcx.lang_items().require(LangItem::Unsize).unwrap_or_else(|err| {
+ tcx.sess.fatal(&format!("`CoerceUnsized` implementation {}", err.to_string()));
+ });
+
+ let source = tcx.type_of(impl_did);
+ let trait_ref = tcx.impl_trait_ref(impl_did).unwrap();
+ assert_eq!(trait_ref.def_id, coerce_unsized_trait);
+ let target = trait_ref.substs.type_at(1);
+ debug!("visit_implementation_of_coerce_unsized: {:?} -> {:?} (bound)", source, target);
+
+ let param_env = tcx.param_env(impl_did);
+ assert!(!source.has_escaping_bound_vars());
+
+ let err_info = CoerceUnsizedInfo { custom_kind: None };
+
+ debug!("visit_implementation_of_coerce_unsized: {:?} -> {:?} (free)", source, target);
+
+ let infcx = tcx.infer_ctxt().build();
+ let impl_hir_id = tcx.hir().local_def_id_to_hir_id(impl_did);
+ let cause = ObligationCause::misc(span, impl_hir_id);
+ let check_mutbl = |mt_a: ty::TypeAndMut<'tcx>,
+ mt_b: ty::TypeAndMut<'tcx>,
+ mk_ptr: &dyn Fn(Ty<'tcx>) -> Ty<'tcx>| {
+ if (mt_a.mutbl, mt_b.mutbl) == (hir::Mutability::Not, hir::Mutability::Mut) {
+ infcx
+ .err_ctxt()
+ .report_mismatched_types(
+ &cause,
+ mk_ptr(mt_b.ty),
+ target,
+ ty::error::TypeError::Mutability,
+ )
+ .emit();
+ }
+ (mt_a.ty, mt_b.ty, unsize_trait, None)
+ };
+ let (source, target, trait_def_id, kind) = match (source.kind(), target.kind()) {
+ (&ty::Ref(r_a, ty_a, mutbl_a), &ty::Ref(r_b, ty_b, mutbl_b)) => {
+ infcx.sub_regions(infer::RelateObjectBound(span), r_b, r_a);
+ let mt_a = ty::TypeAndMut { ty: ty_a, mutbl: mutbl_a };
+ let mt_b = ty::TypeAndMut { ty: ty_b, mutbl: mutbl_b };
+ check_mutbl(mt_a, mt_b, &|ty| tcx.mk_imm_ref(r_b, ty))
+ }
+
+ (&ty::Ref(_, ty_a, mutbl_a), &ty::RawPtr(mt_b)) => {
+ let mt_a = ty::TypeAndMut { ty: ty_a, mutbl: mutbl_a };
+ check_mutbl(mt_a, mt_b, &|ty| tcx.mk_imm_ptr(ty))
+ }
+
+ (&ty::RawPtr(mt_a), &ty::RawPtr(mt_b)) => check_mutbl(mt_a, mt_b, &|ty| tcx.mk_imm_ptr(ty)),
+
+ (&ty::Adt(def_a, substs_a), &ty::Adt(def_b, substs_b))
+ if def_a.is_struct() && def_b.is_struct() =>
+ {
+ if def_a != def_b {
+ let source_path = tcx.def_path_str(def_a.did());
+ let target_path = tcx.def_path_str(def_b.did());
+ struct_span_err!(
+ tcx.sess,
+ span,
+ E0377,
+ "the trait `CoerceUnsized` may only be implemented \
+ for a coercion between structures with the same \
+ definition; expected `{}`, found `{}`",
+ source_path,
+ target_path
+ )
+ .emit();
+ return err_info;
+ }
+
+ // Here we are considering a case of converting
+ // `S<P0...Pn>` to S<Q0...Qn>`. As an example, let's imagine a struct `Foo<T, U>`,
+ // which acts like a pointer to `U`, but carries along some extra data of type `T`:
+ //
+ // struct Foo<T, U> {
+ // extra: T,
+ // ptr: *mut U,
+ // }
+ //
+ // We might have an impl that allows (e.g.) `Foo<T, [i32; 3]>` to be unsized
+ // to `Foo<T, [i32]>`. That impl would look like:
+ //
+ // impl<T, U: Unsize<V>, V> CoerceUnsized<Foo<T, V>> for Foo<T, U> {}
+ //
+ // Here `U = [i32; 3]` and `V = [i32]`. At runtime,
+ // when this coercion occurs, we would be changing the
+ // field `ptr` from a thin pointer of type `*mut [i32;
+ // 3]` to a fat pointer of type `*mut [i32]` (with
+ // extra data `3`). **The purpose of this check is to
+ // make sure that we know how to do this conversion.**
+ //
+ // To check if this impl is legal, we would walk down
+ // the fields of `Foo` and consider their types with
+ // both substitutes. We are looking to find that
+ // exactly one (non-phantom) field has changed its
+ // type, which we will expect to be the pointer that
+ // is becoming fat (we could probably generalize this
+ // to multiple thin pointers of the same type becoming
+ // fat, but we don't). In this case:
+ //
+ // - `extra` has type `T` before and type `T` after
+ // - `ptr` has type `*mut U` before and type `*mut V` after
+ //
+ // Since just one field changed, we would then check
+ // that `*mut U: CoerceUnsized<*mut V>` is implemented
+ // (in other words, that we know how to do this
+ // conversion). This will work out because `U:
+ // Unsize<V>`, and we have a builtin rule that `*mut
+ // U` can be coerced to `*mut V` if `U: Unsize<V>`.
+ let fields = &def_a.non_enum_variant().fields;
+ let diff_fields = fields
+ .iter()
+ .enumerate()
+ .filter_map(|(i, f)| {
+ let (a, b) = (f.ty(tcx, substs_a), f.ty(tcx, substs_b));
+
+ if tcx.type_of(f.did).is_phantom_data() {
+ // Ignore PhantomData fields
+ return None;
+ }
+
+ // Ignore fields that aren't changed; it may
+ // be that we could get away with subtyping or
+ // something more accepting, but we use
+ // equality because we want to be able to
+ // perform this check without computing
+ // variance where possible. (This is because
+ // we may have to evaluate constraint
+ // expressions in the course of execution.)
+ // See e.g., #41936.
+ if let Ok(ok) = infcx.at(&cause, param_env).eq(a, b) {
+ if ok.obligations.is_empty() {
+ return None;
+ }
+ }
+
+ // Collect up all fields that were significantly changed
+ // i.e., those that contain T in coerce_unsized T -> U
+ Some((i, a, b))
+ })
+ .collect::<Vec<_>>();
+
+ if diff_fields.is_empty() {
+ struct_span_err!(
+ tcx.sess,
+ span,
+ E0374,
+ "the trait `CoerceUnsized` may only be implemented \
+ for a coercion between structures with one field \
+ being coerced, none found"
+ )
+ .emit();
+ return err_info;
+ } else if diff_fields.len() > 1 {
+ let item = tcx.hir().expect_item(impl_did);
+ let span =
+ if let ItemKind::Impl(hir::Impl { of_trait: Some(ref t), .. }) = item.kind {
+ t.path.span
+ } else {
+ tcx.def_span(impl_did)
+ };
+
+ struct_span_err!(
+ tcx.sess,
+ span,
+ E0375,
+ "implementing the trait \
+ `CoerceUnsized` requires multiple \
+ coercions"
+ )
+ .note(
+ "`CoerceUnsized` may only be implemented for \
+ a coercion between structures with one field being coerced",
+ )
+ .note(&format!(
+ "currently, {} fields need coercions: {}",
+ diff_fields.len(),
+ diff_fields
+ .iter()
+ .map(|&(i, a, b)| { format!("`{}` (`{}` to `{}`)", fields[i].name, a, b) })
+ .collect::<Vec<_>>()
+ .join(", ")
+ ))
+ .span_label(span, "requires multiple coercions")
+ .emit();
+ return err_info;
+ }
+
+ let (i, a, b) = diff_fields[0];
+ let kind = ty::adjustment::CustomCoerceUnsized::Struct(i);
+ (a, b, coerce_unsized_trait, Some(kind))
+ }
+
+ _ => {
+ struct_span_err!(
+ tcx.sess,
+ span,
+ E0376,
+ "the trait `CoerceUnsized` may only be implemented \
+ for a coercion between structures"
+ )
+ .emit();
+ return err_info;
+ }
+ };
+
+ // Register an obligation for `A: Trait<B>`.
+ let cause = traits::ObligationCause::misc(span, impl_hir_id);
+ let predicate =
+ predicate_for_trait_def(tcx, param_env, cause, trait_def_id, 0, source, &[target.into()]);
+ let errors = traits::fully_solve_obligation(&infcx, predicate);
+ if !errors.is_empty() {
+ infcx.err_ctxt().report_fulfillment_errors(&errors, None, false);
+ }
+
+ // Finally, resolve all regions.
+ let outlives_env = OutlivesEnvironment::new(param_env);
+ infcx.check_region_obligations_and_report_errors(impl_did, &outlives_env);
+
+ CoerceUnsizedInfo { custom_kind: kind }
+}
diff --git a/compiler/rustc_typeck/src/coherence/inherent_impls.rs b/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs
index 52aad636f..2890c149b 100644
--- a/compiler/rustc_typeck/src/coherence/inherent_impls.rs
+++ b/compiler/rustc_hir_analysis/src/coherence/inherent_impls.rs
@@ -58,7 +58,7 @@ const ADD_ATTR: &str =
impl<'tcx> InherentCollect<'tcx> {
fn check_def_id(&mut self, item: &hir::Item<'_>, self_ty: Ty<'tcx>, def_id: DefId) {
- let impl_def_id = item.def_id;
+ let impl_def_id = item.owner_id;
if let Some(def_id) = def_id.as_local() {
// Add the implementation to the mapping from implementation to base
// type def ID, if there is a base type for this implementation and
@@ -89,7 +89,7 @@ impl<'tcx> InherentCollect<'tcx> {
for impl_item in items {
if !self
.tcx
- .has_attr(impl_item.id.def_id.to_def_id(), sym::rustc_allow_incoherent_impl)
+ .has_attr(impl_item.id.owner_id.to_def_id(), sym::rustc_allow_incoherent_impl)
{
struct_span_err!(
self.tcx.sess,
@@ -105,7 +105,7 @@ impl<'tcx> InherentCollect<'tcx> {
}
if let Some(simp) = simplify_type(self.tcx, self_ty, TreatParams::AsInfer) {
- self.impls_map.incoherent_impls.entry(simp).or_default().push(impl_def_id);
+ self.impls_map.incoherent_impls.entry(simp).or_default().push(impl_def_id.def_id);
} else {
bug!("unexpected self type: {:?}", self_ty);
}
@@ -135,7 +135,7 @@ impl<'tcx> InherentCollect<'tcx> {
for item in items {
if !self
.tcx
- .has_attr(item.id.def_id.to_def_id(), sym::rustc_allow_incoherent_impl)
+ .has_attr(item.id.owner_id.to_def_id(), sym::rustc_allow_incoherent_impl)
{
struct_span_err!(
self.tcx.sess,
@@ -177,7 +177,7 @@ impl<'tcx> InherentCollect<'tcx> {
}
fn check_item(&mut self, id: hir::ItemId) {
- if !matches!(self.tcx.def_kind(id.def_id), DefKind::Impl) {
+ if !matches!(self.tcx.def_kind(id.owner_id), DefKind::Impl) {
return;
}
@@ -186,7 +186,7 @@ impl<'tcx> InherentCollect<'tcx> {
return;
};
- let self_ty = self.tcx.type_of(item.def_id);
+ let self_ty = self.tcx.type_of(item.owner_id);
match *self_ty.kind() {
ty::Adt(def, _) => {
self.check_def_id(item, self_ty, def.did());
@@ -220,7 +220,9 @@ impl<'tcx> InherentCollect<'tcx> {
| ty::Ref(..)
| ty::Never
| ty::FnPtr(_)
- | ty::Tuple(..) => self.check_primitive_impl(item.def_id, self_ty, items, ty.span),
+ | ty::Tuple(..) => {
+ self.check_primitive_impl(item.owner_id.def_id, self_ty, items, ty.span)
+ }
ty::Projection(..) | ty::Opaque(..) | ty::Param(_) => {
let mut err = struct_span_err!(
self.tcx.sess,
@@ -241,7 +243,7 @@ impl<'tcx> InherentCollect<'tcx> {
| ty::Bound(..)
| ty::Placeholder(_)
| ty::Infer(_) => {
- bug!("unexpected impl self type of impl: {:?} {:?}", item.def_id, self_ty);
+ bug!("unexpected impl self type of impl: {:?} {:?}", item.owner_id, self_ty);
}
ty::Error(_) => {}
}
diff --git a/compiler/rustc_typeck/src/coherence/inherent_impls_overlap.rs b/compiler/rustc_hir_analysis/src/coherence/inherent_impls_overlap.rs
index 03e076bf5..972769eb1 100644
--- a/compiler/rustc_typeck/src/coherence/inherent_impls_overlap.rs
+++ b/compiler/rustc_hir_analysis/src/coherence/inherent_impls_overlap.rs
@@ -58,6 +58,37 @@ impl<'tcx> InherentOverlapChecker<'tcx> {
== item2.ident(self.tcx).normalize_to_macros_2_0()
}
+ fn check_for_duplicate_items_in_impl(&self, impl_: DefId) {
+ let impl_items = self.tcx.associated_items(impl_);
+
+ let mut seen_items = FxHashMap::default();
+ for impl_item in impl_items.in_definition_order() {
+ let span = self.tcx.def_span(impl_item.def_id);
+ let ident = impl_item.ident(self.tcx);
+
+ let norm_ident = ident.normalize_to_macros_2_0();
+ match seen_items.entry(norm_ident) {
+ Entry::Occupied(entry) => {
+ let former = entry.get();
+ let mut err = struct_span_err!(
+ self.tcx.sess,
+ span,
+ E0592,
+ "duplicate definitions with name `{}`",
+ ident,
+ );
+ err.span_label(span, format!("duplicate definitions for `{}`", ident));
+ err.span_label(*former, format!("other definition for `{}`", ident));
+
+ err.emit();
+ }
+ Entry::Vacant(entry) => {
+ entry.insert(span);
+ }
+ }
+ }
+ }
+
fn check_for_common_items_in_impls(
&self,
impl1: DefId,
@@ -117,29 +148,22 @@ impl<'tcx> InherentOverlapChecker<'tcx> {
// inherent impls without warning.
SkipLeakCheck::Yes,
overlap_mode,
- |overlap| {
- self.check_for_common_items_in_impls(impl1_def_id, impl2_def_id, overlap);
- false
- },
- || true,
- );
+ )
+ .map_or(true, |overlap| {
+ self.check_for_common_items_in_impls(impl1_def_id, impl2_def_id, overlap);
+ false
+ });
}
fn check_item(&mut self, id: hir::ItemId) {
- let def_kind = self.tcx.def_kind(id.def_id);
+ let def_kind = self.tcx.def_kind(id.owner_id);
if !matches!(def_kind, DefKind::Enum | DefKind::Struct | DefKind::Trait | DefKind::Union) {
return;
}
- let impls = self.tcx.inherent_impls(id.def_id);
+ let impls = self.tcx.inherent_impls(id.owner_id);
- // If there is only one inherent impl block,
- // there is nothing to overlap check it with
- if impls.len() <= 1 {
- return;
- }
-
- let overlap_mode = OverlapMode::get(self.tcx, id.def_id.to_def_id());
+ let overlap_mode = OverlapMode::get(self.tcx, id.owner_id.to_def_id());
let impls_items = impls
.iter()
@@ -152,6 +176,8 @@ impl<'tcx> InherentOverlapChecker<'tcx> {
const ALLOCATING_ALGO_THRESHOLD: usize = 500;
if impls.len() < ALLOCATING_ALGO_THRESHOLD {
for (i, &(&impl1_def_id, impl_items1)) in impls_items.iter().enumerate() {
+ self.check_for_duplicate_items_in_impl(impl1_def_id);
+
for &(&impl2_def_id, impl_items2) in &impls_items[(i + 1)..] {
if self.impls_have_common_items(impl_items1, impl_items2) {
self.check_for_overlapping_inherent_impls(
@@ -290,6 +316,8 @@ impl<'tcx> InherentOverlapChecker<'tcx> {
impl_blocks.sort_unstable();
for (i, &impl1_items_idx) in impl_blocks.iter().enumerate() {
let &(&impl1_def_id, impl_items1) = &impls_items[impl1_items_idx];
+ self.check_for_duplicate_items_in_impl(impl1_def_id);
+
for &impl2_items_idx in impl_blocks[(i + 1)..].iter() {
let &(&impl2_def_id, impl_items2) = &impls_items[impl2_items_idx];
if self.impls_have_common_items(impl_items1, impl_items2) {
diff --git a/compiler/rustc_typeck/src/coherence/mod.rs b/compiler/rustc_hir_analysis/src/coherence/mod.rs
index ae9ebe590..ae9ebe590 100644
--- a/compiler/rustc_typeck/src/coherence/mod.rs
+++ b/compiler/rustc_hir_analysis/src/coherence/mod.rs
diff --git a/compiler/rustc_typeck/src/coherence/orphan.rs b/compiler/rustc_hir_analysis/src/coherence/orphan.rs
index 1608550aa..bb45c3823 100644
--- a/compiler/rustc_typeck/src/coherence/orphan.rs
+++ b/compiler/rustc_hir_analysis/src/coherence/orphan.rs
@@ -2,10 +2,9 @@
//! crate or pertains to a type defined in this crate.
use rustc_data_structures::fx::FxHashSet;
-use rustc_errors::struct_span_err;
+use rustc_errors::{struct_span_err, DelayDm};
use rustc_errors::{Diagnostic, ErrorGuaranteed};
use rustc_hir as hir;
-use rustc_infer::infer::TyCtxtInferExt;
use rustc_middle::ty::subst::GenericArgKind;
use rustc_middle::ty::subst::InternalSubsts;
use rustc_middle::ty::util::IgnoreRegions;
@@ -43,7 +42,7 @@ fn do_orphan_check_impl<'tcx>(
) -> Result<(), ErrorGuaranteed> {
let trait_def_id = trait_ref.def_id;
- let item = tcx.hir().item(hir::ItemId { def_id });
+ let item = tcx.hir().expect_item(def_id);
let hir::ItemKind::Impl(ref impl_) = item.kind else {
bug!("{:?} is not an impl: {:?}", def_id, item);
};
@@ -102,7 +101,7 @@ fn do_orphan_check_impl<'tcx>(
span_bug!(sp, "opaque type not found, but `has_opaque_types` is set")
}
- match traits::orphan_check(tcx, item.def_id.to_def_id()) {
+ match traits::orphan_check(tcx, item.owner_id.to_def_id()) {
Ok(()) => {}
Err(err) => emit_orphan_check_error(
tcx,
@@ -229,12 +228,8 @@ fn emit_orphan_check_error<'tcx>(
"only traits defined in the current crate {msg}"
);
err.span_label(sp, "impl doesn't use only types from inside the current crate");
- for (ty, is_target_ty) in &tys {
- let mut ty = *ty;
- tcx.infer_ctxt().enter(|infcx| {
- // Remove the lifetimes unnecessary for this error.
- ty = infcx.freshen(ty);
- });
+ for &(mut ty, is_target_ty) in &tys {
+ ty = tcx.erase_regions(ty);
ty = match ty.kind() {
// Remove the type arguments from the output, as they are not relevant.
// You can think of this as the reverse of `resolve_vars_if_possible`.
@@ -264,7 +259,7 @@ fn emit_orphan_check_error<'tcx>(
};
let msg = format!("{} is not defined in the current crate{}", ty, postfix);
- if *is_target_ty {
+ if is_target_ty {
// Point at `D<A>` in `impl<A, B> for C<B> in D<A>`
err.span_label(self_ty_span, &msg);
} else {
@@ -417,30 +412,31 @@ fn lint_auto_trait_impl<'tcx>(
lint::builtin::SUSPICIOUS_AUTO_TRAIT_IMPLS,
tcx.hir().local_def_id_to_hir_id(impl_def_id),
tcx.def_span(impl_def_id),
- |err| {
- let item_span = tcx.def_span(self_type_did);
- let self_descr = tcx.def_kind(self_type_did).descr(self_type_did);
- let mut err = err.build(&format!(
+ DelayDm(|| {
+ format!(
"cross-crate traits with a default impl, like `{}`, \
should not be specialized",
tcx.def_path_str(trait_ref.def_id),
- ));
+ )
+ }),
+ |lint| {
+ let item_span = tcx.def_span(self_type_did);
+ let self_descr = tcx.def_kind(self_type_did).descr(self_type_did);
match arg {
ty::util::NotUniqueParam::DuplicateParam(arg) => {
- err.note(&format!("`{}` is mentioned multiple times", arg));
+ lint.note(&format!("`{}` is mentioned multiple times", arg));
}
ty::util::NotUniqueParam::NotParam(arg) => {
- err.note(&format!("`{}` is not a generic parameter", arg));
+ lint.note(&format!("`{}` is not a generic parameter", arg));
}
}
- err.span_note(
+ lint.span_note(
item_span,
&format!(
"try using the same sequence of generic parameters as the {} definition",
self_descr,
),
- );
- err.emit();
+ )
},
);
}
diff --git a/compiler/rustc_hir_analysis/src/coherence/unsafety.rs b/compiler/rustc_hir_analysis/src/coherence/unsafety.rs
new file mode 100644
index 000000000..a34815b45
--- /dev/null
+++ b/compiler/rustc_hir_analysis/src/coherence/unsafety.rs
@@ -0,0 +1,96 @@
+//! Unsafety checker: every impl either implements a trait defined in this
+//! crate or pertains to a type defined in this crate.
+
+use rustc_errors::struct_span_err;
+use rustc_hir as hir;
+use rustc_hir::def::DefKind;
+use rustc_hir::Unsafety;
+use rustc_middle::ty::TyCtxt;
+use rustc_span::def_id::LocalDefId;
+
+pub(super) fn check_item(tcx: TyCtxt<'_>, def_id: LocalDefId) {
+ debug_assert!(matches!(tcx.def_kind(def_id), DefKind::Impl));
+ let item = tcx.hir().expect_item(def_id);
+ let hir::ItemKind::Impl(ref impl_) = item.kind else { bug!() };
+
+ if let Some(trait_ref) = tcx.impl_trait_ref(item.owner_id) {
+ let trait_def = tcx.trait_def(trait_ref.def_id);
+ let unsafe_attr =
+ impl_.generics.params.iter().find(|p| p.pure_wrt_drop).map(|_| "may_dangle");
+ match (trait_def.unsafety, unsafe_attr, impl_.unsafety, impl_.polarity) {
+ (Unsafety::Normal, None, Unsafety::Unsafe, hir::ImplPolarity::Positive) => {
+ struct_span_err!(
+ tcx.sess,
+ item.span,
+ E0199,
+ "implementing the trait `{}` is not unsafe",
+ trait_ref.print_only_trait_path()
+ )
+ .span_suggestion_verbose(
+ item.span.with_hi(item.span.lo() + rustc_span::BytePos(7)),
+ "remove `unsafe` from this trait implementation",
+ "",
+ rustc_errors::Applicability::MachineApplicable,
+ )
+ .emit();
+ }
+
+ (Unsafety::Unsafe, _, Unsafety::Normal, hir::ImplPolarity::Positive) => {
+ struct_span_err!(
+ tcx.sess,
+ item.span,
+ E0200,
+ "the trait `{}` requires an `unsafe impl` declaration",
+ trait_ref.print_only_trait_path()
+ )
+ .note(format!(
+ "the trait `{}` enforces invariants that the compiler can't check. \
+ Review the trait documentation and make sure this implementation \
+ upholds those invariants before adding the `unsafe` keyword",
+ trait_ref.print_only_trait_path()
+ ))
+ .span_suggestion_verbose(
+ item.span.shrink_to_lo(),
+ "add `unsafe` to this trait implementation",
+ "unsafe ",
+ rustc_errors::Applicability::MaybeIncorrect,
+ )
+ .emit();
+ }
+
+ (Unsafety::Normal, Some(attr_name), Unsafety::Normal, hir::ImplPolarity::Positive) => {
+ struct_span_err!(
+ tcx.sess,
+ item.span,
+ E0569,
+ "requires an `unsafe impl` declaration due to `#[{}]` attribute",
+ attr_name
+ )
+ .note(format!(
+ "the trait `{}` enforces invariants that the compiler can't check. \
+ Review the trait documentation and make sure this implementation \
+ upholds those invariants before adding the `unsafe` keyword",
+ trait_ref.print_only_trait_path()
+ ))
+ .span_suggestion_verbose(
+ item.span.shrink_to_lo(),
+ "add `unsafe` to this trait implementation",
+ "unsafe ",
+ rustc_errors::Applicability::MaybeIncorrect,
+ )
+ .emit();
+ }
+
+ (_, _, Unsafety::Unsafe, hir::ImplPolarity::Negative(_)) => {
+ // Reported in AST validation
+ tcx.sess.delay_span_bug(item.span, "unsafe negative impl");
+ }
+ (_, _, Unsafety::Normal, hir::ImplPolarity::Negative(_))
+ | (Unsafety::Unsafe, _, Unsafety::Unsafe, hir::ImplPolarity::Positive)
+ | (Unsafety::Normal, Some(_), Unsafety::Unsafe, hir::ImplPolarity::Positive)
+ | (Unsafety::Normal, None, Unsafety::Normal, _) => {
+ // OK
+ }
+ }
+ }
+}
diff --git a/compiler/rustc_hir_analysis/src/collect.rs b/compiler/rustc_hir_analysis/src/collect.rs
new file mode 100644
index 000000000..346d2e2fc
--- /dev/null
+++ b/compiler/rustc_hir_analysis/src/collect.rs
@@ -0,0 +1,2263 @@
+//! "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
+//! definition, collection will figure out the type and signature of the
+//! function, but it will not visit the *body* of the function in any way,
+//! nor examine type annotations on local variables (that's the job of
+//! type *checking*).
+//!
+//! Collecting is ultimately defined by a bundle of queries that
+//! inquire after various facts about the items in the crate (e.g.,
+//! `type_of`, `generics_of`, `predicates_of`, etc). See the `provide` function
+//! for the full set.
+//!
+//! At present, however, we do run collection across all items in the
+//! crate as a kind of pass. This should eventually be factored away.
+
+use crate::astconv::AstConv;
+use crate::check::intrinsic::intrinsic_operation_unsafety;
+use crate::errors;
+use rustc_ast as ast;
+use rustc_ast::{MetaItemKind, NestedMetaItem};
+use rustc_attr::{list_contains_name, InlineAttr, InstructionSetAttr, OptimizeAttr};
+use rustc_data_structures::captures::Captures;
+use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed, StashKey};
+use rustc_hir as hir;
+use rustc_hir::def::CtorKind;
+use rustc_hir::def_id::{DefId, LocalDefId, LOCAL_CRATE};
+use rustc_hir::intravisit::{self, Visitor};
+use rustc_hir::weak_lang_items;
+use rustc_hir::{GenericParamKind, Node};
+use rustc_middle::hir::nested_filter;
+use rustc_middle::middle::codegen_fn_attrs::{CodegenFnAttrFlags, CodegenFnAttrs};
+use rustc_middle::mir::mono::Linkage;
+use rustc_middle::ty::query::Providers;
+use rustc_middle::ty::util::{Discr, IntTypeExt};
+use rustc_middle::ty::ReprOptions;
+use rustc_middle::ty::{self, AdtKind, Const, DefIdTree, IsSuggestable, Ty, TyCtxt};
+use rustc_session::lint;
+use rustc_session::parse::feature_err;
+use rustc_span::symbol::{kw, sym, Ident, Symbol};
+use rustc_span::Span;
+use rustc_target::spec::{abi, SanitizerSet};
+use rustc_trait_selection::traits::error_reporting::suggestions::NextTypeParamName;
+use std::iter;
+
+mod generics_of;
+mod item_bounds;
+mod lifetimes;
+mod predicates_of;
+mod type_of;
+
+///////////////////////////////////////////////////////////////////////////
+// Main entry point
+
+fn collect_mod_item_types(tcx: TyCtxt<'_>, module_def_id: LocalDefId) {
+ tcx.hir().visit_item_likes_in_module(module_def_id, &mut CollectItemTypesVisitor { tcx });
+}
+
+pub fn provide(providers: &mut Providers) {
+ lifetimes::provide(providers);
+ *providers = Providers {
+ opt_const_param_of: type_of::opt_const_param_of,
+ type_of: type_of::type_of,
+ item_bounds: item_bounds::item_bounds,
+ explicit_item_bounds: item_bounds::explicit_item_bounds,
+ generics_of: generics_of::generics_of,
+ predicates_of: predicates_of::predicates_of,
+ predicates_defined_on,
+ explicit_predicates_of: predicates_of::explicit_predicates_of,
+ super_predicates_of: predicates_of::super_predicates_of,
+ super_predicates_that_define_assoc_type:
+ predicates_of::super_predicates_that_define_assoc_type,
+ trait_explicit_predicates_and_bounds: predicates_of::trait_explicit_predicates_and_bounds,
+ type_param_predicates: predicates_of::type_param_predicates,
+ trait_def,
+ adt_def,
+ fn_sig,
+ impl_trait_ref,
+ impl_polarity,
+ is_foreign_item,
+ generator_kind,
+ codegen_fn_attrs,
+ asm_target_features,
+ collect_mod_item_types,
+ should_inherit_track_caller,
+ ..*providers
+ };
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+/// Context specific to some particular item. This is what implements
+/// [`AstConv`].
+///
+/// # `ItemCtxt` vs `FnCtxt`
+///
+/// `ItemCtxt` is primarily used to type-check item signatures and lower them
+/// from HIR to their [`ty::Ty`] representation, which is exposed using [`AstConv`].
+/// It's also used for the bodies of items like structs where the body (the fields)
+/// are just signatures.
+///
+/// This is in contrast to `FnCtxt`, which is used to type-check bodies of
+/// functions, closures, and `const`s -- anywhere that expressions and statements show up.
+///
+/// An important thing to note is that `ItemCtxt` does no inference -- it has no [`InferCtxt`] --
+/// while `FnCtxt` does do inference.
+///
+/// [`InferCtxt`]: rustc_infer::infer::InferCtxt
+///
+/// # Trait predicates
+///
+/// `ItemCtxt` has information about the predicates that are defined
+/// on the trait. Unfortunately, this predicate information is
+/// available in various different forms at various points in the
+/// process. So we can't just store a pointer to e.g., the AST or the
+/// parsed ty form, we have to be more flexible. To this end, the
+/// `ItemCtxt` is parameterized by a `DefId` that it uses to satisfy
+/// `get_type_parameter_bounds` requests, drawing the information from
+/// the AST (`hir::Generics`), recursively.
+pub struct ItemCtxt<'tcx> {
+ tcx: TyCtxt<'tcx>,
+ item_def_id: DefId,
+}
+
+///////////////////////////////////////////////////////////////////////////
+
+#[derive(Default)]
+pub(crate) struct HirPlaceholderCollector(pub(crate) Vec<Span>);
+
+impl<'v> Visitor<'v> for HirPlaceholderCollector {
+ fn visit_ty(&mut self, t: &'v hir::Ty<'v>) {
+ if let hir::TyKind::Infer = t.kind {
+ self.0.push(t.span);
+ }
+ intravisit::walk_ty(self, t)
+ }
+ fn visit_generic_arg(&mut self, generic_arg: &'v hir::GenericArg<'v>) {
+ match generic_arg {
+ hir::GenericArg::Infer(inf) => {
+ self.0.push(inf.span);
+ intravisit::walk_inf(self, inf);
+ }
+ hir::GenericArg::Type(t) => self.visit_ty(t),
+ _ => {}
+ }
+ }
+ fn visit_array_length(&mut self, length: &'v hir::ArrayLen) {
+ if let &hir::ArrayLen::Infer(_, span) = length {
+ self.0.push(span);
+ }
+ intravisit::walk_array_len(self, length)
+ }
+}
+
+struct CollectItemTypesVisitor<'tcx> {
+ tcx: TyCtxt<'tcx>,
+}
+
+/// If there are any placeholder types (`_`), emit an error explaining that this is not allowed
+/// and suggest adding type parameters in the appropriate place, taking into consideration any and
+/// all already existing generic type parameters to avoid suggesting a name that is already in use.
+pub(crate) fn placeholder_type_error<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ generics: Option<&hir::Generics<'_>>,
+ placeholder_types: Vec<Span>,
+ suggest: bool,
+ hir_ty: Option<&hir::Ty<'_>>,
+ kind: &'static str,
+) {
+ if placeholder_types.is_empty() {
+ return;
+ }
+
+ placeholder_type_error_diag(tcx, generics, placeholder_types, vec![], suggest, hir_ty, kind)
+ .emit();
+}
+
+pub(crate) fn placeholder_type_error_diag<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ generics: Option<&hir::Generics<'_>>,
+ placeholder_types: Vec<Span>,
+ additional_spans: Vec<Span>,
+ suggest: bool,
+ hir_ty: Option<&hir::Ty<'_>>,
+ kind: &'static str,
+) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+ if placeholder_types.is_empty() {
+ return bad_placeholder(tcx, additional_spans, kind);
+ }
+
+ let params = generics.map(|g| g.params).unwrap_or_default();
+ let type_name = params.next_type_param_name(None);
+ let mut sugg: Vec<_> =
+ placeholder_types.iter().map(|sp| (*sp, (*type_name).to_string())).collect();
+
+ if let Some(generics) = generics {
+ if let Some(arg) = params.iter().find(|arg| {
+ matches!(arg.name, hir::ParamName::Plain(Ident { name: kw::Underscore, .. }))
+ }) {
+ // Account for `_` already present in cases like `struct S<_>(_);` and suggest
+ // `struct S<T>(T);` instead of `struct S<_, T>(T);`.
+ sugg.push((arg.span, (*type_name).to_string()));
+ } else if let Some(span) = generics.span_for_param_suggestion() {
+ // Account for bounds, we want `fn foo<T: E, K>(_: K)` not `fn foo<T, K: E>(_: K)`.
+ sugg.push((span, format!(", {}", type_name)));
+ } else {
+ sugg.push((generics.span, format!("<{}>", type_name)));
+ }
+ }
+
+ let mut err =
+ bad_placeholder(tcx, placeholder_types.into_iter().chain(additional_spans).collect(), kind);
+
+ // Suggest, but only if it is not a function in const or static
+ if suggest {
+ let mut is_fn = false;
+ let mut is_const_or_static = false;
+
+ if let Some(hir_ty) = hir_ty && let hir::TyKind::BareFn(_) = hir_ty.kind {
+ is_fn = true;
+
+ // Check if parent is const or static
+ let parent_id = tcx.hir().get_parent_node(hir_ty.hir_id);
+ let parent_node = tcx.hir().get(parent_id);
+
+ is_const_or_static = matches!(
+ parent_node,
+ Node::Item(&hir::Item {
+ kind: hir::ItemKind::Const(..) | hir::ItemKind::Static(..),
+ ..
+ }) | Node::TraitItem(&hir::TraitItem {
+ kind: hir::TraitItemKind::Const(..),
+ ..
+ }) | Node::ImplItem(&hir::ImplItem { kind: hir::ImplItemKind::Const(..), .. })
+ );
+ }
+
+ // if function is wrapped around a const or static,
+ // then don't show the suggestion
+ if !(is_fn && is_const_or_static) {
+ err.multipart_suggestion(
+ "use type parameters instead",
+ sugg,
+ Applicability::HasPlaceholders,
+ );
+ }
+ }
+
+ err
+}
+
+fn reject_placeholder_type_signatures_in_item<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ item: &'tcx hir::Item<'tcx>,
+) {
+ let (generics, suggest) = match &item.kind {
+ hir::ItemKind::Union(_, generics)
+ | hir::ItemKind::Enum(_, generics)
+ | hir::ItemKind::TraitAlias(generics, _)
+ | hir::ItemKind::Trait(_, _, generics, ..)
+ | hir::ItemKind::Impl(hir::Impl { generics, .. })
+ | hir::ItemKind::Struct(_, generics) => (generics, true),
+ hir::ItemKind::OpaqueTy(hir::OpaqueTy { generics, .. })
+ | hir::ItemKind::TyAlias(_, generics) => (generics, false),
+ // `static`, `fn` and `const` are handled elsewhere to suggest appropriate type.
+ _ => return,
+ };
+
+ let mut visitor = HirPlaceholderCollector::default();
+ visitor.visit_item(item);
+
+ placeholder_type_error(tcx, Some(generics), visitor.0, suggest, None, item.kind.descr());
+}
+
+impl<'tcx> Visitor<'tcx> for CollectItemTypesVisitor<'tcx> {
+ type NestedFilter = nested_filter::OnlyBodies;
+
+ fn nested_visit_map(&mut self) -> Self::Map {
+ self.tcx.hir()
+ }
+
+ fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) {
+ convert_item(self.tcx, item.item_id());
+ reject_placeholder_type_signatures_in_item(self.tcx, item);
+ intravisit::walk_item(self, item);
+ }
+
+ fn visit_generics(&mut self, generics: &'tcx hir::Generics<'tcx>) {
+ for param in generics.params {
+ match param.kind {
+ hir::GenericParamKind::Lifetime { .. } => {}
+ hir::GenericParamKind::Type { default: Some(_), .. } => {
+ let def_id = self.tcx.hir().local_def_id(param.hir_id);
+ self.tcx.ensure().type_of(def_id);
+ }
+ hir::GenericParamKind::Type { .. } => {}
+ hir::GenericParamKind::Const { default, .. } => {
+ let def_id = self.tcx.hir().local_def_id(param.hir_id);
+ self.tcx.ensure().type_of(def_id);
+ if let Some(default) = default {
+ let default_def_id = self.tcx.hir().local_def_id(default.hir_id);
+ // need to store default and type of default
+ self.tcx.ensure().type_of(default_def_id);
+ self.tcx.ensure().const_param_default(def_id);
+ }
+ }
+ }
+ }
+ intravisit::walk_generics(self, generics);
+ }
+
+ fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) {
+ if let hir::ExprKind::Closure { .. } = expr.kind {
+ let def_id = self.tcx.hir().local_def_id(expr.hir_id);
+ self.tcx.ensure().generics_of(def_id);
+ // We do not call `type_of` for closures here as that
+ // depends on typecheck and would therefore hide
+ // any further errors in case one typeck fails.
+ }
+ intravisit::walk_expr(self, expr);
+ }
+
+ fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem<'tcx>) {
+ convert_trait_item(self.tcx, trait_item.trait_item_id());
+ intravisit::walk_trait_item(self, trait_item);
+ }
+
+ fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem<'tcx>) {
+ convert_impl_item(self.tcx, impl_item.impl_item_id());
+ intravisit::walk_impl_item(self, impl_item);
+ }
+}
+
+///////////////////////////////////////////////////////////////////////////
+// Utility types and common code for the above passes.
+
+fn bad_placeholder<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ mut spans: Vec<Span>,
+ kind: &'static str,
+) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+ let kind = if kind.ends_with('s') { format!("{}es", kind) } else { format!("{}s", kind) };
+
+ spans.sort();
+ let mut err = struct_span_err!(
+ tcx.sess,
+ spans.clone(),
+ E0121,
+ "the placeholder `_` is not allowed within types on item signatures for {}",
+ kind
+ );
+ for span in spans {
+ err.span_label(span, "not allowed in type signatures");
+ }
+ err
+}
+
+impl<'tcx> ItemCtxt<'tcx> {
+ pub fn new(tcx: TyCtxt<'tcx>, item_def_id: DefId) -> ItemCtxt<'tcx> {
+ ItemCtxt { tcx, item_def_id }
+ }
+
+ pub fn to_ty(&self, ast_ty: &hir::Ty<'_>) -> Ty<'tcx> {
+ <dyn AstConv<'_>>::ast_ty_to_ty(self, ast_ty)
+ }
+
+ pub fn hir_id(&self) -> hir::HirId {
+ self.tcx.hir().local_def_id_to_hir_id(self.item_def_id.expect_local())
+ }
+
+ pub fn node(&self) -> hir::Node<'tcx> {
+ self.tcx.hir().get(self.hir_id())
+ }
+}
+
+impl<'tcx> AstConv<'tcx> for ItemCtxt<'tcx> {
+ fn tcx(&self) -> TyCtxt<'tcx> {
+ self.tcx
+ }
+
+ fn item_def_id(&self) -> Option<DefId> {
+ Some(self.item_def_id)
+ }
+
+ fn get_type_parameter_bounds(
+ &self,
+ span: Span,
+ def_id: DefId,
+ assoc_name: Ident,
+ ) -> ty::GenericPredicates<'tcx> {
+ self.tcx.at(span).type_param_predicates((
+ self.item_def_id,
+ def_id.expect_local(),
+ assoc_name,
+ ))
+ }
+
+ fn re_infer(&self, _: Option<&ty::GenericParamDef>, _: Span) -> Option<ty::Region<'tcx>> {
+ None
+ }
+
+ fn allow_ty_infer(&self) -> bool {
+ false
+ }
+
+ fn ty_infer(&self, _: Option<&ty::GenericParamDef>, span: Span) -> Ty<'tcx> {
+ self.tcx().ty_error_with_message(span, "bad placeholder type")
+ }
+
+ fn ct_infer(&self, ty: Ty<'tcx>, _: Option<&ty::GenericParamDef>, span: Span) -> Const<'tcx> {
+ let ty = self.tcx.fold_regions(ty, |r, _| match *r {
+ ty::ReErased => self.tcx.lifetimes.re_static,
+ _ => r,
+ });
+ self.tcx().const_error_with_message(ty, span, "bad placeholder constant")
+ }
+
+ fn projected_ty_from_poly_trait_ref(
+ &self,
+ span: Span,
+ item_def_id: DefId,
+ item_segment: &hir::PathSegment<'_>,
+ poly_trait_ref: ty::PolyTraitRef<'tcx>,
+ ) -> Ty<'tcx> {
+ if let Some(trait_ref) = poly_trait_ref.no_bound_vars() {
+ let item_substs = <dyn AstConv<'tcx>>::create_substs_for_associated_item(
+ self,
+ span,
+ item_def_id,
+ item_segment,
+ trait_ref.substs,
+ );
+ self.tcx().mk_projection(item_def_id, item_substs)
+ } else {
+ // There are no late-bound regions; we can just ignore the binder.
+ let mut err = struct_span_err!(
+ self.tcx().sess,
+ span,
+ E0212,
+ "cannot use the associated type of a trait \
+ with uninferred generic parameters"
+ );
+
+ match self.node() {
+ hir::Node::Field(_) | hir::Node::Ctor(_) | hir::Node::Variant(_) => {
+ let item = self
+ .tcx
+ .hir()
+ .expect_item(self.tcx.hir().get_parent_item(self.hir_id()).def_id);
+ match &item.kind {
+ hir::ItemKind::Enum(_, generics)
+ | hir::ItemKind::Struct(_, generics)
+ | hir::ItemKind::Union(_, generics) => {
+ let lt_name = get_new_lifetime_name(self.tcx, poly_trait_ref, generics);
+ let (lt_sp, sugg) = match generics.params {
+ [] => (generics.span, format!("<{}>", lt_name)),
+ [bound, ..] => {
+ (bound.span.shrink_to_lo(), format!("{}, ", lt_name))
+ }
+ };
+ let suggestions = vec![
+ (lt_sp, sugg),
+ (
+ span.with_hi(item_segment.ident.span.lo()),
+ format!(
+ "{}::",
+ // Replace the existing lifetimes with a new named lifetime.
+ self.tcx.replace_late_bound_regions_uncached(
+ poly_trait_ref,
+ |_| {
+ self.tcx.mk_region(ty::ReEarlyBound(
+ ty::EarlyBoundRegion {
+ def_id: item_def_id,
+ index: 0,
+ name: Symbol::intern(&lt_name),
+ },
+ ))
+ }
+ ),
+ ),
+ ),
+ ];
+ err.multipart_suggestion(
+ "use a fully qualified path with explicit lifetimes",
+ suggestions,
+ Applicability::MaybeIncorrect,
+ );
+ }
+ _ => {}
+ }
+ }
+ hir::Node::Item(hir::Item {
+ kind:
+ hir::ItemKind::Struct(..) | hir::ItemKind::Enum(..) | hir::ItemKind::Union(..),
+ ..
+ }) => {}
+ hir::Node::Item(_)
+ | hir::Node::ForeignItem(_)
+ | hir::Node::TraitItem(_)
+ | hir::Node::ImplItem(_) => {
+ err.span_suggestion_verbose(
+ span.with_hi(item_segment.ident.span.lo()),
+ "use a fully qualified path with inferred lifetimes",
+ format!(
+ "{}::",
+ // Erase named lt, we want `<A as B<'_>::C`, not `<A as B<'a>::C`.
+ self.tcx.anonymize_late_bound_regions(poly_trait_ref).skip_binder(),
+ ),
+ Applicability::MaybeIncorrect,
+ );
+ }
+ _ => {}
+ }
+ err.emit();
+ self.tcx().ty_error()
+ }
+ }
+
+ fn normalize_ty(&self, _span: Span, ty: Ty<'tcx>) -> Ty<'tcx> {
+ // Types in item signatures are not normalized to avoid undue dependencies.
+ ty
+ }
+
+ fn set_tainted_by_errors(&self) {
+ // There's no obvious place to track this, so just let it go.
+ }
+
+ fn record_ty(&self, _hir_id: hir::HirId, _ty: Ty<'tcx>, _span: Span) {
+ // There's no place to record types from signatures?
+ }
+}
+
+/// Synthesize a new lifetime name that doesn't clash with any of the lifetimes already present.
+fn get_new_lifetime_name<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ poly_trait_ref: ty::PolyTraitRef<'tcx>,
+ generics: &hir::Generics<'tcx>,
+) -> String {
+ let existing_lifetimes = tcx
+ .collect_referenced_late_bound_regions(&poly_trait_ref)
+ .into_iter()
+ .filter_map(|lt| {
+ if let ty::BoundRegionKind::BrNamed(_, name) = lt {
+ Some(name.as_str().to_string())
+ } else {
+ None
+ }
+ })
+ .chain(generics.params.iter().filter_map(|param| {
+ if let hir::GenericParamKind::Lifetime { .. } = &param.kind {
+ Some(param.name.ident().as_str().to_string())
+ } else {
+ None
+ }
+ }))
+ .collect::<FxHashSet<String>>();
+
+ let a_to_z_repeat_n = |n| {
+ (b'a'..=b'z').map(move |c| {
+ let mut s = '\''.to_string();
+ s.extend(std::iter::repeat(char::from(c)).take(n));
+ s
+ })
+ };
+
+ // If all single char lifetime names are present, we wrap around and double the chars.
+ (1..).flat_map(a_to_z_repeat_n).find(|lt| !existing_lifetimes.contains(lt.as_str())).unwrap()
+}
+
+fn convert_item(tcx: TyCtxt<'_>, item_id: hir::ItemId) {
+ let it = tcx.hir().item(item_id);
+ debug!("convert: item {} with id {}", it.ident, it.hir_id());
+ let def_id = item_id.owner_id.def_id;
+
+ match it.kind {
+ // These don't define types.
+ hir::ItemKind::ExternCrate(_)
+ | hir::ItemKind::Use(..)
+ | hir::ItemKind::Macro(..)
+ | hir::ItemKind::Mod(_)
+ | hir::ItemKind::GlobalAsm(_) => {}
+ hir::ItemKind::ForeignMod { items, .. } => {
+ for item in items {
+ let item = tcx.hir().foreign_item(item.id);
+ tcx.ensure().generics_of(item.owner_id);
+ tcx.ensure().type_of(item.owner_id);
+ tcx.ensure().predicates_of(item.owner_id);
+ match item.kind {
+ hir::ForeignItemKind::Fn(..) => tcx.ensure().fn_sig(item.owner_id),
+ hir::ForeignItemKind::Static(..) => {
+ let mut visitor = HirPlaceholderCollector::default();
+ visitor.visit_foreign_item(item);
+ placeholder_type_error(
+ tcx,
+ None,
+ visitor.0,
+ false,
+ None,
+ "static variable",
+ );
+ }
+ _ => (),
+ }
+ }
+ }
+ hir::ItemKind::Enum(ref enum_definition, _) => {
+ tcx.ensure().generics_of(def_id);
+ tcx.ensure().type_of(def_id);
+ tcx.ensure().predicates_of(def_id);
+ convert_enum_variant_types(tcx, def_id.to_def_id(), enum_definition.variants);
+ }
+ hir::ItemKind::Impl { .. } => {
+ tcx.ensure().generics_of(def_id);
+ tcx.ensure().type_of(def_id);
+ tcx.ensure().impl_trait_ref(def_id);
+ tcx.ensure().predicates_of(def_id);
+ }
+ hir::ItemKind::Trait(..) => {
+ tcx.ensure().generics_of(def_id);
+ tcx.ensure().trait_def(def_id);
+ tcx.at(it.span).super_predicates_of(def_id);
+ tcx.ensure().predicates_of(def_id);
+ }
+ hir::ItemKind::TraitAlias(..) => {
+ tcx.ensure().generics_of(def_id);
+ tcx.at(it.span).super_predicates_of(def_id);
+ tcx.ensure().predicates_of(def_id);
+ }
+ hir::ItemKind::Struct(ref struct_def, _) | hir::ItemKind::Union(ref struct_def, _) => {
+ tcx.ensure().generics_of(def_id);
+ tcx.ensure().type_of(def_id);
+ tcx.ensure().predicates_of(def_id);
+
+ for f in struct_def.fields() {
+ let def_id = tcx.hir().local_def_id(f.hir_id);
+ tcx.ensure().generics_of(def_id);
+ tcx.ensure().type_of(def_id);
+ tcx.ensure().predicates_of(def_id);
+ }
+
+ if let Some(ctor_hir_id) = struct_def.ctor_hir_id() {
+ convert_variant_ctor(tcx, ctor_hir_id);
+ }
+ }
+
+ // Desugared from `impl Trait`, so visited by the function's return type.
+ hir::ItemKind::OpaqueTy(hir::OpaqueTy {
+ origin: hir::OpaqueTyOrigin::FnReturn(..) | hir::OpaqueTyOrigin::AsyncFn(..),
+ ..
+ }) => {}
+
+ // Don't call `type_of` on opaque types, since that depends on type
+ // checking function bodies. `check_item_type` ensures that it's called
+ // instead.
+ hir::ItemKind::OpaqueTy(..) => {
+ tcx.ensure().generics_of(def_id);
+ tcx.ensure().predicates_of(def_id);
+ tcx.ensure().explicit_item_bounds(def_id);
+ }
+ hir::ItemKind::TyAlias(..)
+ | hir::ItemKind::Static(..)
+ | hir::ItemKind::Const(..)
+ | hir::ItemKind::Fn(..) => {
+ tcx.ensure().generics_of(def_id);
+ tcx.ensure().type_of(def_id);
+ tcx.ensure().predicates_of(def_id);
+ match it.kind {
+ hir::ItemKind::Fn(..) => tcx.ensure().fn_sig(def_id),
+ hir::ItemKind::OpaqueTy(..) => tcx.ensure().item_bounds(def_id),
+ hir::ItemKind::Const(ty, ..) | hir::ItemKind::Static(ty, ..) => {
+ if !is_suggestable_infer_ty(ty) {
+ let mut visitor = HirPlaceholderCollector::default();
+ visitor.visit_item(it);
+ placeholder_type_error(tcx, None, visitor.0, false, None, it.kind.descr());
+ }
+ }
+ _ => (),
+ }
+ }
+ }
+}
+
+fn convert_trait_item(tcx: TyCtxt<'_>, trait_item_id: hir::TraitItemId) {
+ let trait_item = tcx.hir().trait_item(trait_item_id);
+ let def_id = trait_item_id.owner_id;
+ tcx.ensure().generics_of(def_id);
+
+ match trait_item.kind {
+ hir::TraitItemKind::Fn(..) => {
+ tcx.ensure().type_of(def_id);
+ tcx.ensure().fn_sig(def_id);
+ }
+
+ hir::TraitItemKind::Const(.., Some(_)) => {
+ tcx.ensure().type_of(def_id);
+ }
+
+ hir::TraitItemKind::Const(hir_ty, _) => {
+ tcx.ensure().type_of(def_id);
+ // Account for `const C: _;`.
+ let mut visitor = HirPlaceholderCollector::default();
+ visitor.visit_trait_item(trait_item);
+ if !tcx.sess.diagnostic().has_stashed_diagnostic(hir_ty.span, StashKey::ItemNoType) {
+ placeholder_type_error(tcx, None, visitor.0, false, None, "constant");
+ }
+ }
+
+ hir::TraitItemKind::Type(_, Some(_)) => {
+ tcx.ensure().item_bounds(def_id);
+ tcx.ensure().type_of(def_id);
+ // Account for `type T = _;`.
+ let mut visitor = HirPlaceholderCollector::default();
+ visitor.visit_trait_item(trait_item);
+ placeholder_type_error(tcx, None, visitor.0, false, None, "associated type");
+ }
+
+ hir::TraitItemKind::Type(_, None) => {
+ tcx.ensure().item_bounds(def_id);
+ // #74612: Visit and try to find bad placeholders
+ // even if there is no concrete type.
+ let mut visitor = HirPlaceholderCollector::default();
+ visitor.visit_trait_item(trait_item);
+
+ placeholder_type_error(tcx, None, visitor.0, false, None, "associated type");
+ }
+ };
+
+ tcx.ensure().predicates_of(def_id);
+}
+
+fn convert_impl_item(tcx: TyCtxt<'_>, impl_item_id: hir::ImplItemId) {
+ let def_id = impl_item_id.owner_id;
+ tcx.ensure().generics_of(def_id);
+ tcx.ensure().type_of(def_id);
+ tcx.ensure().predicates_of(def_id);
+ let impl_item = tcx.hir().impl_item(impl_item_id);
+ match impl_item.kind {
+ hir::ImplItemKind::Fn(..) => {
+ tcx.ensure().fn_sig(def_id);
+ }
+ hir::ImplItemKind::Type(_) => {
+ // Account for `type T = _;`
+ let mut visitor = HirPlaceholderCollector::default();
+ visitor.visit_impl_item(impl_item);
+
+ placeholder_type_error(tcx, None, visitor.0, false, None, "associated type");
+ }
+ hir::ImplItemKind::Const(..) => {}
+ }
+}
+
+fn convert_variant_ctor(tcx: TyCtxt<'_>, ctor_id: hir::HirId) {
+ let def_id = tcx.hir().local_def_id(ctor_id);
+ tcx.ensure().generics_of(def_id);
+ tcx.ensure().type_of(def_id);
+ tcx.ensure().predicates_of(def_id);
+}
+
+fn convert_enum_variant_types(tcx: TyCtxt<'_>, def_id: DefId, variants: &[hir::Variant<'_>]) {
+ let def = tcx.adt_def(def_id);
+ let repr_type = def.repr().discr_type();
+ let initial = repr_type.initial_discriminant(tcx);
+ let mut prev_discr = None::<Discr<'_>>;
+
+ // fill the discriminant values and field types
+ for variant in variants {
+ let wrapped_discr = prev_discr.map_or(initial, |d| d.wrap_incr(tcx));
+ prev_discr = Some(
+ if let Some(ref e) = variant.disr_expr {
+ let expr_did = tcx.hir().local_def_id(e.hir_id);
+ def.eval_explicit_discr(tcx, expr_did.to_def_id())
+ } else if let Some(discr) = repr_type.disr_incr(tcx, prev_discr) {
+ Some(discr)
+ } else {
+ struct_span_err!(tcx.sess, variant.span, E0370, "enum discriminant overflowed")
+ .span_label(
+ variant.span,
+ format!("overflowed on value after {}", prev_discr.unwrap()),
+ )
+ .note(&format!(
+ "explicitly set `{} = {}` if that is desired outcome",
+ variant.ident, wrapped_discr
+ ))
+ .emit();
+ None
+ }
+ .unwrap_or(wrapped_discr),
+ );
+
+ for f in variant.data.fields() {
+ let def_id = tcx.hir().local_def_id(f.hir_id);
+ tcx.ensure().generics_of(def_id);
+ tcx.ensure().type_of(def_id);
+ tcx.ensure().predicates_of(def_id);
+ }
+
+ // Convert the ctor, if any. This also registers the variant as
+ // an item.
+ if let Some(ctor_hir_id) = variant.data.ctor_hir_id() {
+ convert_variant_ctor(tcx, ctor_hir_id);
+ }
+ }
+}
+
+fn convert_variant(
+ tcx: TyCtxt<'_>,
+ variant_did: Option<LocalDefId>,
+ ctor_did: Option<LocalDefId>,
+ ident: Ident,
+ discr: ty::VariantDiscr,
+ def: &hir::VariantData<'_>,
+ adt_kind: ty::AdtKind,
+ parent_did: LocalDefId,
+) -> ty::VariantDef {
+ let mut seen_fields: FxHashMap<Ident, Span> = Default::default();
+ let fields = def
+ .fields()
+ .iter()
+ .map(|f| {
+ let fid = tcx.hir().local_def_id(f.hir_id);
+ let dup_span = seen_fields.get(&f.ident.normalize_to_macros_2_0()).cloned();
+ if let Some(prev_span) = dup_span {
+ tcx.sess.emit_err(errors::FieldAlreadyDeclared {
+ field_name: f.ident,
+ span: f.span,
+ prev_span,
+ });
+ } else {
+ seen_fields.insert(f.ident.normalize_to_macros_2_0(), f.span);
+ }
+
+ ty::FieldDef { did: fid.to_def_id(), name: f.ident.name, vis: tcx.visibility(fid) }
+ })
+ .collect();
+ let recovered = match def {
+ hir::VariantData::Struct(_, r) => *r,
+ _ => false,
+ };
+ ty::VariantDef::new(
+ ident.name,
+ variant_did.map(LocalDefId::to_def_id),
+ ctor_did.map(LocalDefId::to_def_id),
+ discr,
+ fields,
+ CtorKind::from_hir(def),
+ adt_kind,
+ parent_did.to_def_id(),
+ recovered,
+ adt_kind == AdtKind::Struct && tcx.has_attr(parent_did.to_def_id(), sym::non_exhaustive)
+ || variant_did.map_or(false, |variant_did| {
+ tcx.has_attr(variant_did.to_def_id(), sym::non_exhaustive)
+ }),
+ )
+}
+
+fn adt_def<'tcx>(tcx: TyCtxt<'tcx>, def_id: DefId) -> ty::AdtDef<'tcx> {
+ use rustc_hir::*;
+
+ let def_id = def_id.expect_local();
+ let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
+ let Node::Item(item) = tcx.hir().get(hir_id) else {
+ bug!();
+ };
+
+ let repr = ReprOptions::new(tcx, def_id.to_def_id());
+ let (kind, variants) = match item.kind {
+ ItemKind::Enum(ref def, _) => {
+ let mut distance_from_explicit = 0;
+ let variants = def
+ .variants
+ .iter()
+ .map(|v| {
+ let variant_did = Some(tcx.hir().local_def_id(v.id));
+ let ctor_did =
+ v.data.ctor_hir_id().map(|hir_id| tcx.hir().local_def_id(hir_id));
+
+ let discr = if let Some(ref e) = v.disr_expr {
+ distance_from_explicit = 0;
+ ty::VariantDiscr::Explicit(tcx.hir().local_def_id(e.hir_id).to_def_id())
+ } else {
+ ty::VariantDiscr::Relative(distance_from_explicit)
+ };
+ distance_from_explicit += 1;
+
+ convert_variant(
+ tcx,
+ variant_did,
+ ctor_did,
+ v.ident,
+ discr,
+ &v.data,
+ AdtKind::Enum,
+ def_id,
+ )
+ })
+ .collect();
+
+ (AdtKind::Enum, variants)
+ }
+ ItemKind::Struct(ref def, _) => {
+ let variant_did = None::<LocalDefId>;
+ let ctor_did = def.ctor_hir_id().map(|hir_id| tcx.hir().local_def_id(hir_id));
+
+ let variants = std::iter::once(convert_variant(
+ tcx,
+ variant_did,
+ ctor_did,
+ item.ident,
+ ty::VariantDiscr::Relative(0),
+ def,
+ AdtKind::Struct,
+ def_id,
+ ))
+ .collect();
+
+ (AdtKind::Struct, variants)
+ }
+ ItemKind::Union(ref def, _) => {
+ let variant_did = None;
+ let ctor_did = def.ctor_hir_id().map(|hir_id| tcx.hir().local_def_id(hir_id));
+
+ let variants = std::iter::once(convert_variant(
+ tcx,
+ variant_did,
+ ctor_did,
+ item.ident,
+ ty::VariantDiscr::Relative(0),
+ def,
+ AdtKind::Union,
+ def_id,
+ ))
+ .collect();
+
+ (AdtKind::Union, variants)
+ }
+ _ => bug!(),
+ };
+ tcx.alloc_adt_def(def_id.to_def_id(), kind, variants, repr)
+}
+
+fn trait_def(tcx: TyCtxt<'_>, def_id: DefId) -> ty::TraitDef {
+ let item = tcx.hir().expect_item(def_id.expect_local());
+
+ let (is_auto, unsafety, items) = match item.kind {
+ hir::ItemKind::Trait(is_auto, unsafety, .., items) => {
+ (is_auto == hir::IsAuto::Yes, unsafety, items)
+ }
+ hir::ItemKind::TraitAlias(..) => (false, hir::Unsafety::Normal, &[][..]),
+ _ => span_bug!(item.span, "trait_def_of_item invoked on non-trait"),
+ };
+
+ let paren_sugar = tcx.has_attr(def_id, sym::rustc_paren_sugar);
+ if paren_sugar && !tcx.features().unboxed_closures {
+ tcx.sess
+ .struct_span_err(
+ item.span,
+ "the `#[rustc_paren_sugar]` attribute is a temporary means of controlling \
+ which traits can use parenthetical notation",
+ )
+ .help("add `#![feature(unboxed_closures)]` to the crate attributes to use it")
+ .emit();
+ }
+
+ let is_marker = tcx.has_attr(def_id, sym::marker);
+ let skip_array_during_method_dispatch =
+ tcx.has_attr(def_id, sym::rustc_skip_array_during_method_dispatch);
+ let spec_kind = if tcx.has_attr(def_id, sym::rustc_unsafe_specialization_marker) {
+ ty::trait_def::TraitSpecializationKind::Marker
+ } else if tcx.has_attr(def_id, sym::rustc_specialization_trait) {
+ ty::trait_def::TraitSpecializationKind::AlwaysApplicable
+ } else {
+ ty::trait_def::TraitSpecializationKind::None
+ };
+ let must_implement_one_of = tcx
+ .get_attr(def_id, sym::rustc_must_implement_one_of)
+ // Check that there are at least 2 arguments of `#[rustc_must_implement_one_of]`
+ // and that they are all identifiers
+ .and_then(|attr| match attr.meta_item_list() {
+ Some(items) if items.len() < 2 => {
+ tcx.sess
+ .struct_span_err(
+ attr.span,
+ "the `#[rustc_must_implement_one_of]` attribute must be \
+ used with at least 2 args",
+ )
+ .emit();
+
+ None
+ }
+ Some(items) => items
+ .into_iter()
+ .map(|item| item.ident().ok_or(item.span()))
+ .collect::<Result<Box<[_]>, _>>()
+ .map_err(|span| {
+ tcx.sess
+ .struct_span_err(span, "must be a name of an associated function")
+ .emit();
+ })
+ .ok()
+ .zip(Some(attr.span)),
+ // Error is reported by `rustc_attr!`
+ None => None,
+ })
+ // Check that all arguments of `#[rustc_must_implement_one_of]` reference
+ // functions in the trait with default implementations
+ .and_then(|(list, attr_span)| {
+ let errors = list.iter().filter_map(|ident| {
+ let item = items.iter().find(|item| item.ident == *ident);
+
+ match item {
+ Some(item) if matches!(item.kind, hir::AssocItemKind::Fn { .. }) => {
+ if !tcx.impl_defaultness(item.id.owner_id).has_value() {
+ tcx.sess
+ .struct_span_err(
+ item.span,
+ "This function doesn't have a default implementation",
+ )
+ .span_note(attr_span, "required by this annotation")
+ .emit();
+
+ return Some(());
+ }
+
+ return None;
+ }
+ Some(item) => {
+ tcx.sess
+ .struct_span_err(item.span, "Not a function")
+ .span_note(attr_span, "required by this annotation")
+ .note(
+ "All `#[rustc_must_implement_one_of]` arguments \
+ must be associated function names",
+ )
+ .emit();
+ }
+ None => {
+ tcx.sess
+ .struct_span_err(ident.span, "Function not found in this trait")
+ .emit();
+ }
+ }
+
+ Some(())
+ });
+
+ (errors.count() == 0).then_some(list)
+ })
+ // Check for duplicates
+ .and_then(|list| {
+ let mut set: FxHashMap<Symbol, Span> = FxHashMap::default();
+ let mut no_dups = true;
+
+ for ident in &*list {
+ if let Some(dup) = set.insert(ident.name, ident.span) {
+ tcx.sess
+ .struct_span_err(vec![dup, ident.span], "Functions names are duplicated")
+ .note(
+ "All `#[rustc_must_implement_one_of]` arguments \
+ must be unique",
+ )
+ .emit();
+
+ no_dups = false;
+ }
+ }
+
+ no_dups.then_some(list)
+ });
+
+ ty::TraitDef::new(
+ def_id,
+ unsafety,
+ paren_sugar,
+ is_auto,
+ is_marker,
+ skip_array_during_method_dispatch,
+ spec_kind,
+ must_implement_one_of,
+ )
+}
+
+fn are_suggestable_generic_args(generic_args: &[hir::GenericArg<'_>]) -> bool {
+ generic_args.iter().any(|arg| match arg {
+ hir::GenericArg::Type(ty) => is_suggestable_infer_ty(ty),
+ hir::GenericArg::Infer(_) => true,
+ _ => false,
+ })
+}
+
+/// Whether `ty` is a type with `_` placeholders that can be inferred. Used in diagnostics only to
+/// use inference to provide suggestions for the appropriate type if possible.
+fn is_suggestable_infer_ty(ty: &hir::Ty<'_>) -> bool {
+ debug!(?ty);
+ use hir::TyKind::*;
+ match &ty.kind {
+ Infer => true,
+ Slice(ty) => is_suggestable_infer_ty(ty),
+ Array(ty, length) => {
+ is_suggestable_infer_ty(ty) || matches!(length, hir::ArrayLen::Infer(_, _))
+ }
+ Tup(tys) => tys.iter().any(is_suggestable_infer_ty),
+ Ptr(mut_ty) | Rptr(_, mut_ty) => is_suggestable_infer_ty(mut_ty.ty),
+ OpaqueDef(_, generic_args, _) => are_suggestable_generic_args(generic_args),
+ Path(hir::QPath::TypeRelative(ty, segment)) => {
+ is_suggestable_infer_ty(ty) || are_suggestable_generic_args(segment.args().args)
+ }
+ Path(hir::QPath::Resolved(ty_opt, hir::Path { segments, .. })) => {
+ ty_opt.map_or(false, is_suggestable_infer_ty)
+ || segments.iter().any(|segment| are_suggestable_generic_args(segment.args().args))
+ }
+ _ => false,
+ }
+}
+
+pub fn get_infer_ret_ty<'hir>(output: &'hir hir::FnRetTy<'hir>) -> Option<&'hir hir::Ty<'hir>> {
+ if let hir::FnRetTy::Return(ty) = output {
+ if is_suggestable_infer_ty(ty) {
+ return Some(&*ty);
+ }
+ }
+ None
+}
+
+#[instrument(level = "debug", skip(tcx))]
+fn fn_sig(tcx: TyCtxt<'_>, def_id: DefId) -> ty::PolyFnSig<'_> {
+ use rustc_hir::Node::*;
+ use rustc_hir::*;
+
+ let def_id = def_id.expect_local();
+ let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
+
+ let icx = ItemCtxt::new(tcx, def_id.to_def_id());
+
+ match tcx.hir().get(hir_id) {
+ TraitItem(hir::TraitItem {
+ kind: TraitItemKind::Fn(sig, TraitFn::Provided(_)),
+ generics,
+ ..
+ })
+ | Item(hir::Item { kind: ItemKind::Fn(sig, generics, _), .. }) => {
+ infer_return_ty_for_fn_sig(tcx, sig, generics, def_id, &icx)
+ }
+
+ ImplItem(hir::ImplItem { kind: ImplItemKind::Fn(sig, _), generics, .. }) => {
+ // Do not try to infer the return type for a impl method coming from a trait
+ if let Item(hir::Item { kind: ItemKind::Impl(i), .. }) =
+ tcx.hir().get(tcx.hir().get_parent_node(hir_id))
+ && i.of_trait.is_some()
+ {
+ <dyn AstConv<'_>>::ty_of_fn(
+ &icx,
+ hir_id,
+ sig.header.unsafety,
+ sig.header.abi,
+ sig.decl,
+ Some(generics),
+ None,
+ )
+ } else {
+ infer_return_ty_for_fn_sig(tcx, sig, generics, def_id, &icx)
+ }
+ }
+
+ TraitItem(hir::TraitItem {
+ kind: TraitItemKind::Fn(FnSig { header, decl, span: _ }, _),
+ generics,
+ ..
+ }) => <dyn AstConv<'_>>::ty_of_fn(
+ &icx,
+ hir_id,
+ header.unsafety,
+ header.abi,
+ decl,
+ Some(generics),
+ None,
+ ),
+
+ ForeignItem(&hir::ForeignItem { kind: ForeignItemKind::Fn(fn_decl, _, _), .. }) => {
+ let abi = tcx.hir().get_foreign_abi(hir_id);
+ compute_sig_of_foreign_fn_decl(tcx, def_id.to_def_id(), fn_decl, abi)
+ }
+
+ Ctor(data) | Variant(hir::Variant { data, .. }) if data.ctor_hir_id().is_some() => {
+ let ty = tcx.type_of(tcx.hir().get_parent_item(hir_id));
+ let inputs =
+ data.fields().iter().map(|f| tcx.type_of(tcx.hir().local_def_id(f.hir_id)));
+ ty::Binder::dummy(tcx.mk_fn_sig(
+ inputs,
+ ty,
+ false,
+ hir::Unsafety::Normal,
+ abi::Abi::Rust,
+ ))
+ }
+
+ Expr(&hir::Expr { kind: hir::ExprKind::Closure { .. }, .. }) => {
+ // Closure signatures are not like other function
+ // signatures and cannot be accessed through `fn_sig`. For
+ // example, a closure signature excludes the `self`
+ // argument. In any case they are embedded within the
+ // closure type as part of the `ClosureSubsts`.
+ //
+ // To get the signature of a closure, you should use the
+ // `sig` method on the `ClosureSubsts`:
+ //
+ // substs.as_closure().sig(def_id, tcx)
+ bug!(
+ "to get the signature of a closure, use `substs.as_closure().sig()` not `fn_sig()`",
+ );
+ }
+
+ x => {
+ bug!("unexpected sort of node in fn_sig(): {:?}", x);
+ }
+ }
+}
+
+fn infer_return_ty_for_fn_sig<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ sig: &hir::FnSig<'_>,
+ generics: &hir::Generics<'_>,
+ def_id: LocalDefId,
+ icx: &ItemCtxt<'tcx>,
+) -> ty::PolyFnSig<'tcx> {
+ let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
+
+ match get_infer_ret_ty(&sig.decl.output) {
+ Some(ty) => {
+ let fn_sig = tcx.typeck(def_id).liberated_fn_sigs()[hir_id];
+ // Typeck doesn't expect erased regions to be returned from `type_of`.
+ let fn_sig = tcx.fold_regions(fn_sig, |r, _| match *r {
+ ty::ReErased => tcx.lifetimes.re_static,
+ _ => r,
+ });
+ let fn_sig = ty::Binder::dummy(fn_sig);
+
+ let mut visitor = HirPlaceholderCollector::default();
+ visitor.visit_ty(ty);
+ let mut diag = bad_placeholder(tcx, visitor.0, "return type");
+ let ret_ty = fn_sig.skip_binder().output();
+ if ret_ty.is_suggestable(tcx, false) {
+ diag.span_suggestion(
+ ty.span,
+ "replace with the correct return type",
+ ret_ty,
+ Applicability::MachineApplicable,
+ );
+ } else if matches!(ret_ty.kind(), ty::FnDef(..)) {
+ let fn_sig = ret_ty.fn_sig(tcx);
+ if fn_sig
+ .skip_binder()
+ .inputs_and_output
+ .iter()
+ .all(|t| t.is_suggestable(tcx, false))
+ {
+ diag.span_suggestion(
+ ty.span,
+ "replace with the correct return type",
+ fn_sig,
+ Applicability::MachineApplicable,
+ );
+ }
+ } else if ret_ty.is_closure() {
+ // We're dealing with a closure, so we should suggest using `impl Fn` or trait bounds
+ // to prevent the user from getting a papercut while trying to use the unique closure
+ // syntax (e.g. `[closure@src/lib.rs:2:5: 2:9]`).
+ diag.help("consider using an `Fn`, `FnMut`, or `FnOnce` trait bound");
+ diag.note("for more information on `Fn` traits and closure types, see https://doc.rust-lang.org/book/ch13-01-closures.html");
+ }
+ diag.emit();
+
+ fn_sig
+ }
+ None => <dyn AstConv<'_>>::ty_of_fn(
+ icx,
+ hir_id,
+ sig.header.unsafety,
+ sig.header.abi,
+ sig.decl,
+ Some(generics),
+ None,
+ ),
+ }
+}
+
+fn impl_trait_ref(tcx: TyCtxt<'_>, def_id: DefId) -> Option<ty::TraitRef<'_>> {
+ let icx = ItemCtxt::new(tcx, def_id);
+ let item = tcx.hir().expect_item(def_id.expect_local());
+ match item.kind {
+ hir::ItemKind::Impl(ref impl_) => impl_.of_trait.as_ref().map(|ast_trait_ref| {
+ let selfty = tcx.type_of(def_id);
+ <dyn AstConv<'_>>::instantiate_mono_trait_ref(
+ &icx,
+ ast_trait_ref,
+ selfty,
+ check_impl_constness(tcx, impl_.constness, ast_trait_ref),
+ )
+ }),
+ _ => bug!(),
+ }
+}
+
+fn check_impl_constness(
+ tcx: TyCtxt<'_>,
+ constness: hir::Constness,
+ ast_trait_ref: &hir::TraitRef<'_>,
+) -> ty::BoundConstness {
+ match constness {
+ hir::Constness::Const => {
+ if let Some(trait_def_id) = ast_trait_ref.trait_def_id() && !tcx.has_attr(trait_def_id, sym::const_trait) {
+ let trait_name = tcx.item_name(trait_def_id).to_string();
+ tcx.sess.emit_err(errors::ConstImplForNonConstTrait {
+ trait_ref_span: ast_trait_ref.path.span,
+ trait_name,
+ local_trait_span: trait_def_id.as_local().map(|_| tcx.def_span(trait_def_id).shrink_to_lo()),
+ marking: (),
+ adding: (),
+ });
+ ty::BoundConstness::NotConst
+ } else {
+ ty::BoundConstness::ConstIfConst
+ }
+ },
+ hir::Constness::NotConst => ty::BoundConstness::NotConst,
+ }
+}
+
+fn impl_polarity(tcx: TyCtxt<'_>, def_id: DefId) -> ty::ImplPolarity {
+ let is_rustc_reservation = tcx.has_attr(def_id, sym::rustc_reservation_impl);
+ let item = tcx.hir().expect_item(def_id.expect_local());
+ match &item.kind {
+ hir::ItemKind::Impl(hir::Impl {
+ polarity: hir::ImplPolarity::Negative(span),
+ of_trait,
+ ..
+ }) => {
+ if is_rustc_reservation {
+ let span = span.to(of_trait.as_ref().map_or(*span, |t| t.path.span));
+ tcx.sess.span_err(span, "reservation impls can't be negative");
+ }
+ ty::ImplPolarity::Negative
+ }
+ hir::ItemKind::Impl(hir::Impl {
+ polarity: hir::ImplPolarity::Positive,
+ of_trait: None,
+ ..
+ }) => {
+ if is_rustc_reservation {
+ tcx.sess.span_err(item.span, "reservation impls can't be inherent");
+ }
+ ty::ImplPolarity::Positive
+ }
+ hir::ItemKind::Impl(hir::Impl {
+ polarity: hir::ImplPolarity::Positive,
+ of_trait: Some(_),
+ ..
+ }) => {
+ if is_rustc_reservation {
+ ty::ImplPolarity::Reservation
+ } else {
+ ty::ImplPolarity::Positive
+ }
+ }
+ item => bug!("impl_polarity: {:?} not an impl", item),
+ }
+}
+
+/// Returns the early-bound lifetimes declared in this generics
+/// listing. For anything other than fns/methods, this is just all
+/// the lifetimes that are declared. For fns or methods, we have to
+/// screen out those that do not appear in any where-clauses etc using
+/// `resolve_lifetime::early_bound_lifetimes`.
+fn early_bound_lifetimes_from_generics<'a, 'tcx: 'a>(
+ tcx: TyCtxt<'tcx>,
+ generics: &'a hir::Generics<'a>,
+) -> impl Iterator<Item = &'a hir::GenericParam<'a>> + Captures<'tcx> {
+ generics.params.iter().filter(move |param| match param.kind {
+ GenericParamKind::Lifetime { .. } => !tcx.is_late_bound(param.hir_id),
+ _ => false,
+ })
+}
+
+/// Returns a list of type predicates for the definition with ID `def_id`, including inferred
+/// lifetime constraints. This includes all predicates returned by `explicit_predicates_of`, plus
+/// inferred constraints concerning which regions outlive other regions.
+#[instrument(level = "debug", skip(tcx))]
+fn predicates_defined_on(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicates<'_> {
+ let mut result = tcx.explicit_predicates_of(def_id);
+ debug!("predicates_defined_on: explicit_predicates_of({:?}) = {:?}", def_id, result,);
+ let inferred_outlives = tcx.inferred_outlives_of(def_id);
+ if !inferred_outlives.is_empty() {
+ debug!(
+ "predicates_defined_on: inferred_outlives_of({:?}) = {:?}",
+ def_id, inferred_outlives,
+ );
+ if result.predicates.is_empty() {
+ result.predicates = inferred_outlives;
+ } else {
+ result.predicates = tcx
+ .arena
+ .alloc_from_iter(result.predicates.iter().chain(inferred_outlives).copied());
+ }
+ }
+
+ debug!("predicates_defined_on({:?}) = {:?}", def_id, result);
+ result
+}
+
+fn compute_sig_of_foreign_fn_decl<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ def_id: DefId,
+ decl: &'tcx hir::FnDecl<'tcx>,
+ abi: abi::Abi,
+) -> ty::PolyFnSig<'tcx> {
+ let unsafety = if abi == abi::Abi::RustIntrinsic {
+ intrinsic_operation_unsafety(tcx, def_id)
+ } else {
+ hir::Unsafety::Unsafe
+ };
+ let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local());
+ let fty = <dyn AstConv<'_>>::ty_of_fn(
+ &ItemCtxt::new(tcx, def_id),
+ hir_id,
+ unsafety,
+ abi,
+ decl,
+ None,
+ None,
+ );
+
+ // Feature gate SIMD types in FFI, since I am not sure that the
+ // ABIs are handled at all correctly. -huonw
+ if abi != abi::Abi::RustIntrinsic
+ && abi != abi::Abi::PlatformIntrinsic
+ && !tcx.features().simd_ffi
+ {
+ let check = |ast_ty: &hir::Ty<'_>, ty: Ty<'_>| {
+ if ty.is_simd() {
+ let snip = tcx
+ .sess
+ .source_map()
+ .span_to_snippet(ast_ty.span)
+ .map_or_else(|_| String::new(), |s| format!(" `{}`", s));
+ tcx.sess
+ .struct_span_err(
+ ast_ty.span,
+ &format!(
+ "use of SIMD type{} in FFI is highly experimental and \
+ may result in invalid code",
+ snip
+ ),
+ )
+ .help("add `#![feature(simd_ffi)]` to the crate attributes to enable")
+ .emit();
+ }
+ };
+ for (input, ty) in iter::zip(decl.inputs, fty.inputs().skip_binder()) {
+ check(input, *ty)
+ }
+ if let hir::FnRetTy::Return(ref ty) = decl.output {
+ check(ty, fty.output().skip_binder())
+ }
+ }
+
+ fty
+}
+
+fn is_foreign_item(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
+ match tcx.hir().get_if_local(def_id) {
+ Some(Node::ForeignItem(..)) => true,
+ Some(_) => false,
+ _ => bug!("is_foreign_item applied to non-local def-id {:?}", def_id),
+ }
+}
+
+fn generator_kind(tcx: TyCtxt<'_>, def_id: DefId) -> Option<hir::GeneratorKind> {
+ match tcx.hir().get_if_local(def_id) {
+ Some(Node::Expr(&rustc_hir::Expr {
+ kind: rustc_hir::ExprKind::Closure(&rustc_hir::Closure { body, .. }),
+ ..
+ })) => tcx.hir().body(body).generator_kind(),
+ Some(_) => None,
+ _ => bug!("generator_kind applied to non-local def-id {:?}", def_id),
+ }
+}
+
+fn from_target_feature(
+ tcx: TyCtxt<'_>,
+ attr: &ast::Attribute,
+ supported_target_features: &FxHashMap<String, Option<Symbol>>,
+ target_features: &mut Vec<Symbol>,
+) {
+ let Some(list) = attr.meta_item_list() else { return };
+ let bad_item = |span| {
+ let msg = "malformed `target_feature` attribute input";
+ let code = "enable = \"..\"";
+ tcx.sess
+ .struct_span_err(span, msg)
+ .span_suggestion(span, "must be of the form", code, Applicability::HasPlaceholders)
+ .emit();
+ };
+ let rust_features = tcx.features();
+ for item in list {
+ // Only `enable = ...` is accepted in the meta-item list.
+ if !item.has_name(sym::enable) {
+ bad_item(item.span());
+ continue;
+ }
+
+ // Must be of the form `enable = "..."` (a string).
+ let Some(value) = item.value_str() else {
+ bad_item(item.span());
+ continue;
+ };
+
+ // We allow comma separation to enable multiple features.
+ target_features.extend(value.as_str().split(',').filter_map(|feature| {
+ let Some(feature_gate) = supported_target_features.get(feature) else {
+ let msg =
+ format!("the feature named `{}` is not valid for this target", feature);
+ let mut err = tcx.sess.struct_span_err(item.span(), &msg);
+ err.span_label(
+ item.span(),
+ format!("`{}` is not valid for this target", feature),
+ );
+ if let Some(stripped) = feature.strip_prefix('+') {
+ let valid = supported_target_features.contains_key(stripped);
+ if valid {
+ err.help("consider removing the leading `+` in the feature name");
+ }
+ }
+ err.emit();
+ return None;
+ };
+
+ // Only allow features whose feature gates have been enabled.
+ let allowed = match feature_gate.as_ref().copied() {
+ Some(sym::arm_target_feature) => rust_features.arm_target_feature,
+ Some(sym::hexagon_target_feature) => rust_features.hexagon_target_feature,
+ Some(sym::powerpc_target_feature) => rust_features.powerpc_target_feature,
+ Some(sym::mips_target_feature) => rust_features.mips_target_feature,
+ Some(sym::riscv_target_feature) => rust_features.riscv_target_feature,
+ Some(sym::avx512_target_feature) => rust_features.avx512_target_feature,
+ Some(sym::sse4a_target_feature) => rust_features.sse4a_target_feature,
+ Some(sym::tbm_target_feature) => rust_features.tbm_target_feature,
+ Some(sym::wasm_target_feature) => rust_features.wasm_target_feature,
+ Some(sym::cmpxchg16b_target_feature) => rust_features.cmpxchg16b_target_feature,
+ Some(sym::movbe_target_feature) => rust_features.movbe_target_feature,
+ Some(sym::rtm_target_feature) => rust_features.rtm_target_feature,
+ Some(sym::f16c_target_feature) => rust_features.f16c_target_feature,
+ Some(sym::ermsb_target_feature) => rust_features.ermsb_target_feature,
+ Some(sym::bpf_target_feature) => rust_features.bpf_target_feature,
+ Some(sym::aarch64_ver_target_feature) => rust_features.aarch64_ver_target_feature,
+ Some(name) => bug!("unknown target feature gate {}", name),
+ None => true,
+ };
+ if !allowed {
+ feature_err(
+ &tcx.sess.parse_sess,
+ feature_gate.unwrap(),
+ item.span(),
+ &format!("the target feature `{}` is currently unstable", feature),
+ )
+ .emit();
+ }
+ Some(Symbol::intern(feature))
+ }));
+ }
+}
+
+fn linkage_by_name(tcx: TyCtxt<'_>, def_id: LocalDefId, name: &str) -> Linkage {
+ use rustc_middle::mir::mono::Linkage::*;
+
+ // Use the names from src/llvm/docs/LangRef.rst here. Most types are only
+ // applicable to variable declarations and may not really make sense for
+ // Rust code in the first place but allow them anyway and trust that the
+ // user knows what they're doing. Who knows, unanticipated use cases may pop
+ // up in the future.
+ //
+ // ghost, dllimport, dllexport and linkonce_odr_autohide are not supported
+ // and don't have to be, LLVM treats them as no-ops.
+ match name {
+ "appending" => Appending,
+ "available_externally" => AvailableExternally,
+ "common" => Common,
+ "extern_weak" => ExternalWeak,
+ "external" => External,
+ "internal" => Internal,
+ "linkonce" => LinkOnceAny,
+ "linkonce_odr" => LinkOnceODR,
+ "private" => Private,
+ "weak" => WeakAny,
+ "weak_odr" => WeakODR,
+ _ => tcx.sess.span_fatal(tcx.def_span(def_id), "invalid linkage specified"),
+ }
+}
+
+fn codegen_fn_attrs(tcx: TyCtxt<'_>, did: DefId) -> CodegenFnAttrs {
+ if cfg!(debug_assertions) {
+ let def_kind = tcx.def_kind(did);
+ assert!(
+ def_kind.has_codegen_attrs(),
+ "unexpected `def_kind` in `codegen_fn_attrs`: {def_kind:?}",
+ );
+ }
+
+ let did = did.expect_local();
+ let attrs = tcx.hir().attrs(tcx.hir().local_def_id_to_hir_id(did));
+ let mut codegen_fn_attrs = CodegenFnAttrs::new();
+ if tcx.should_inherit_track_caller(did) {
+ codegen_fn_attrs.flags |= CodegenFnAttrFlags::TRACK_CALLER;
+ }
+
+ let supported_target_features = tcx.supported_target_features(LOCAL_CRATE);
+
+ let mut inline_span = None;
+ let mut link_ordinal_span = None;
+ let mut no_sanitize_span = None;
+ for attr in attrs.iter() {
+ if attr.has_name(sym::cold) {
+ codegen_fn_attrs.flags |= CodegenFnAttrFlags::COLD;
+ } else if attr.has_name(sym::rustc_allocator) {
+ codegen_fn_attrs.flags |= CodegenFnAttrFlags::ALLOCATOR;
+ } else if attr.has_name(sym::ffi_returns_twice) {
+ if tcx.is_foreign_item(did) {
+ codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_RETURNS_TWICE;
+ } else {
+ // `#[ffi_returns_twice]` is only allowed `extern fn`s.
+ struct_span_err!(
+ tcx.sess,
+ attr.span,
+ E0724,
+ "`#[ffi_returns_twice]` may only be used on foreign functions"
+ )
+ .emit();
+ }
+ } else if attr.has_name(sym::ffi_pure) {
+ if tcx.is_foreign_item(did) {
+ if attrs.iter().any(|a| a.has_name(sym::ffi_const)) {
+ // `#[ffi_const]` functions cannot be `#[ffi_pure]`
+ struct_span_err!(
+ tcx.sess,
+ attr.span,
+ E0757,
+ "`#[ffi_const]` function cannot be `#[ffi_pure]`"
+ )
+ .emit();
+ } else {
+ codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_PURE;
+ }
+ } else {
+ // `#[ffi_pure]` is only allowed on foreign functions
+ struct_span_err!(
+ tcx.sess,
+ attr.span,
+ E0755,
+ "`#[ffi_pure]` may only be used on foreign functions"
+ )
+ .emit();
+ }
+ } else if attr.has_name(sym::ffi_const) {
+ if tcx.is_foreign_item(did) {
+ codegen_fn_attrs.flags |= CodegenFnAttrFlags::FFI_CONST;
+ } else {
+ // `#[ffi_const]` is only allowed on foreign functions
+ struct_span_err!(
+ tcx.sess,
+ attr.span,
+ E0756,
+ "`#[ffi_const]` may only be used on foreign functions"
+ )
+ .emit();
+ }
+ } else if attr.has_name(sym::rustc_nounwind) {
+ codegen_fn_attrs.flags |= CodegenFnAttrFlags::NEVER_UNWIND;
+ } else if attr.has_name(sym::rustc_reallocator) {
+ codegen_fn_attrs.flags |= CodegenFnAttrFlags::REALLOCATOR;
+ } else if attr.has_name(sym::rustc_deallocator) {
+ codegen_fn_attrs.flags |= CodegenFnAttrFlags::DEALLOCATOR;
+ } else if attr.has_name(sym::rustc_allocator_zeroed) {
+ codegen_fn_attrs.flags |= CodegenFnAttrFlags::ALLOCATOR_ZEROED;
+ } else if attr.has_name(sym::naked) {
+ codegen_fn_attrs.flags |= CodegenFnAttrFlags::NAKED;
+ } else if attr.has_name(sym::no_mangle) {
+ codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_MANGLE;
+ } else if attr.has_name(sym::no_coverage) {
+ codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_COVERAGE;
+ } else if attr.has_name(sym::rustc_std_internal_symbol) {
+ codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL;
+ } else if attr.has_name(sym::used) {
+ let inner = attr.meta_item_list();
+ match inner.as_deref() {
+ Some([item]) if item.has_name(sym::linker) => {
+ if !tcx.features().used_with_arg {
+ feature_err(
+ &tcx.sess.parse_sess,
+ sym::used_with_arg,
+ attr.span,
+ "`#[used(linker)]` is currently unstable",
+ )
+ .emit();
+ }
+ codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED_LINKER;
+ }
+ Some([item]) if item.has_name(sym::compiler) => {
+ if !tcx.features().used_with_arg {
+ feature_err(
+ &tcx.sess.parse_sess,
+ sym::used_with_arg,
+ attr.span,
+ "`#[used(compiler)]` is currently unstable",
+ )
+ .emit();
+ }
+ codegen_fn_attrs.flags |= CodegenFnAttrFlags::USED;
+ }
+ Some(_) => {
+ tcx.sess.emit_err(errors::ExpectedUsedSymbol { span: attr.span });
+ }
+ None => {
+ // Unfortunately, unconditionally using `llvm.used` causes
+ // issues in handling `.init_array` with the gold linker,
+ // but using `llvm.compiler.used` caused a nontrival amount
+ // of unintentional ecosystem breakage -- particularly on
+ // Mach-O targets.
+ //
+ // As a result, we emit `llvm.compiler.used` only on ELF
+ // targets. This is somewhat ad-hoc, but actually follows
+ // our pre-LLVM 13 behavior (prior to the ecosystem
+ // breakage), and seems to match `clang`'s behavior as well
+ // (both before and after LLVM 13), possibly because they
+ // have similar compatibility concerns to us. See
+ // https://github.com/rust-lang/rust/issues/47384#issuecomment-1019080146
+ // and following comments for some discussion of this, as
+ // well as the comments in `rustc_codegen_llvm` where these
+ // flags are handled.
+ //
+ // Anyway, to be clear: this is still up in the air
+ // somewhat, and is subject to change in the future (which
+ // is a good thing, because this would ideally be a bit
+ // more firmed up).
+ let is_like_elf = !(tcx.sess.target.is_like_osx
+ || tcx.sess.target.is_like_windows
+ || tcx.sess.target.is_like_wasm);
+ codegen_fn_attrs.flags |= if is_like_elf {
+ CodegenFnAttrFlags::USED
+ } else {
+ CodegenFnAttrFlags::USED_LINKER
+ };
+ }
+ }
+ } else if attr.has_name(sym::cmse_nonsecure_entry) {
+ if !matches!(tcx.fn_sig(did).abi(), abi::Abi::C { .. }) {
+ struct_span_err!(
+ tcx.sess,
+ attr.span,
+ E0776,
+ "`#[cmse_nonsecure_entry]` requires C ABI"
+ )
+ .emit();
+ }
+ if !tcx.sess.target.llvm_target.contains("thumbv8m") {
+ struct_span_err!(tcx.sess, attr.span, E0775, "`#[cmse_nonsecure_entry]` is only valid for targets with the TrustZone-M extension")
+ .emit();
+ }
+ codegen_fn_attrs.flags |= CodegenFnAttrFlags::CMSE_NONSECURE_ENTRY;
+ } else if attr.has_name(sym::thread_local) {
+ codegen_fn_attrs.flags |= CodegenFnAttrFlags::THREAD_LOCAL;
+ } else if attr.has_name(sym::track_caller) {
+ if !tcx.is_closure(did.to_def_id()) && tcx.fn_sig(did).abi() != abi::Abi::Rust {
+ struct_span_err!(tcx.sess, attr.span, E0737, "`#[track_caller]` requires Rust ABI")
+ .emit();
+ }
+ if tcx.is_closure(did.to_def_id()) && !tcx.features().closure_track_caller {
+ feature_err(
+ &tcx.sess.parse_sess,
+ sym::closure_track_caller,
+ attr.span,
+ "`#[track_caller]` on closures is currently unstable",
+ )
+ .emit();
+ }
+ codegen_fn_attrs.flags |= CodegenFnAttrFlags::TRACK_CALLER;
+ } else if attr.has_name(sym::export_name) {
+ if let Some(s) = attr.value_str() {
+ if s.as_str().contains('\0') {
+ // `#[export_name = ...]` will be converted to a null-terminated string,
+ // so it may not contain any null characters.
+ struct_span_err!(
+ tcx.sess,
+ attr.span,
+ E0648,
+ "`export_name` may not contain null characters"
+ )
+ .emit();
+ }
+ codegen_fn_attrs.export_name = Some(s);
+ }
+ } else if attr.has_name(sym::target_feature) {
+ if !tcx.is_closure(did.to_def_id())
+ && tcx.fn_sig(did).unsafety() == hir::Unsafety::Normal
+ {
+ if tcx.sess.target.is_like_wasm || tcx.sess.opts.actually_rustdoc {
+ // The `#[target_feature]` attribute is allowed on
+ // WebAssembly targets on all functions, including safe
+ // ones. Other targets require that `#[target_feature]` is
+ // only applied to unsafe functions (pending the
+ // `target_feature_11` feature) because on most targets
+ // execution of instructions that are not supported is
+ // considered undefined behavior. For WebAssembly which is a
+ // 100% safe target at execution time it's not possible to
+ // execute undefined instructions, and even if a future
+ // feature was added in some form for this it would be a
+ // deterministic trap. There is no undefined behavior when
+ // executing WebAssembly so `#[target_feature]` is allowed
+ // on safe functions (but again, only for WebAssembly)
+ //
+ // Note that this is also allowed if `actually_rustdoc` so
+ // if a target is documenting some wasm-specific code then
+ // it's not spuriously denied.
+ } else if !tcx.features().target_feature_11 {
+ let mut err = feature_err(
+ &tcx.sess.parse_sess,
+ sym::target_feature_11,
+ attr.span,
+ "`#[target_feature(..)]` can only be applied to `unsafe` functions",
+ );
+ err.span_label(tcx.def_span(did), "not an `unsafe` function");
+ err.emit();
+ } else {
+ check_target_feature_trait_unsafe(tcx, did, attr.span);
+ }
+ }
+ from_target_feature(
+ tcx,
+ attr,
+ supported_target_features,
+ &mut codegen_fn_attrs.target_features,
+ );
+ } else if attr.has_name(sym::linkage) {
+ if let Some(val) = attr.value_str() {
+ codegen_fn_attrs.linkage = Some(linkage_by_name(tcx, did, val.as_str()));
+ }
+ } else if attr.has_name(sym::link_section) {
+ if let Some(val) = attr.value_str() {
+ if val.as_str().bytes().any(|b| b == 0) {
+ let msg = format!(
+ "illegal null byte in link_section \
+ value: `{}`",
+ &val
+ );
+ tcx.sess.span_err(attr.span, &msg);
+ } else {
+ codegen_fn_attrs.link_section = Some(val);
+ }
+ }
+ } else if attr.has_name(sym::link_name) {
+ codegen_fn_attrs.link_name = attr.value_str();
+ } else if attr.has_name(sym::link_ordinal) {
+ link_ordinal_span = Some(attr.span);
+ if let ordinal @ Some(_) = check_link_ordinal(tcx, attr) {
+ codegen_fn_attrs.link_ordinal = ordinal;
+ }
+ } else if attr.has_name(sym::no_sanitize) {
+ no_sanitize_span = Some(attr.span);
+ if let Some(list) = attr.meta_item_list() {
+ for item in list.iter() {
+ if item.has_name(sym::address) {
+ codegen_fn_attrs.no_sanitize |= SanitizerSet::ADDRESS;
+ } else if item.has_name(sym::cfi) {
+ codegen_fn_attrs.no_sanitize |= SanitizerSet::CFI;
+ } else if item.has_name(sym::memory) {
+ codegen_fn_attrs.no_sanitize |= SanitizerSet::MEMORY;
+ } else if item.has_name(sym::memtag) {
+ codegen_fn_attrs.no_sanitize |= SanitizerSet::MEMTAG;
+ } else if item.has_name(sym::shadow_call_stack) {
+ codegen_fn_attrs.no_sanitize |= SanitizerSet::SHADOWCALLSTACK;
+ } else if item.has_name(sym::thread) {
+ codegen_fn_attrs.no_sanitize |= SanitizerSet::THREAD;
+ } else if item.has_name(sym::hwaddress) {
+ codegen_fn_attrs.no_sanitize |= SanitizerSet::HWADDRESS;
+ } else {
+ tcx.sess
+ .struct_span_err(item.span(), "invalid argument for `no_sanitize`")
+ .note("expected one of: `address`, `cfi`, `hwaddress`, `memory`, `memtag`, `shadow-call-stack`, or `thread`")
+ .emit();
+ }
+ }
+ }
+ } else if attr.has_name(sym::instruction_set) {
+ codegen_fn_attrs.instruction_set = match attr.meta_kind() {
+ Some(MetaItemKind::List(ref items)) => match items.as_slice() {
+ [NestedMetaItem::MetaItem(set)] => {
+ let segments =
+ set.path.segments.iter().map(|x| x.ident.name).collect::<Vec<_>>();
+ match segments.as_slice() {
+ [sym::arm, sym::a32] | [sym::arm, sym::t32] => {
+ if !tcx.sess.target.has_thumb_interworking {
+ struct_span_err!(
+ tcx.sess.diagnostic(),
+ attr.span,
+ E0779,
+ "target does not support `#[instruction_set]`"
+ )
+ .emit();
+ None
+ } else if segments[1] == sym::a32 {
+ Some(InstructionSetAttr::ArmA32)
+ } else if segments[1] == sym::t32 {
+ Some(InstructionSetAttr::ArmT32)
+ } else {
+ unreachable!()
+ }
+ }
+ _ => {
+ struct_span_err!(
+ tcx.sess.diagnostic(),
+ attr.span,
+ E0779,
+ "invalid instruction set specified",
+ )
+ .emit();
+ None
+ }
+ }
+ }
+ [] => {
+ struct_span_err!(
+ tcx.sess.diagnostic(),
+ attr.span,
+ E0778,
+ "`#[instruction_set]` requires an argument"
+ )
+ .emit();
+ None
+ }
+ _ => {
+ struct_span_err!(
+ tcx.sess.diagnostic(),
+ attr.span,
+ E0779,
+ "cannot specify more than one instruction set"
+ )
+ .emit();
+ None
+ }
+ },
+ _ => {
+ struct_span_err!(
+ tcx.sess.diagnostic(),
+ attr.span,
+ E0778,
+ "must specify an instruction set"
+ )
+ .emit();
+ None
+ }
+ };
+ } else if attr.has_name(sym::repr) {
+ codegen_fn_attrs.alignment = match attr.meta_item_list() {
+ Some(items) => match items.as_slice() {
+ [item] => match item.name_value_literal() {
+ Some((sym::align, literal)) => {
+ let alignment = rustc_attr::parse_alignment(&literal.kind);
+
+ match alignment {
+ Ok(align) => Some(align),
+ Err(msg) => {
+ struct_span_err!(
+ tcx.sess.diagnostic(),
+ attr.span,
+ E0589,
+ "invalid `repr(align)` attribute: {}",
+ msg
+ )
+ .emit();
+
+ None
+ }
+ }
+ }
+ _ => None,
+ },
+ [] => None,
+ _ => None,
+ },
+ None => None,
+ };
+ }
+ }
+
+ codegen_fn_attrs.inline = attrs.iter().fold(InlineAttr::None, |ia, attr| {
+ if !attr.has_name(sym::inline) {
+ return ia;
+ }
+ match attr.meta_kind() {
+ Some(MetaItemKind::Word) => InlineAttr::Hint,
+ Some(MetaItemKind::List(ref items)) => {
+ inline_span = Some(attr.span);
+ if items.len() != 1 {
+ struct_span_err!(
+ tcx.sess.diagnostic(),
+ attr.span,
+ E0534,
+ "expected one argument"
+ )
+ .emit();
+ InlineAttr::None
+ } else if list_contains_name(&items, sym::always) {
+ InlineAttr::Always
+ } else if list_contains_name(&items, sym::never) {
+ InlineAttr::Never
+ } else {
+ struct_span_err!(
+ tcx.sess.diagnostic(),
+ items[0].span(),
+ E0535,
+ "invalid argument"
+ )
+ .help("valid inline arguments are `always` and `never`")
+ .emit();
+
+ InlineAttr::None
+ }
+ }
+ Some(MetaItemKind::NameValue(_)) => ia,
+ None => ia,
+ }
+ });
+
+ codegen_fn_attrs.optimize = attrs.iter().fold(OptimizeAttr::None, |ia, attr| {
+ if !attr.has_name(sym::optimize) {
+ return ia;
+ }
+ let err = |sp, s| struct_span_err!(tcx.sess.diagnostic(), sp, E0722, "{}", s).emit();
+ match attr.meta_kind() {
+ Some(MetaItemKind::Word) => {
+ err(attr.span, "expected one argument");
+ ia
+ }
+ Some(MetaItemKind::List(ref items)) => {
+ inline_span = Some(attr.span);
+ if items.len() != 1 {
+ err(attr.span, "expected one argument");
+ OptimizeAttr::None
+ } else if list_contains_name(&items, sym::size) {
+ OptimizeAttr::Size
+ } else if list_contains_name(&items, sym::speed) {
+ OptimizeAttr::Speed
+ } else {
+ err(items[0].span(), "invalid argument");
+ OptimizeAttr::None
+ }
+ }
+ Some(MetaItemKind::NameValue(_)) => ia,
+ None => ia,
+ }
+ });
+
+ // #73631: closures inherit `#[target_feature]` annotations
+ if tcx.features().target_feature_11 && tcx.is_closure(did.to_def_id()) {
+ let owner_id = tcx.parent(did.to_def_id());
+ if tcx.def_kind(owner_id).has_codegen_attrs() {
+ codegen_fn_attrs
+ .target_features
+ .extend(tcx.codegen_fn_attrs(owner_id).target_features.iter().copied());
+ }
+ }
+
+ // If a function uses #[target_feature] it can't be inlined into general
+ // purpose functions as they wouldn't have the right target features
+ // enabled. For that reason we also forbid #[inline(always)] as it can't be
+ // respected.
+ if !codegen_fn_attrs.target_features.is_empty() {
+ if codegen_fn_attrs.inline == InlineAttr::Always {
+ if let Some(span) = inline_span {
+ tcx.sess.span_err(
+ span,
+ "cannot use `#[inline(always)]` with \
+ `#[target_feature]`",
+ );
+ }
+ }
+ }
+
+ if !codegen_fn_attrs.no_sanitize.is_empty() {
+ if codegen_fn_attrs.inline == InlineAttr::Always {
+ if let (Some(no_sanitize_span), Some(inline_span)) = (no_sanitize_span, inline_span) {
+ let hir_id = tcx.hir().local_def_id_to_hir_id(did);
+ tcx.struct_span_lint_hir(
+ lint::builtin::INLINE_NO_SANITIZE,
+ hir_id,
+ no_sanitize_span,
+ "`no_sanitize` will have no effect after inlining",
+ |lint| lint.span_note(inline_span, "inlining requested here"),
+ )
+ }
+ }
+ }
+
+ // Weak lang items have the same semantics as "std internal" symbols in the
+ // sense that they're preserved through all our LTO passes and only
+ // strippable by the linker.
+ //
+ // Additionally weak lang items have predetermined symbol names.
+ if tcx.is_weak_lang_item(did.to_def_id()) {
+ codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL;
+ }
+ if let Some(name) = weak_lang_items::link_name(attrs) {
+ codegen_fn_attrs.export_name = Some(name);
+ codegen_fn_attrs.link_name = Some(name);
+ }
+ check_link_name_xor_ordinal(tcx, &codegen_fn_attrs, link_ordinal_span);
+
+ // Internal symbols to the standard library all have no_mangle semantics in
+ // that they have defined symbol names present in the function name. This
+ // also applies to weak symbols where they all have known symbol names.
+ if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL) {
+ codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_MANGLE;
+ }
+
+ // Any linkage to LLVM intrinsics for now forcibly marks them all as never
+ // unwinds since LLVM sometimes can't handle codegen which `invoke`s
+ // intrinsic functions.
+ if let Some(name) = &codegen_fn_attrs.link_name {
+ if name.as_str().starts_with("llvm.") {
+ codegen_fn_attrs.flags |= CodegenFnAttrFlags::NEVER_UNWIND;
+ }
+ }
+
+ codegen_fn_attrs
+}
+
+/// Computes the set of target features used in a function for the purposes of
+/// inline assembly.
+fn asm_target_features<'tcx>(tcx: TyCtxt<'tcx>, did: DefId) -> &'tcx FxHashSet<Symbol> {
+ let mut target_features = tcx.sess.unstable_target_features.clone();
+ if tcx.def_kind(did).has_codegen_attrs() {
+ let attrs = tcx.codegen_fn_attrs(did);
+ target_features.extend(&attrs.target_features);
+ match attrs.instruction_set {
+ None => {}
+ Some(InstructionSetAttr::ArmA32) => {
+ target_features.remove(&sym::thumb_mode);
+ }
+ Some(InstructionSetAttr::ArmT32) => {
+ target_features.insert(sym::thumb_mode);
+ }
+ }
+ }
+
+ tcx.arena.alloc(target_features)
+}
+
+/// Checks if the provided DefId is a method in a trait impl for a trait which has track_caller
+/// applied to the method prototype.
+fn should_inherit_track_caller(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
+ if let Some(impl_item) = tcx.opt_associated_item(def_id)
+ && let ty::AssocItemContainer::ImplContainer = impl_item.container
+ && let Some(trait_item) = impl_item.trait_item_def_id
+ {
+ return tcx
+ .codegen_fn_attrs(trait_item)
+ .flags
+ .intersects(CodegenFnAttrFlags::TRACK_CALLER);
+ }
+
+ false
+}
+
+fn check_link_ordinal(tcx: TyCtxt<'_>, attr: &ast::Attribute) -> Option<u16> {
+ use rustc_ast::{Lit, LitIntType, LitKind};
+ if !tcx.features().raw_dylib && tcx.sess.target.arch == "x86" {
+ feature_err(
+ &tcx.sess.parse_sess,
+ sym::raw_dylib,
+ attr.span,
+ "`#[link_ordinal]` is unstable on x86",
+ )
+ .emit();
+ }
+ let meta_item_list = attr.meta_item_list();
+ let meta_item_list: Option<&[ast::NestedMetaItem]> = meta_item_list.as_ref().map(Vec::as_ref);
+ let sole_meta_list = match meta_item_list {
+ Some([item]) => item.literal(),
+ Some(_) => {
+ tcx.sess
+ .struct_span_err(attr.span, "incorrect number of arguments to `#[link_ordinal]`")
+ .note("the attribute requires exactly one argument")
+ .emit();
+ return None;
+ }
+ _ => None,
+ };
+ if let Some(Lit { kind: LitKind::Int(ordinal, LitIntType::Unsuffixed), .. }) = sole_meta_list {
+ // According to the table at https://docs.microsoft.com/en-us/windows/win32/debug/pe-format#import-header,
+ // the ordinal must fit into 16 bits. Similarly, the Ordinal field in COFFShortExport (defined
+ // in llvm/include/llvm/Object/COFFImportFile.h), which we use to communicate import information
+ // to LLVM for `#[link(kind = "raw-dylib"_])`, is also defined to be uint16_t.
+ //
+ // FIXME: should we allow an ordinal of 0? The MSVC toolchain has inconsistent support for this:
+ // both LINK.EXE and LIB.EXE signal errors and abort when given a .DEF file that specifies
+ // a zero ordinal. However, llvm-dlltool is perfectly happy to generate an import library
+ // for such a .DEF file, and MSVC's LINK.EXE is also perfectly happy to consume an import
+ // library produced by LLVM with an ordinal of 0, and it generates an .EXE. (I don't know yet
+ // if the resulting EXE runs, as I haven't yet built the necessary DLL -- see earlier comment
+ // about LINK.EXE failing.)
+ if *ordinal <= u16::MAX as u128 {
+ Some(*ordinal as u16)
+ } else {
+ let msg = format!("ordinal value in `link_ordinal` is too large: `{}`", &ordinal);
+ tcx.sess
+ .struct_span_err(attr.span, &msg)
+ .note("the value may not exceed `u16::MAX`")
+ .emit();
+ None
+ }
+ } else {
+ tcx.sess
+ .struct_span_err(attr.span, "illegal ordinal format in `link_ordinal`")
+ .note("an unsuffixed integer value, e.g., `1`, is expected")
+ .emit();
+ None
+ }
+}
+
+fn check_link_name_xor_ordinal(
+ tcx: TyCtxt<'_>,
+ codegen_fn_attrs: &CodegenFnAttrs,
+ inline_span: Option<Span>,
+) {
+ if codegen_fn_attrs.link_name.is_none() || codegen_fn_attrs.link_ordinal.is_none() {
+ return;
+ }
+ let msg = "cannot use `#[link_name]` with `#[link_ordinal]`";
+ if let Some(span) = inline_span {
+ tcx.sess.span_err(span, msg);
+ } else {
+ tcx.sess.err(msg);
+ }
+}
+
+/// Checks the function annotated with `#[target_feature]` is not a safe
+/// trait method implementation, reporting an error if it is.
+fn check_target_feature_trait_unsafe(tcx: TyCtxt<'_>, id: LocalDefId, attr_span: Span) {
+ let hir_id = tcx.hir().local_def_id_to_hir_id(id);
+ let node = tcx.hir().get(hir_id);
+ if let Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Fn(..), .. }) = node {
+ let parent_id = tcx.hir().get_parent_item(hir_id);
+ let parent_item = tcx.hir().expect_item(parent_id.def_id);
+ if let hir::ItemKind::Impl(hir::Impl { of_trait: Some(_), .. }) = parent_item.kind {
+ tcx.sess
+ .struct_span_err(
+ attr_span,
+ "`#[target_feature(..)]` cannot be applied to safe trait method",
+ )
+ .span_label(attr_span, "cannot be applied to safe trait method")
+ .span_label(tcx.def_span(id), "not an `unsafe` function")
+ .emit();
+ }
+ }
+}
diff --git a/compiler/rustc_hir_analysis/src/collect/generics_of.rs b/compiler/rustc_hir_analysis/src/collect/generics_of.rs
new file mode 100644
index 000000000..c7777a946
--- /dev/null
+++ b/compiler/rustc_hir_analysis/src/collect/generics_of.rs
@@ -0,0 +1,481 @@
+use crate::middle::resolve_lifetime as rl;
+use hir::{
+ intravisit::{self, Visitor},
+ GenericParamKind, HirId, Node,
+};
+use rustc_hir as hir;
+use rustc_hir::def::DefKind;
+use rustc_hir::def_id::DefId;
+use rustc_middle::ty::{self, TyCtxt};
+use rustc_session::lint;
+use rustc_span::symbol::{kw, Symbol};
+use rustc_span::Span;
+
+pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::Generics {
+ use rustc_hir::*;
+
+ let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local());
+
+ let node = tcx.hir().get(hir_id);
+ let parent_def_id = match node {
+ Node::ImplItem(_)
+ | Node::TraitItem(_)
+ | Node::Variant(_)
+ | Node::Ctor(..)
+ | Node::Field(_) => {
+ let parent_id = tcx.hir().get_parent_item(hir_id);
+ Some(parent_id.to_def_id())
+ }
+ // FIXME(#43408) always enable this once `lazy_normalization` is
+ // stable enough and does not need a feature gate anymore.
+ Node::AnonConst(_) => {
+ let parent_def_id = tcx.hir().get_parent_item(hir_id);
+
+ let mut in_param_ty = false;
+ for (_parent, node) in tcx.hir().parent_iter(hir_id) {
+ if let Some(generics) = node.generics() {
+ let mut visitor = AnonConstInParamTyDetector {
+ in_param_ty: false,
+ found_anon_const_in_param_ty: false,
+ ct: hir_id,
+ };
+
+ visitor.visit_generics(generics);
+ in_param_ty = visitor.found_anon_const_in_param_ty;
+ break;
+ }
+ }
+
+ if in_param_ty {
+ // We do not allow generic parameters in anon consts if we are inside
+ // of a const parameter type, e.g. `struct Foo<const N: usize, const M: [u8; N]>` is not allowed.
+ None
+ } else if tcx.lazy_normalization() {
+ if let Some(param_id) = tcx.hir().opt_const_param_default_param_hir_id(hir_id) {
+ // If the def_id we are calling generics_of on is an anon ct default i.e:
+ //
+ // struct Foo<const N: usize = { .. }>;
+ // ^^^ ^ ^^^^^^ def id of this anon const
+ // ^ ^ param_id
+ // ^ parent_def_id
+ //
+ // then we only want to return generics for params to the left of `N`. If we don't do that we
+ // end up with that const looking like: `ty::ConstKind::Unevaluated(def_id, substs: [N#0])`.
+ //
+ // This causes ICEs (#86580) when building the substs for Foo in `fn foo() -> Foo { .. }` as
+ // we substitute the defaults with the partially built substs when we build the substs. Subst'ing
+ // the `N#0` on the unevaluated const indexes into the empty substs we're in the process of building.
+ //
+ // We fix this by having this function return the parent's generics ourselves and truncating the
+ // generics to only include non-forward declared params (with the exception of the `Self` ty)
+ //
+ // For the above code example that means we want `substs: []`
+ // For the following struct def we want `substs: [N#0]` when generics_of is called on
+ // the def id of the `{ N + 1 }` anon const
+ // struct Foo<const N: usize, const M: usize = { N + 1 }>;
+ //
+ // This has some implications for how we get the predicates available to the anon const
+ // see `explicit_predicates_of` for more information on this
+ let generics = tcx.generics_of(parent_def_id.to_def_id());
+ let param_def = tcx.hir().local_def_id(param_id).to_def_id();
+ let param_def_idx = generics.param_def_id_to_index[&param_def];
+ // In the above example this would be .params[..N#0]
+ let params = generics.params[..param_def_idx as usize].to_owned();
+ let param_def_id_to_index =
+ params.iter().map(|param| (param.def_id, param.index)).collect();
+
+ return ty::Generics {
+ // we set the parent of these generics to be our parent's parent so that we
+ // dont end up with substs: [N, M, N] for the const default on a struct like this:
+ // struct Foo<const N: usize, const M: usize = { ... }>;
+ parent: generics.parent,
+ parent_count: generics.parent_count,
+ params,
+ param_def_id_to_index,
+ has_self: generics.has_self,
+ has_late_bound_regions: generics.has_late_bound_regions,
+ };
+ }
+
+ // HACK(eddyb) this provides the correct generics when
+ // `feature(generic_const_expressions)` is enabled, so that const expressions
+ // used with const generics, e.g. `Foo<{N+1}>`, can work at all.
+ //
+ // Note that we do not supply the parent generics when using
+ // `min_const_generics`.
+ Some(parent_def_id.to_def_id())
+ } else {
+ let parent_node = tcx.hir().get(tcx.hir().get_parent_node(hir_id));
+ match parent_node {
+ // HACK(eddyb) this provides the correct generics for repeat
+ // expressions' count (i.e. `N` in `[x; N]`), and explicit
+ // `enum` discriminants (i.e. `D` in `enum Foo { Bar = D }`),
+ // as they shouldn't be able to cause query cycle errors.
+ Node::Expr(&Expr { kind: ExprKind::Repeat(_, ref constant), .. })
+ if constant.hir_id() == hir_id =>
+ {
+ Some(parent_def_id.to_def_id())
+ }
+ Node::Variant(Variant { disr_expr: Some(ref constant), .. })
+ if constant.hir_id == hir_id =>
+ {
+ Some(parent_def_id.to_def_id())
+ }
+ Node::Expr(&Expr { kind: ExprKind::ConstBlock(_), .. }) => {
+ Some(tcx.typeck_root_def_id(def_id))
+ }
+ // Exclude `GlobalAsm` here which cannot have generics.
+ Node::Expr(&Expr { kind: ExprKind::InlineAsm(asm), .. })
+ if asm.operands.iter().any(|(op, _op_sp)| match op {
+ hir::InlineAsmOperand::Const { anon_const }
+ | hir::InlineAsmOperand::SymFn { anon_const } => {
+ anon_const.hir_id == hir_id
+ }
+ _ => false,
+ }) =>
+ {
+ Some(parent_def_id.to_def_id())
+ }
+ _ => None,
+ }
+ }
+ }
+ Node::Expr(&hir::Expr { kind: hir::ExprKind::Closure { .. }, .. }) => {
+ Some(tcx.typeck_root_def_id(def_id))
+ }
+ Node::Item(item) => match item.kind {
+ ItemKind::OpaqueTy(hir::OpaqueTy {
+ origin:
+ hir::OpaqueTyOrigin::FnReturn(fn_def_id) | hir::OpaqueTyOrigin::AsyncFn(fn_def_id),
+ in_trait,
+ ..
+ }) => {
+ if in_trait {
+ assert!(matches!(tcx.def_kind(fn_def_id), DefKind::AssocFn))
+ } else {
+ assert!(matches!(tcx.def_kind(fn_def_id), DefKind::AssocFn | DefKind::Fn))
+ }
+ Some(fn_def_id.to_def_id())
+ }
+ ItemKind::OpaqueTy(hir::OpaqueTy { origin: hir::OpaqueTyOrigin::TyAlias, .. }) => {
+ let parent_id = tcx.hir().get_parent_item(hir_id);
+ assert_ne!(parent_id, hir::CRATE_OWNER_ID);
+ debug!("generics_of: parent of opaque ty {:?} is {:?}", def_id, parent_id);
+ // Opaque types are always nested within another item, and
+ // inherit the generics of the item.
+ Some(parent_id.to_def_id())
+ }
+ _ => None,
+ },
+ _ => None,
+ };
+
+ enum Defaults {
+ Allowed,
+ // See #36887
+ FutureCompatDisallowed,
+ Deny,
+ }
+
+ let no_generics = hir::Generics::empty();
+ let ast_generics = node.generics().unwrap_or(&no_generics);
+ let (opt_self, allow_defaults) = match node {
+ Node::Item(item) => {
+ match item.kind {
+ ItemKind::Trait(..) | ItemKind::TraitAlias(..) => {
+ // Add in the self type parameter.
+ //
+ // Something of a hack: use the node id for the trait, also as
+ // the node id for the Self type parameter.
+ let opt_self = Some(ty::GenericParamDef {
+ index: 0,
+ name: kw::SelfUpper,
+ def_id,
+ pure_wrt_drop: false,
+ kind: ty::GenericParamDefKind::Type {
+ has_default: false,
+ synthetic: false,
+ },
+ });
+
+ (opt_self, Defaults::Allowed)
+ }
+ ItemKind::TyAlias(..)
+ | ItemKind::Enum(..)
+ | ItemKind::Struct(..)
+ | ItemKind::OpaqueTy(..)
+ | ItemKind::Union(..) => (None, Defaults::Allowed),
+ _ => (None, Defaults::FutureCompatDisallowed),
+ }
+ }
+
+ // GATs
+ Node::TraitItem(item) if matches!(item.kind, TraitItemKind::Type(..)) => {
+ (None, Defaults::Deny)
+ }
+ Node::ImplItem(item) if matches!(item.kind, ImplItemKind::Type(..)) => {
+ (None, Defaults::Deny)
+ }
+
+ _ => (None, Defaults::FutureCompatDisallowed),
+ };
+
+ let has_self = opt_self.is_some();
+ let mut parent_has_self = false;
+ let mut own_start = has_self as u32;
+ let parent_count = parent_def_id.map_or(0, |def_id| {
+ let generics = tcx.generics_of(def_id);
+ assert!(!has_self);
+ parent_has_self = generics.has_self;
+ own_start = generics.count() as u32;
+ generics.parent_count + generics.params.len()
+ });
+
+ let mut params: Vec<_> = Vec::with_capacity(ast_generics.params.len() + has_self as usize);
+
+ if let Some(opt_self) = opt_self {
+ params.push(opt_self);
+ }
+
+ let early_lifetimes = super::early_bound_lifetimes_from_generics(tcx, ast_generics);
+ params.extend(early_lifetimes.enumerate().map(|(i, param)| ty::GenericParamDef {
+ name: param.name.ident().name,
+ index: own_start + i as u32,
+ def_id: tcx.hir().local_def_id(param.hir_id).to_def_id(),
+ pure_wrt_drop: param.pure_wrt_drop,
+ kind: ty::GenericParamDefKind::Lifetime,
+ }));
+
+ // Now create the real type and const parameters.
+ let type_start = own_start - has_self as u32 + params.len() as u32;
+ let mut i = 0;
+ let mut next_index = || {
+ let prev = i;
+ i += 1;
+ prev as u32 + type_start
+ };
+
+ const TYPE_DEFAULT_NOT_ALLOWED: &'static str = "defaults for type parameters are only allowed in \
+ `struct`, `enum`, `type`, or `trait` definitions";
+
+ params.extend(ast_generics.params.iter().filter_map(|param| match param.kind {
+ GenericParamKind::Lifetime { .. } => None,
+ GenericParamKind::Type { ref default, synthetic, .. } => {
+ if default.is_some() {
+ match allow_defaults {
+ Defaults::Allowed => {}
+ Defaults::FutureCompatDisallowed
+ if tcx.features().default_type_parameter_fallback => {}
+ Defaults::FutureCompatDisallowed => {
+ tcx.struct_span_lint_hir(
+ lint::builtin::INVALID_TYPE_PARAM_DEFAULT,
+ param.hir_id,
+ param.span,
+ TYPE_DEFAULT_NOT_ALLOWED,
+ |lint| lint,
+ );
+ }
+ Defaults::Deny => {
+ tcx.sess.span_err(param.span, TYPE_DEFAULT_NOT_ALLOWED);
+ }
+ }
+ }
+
+ let kind = ty::GenericParamDefKind::Type { has_default: default.is_some(), synthetic };
+
+ Some(ty::GenericParamDef {
+ index: next_index(),
+ name: param.name.ident().name,
+ def_id: tcx.hir().local_def_id(param.hir_id).to_def_id(),
+ pure_wrt_drop: param.pure_wrt_drop,
+ kind,
+ })
+ }
+ GenericParamKind::Const { default, .. } => {
+ if !matches!(allow_defaults, Defaults::Allowed) && default.is_some() {
+ tcx.sess.span_err(
+ param.span,
+ "defaults for const parameters are only allowed in \
+ `struct`, `enum`, `type`, or `trait` definitions",
+ );
+ }
+
+ Some(ty::GenericParamDef {
+ index: next_index(),
+ name: param.name.ident().name,
+ def_id: tcx.hir().local_def_id(param.hir_id).to_def_id(),
+ pure_wrt_drop: param.pure_wrt_drop,
+ kind: ty::GenericParamDefKind::Const { has_default: default.is_some() },
+ })
+ }
+ }));
+
+ // provide junk type parameter defs - the only place that
+ // cares about anything but the length is instantiation,
+ // and we don't do that for closures.
+ if let Node::Expr(&hir::Expr {
+ kind: hir::ExprKind::Closure(hir::Closure { movability: gen, .. }),
+ ..
+ }) = node
+ {
+ let dummy_args = if gen.is_some() {
+ &["<resume_ty>", "<yield_ty>", "<return_ty>", "<witness>", "<upvars>"][..]
+ } else {
+ &["<closure_kind>", "<closure_signature>", "<upvars>"][..]
+ };
+
+ params.extend(dummy_args.iter().map(|&arg| ty::GenericParamDef {
+ index: next_index(),
+ name: Symbol::intern(arg),
+ def_id,
+ pure_wrt_drop: false,
+ kind: ty::GenericParamDefKind::Type { has_default: false, synthetic: false },
+ }));
+ }
+
+ // provide junk type parameter defs for const blocks.
+ if let Node::AnonConst(_) = node {
+ let parent_node = tcx.hir().get(tcx.hir().get_parent_node(hir_id));
+ if let Node::Expr(&Expr { kind: ExprKind::ConstBlock(_), .. }) = parent_node {
+ params.push(ty::GenericParamDef {
+ index: next_index(),
+ name: Symbol::intern("<const_ty>"),
+ def_id,
+ pure_wrt_drop: false,
+ kind: ty::GenericParamDefKind::Type { has_default: false, synthetic: false },
+ });
+ }
+ }
+
+ let param_def_id_to_index = params.iter().map(|param| (param.def_id, param.index)).collect();
+
+ ty::Generics {
+ parent: parent_def_id,
+ parent_count,
+ params,
+ param_def_id_to_index,
+ has_self: has_self || parent_has_self,
+ has_late_bound_regions: has_late_bound_regions(tcx, node),
+ }
+}
+
+fn has_late_bound_regions<'tcx>(tcx: TyCtxt<'tcx>, node: Node<'tcx>) -> Option<Span> {
+ struct LateBoundRegionsDetector<'tcx> {
+ tcx: TyCtxt<'tcx>,
+ outer_index: ty::DebruijnIndex,
+ has_late_bound_regions: Option<Span>,
+ }
+
+ impl<'tcx> Visitor<'tcx> for LateBoundRegionsDetector<'tcx> {
+ fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx>) {
+ if self.has_late_bound_regions.is_some() {
+ return;
+ }
+ match ty.kind {
+ hir::TyKind::BareFn(..) => {
+ self.outer_index.shift_in(1);
+ intravisit::walk_ty(self, ty);
+ self.outer_index.shift_out(1);
+ }
+ _ => intravisit::walk_ty(self, ty),
+ }
+ }
+
+ fn visit_poly_trait_ref(&mut self, tr: &'tcx hir::PolyTraitRef<'tcx>) {
+ if self.has_late_bound_regions.is_some() {
+ return;
+ }
+ self.outer_index.shift_in(1);
+ intravisit::walk_poly_trait_ref(self, tr);
+ self.outer_index.shift_out(1);
+ }
+
+ fn visit_lifetime(&mut self, lt: &'tcx hir::Lifetime) {
+ if self.has_late_bound_regions.is_some() {
+ return;
+ }
+
+ match self.tcx.named_region(lt.hir_id) {
+ Some(rl::Region::Static | rl::Region::EarlyBound(..)) => {}
+ Some(rl::Region::LateBound(debruijn, _, _)) if debruijn < self.outer_index => {}
+ Some(rl::Region::LateBound(..) | rl::Region::Free(..)) | None => {
+ self.has_late_bound_regions = Some(lt.span);
+ }
+ }
+ }
+ }
+
+ fn has_late_bound_regions<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ generics: &'tcx hir::Generics<'tcx>,
+ decl: &'tcx hir::FnDecl<'tcx>,
+ ) -> Option<Span> {
+ let mut visitor = LateBoundRegionsDetector {
+ tcx,
+ outer_index: ty::INNERMOST,
+ has_late_bound_regions: None,
+ };
+ for param in generics.params {
+ if let GenericParamKind::Lifetime { .. } = param.kind {
+ if tcx.is_late_bound(param.hir_id) {
+ return Some(param.span);
+ }
+ }
+ }
+ visitor.visit_fn_decl(decl);
+ visitor.has_late_bound_regions
+ }
+
+ match node {
+ Node::TraitItem(item) => match item.kind {
+ hir::TraitItemKind::Fn(ref sig, _) => {
+ has_late_bound_regions(tcx, &item.generics, sig.decl)
+ }
+ _ => None,
+ },
+ Node::ImplItem(item) => match item.kind {
+ hir::ImplItemKind::Fn(ref sig, _) => {
+ has_late_bound_regions(tcx, &item.generics, sig.decl)
+ }
+ _ => None,
+ },
+ Node::ForeignItem(item) => match item.kind {
+ hir::ForeignItemKind::Fn(fn_decl, _, ref generics) => {
+ has_late_bound_regions(tcx, generics, fn_decl)
+ }
+ _ => None,
+ },
+ Node::Item(item) => match item.kind {
+ hir::ItemKind::Fn(ref sig, .., ref generics, _) => {
+ has_late_bound_regions(tcx, generics, sig.decl)
+ }
+ _ => None,
+ },
+ _ => None,
+ }
+}
+
+struct AnonConstInParamTyDetector {
+ in_param_ty: bool,
+ found_anon_const_in_param_ty: bool,
+ ct: HirId,
+}
+
+impl<'v> Visitor<'v> for AnonConstInParamTyDetector {
+ fn visit_generic_param(&mut self, p: &'v hir::GenericParam<'v>) {
+ if let GenericParamKind::Const { ty, default: _ } = p.kind {
+ let prev = self.in_param_ty;
+ self.in_param_ty = true;
+ self.visit_ty(ty);
+ self.in_param_ty = prev;
+ }
+ }
+
+ fn visit_anon_const(&mut self, c: &'v hir::AnonConst) {
+ if self.in_param_ty && self.ct == c.hir_id {
+ self.found_anon_const_in_param_ty = true;
+ } else {
+ intravisit::walk_anon_const(self, c)
+ }
+ }
+}
diff --git a/compiler/rustc_typeck/src/collect/item_bounds.rs b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs
index 0d2b75d33..0d34a8bfe 100644
--- a/compiler/rustc_typeck/src/collect/item_bounds.rs
+++ b/compiler/rustc_hir_analysis/src/collect/item_bounds.rs
@@ -53,20 +53,28 @@ fn associated_type_bounds<'tcx>(
/// impl trait it isn't possible to write a suitable predicate on the
/// containing function and for type-alias impl trait we don't have a backwards
/// compatibility issue.
+#[instrument(level = "trace", skip(tcx), ret)]
fn opaque_type_bounds<'tcx>(
tcx: TyCtxt<'tcx>,
opaque_def_id: DefId,
ast_bounds: &'tcx [hir::GenericBound<'tcx>],
span: Span,
+ in_trait: bool,
) -> &'tcx [(ty::Predicate<'tcx>, Span)] {
ty::print::with_no_queries!({
- let item_ty =
- tcx.mk_opaque(opaque_def_id, InternalSubsts::identity_for_item(tcx, opaque_def_id));
+ let substs = InternalSubsts::identity_for_item(tcx, opaque_def_id);
+ let item_ty = if in_trait {
+ tcx.mk_projection(opaque_def_id, substs)
+ } else {
+ tcx.mk_opaque(opaque_def_id, substs)
+ };
let icx = ItemCtxt::new(tcx, opaque_def_id);
let mut bounds = <dyn AstConv<'_>>::compute_bounds(&icx, item_ty, ast_bounds);
// Opaque types are implicitly sized unless a `?Sized` bound is found
<dyn AstConv<'_>>::add_implicitly_sized(&icx, &mut bounds, ast_bounds, None, span);
+ debug!(?bounds);
+
tcx.arena.alloc_from_iter(bounds.predicates(tcx, item_ty))
})
}
@@ -83,10 +91,10 @@ pub(super) fn explicit_item_bounds(
..
}) => associated_type_bounds(tcx, def_id, bounds, *span),
hir::Node::Item(hir::Item {
- kind: hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds, .. }),
+ kind: hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds, in_trait, .. }),
span,
..
- }) => opaque_type_bounds(tcx, def_id, bounds, *span),
+ }) => opaque_type_bounds(tcx, def_id, bounds, *span, *in_trait),
_ => bug!("item_bounds called on {:?}", def_id),
}
}
diff --git a/compiler/rustc_hir_analysis/src/collect/lifetimes.rs b/compiler/rustc_hir_analysis/src/collect/lifetimes.rs
new file mode 100644
index 000000000..3f263a6de
--- /dev/null
+++ b/compiler/rustc_hir_analysis/src/collect/lifetimes.rs
@@ -0,0 +1,1888 @@
+//! Resolution of early vs late bound lifetimes.
+//!
+//! Name resolution for lifetimes is performed on the AST and embedded into HIR. From this
+//! information, typechecking needs to transform the lifetime parameters into bound lifetimes.
+//! Lifetimes can be early-bound or late-bound. Construction of typechecking terms needs to visit
+//! the types in HIR to identify late-bound lifetimes and assign their Debruijn indices. This file
+//! is also responsible for assigning their semantics to implicit lifetimes in trait objects.
+
+use rustc_ast::walk_list;
+use rustc_data_structures::fx::{FxHashSet, FxIndexMap, FxIndexSet};
+use rustc_errors::struct_span_err;
+use rustc_hir as hir;
+use rustc_hir::def::{DefKind, Res};
+use rustc_hir::def_id::LocalDefId;
+use rustc_hir::intravisit::{self, Visitor};
+use rustc_hir::{GenericArg, GenericParam, GenericParamKind, HirIdMap, LifetimeName, Node};
+use rustc_middle::bug;
+use rustc_middle::hir::map::Map;
+use rustc_middle::hir::nested_filter;
+use rustc_middle::middle::resolve_lifetime::*;
+use rustc_middle::ty::{self, DefIdTree, TyCtxt};
+use rustc_span::def_id::DefId;
+use rustc_span::symbol::{sym, Ident};
+use rustc_span::Span;
+use std::fmt;
+
+trait RegionExt {
+ fn early(hir_map: Map<'_>, param: &GenericParam<'_>) -> (LocalDefId, Region);
+
+ fn late(index: u32, hir_map: Map<'_>, param: &GenericParam<'_>) -> (LocalDefId, Region);
+
+ fn id(&self) -> Option<DefId>;
+
+ fn shifted(self, amount: u32) -> Region;
+}
+
+impl RegionExt for Region {
+ fn early(hir_map: Map<'_>, param: &GenericParam<'_>) -> (LocalDefId, Region) {
+ let def_id = hir_map.local_def_id(param.hir_id);
+ debug!("Region::early: def_id={:?}", def_id);
+ (def_id, Region::EarlyBound(def_id.to_def_id()))
+ }
+
+ fn late(idx: u32, hir_map: Map<'_>, param: &GenericParam<'_>) -> (LocalDefId, Region) {
+ let depth = ty::INNERMOST;
+ let def_id = hir_map.local_def_id(param.hir_id);
+ debug!(
+ "Region::late: idx={:?}, param={:?} depth={:?} def_id={:?}",
+ idx, param, depth, def_id,
+ );
+ (def_id, Region::LateBound(depth, idx, def_id.to_def_id()))
+ }
+
+ fn id(&self) -> Option<DefId> {
+ match *self {
+ Region::Static => None,
+
+ Region::EarlyBound(id) | Region::LateBound(_, _, id) | Region::Free(_, id) => Some(id),
+ }
+ }
+
+ fn shifted(self, amount: u32) -> Region {
+ match self {
+ Region::LateBound(debruijn, idx, id) => {
+ Region::LateBound(debruijn.shifted_in(amount), idx, id)
+ }
+ _ => self,
+ }
+ }
+}
+
+/// Maps the id of each lifetime reference to the lifetime decl
+/// that it corresponds to.
+///
+/// FIXME. This struct gets converted to a `ResolveLifetimes` for
+/// actual use. It has the same data, but indexed by `LocalDefId`. This
+/// is silly.
+#[derive(Debug, Default)]
+struct NamedRegionMap {
+ // maps from every use of a named (not anonymous) lifetime to a
+ // `Region` describing how that region is bound
+ defs: HirIdMap<Region>,
+
+ // Maps relevant hir items to the bound vars on them. These include:
+ // - function defs
+ // - function pointers
+ // - closures
+ // - trait refs
+ // - bound types (like `T` in `for<'a> T<'a>: Foo`)
+ late_bound_vars: HirIdMap<Vec<ty::BoundVariableKind>>,
+}
+
+struct LifetimeContext<'a, 'tcx> {
+ tcx: TyCtxt<'tcx>,
+ map: &'a mut NamedRegionMap,
+ scope: ScopeRef<'a>,
+
+ /// Indicates that we only care about the definition of a trait. This should
+ /// be false if the `Item` we are resolving lifetimes for is not a trait or
+ /// we eventually need lifetimes resolve for trait items.
+ trait_definition_only: bool,
+}
+
+#[derive(Debug)]
+enum Scope<'a> {
+ /// Declares lifetimes, and each can be early-bound or late-bound.
+ /// The `DebruijnIndex` of late-bound lifetimes starts at `1` and
+ /// it should be shifted by the number of `Binder`s in between the
+ /// declaration `Binder` and the location it's referenced from.
+ Binder {
+ /// We use an IndexMap here because we want these lifetimes in order
+ /// for diagnostics.
+ lifetimes: FxIndexMap<LocalDefId, Region>,
+
+ scope_type: BinderScopeType,
+
+ /// The late bound vars for a given item are stored by `HirId` to be
+ /// queried later. However, if we enter an elision scope, we have to
+ /// later append the elided bound vars to the list and need to know what
+ /// to append to.
+ hir_id: hir::HirId,
+
+ s: ScopeRef<'a>,
+
+ /// If this binder comes from a where clause, specify how it was created.
+ /// This is used to diagnose inaccessible lifetimes in APIT:
+ /// ```ignore (illustrative)
+ /// fn foo(x: impl for<'a> Trait<'a, Assoc = impl Copy + 'a>) {}
+ /// ```
+ where_bound_origin: Option<hir::PredicateOrigin>,
+ },
+
+ /// Lifetimes introduced by a fn are scoped to the call-site for that fn,
+ /// if this is a fn body, otherwise the original definitions are used.
+ /// Unspecified lifetimes are inferred, unless an elision scope is nested,
+ /// e.g., `(&T, fn(&T) -> &T);` becomes `(&'_ T, for<'a> fn(&'a T) -> &'a T)`.
+ Body {
+ id: hir::BodyId,
+ s: ScopeRef<'a>,
+ },
+
+ /// A scope which either determines unspecified lifetimes or errors
+ /// on them (e.g., due to ambiguity).
+ Elision {
+ s: ScopeRef<'a>,
+ },
+
+ /// Use a specific lifetime (if `Some`) or leave it unset (to be
+ /// inferred in a function body or potentially error outside one),
+ /// for the default choice of lifetime in a trait object type.
+ ObjectLifetimeDefault {
+ lifetime: Option<Region>,
+ s: ScopeRef<'a>,
+ },
+
+ /// When we have nested trait refs, we concatenate late bound vars for inner
+ /// trait refs from outer ones. But we also need to include any HRTB
+ /// lifetimes encountered when identifying the trait that an associated type
+ /// is declared on.
+ Supertrait {
+ lifetimes: Vec<ty::BoundVariableKind>,
+ s: ScopeRef<'a>,
+ },
+
+ TraitRefBoundary {
+ s: ScopeRef<'a>,
+ },
+
+ Root,
+}
+
+#[derive(Copy, Clone, Debug)]
+enum BinderScopeType {
+ /// Any non-concatenating binder scopes.
+ Normal,
+ /// Within a syntactic trait ref, there may be multiple poly trait refs that
+ /// are nested (under the `associated_type_bounds` feature). The binders of
+ /// the inner poly trait refs are extended from the outer poly trait refs
+ /// and don't increase the late bound depth. If you had
+ /// `T: for<'a> Foo<Bar: for<'b> Baz<'a, 'b>>`, then the `for<'b>` scope
+ /// would be `Concatenating`. This also used in trait refs in where clauses
+ /// where we have two binders `for<> T: for<> Foo` (I've intentionally left
+ /// out any lifetimes because they aren't needed to show the two scopes).
+ /// The inner `for<>` has a scope of `Concatenating`.
+ Concatenating,
+}
+
+// A helper struct for debugging scopes without printing parent scopes
+struct TruncatedScopeDebug<'a>(&'a Scope<'a>);
+
+impl<'a> fmt::Debug for TruncatedScopeDebug<'a> {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self.0 {
+ Scope::Binder { lifetimes, scope_type, hir_id, where_bound_origin, s: _ } => f
+ .debug_struct("Binder")
+ .field("lifetimes", lifetimes)
+ .field("scope_type", scope_type)
+ .field("hir_id", hir_id)
+ .field("where_bound_origin", where_bound_origin)
+ .field("s", &"..")
+ .finish(),
+ Scope::Body { id, s: _ } => {
+ f.debug_struct("Body").field("id", id).field("s", &"..").finish()
+ }
+ Scope::Elision { s: _ } => f.debug_struct("Elision").field("s", &"..").finish(),
+ Scope::ObjectLifetimeDefault { lifetime, s: _ } => f
+ .debug_struct("ObjectLifetimeDefault")
+ .field("lifetime", lifetime)
+ .field("s", &"..")
+ .finish(),
+ Scope::Supertrait { lifetimes, s: _ } => f
+ .debug_struct("Supertrait")
+ .field("lifetimes", lifetimes)
+ .field("s", &"..")
+ .finish(),
+ Scope::TraitRefBoundary { s: _ } => f.debug_struct("TraitRefBoundary").finish(),
+ Scope::Root => f.debug_struct("Root").finish(),
+ }
+ }
+}
+
+type ScopeRef<'a> = &'a Scope<'a>;
+
+const ROOT_SCOPE: ScopeRef<'static> = &Scope::Root;
+
+pub(crate) fn provide(providers: &mut ty::query::Providers) {
+ *providers = ty::query::Providers {
+ resolve_lifetimes_trait_definition,
+ resolve_lifetimes,
+
+ named_region_map: |tcx, id| resolve_lifetimes_for(tcx, id).defs.get(&id),
+ is_late_bound_map,
+ object_lifetime_default,
+ late_bound_vars_map: |tcx, id| resolve_lifetimes_for(tcx, id).late_bound_vars.get(&id),
+
+ ..*providers
+ };
+}
+
+/// Like `resolve_lifetimes`, but does not resolve lifetimes for trait items.
+/// Also does not generate any diagnostics.
+///
+/// This is ultimately a subset of the `resolve_lifetimes` work. It effectively
+/// resolves lifetimes only within the trait "header" -- that is, the trait
+/// and supertrait list. In contrast, `resolve_lifetimes` resolves all the
+/// lifetimes within the trait and its items. There is room to refactor this,
+/// for example to resolve lifetimes for each trait item in separate queries,
+/// but it's convenient to do the entire trait at once because the lifetimes
+/// from the trait definition are in scope within the trait items as well.
+///
+/// The reason for this separate call is to resolve what would otherwise
+/// be a cycle. Consider this example:
+///
+/// ```ignore UNSOLVED (maybe @jackh726 knows what lifetime parameter to give Sub)
+/// trait Base<'a> {
+/// type BaseItem;
+/// }
+/// trait Sub<'b>: for<'a> Base<'a> {
+/// type SubItem: Sub<BaseItem = &'b u32>;
+/// }
+/// ```
+///
+/// When we resolve `Sub` and all its items, we also have to resolve `Sub<BaseItem = &'b u32>`.
+/// To figure out the index of `'b`, we have to know about the supertraits
+/// of `Sub` so that we can determine that the `for<'a>` will be in scope.
+/// (This is because we -- currently at least -- flatten all the late-bound
+/// lifetimes into a single binder.) This requires us to resolve the
+/// *trait definition* of `Sub`; basically just enough lifetime information
+/// to look at the supertraits.
+#[instrument(level = "debug", skip(tcx))]
+fn resolve_lifetimes_trait_definition(
+ tcx: TyCtxt<'_>,
+ local_def_id: LocalDefId,
+) -> ResolveLifetimes {
+ convert_named_region_map(do_resolve(tcx, local_def_id, true))
+}
+
+/// Computes the `ResolveLifetimes` map that contains data for an entire `Item`.
+/// You should not read the result of this query directly, but rather use
+/// `named_region_map`, `is_late_bound_map`, etc.
+#[instrument(level = "debug", skip(tcx))]
+fn resolve_lifetimes(tcx: TyCtxt<'_>, local_def_id: LocalDefId) -> ResolveLifetimes {
+ convert_named_region_map(do_resolve(tcx, local_def_id, false))
+}
+
+fn do_resolve(
+ tcx: TyCtxt<'_>,
+ local_def_id: LocalDefId,
+ trait_definition_only: bool,
+) -> NamedRegionMap {
+ let item = tcx.hir().expect_item(local_def_id);
+ let mut named_region_map =
+ NamedRegionMap { defs: Default::default(), late_bound_vars: Default::default() };
+ let mut visitor = LifetimeContext {
+ tcx,
+ map: &mut named_region_map,
+ scope: ROOT_SCOPE,
+ trait_definition_only,
+ };
+ visitor.visit_item(item);
+
+ named_region_map
+}
+
+fn convert_named_region_map(named_region_map: NamedRegionMap) -> ResolveLifetimes {
+ let mut rl = ResolveLifetimes::default();
+
+ for (hir_id, v) in named_region_map.defs {
+ let map = rl.defs.entry(hir_id.owner).or_default();
+ map.insert(hir_id.local_id, v);
+ }
+ for (hir_id, v) in named_region_map.late_bound_vars {
+ let map = rl.late_bound_vars.entry(hir_id.owner).or_default();
+ map.insert(hir_id.local_id, v);
+ }
+
+ debug!(?rl.defs);
+ debug!(?rl.late_bound_vars);
+ rl
+}
+
+/// Given `any` owner (structs, traits, trait methods, etc.), does lifetime resolution.
+/// There are two important things this does.
+/// First, we have to resolve lifetimes for
+/// the entire *`Item`* that contains this owner, because that's the largest "scope"
+/// where we can have relevant lifetimes.
+/// Second, if we are asking for lifetimes in a trait *definition*, we use `resolve_lifetimes_trait_definition`
+/// instead of `resolve_lifetimes`, which does not descend into the trait items and does not emit diagnostics.
+/// This allows us to avoid cycles. Importantly, if we ask for lifetimes for lifetimes that have an owner
+/// other than the trait itself (like the trait methods or associated types), then we just use the regular
+/// `resolve_lifetimes`.
+fn resolve_lifetimes_for<'tcx>(tcx: TyCtxt<'tcx>, def_id: hir::OwnerId) -> &'tcx ResolveLifetimes {
+ let item_id = item_for(tcx, def_id.def_id);
+ let local_def_id = item_id.owner_id.def_id;
+ if item_id.owner_id == def_id {
+ let item = tcx.hir().item(item_id);
+ match item.kind {
+ hir::ItemKind::Trait(..) => tcx.resolve_lifetimes_trait_definition(local_def_id),
+ _ => tcx.resolve_lifetimes(local_def_id),
+ }
+ } else {
+ tcx.resolve_lifetimes(local_def_id)
+ }
+}
+
+/// Finds the `Item` that contains the given `LocalDefId`
+fn item_for(tcx: TyCtxt<'_>, local_def_id: LocalDefId) -> hir::ItemId {
+ match tcx.hir().find_by_def_id(local_def_id) {
+ Some(Node::Item(item)) => {
+ return item.item_id();
+ }
+ _ => {}
+ }
+ let item = {
+ let hir_id = tcx.hir().local_def_id_to_hir_id(local_def_id);
+ let mut parent_iter = tcx.hir().parent_iter(hir_id);
+ loop {
+ let node = parent_iter.next().map(|n| n.1);
+ match node {
+ Some(hir::Node::Item(item)) => break item.item_id(),
+ Some(hir::Node::Crate(_)) | None => bug!("Called `item_for` on an Item."),
+ _ => {}
+ }
+ }
+ };
+ item
+}
+
+fn late_region_as_bound_region<'tcx>(tcx: TyCtxt<'tcx>, region: &Region) -> ty::BoundVariableKind {
+ match region {
+ Region::LateBound(_, _, def_id) => {
+ let name = tcx.hir().name(tcx.hir().local_def_id_to_hir_id(def_id.expect_local()));
+ ty::BoundVariableKind::Region(ty::BrNamed(*def_id, name))
+ }
+ _ => bug!("{:?} is not a late region", region),
+ }
+}
+
+impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
+ /// Returns the binders in scope and the type of `Binder` that should be created for a poly trait ref.
+ fn poly_trait_ref_binder_info(&mut self) -> (Vec<ty::BoundVariableKind>, BinderScopeType) {
+ let mut scope = self.scope;
+ let mut supertrait_lifetimes = vec![];
+ loop {
+ match scope {
+ Scope::Body { .. } | Scope::Root => {
+ break (vec![], BinderScopeType::Normal);
+ }
+
+ Scope::Elision { s, .. } | Scope::ObjectLifetimeDefault { s, .. } => {
+ scope = s;
+ }
+
+ Scope::Supertrait { s, lifetimes } => {
+ supertrait_lifetimes = lifetimes.clone();
+ scope = s;
+ }
+
+ Scope::TraitRefBoundary { .. } => {
+ // We should only see super trait lifetimes if there is a `Binder` above
+ assert!(supertrait_lifetimes.is_empty());
+ break (vec![], BinderScopeType::Normal);
+ }
+
+ Scope::Binder { hir_id, .. } => {
+ // Nested poly trait refs have the binders concatenated
+ let mut full_binders =
+ self.map.late_bound_vars.entry(*hir_id).or_default().clone();
+ full_binders.extend(supertrait_lifetimes.into_iter());
+ break (full_binders, BinderScopeType::Concatenating);
+ }
+ }
+ }
+ }
+}
+impl<'a, 'tcx> Visitor<'tcx> for LifetimeContext<'a, 'tcx> {
+ type NestedFilter = nested_filter::All;
+
+ fn nested_visit_map(&mut self) -> Self::Map {
+ self.tcx.hir()
+ }
+
+ // We want to nest trait/impl items in their parent, but nothing else.
+ fn visit_nested_item(&mut self, _: hir::ItemId) {}
+
+ fn visit_trait_item_ref(&mut self, ii: &'tcx hir::TraitItemRef) {
+ if !self.trait_definition_only {
+ intravisit::walk_trait_item_ref(self, ii)
+ }
+ }
+
+ fn visit_nested_body(&mut self, body: hir::BodyId) {
+ let body = self.tcx.hir().body(body);
+ self.with(Scope::Body { id: body.id(), s: self.scope }, |this| {
+ this.visit_body(body);
+ });
+ }
+
+ fn visit_expr(&mut self, e: &'tcx hir::Expr<'tcx>) {
+ if let hir::ExprKind::Closure(hir::Closure {
+ binder, bound_generic_params, fn_decl, ..
+ }) = e.kind
+ {
+ if let &hir::ClosureBinder::For { span: for_sp, .. } = binder {
+ fn span_of_infer(ty: &hir::Ty<'_>) -> Option<Span> {
+ struct V(Option<Span>);
+
+ impl<'v> Visitor<'v> for V {
+ fn visit_ty(&mut self, t: &'v hir::Ty<'v>) {
+ match t.kind {
+ _ if self.0.is_some() => (),
+ hir::TyKind::Infer => {
+ self.0 = Some(t.span);
+ }
+ _ => intravisit::walk_ty(self, t),
+ }
+ }
+ }
+
+ let mut v = V(None);
+ v.visit_ty(ty);
+ v.0
+ }
+
+ let infer_in_rt_sp = match fn_decl.output {
+ hir::FnRetTy::DefaultReturn(sp) => Some(sp),
+ hir::FnRetTy::Return(ty) => span_of_infer(ty),
+ };
+
+ let infer_spans = fn_decl
+ .inputs
+ .into_iter()
+ .filter_map(span_of_infer)
+ .chain(infer_in_rt_sp)
+ .collect::<Vec<_>>();
+
+ if !infer_spans.is_empty() {
+ self.tcx.sess
+ .struct_span_err(
+ infer_spans,
+ "implicit types in closure signatures are forbidden when `for<...>` is present",
+ )
+ .span_label(for_sp, "`for<...>` is here")
+ .emit();
+ }
+ }
+
+ let (lifetimes, binders): (FxIndexMap<LocalDefId, Region>, Vec<_>) =
+ bound_generic_params
+ .iter()
+ .filter(|param| matches!(param.kind, GenericParamKind::Lifetime { .. }))
+ .enumerate()
+ .map(|(late_bound_idx, param)| {
+ let pair = Region::late(late_bound_idx as u32, self.tcx.hir(), param);
+ let r = late_region_as_bound_region(self.tcx, &pair.1);
+ (pair, r)
+ })
+ .unzip();
+
+ self.record_late_bound_vars(e.hir_id, binders);
+ let scope = Scope::Binder {
+ hir_id: e.hir_id,
+ lifetimes,
+ s: self.scope,
+ scope_type: BinderScopeType::Normal,
+ where_bound_origin: None,
+ };
+
+ self.with(scope, |this| {
+ // a closure has no bounds, so everything
+ // contained within is scoped within its binder.
+ intravisit::walk_expr(this, e)
+ });
+ } else {
+ intravisit::walk_expr(self, e)
+ }
+ }
+
+ #[instrument(level = "debug", skip(self))]
+ fn visit_item(&mut self, item: &'tcx hir::Item<'tcx>) {
+ match &item.kind {
+ hir::ItemKind::Impl(hir::Impl { of_trait, .. }) => {
+ if let Some(of_trait) = of_trait {
+ self.record_late_bound_vars(of_trait.hir_ref_id, Vec::default());
+ }
+ }
+ _ => {}
+ }
+ match item.kind {
+ hir::ItemKind::Fn(_, ref generics, _) => {
+ self.visit_early_late(item.hir_id(), generics, |this| {
+ intravisit::walk_item(this, item);
+ });
+ }
+
+ hir::ItemKind::ExternCrate(_)
+ | hir::ItemKind::Use(..)
+ | hir::ItemKind::Macro(..)
+ | hir::ItemKind::Mod(..)
+ | hir::ItemKind::ForeignMod { .. }
+ | hir::ItemKind::GlobalAsm(..) => {
+ // These sorts of items have no lifetime parameters at all.
+ intravisit::walk_item(self, item);
+ }
+ hir::ItemKind::Static(..) | hir::ItemKind::Const(..) => {
+ // No lifetime parameters, but implied 'static.
+ self.with(Scope::Elision { s: self.scope }, |this| {
+ intravisit::walk_item(this, item)
+ });
+ }
+ hir::ItemKind::OpaqueTy(hir::OpaqueTy { .. }) => {
+ // Opaque types are visited when we visit the
+ // `TyKind::OpaqueDef`, so that they have the lifetimes from
+ // their parent opaque_ty in scope.
+ //
+ // The core idea here is that since OpaqueTys are generated with the impl Trait as
+ // their owner, we can keep going until we find the Item that owns that. We then
+ // conservatively add all resolved lifetimes. Otherwise we run into problems in
+ // cases like `type Foo<'a> = impl Bar<As = impl Baz + 'a>`.
+ for (_hir_id, node) in self.tcx.hir().parent_iter(item.owner_id.into()) {
+ match node {
+ hir::Node::Item(parent_item) => {
+ let resolved_lifetimes: &ResolveLifetimes = self.tcx.resolve_lifetimes(
+ item_for(self.tcx, parent_item.owner_id.def_id).owner_id.def_id,
+ );
+ // We need to add *all* deps, since opaque tys may want them from *us*
+ for (&owner, defs) in resolved_lifetimes.defs.iter() {
+ defs.iter().for_each(|(&local_id, region)| {
+ self.map.defs.insert(hir::HirId { owner, local_id }, *region);
+ });
+ }
+ for (&owner, late_bound_vars) in
+ resolved_lifetimes.late_bound_vars.iter()
+ {
+ late_bound_vars.iter().for_each(|(&local_id, late_bound_vars)| {
+ self.record_late_bound_vars(
+ hir::HirId { owner, local_id },
+ late_bound_vars.clone(),
+ );
+ });
+ }
+ break;
+ }
+ hir::Node::Crate(_) => bug!("No Item about an OpaqueTy"),
+ _ => {}
+ }
+ }
+ }
+ hir::ItemKind::TyAlias(_, ref generics)
+ | hir::ItemKind::Enum(_, ref generics)
+ | hir::ItemKind::Struct(_, ref generics)
+ | hir::ItemKind::Union(_, ref generics)
+ | hir::ItemKind::Trait(_, _, ref generics, ..)
+ | hir::ItemKind::TraitAlias(ref generics, ..)
+ | hir::ItemKind::Impl(hir::Impl { ref generics, .. }) => {
+ // These kinds of items have only early-bound lifetime parameters.
+ let lifetimes = generics
+ .params
+ .iter()
+ .filter_map(|param| match param.kind {
+ GenericParamKind::Lifetime { .. } => {
+ Some(Region::early(self.tcx.hir(), param))
+ }
+ GenericParamKind::Type { .. } | GenericParamKind::Const { .. } => None,
+ })
+ .collect();
+ self.record_late_bound_vars(item.hir_id(), vec![]);
+ let scope = Scope::Binder {
+ hir_id: item.hir_id(),
+ lifetimes,
+ scope_type: BinderScopeType::Normal,
+ s: ROOT_SCOPE,
+ where_bound_origin: None,
+ };
+ self.with(scope, |this| {
+ let scope = Scope::TraitRefBoundary { s: this.scope };
+ this.with(scope, |this| {
+ intravisit::walk_item(this, item);
+ });
+ });
+ }
+ }
+ }
+
+ fn visit_foreign_item(&mut self, item: &'tcx hir::ForeignItem<'tcx>) {
+ match item.kind {
+ hir::ForeignItemKind::Fn(_, _, ref generics) => {
+ self.visit_early_late(item.hir_id(), generics, |this| {
+ intravisit::walk_foreign_item(this, item);
+ })
+ }
+ hir::ForeignItemKind::Static(..) => {
+ intravisit::walk_foreign_item(self, item);
+ }
+ hir::ForeignItemKind::Type => {
+ intravisit::walk_foreign_item(self, item);
+ }
+ }
+ }
+
+ #[instrument(level = "debug", skip(self))]
+ fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx>) {
+ match ty.kind {
+ hir::TyKind::BareFn(ref c) => {
+ let (lifetimes, binders): (FxIndexMap<LocalDefId, Region>, Vec<_>) = c
+ .generic_params
+ .iter()
+ .filter(|param| matches!(param.kind, GenericParamKind::Lifetime { .. }))
+ .enumerate()
+ .map(|(late_bound_idx, param)| {
+ let pair = Region::late(late_bound_idx as u32, self.tcx.hir(), param);
+ let r = late_region_as_bound_region(self.tcx, &pair.1);
+ (pair, r)
+ })
+ .unzip();
+ self.record_late_bound_vars(ty.hir_id, binders);
+ let scope = Scope::Binder {
+ hir_id: ty.hir_id,
+ lifetimes,
+ s: self.scope,
+ scope_type: BinderScopeType::Normal,
+ where_bound_origin: None,
+ };
+ self.with(scope, |this| {
+ // a bare fn has no bounds, so everything
+ // contained within is scoped within its binder.
+ intravisit::walk_ty(this, ty);
+ });
+ }
+ hir::TyKind::TraitObject(bounds, ref lifetime, _) => {
+ debug!(?bounds, ?lifetime, "TraitObject");
+ let scope = Scope::TraitRefBoundary { s: self.scope };
+ self.with(scope, |this| {
+ for bound in bounds {
+ this.visit_poly_trait_ref(bound);
+ }
+ });
+ match lifetime.name {
+ LifetimeName::ImplicitObjectLifetimeDefault => {
+ // If the user does not write *anything*, we
+ // use the object lifetime defaulting
+ // rules. So e.g., `Box<dyn Debug>` becomes
+ // `Box<dyn Debug + 'static>`.
+ self.resolve_object_lifetime_default(lifetime)
+ }
+ LifetimeName::Infer => {
+ // If the user writes `'_`, we use the *ordinary* elision
+ // rules. So the `'_` in e.g., `Box<dyn Debug + '_>` will be
+ // resolved the same as the `'_` in `&'_ Foo`.
+ //
+ // cc #48468
+ }
+ LifetimeName::Param(..) | LifetimeName::Static => {
+ // If the user wrote an explicit name, use that.
+ self.visit_lifetime(lifetime);
+ }
+ LifetimeName::Error => {}
+ }
+ }
+ hir::TyKind::Rptr(ref lifetime_ref, ref mt) => {
+ self.visit_lifetime(lifetime_ref);
+ let scope = Scope::ObjectLifetimeDefault {
+ lifetime: self.map.defs.get(&lifetime_ref.hir_id).cloned(),
+ s: self.scope,
+ };
+ self.with(scope, |this| this.visit_ty(&mt.ty));
+ }
+ hir::TyKind::OpaqueDef(item_id, lifetimes, _in_trait) => {
+ // Resolve the lifetimes in the bounds to the lifetime defs in the generics.
+ // `fn foo<'a>() -> impl MyTrait<'a> { ... }` desugars to
+ // `type MyAnonTy<'b> = impl MyTrait<'b>;`
+ // ^ ^ this gets resolved in the scope of
+ // the opaque_ty generics
+ let opaque_ty = self.tcx.hir().item(item_id);
+ let (generics, bounds) = match opaque_ty.kind {
+ hir::ItemKind::OpaqueTy(hir::OpaqueTy {
+ origin: hir::OpaqueTyOrigin::TyAlias,
+ ..
+ }) => {
+ intravisit::walk_ty(self, ty);
+
+ // Elided lifetimes are not allowed in non-return
+ // position impl Trait
+ let scope = Scope::TraitRefBoundary { s: self.scope };
+ self.with(scope, |this| {
+ let scope = Scope::Elision { s: this.scope };
+ this.with(scope, |this| {
+ intravisit::walk_item(this, opaque_ty);
+ })
+ });
+
+ return;
+ }
+ hir::ItemKind::OpaqueTy(hir::OpaqueTy {
+ origin: hir::OpaqueTyOrigin::FnReturn(..) | hir::OpaqueTyOrigin::AsyncFn(..),
+ ref generics,
+ bounds,
+ ..
+ }) => (generics, bounds),
+ ref i => bug!("`impl Trait` pointed to non-opaque type?? {:#?}", i),
+ };
+
+ // Resolve the lifetimes that are applied to the opaque type.
+ // These are resolved in the current scope.
+ // `fn foo<'a>() -> impl MyTrait<'a> { ... }` desugars to
+ // `fn foo<'a>() -> MyAnonTy<'a> { ... }`
+ // ^ ^this gets resolved in the current scope
+ for lifetime in lifetimes {
+ let hir::GenericArg::Lifetime(lifetime) = lifetime else {
+ continue
+ };
+ self.visit_lifetime(lifetime);
+
+ // Check for predicates like `impl for<'a> Trait<impl OtherTrait<'a>>`
+ // and ban them. Type variables instantiated inside binders aren't
+ // well-supported at the moment, so this doesn't work.
+ // In the future, this should be fixed and this error should be removed.
+ let def = self.map.defs.get(&lifetime.hir_id).cloned();
+ let Some(Region::LateBound(_, _, def_id)) = def else {
+ continue
+ };
+ let Some(def_id) = def_id.as_local() else {
+ continue
+ };
+ let hir_id = self.tcx.hir().local_def_id_to_hir_id(def_id);
+ // Ensure that the parent of the def is an item, not HRTB
+ let parent_id = self.tcx.hir().get_parent_node(hir_id);
+ if !parent_id.is_owner() {
+ if !self.trait_definition_only {
+ struct_span_err!(
+ self.tcx.sess,
+ lifetime.span,
+ E0657,
+ "`impl Trait` can only capture lifetimes \
+ bound at the fn or impl level"
+ )
+ .emit();
+ }
+ self.uninsert_lifetime_on_error(lifetime, def.unwrap());
+ }
+ if let hir::Node::Item(hir::Item {
+ kind: hir::ItemKind::OpaqueTy { .. }, ..
+ }) = self.tcx.hir().get(parent_id)
+ {
+ if !self.trait_definition_only {
+ let mut err = self.tcx.sess.struct_span_err(
+ lifetime.span,
+ "higher kinded lifetime bounds on nested opaque types are not supported yet",
+ );
+ err.span_note(self.tcx.def_span(def_id), "lifetime declared here");
+ err.emit();
+ }
+ self.uninsert_lifetime_on_error(lifetime, def.unwrap());
+ }
+ }
+
+ // We want to start our early-bound indices at the end of the parent scope,
+ // not including any parent `impl Trait`s.
+ let mut lifetimes = FxIndexMap::default();
+ debug!(?generics.params);
+ for param in generics.params {
+ match param.kind {
+ GenericParamKind::Lifetime { .. } => {
+ let (def_id, reg) = Region::early(self.tcx.hir(), &param);
+ lifetimes.insert(def_id, reg);
+ }
+ GenericParamKind::Type { .. } | GenericParamKind::Const { .. } => {}
+ }
+ }
+ self.record_late_bound_vars(ty.hir_id, vec![]);
+
+ let scope = Scope::Binder {
+ hir_id: ty.hir_id,
+ lifetimes,
+ s: self.scope,
+ scope_type: BinderScopeType::Normal,
+ where_bound_origin: None,
+ };
+ self.with(scope, |this| {
+ let scope = Scope::TraitRefBoundary { s: this.scope };
+ this.with(scope, |this| {
+ this.visit_generics(generics);
+ for bound in bounds {
+ this.visit_param_bound(bound);
+ }
+ })
+ });
+ }
+ _ => intravisit::walk_ty(self, ty),
+ }
+ }
+
+ #[instrument(level = "debug", skip(self))]
+ fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem<'tcx>) {
+ use self::hir::TraitItemKind::*;
+ match trait_item.kind {
+ Fn(_, _) => {
+ self.visit_early_late(trait_item.hir_id(), &trait_item.generics, |this| {
+ intravisit::walk_trait_item(this, trait_item)
+ });
+ }
+ Type(bounds, ref ty) => {
+ let generics = &trait_item.generics;
+ let lifetimes = generics
+ .params
+ .iter()
+ .filter_map(|param| match param.kind {
+ GenericParamKind::Lifetime { .. } => {
+ Some(Region::early(self.tcx.hir(), param))
+ }
+ GenericParamKind::Type { .. } | GenericParamKind::Const { .. } => None,
+ })
+ .collect();
+ self.record_late_bound_vars(trait_item.hir_id(), vec![]);
+ let scope = Scope::Binder {
+ hir_id: trait_item.hir_id(),
+ lifetimes,
+ s: self.scope,
+ scope_type: BinderScopeType::Normal,
+ where_bound_origin: None,
+ };
+ self.with(scope, |this| {
+ let scope = Scope::TraitRefBoundary { s: this.scope };
+ this.with(scope, |this| {
+ this.visit_generics(generics);
+ for bound in bounds {
+ this.visit_param_bound(bound);
+ }
+ if let Some(ty) = ty {
+ this.visit_ty(ty);
+ }
+ })
+ });
+ }
+ Const(_, _) => {
+ // Only methods and types support generics.
+ assert!(trait_item.generics.params.is_empty());
+ intravisit::walk_trait_item(self, trait_item);
+ }
+ }
+ }
+
+ #[instrument(level = "debug", skip(self))]
+ fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem<'tcx>) {
+ use self::hir::ImplItemKind::*;
+ match impl_item.kind {
+ Fn(..) => self.visit_early_late(impl_item.hir_id(), &impl_item.generics, |this| {
+ intravisit::walk_impl_item(this, impl_item)
+ }),
+ Type(ref ty) => {
+ let generics = &impl_item.generics;
+ let lifetimes: FxIndexMap<LocalDefId, Region> = generics
+ .params
+ .iter()
+ .filter_map(|param| match param.kind {
+ GenericParamKind::Lifetime { .. } => {
+ Some(Region::early(self.tcx.hir(), param))
+ }
+ GenericParamKind::Const { .. } | GenericParamKind::Type { .. } => None,
+ })
+ .collect();
+ self.record_late_bound_vars(impl_item.hir_id(), vec![]);
+ let scope = Scope::Binder {
+ hir_id: impl_item.hir_id(),
+ lifetimes,
+ s: self.scope,
+ scope_type: BinderScopeType::Normal,
+ where_bound_origin: None,
+ };
+ self.with(scope, |this| {
+ let scope = Scope::TraitRefBoundary { s: this.scope };
+ this.with(scope, |this| {
+ this.visit_generics(generics);
+ this.visit_ty(ty);
+ })
+ });
+ }
+ Const(_, _) => {
+ // Only methods and types support generics.
+ assert!(impl_item.generics.params.is_empty());
+ intravisit::walk_impl_item(self, impl_item);
+ }
+ }
+ }
+
+ #[instrument(level = "debug", skip(self))]
+ fn visit_lifetime(&mut self, lifetime_ref: &'tcx hir::Lifetime) {
+ match lifetime_ref.name {
+ hir::LifetimeName::Static => self.insert_lifetime(lifetime_ref, Region::Static),
+ hir::LifetimeName::Param(param_def_id, _) => {
+ self.resolve_lifetime_ref(param_def_id, lifetime_ref)
+ }
+ // If we've already reported an error, just ignore `lifetime_ref`.
+ hir::LifetimeName::Error => {}
+ // Those will be resolved by typechecking.
+ hir::LifetimeName::ImplicitObjectLifetimeDefault | hir::LifetimeName::Infer => {}
+ }
+ }
+
+ fn visit_path(&mut self, path: &'tcx hir::Path<'tcx>, _: hir::HirId) {
+ for (i, segment) in path.segments.iter().enumerate() {
+ let depth = path.segments.len() - i - 1;
+ if let Some(ref args) = segment.args {
+ self.visit_segment_args(path.res, depth, args);
+ }
+ }
+ }
+
+ fn visit_fn(
+ &mut self,
+ fk: intravisit::FnKind<'tcx>,
+ fd: &'tcx hir::FnDecl<'tcx>,
+ body_id: hir::BodyId,
+ _: Span,
+ _: hir::HirId,
+ ) {
+ let output = match fd.output {
+ hir::FnRetTy::DefaultReturn(_) => None,
+ hir::FnRetTy::Return(ref ty) => Some(&**ty),
+ };
+ self.visit_fn_like_elision(&fd.inputs, output, matches!(fk, intravisit::FnKind::Closure));
+ intravisit::walk_fn_kind(self, fk);
+ self.visit_nested_body(body_id)
+ }
+
+ fn visit_generics(&mut self, generics: &'tcx hir::Generics<'tcx>) {
+ let scope = Scope::TraitRefBoundary { s: self.scope };
+ self.with(scope, |this| {
+ for param in generics.params {
+ match param.kind {
+ GenericParamKind::Lifetime { .. } => {}
+ GenericParamKind::Type { ref default, .. } => {
+ if let Some(ref ty) = default {
+ this.visit_ty(&ty);
+ }
+ }
+ GenericParamKind::Const { ref ty, default } => {
+ this.visit_ty(&ty);
+ if let Some(default) = default {
+ this.visit_body(this.tcx.hir().body(default.body));
+ }
+ }
+ }
+ }
+ for predicate in generics.predicates {
+ match predicate {
+ &hir::WherePredicate::BoundPredicate(hir::WhereBoundPredicate {
+ hir_id,
+ ref bounded_ty,
+ bounds,
+ ref bound_generic_params,
+ origin,
+ ..
+ }) => {
+ let lifetimes: FxIndexMap<LocalDefId, Region> =
+ bound_generic_params
+ .iter()
+ .filter(|param| {
+ matches!(param.kind, GenericParamKind::Lifetime { .. })
+ })
+ .enumerate()
+ .map(|(late_bound_idx, param)| {
+ Region::late(late_bound_idx as u32, this.tcx.hir(), param)
+ })
+ .collect();
+ let binders: Vec<_> =
+ lifetimes
+ .iter()
+ .map(|(_, region)| {
+ late_region_as_bound_region(this.tcx, region)
+ })
+ .collect();
+ this.record_late_bound_vars(hir_id, binders.clone());
+ // Even if there are no lifetimes defined here, we still wrap it in a binder
+ // scope. If there happens to be a nested poly trait ref (an error), that
+ // will be `Concatenating` anyways, so we don't have to worry about the depth
+ // being wrong.
+ let scope = Scope::Binder {
+ hir_id,
+ lifetimes,
+ s: this.scope,
+ scope_type: BinderScopeType::Normal,
+ where_bound_origin: Some(origin),
+ };
+ this.with(scope, |this| {
+ this.visit_ty(&bounded_ty);
+ walk_list!(this, visit_param_bound, bounds);
+ })
+ }
+ &hir::WherePredicate::RegionPredicate(hir::WhereRegionPredicate {
+ ref lifetime,
+ bounds,
+ ..
+ }) => {
+ this.visit_lifetime(lifetime);
+ walk_list!(this, visit_param_bound, bounds);
+
+ if lifetime.name != hir::LifetimeName::Static {
+ for bound in bounds {
+ let hir::GenericBound::Outlives(ref lt) = bound else {
+ continue;
+ };
+ if lt.name != hir::LifetimeName::Static {
+ continue;
+ }
+ this.insert_lifetime(lt, Region::Static);
+ this.tcx
+ .sess
+ .struct_span_warn(
+ lifetime.span,
+ &format!(
+ "unnecessary lifetime parameter `{}`",
+ lifetime.name.ident(),
+ ),
+ )
+ .help(&format!(
+ "you can use the `'static` lifetime directly, in place of `{}`",
+ lifetime.name.ident(),
+ ))
+ .emit();
+ }
+ }
+ }
+ &hir::WherePredicate::EqPredicate(hir::WhereEqPredicate {
+ ref lhs_ty,
+ ref rhs_ty,
+ ..
+ }) => {
+ this.visit_ty(lhs_ty);
+ this.visit_ty(rhs_ty);
+ }
+ }
+ }
+ })
+ }
+
+ fn visit_param_bound(&mut self, bound: &'tcx hir::GenericBound<'tcx>) {
+ match bound {
+ hir::GenericBound::LangItemTrait(_, _, hir_id, _) => {
+ // FIXME(jackh726): This is pretty weird. `LangItemTrait` doesn't go
+ // through the regular poly trait ref code, so we don't get another
+ // chance to introduce a binder. For now, I'm keeping the existing logic
+ // of "if there isn't a Binder scope above us, add one", but I
+ // imagine there's a better way to go about this.
+ let (binders, scope_type) = self.poly_trait_ref_binder_info();
+
+ self.record_late_bound_vars(*hir_id, binders);
+ let scope = Scope::Binder {
+ hir_id: *hir_id,
+ lifetimes: FxIndexMap::default(),
+ s: self.scope,
+ scope_type,
+ where_bound_origin: None,
+ };
+ self.with(scope, |this| {
+ intravisit::walk_param_bound(this, bound);
+ });
+ }
+ _ => intravisit::walk_param_bound(self, bound),
+ }
+ }
+
+ fn visit_poly_trait_ref(&mut self, trait_ref: &'tcx hir::PolyTraitRef<'tcx>) {
+ debug!("visit_poly_trait_ref(trait_ref={:?})", trait_ref);
+
+ let (mut binders, scope_type) = self.poly_trait_ref_binder_info();
+
+ let initial_bound_vars = binders.len() as u32;
+ let mut lifetimes: FxIndexMap<LocalDefId, Region> = FxIndexMap::default();
+ let binders_iter = trait_ref
+ .bound_generic_params
+ .iter()
+ .filter(|param| matches!(param.kind, GenericParamKind::Lifetime { .. }))
+ .enumerate()
+ .map(|(late_bound_idx, param)| {
+ let pair =
+ Region::late(initial_bound_vars + late_bound_idx as u32, self.tcx.hir(), param);
+ let r = late_region_as_bound_region(self.tcx, &pair.1);
+ lifetimes.insert(pair.0, pair.1);
+ r
+ });
+ binders.extend(binders_iter);
+
+ debug!(?binders);
+ self.record_late_bound_vars(trait_ref.trait_ref.hir_ref_id, binders);
+
+ // Always introduce a scope here, even if this is in a where clause and
+ // we introduced the binders around the bounded Ty. In that case, we
+ // just reuse the concatenation functionality also present in nested trait
+ // refs.
+ let scope = Scope::Binder {
+ hir_id: trait_ref.trait_ref.hir_ref_id,
+ lifetimes,
+ s: self.scope,
+ scope_type,
+ where_bound_origin: None,
+ };
+ self.with(scope, |this| {
+ walk_list!(this, visit_generic_param, trait_ref.bound_generic_params);
+ this.visit_trait_ref(&trait_ref.trait_ref);
+ });
+ }
+}
+
+fn object_lifetime_default<'tcx>(tcx: TyCtxt<'tcx>, param_def_id: DefId) -> ObjectLifetimeDefault {
+ debug_assert_eq!(tcx.def_kind(param_def_id), DefKind::TyParam);
+ let param_def_id = param_def_id.expect_local();
+ let parent_def_id = tcx.local_parent(param_def_id);
+ let generics = tcx.hir().get_generics(parent_def_id).unwrap();
+ let param_hir_id = tcx.local_def_id_to_hir_id(param_def_id);
+ let param = generics.params.iter().find(|p| p.hir_id == param_hir_id).unwrap();
+
+ // Scan the bounds and where-clauses on parameters to extract bounds
+ // of the form `T:'a` so as to determine the `ObjectLifetimeDefault`
+ // for each type parameter.
+ match param.kind {
+ GenericParamKind::Type { .. } => {
+ let mut set = Set1::Empty;
+
+ // Look for `type: ...` where clauses.
+ for bound in generics.bounds_for_param(param_def_id) {
+ // Ignore `for<'a> type: ...` as they can change what
+ // lifetimes mean (although we could "just" handle it).
+ if !bound.bound_generic_params.is_empty() {
+ continue;
+ }
+
+ for bound in bound.bounds {
+ if let hir::GenericBound::Outlives(ref lifetime) = *bound {
+ set.insert(lifetime.name.normalize_to_macros_2_0());
+ }
+ }
+ }
+
+ match set {
+ Set1::Empty => ObjectLifetimeDefault::Empty,
+ Set1::One(hir::LifetimeName::Static) => ObjectLifetimeDefault::Static,
+ Set1::One(hir::LifetimeName::Param(param_def_id, _)) => {
+ ObjectLifetimeDefault::Param(param_def_id.to_def_id())
+ }
+ _ => ObjectLifetimeDefault::Ambiguous,
+ }
+ }
+ _ => {
+ bug!("object_lifetime_default_raw must only be called on a type parameter")
+ }
+ }
+}
+
+impl<'a, 'tcx> LifetimeContext<'a, 'tcx> {
+ fn with<F>(&mut self, wrap_scope: Scope<'_>, f: F)
+ where
+ F: for<'b> FnOnce(&mut LifetimeContext<'b, 'tcx>),
+ {
+ let LifetimeContext { tcx, map, .. } = self;
+ let mut this = LifetimeContext {
+ tcx: *tcx,
+ map,
+ scope: &wrap_scope,
+ trait_definition_only: self.trait_definition_only,
+ };
+ let span = debug_span!("scope", scope = ?TruncatedScopeDebug(&this.scope));
+ {
+ let _enter = span.enter();
+ f(&mut this);
+ }
+ }
+
+ fn record_late_bound_vars(&mut self, hir_id: hir::HirId, binder: Vec<ty::BoundVariableKind>) {
+ if let Some(old) = self.map.late_bound_vars.insert(hir_id, binder) {
+ bug!(
+ "overwrote bound vars for {hir_id:?}:\nold={old:?}\nnew={:?}",
+ self.map.late_bound_vars[&hir_id]
+ )
+ }
+ }
+
+ /// Visits self by adding a scope and handling recursive walk over the contents with `walk`.
+ ///
+ /// Handles visiting fns and methods. These are a bit complicated because we must distinguish
+ /// early- vs late-bound lifetime parameters. We do this by checking which lifetimes appear
+ /// within type bounds; those are early bound lifetimes, and the rest are late bound.
+ ///
+ /// For example:
+ ///
+ /// fn foo<'a,'b,'c,T:Trait<'b>>(...)
+ ///
+ /// Here `'a` and `'c` are late bound but `'b` is early bound. Note that early- and late-bound
+ /// lifetimes may be interspersed together.
+ ///
+ /// If early bound lifetimes are present, we separate them into their own list (and likewise
+ /// for late bound). They will be numbered sequentially, starting from the lowest index that is
+ /// already in scope (for a fn item, that will be 0, but for a method it might not be). Late
+ /// bound lifetimes are resolved by name and associated with a binder ID (`binder_id`), so the
+ /// ordering is not important there.
+ fn visit_early_late<F>(
+ &mut self,
+ hir_id: hir::HirId,
+ generics: &'tcx hir::Generics<'tcx>,
+ walk: F,
+ ) where
+ F: for<'b, 'c> FnOnce(&'b mut LifetimeContext<'c, 'tcx>),
+ {
+ let mut named_late_bound_vars = 0;
+ let lifetimes: FxIndexMap<LocalDefId, Region> = generics
+ .params
+ .iter()
+ .filter_map(|param| match param.kind {
+ GenericParamKind::Lifetime { .. } => {
+ if self.tcx.is_late_bound(param.hir_id) {
+ let late_bound_idx = named_late_bound_vars;
+ named_late_bound_vars += 1;
+ Some(Region::late(late_bound_idx, self.tcx.hir(), param))
+ } else {
+ Some(Region::early(self.tcx.hir(), param))
+ }
+ }
+ GenericParamKind::Type { .. } | GenericParamKind::Const { .. } => None,
+ })
+ .collect();
+
+ let binders: Vec<_> = generics
+ .params
+ .iter()
+ .filter(|param| {
+ matches!(param.kind, GenericParamKind::Lifetime { .. })
+ && self.tcx.is_late_bound(param.hir_id)
+ })
+ .enumerate()
+ .map(|(late_bound_idx, param)| {
+ let pair = Region::late(late_bound_idx as u32, self.tcx.hir(), param);
+ late_region_as_bound_region(self.tcx, &pair.1)
+ })
+ .collect();
+ self.record_late_bound_vars(hir_id, binders);
+ let scope = Scope::Binder {
+ hir_id,
+ lifetimes,
+ s: self.scope,
+ scope_type: BinderScopeType::Normal,
+ where_bound_origin: None,
+ };
+ self.with(scope, walk);
+ }
+
+ #[instrument(level = "debug", skip(self))]
+ fn resolve_lifetime_ref(
+ &mut self,
+ region_def_id: LocalDefId,
+ lifetime_ref: &'tcx hir::Lifetime,
+ ) {
+ // Walk up the scope chain, tracking the number of fn scopes
+ // that we pass through, until we find a lifetime with the
+ // given name or we run out of scopes.
+ // search.
+ let mut late_depth = 0;
+ let mut scope = self.scope;
+ let mut outermost_body = None;
+ let result = loop {
+ match *scope {
+ Scope::Body { id, s } => {
+ outermost_body = Some(id);
+ scope = s;
+ }
+
+ Scope::Root => {
+ break None;
+ }
+
+ Scope::Binder { ref lifetimes, scope_type, s, where_bound_origin, .. } => {
+ if let Some(&def) = lifetimes.get(&region_def_id) {
+ break Some(def.shifted(late_depth));
+ }
+ match scope_type {
+ BinderScopeType::Normal => late_depth += 1,
+ BinderScopeType::Concatenating => {}
+ }
+ // Fresh lifetimes in APIT used to be allowed in async fns and forbidden in
+ // regular fns.
+ if let Some(hir::PredicateOrigin::ImplTrait) = where_bound_origin
+ && let hir::LifetimeName::Param(_, hir::ParamName::Fresh) = lifetime_ref.name
+ && let hir::IsAsync::NotAsync = self.tcx.asyncness(lifetime_ref.hir_id.owner.def_id)
+ && !self.tcx.features().anonymous_lifetime_in_impl_trait
+ {
+ let mut diag = rustc_session::parse::feature_err(
+ &self.tcx.sess.parse_sess,
+ sym::anonymous_lifetime_in_impl_trait,
+ lifetime_ref.span,
+ "anonymous lifetimes in `impl Trait` are unstable",
+ );
+
+ match self.tcx.hir().get_generics(lifetime_ref.hir_id.owner.def_id) {
+ Some(generics) => {
+
+ let new_param_sugg_tuple;
+
+ new_param_sugg_tuple = match generics.span_for_param_suggestion() {
+ Some(_) => {
+ Some((self.tcx.sess.source_map().span_through_char(generics.span, '<').shrink_to_hi(), "'a, ".to_owned()))
+ },
+ None => Some((generics.span, "<'a>".to_owned()))
+ };
+
+ let mut multi_sugg_vec = vec![(lifetime_ref.span.shrink_to_hi(), "'a ".to_owned())];
+
+ if let Some(new_tuple) = new_param_sugg_tuple{
+ multi_sugg_vec.push(new_tuple);
+ }
+
+ diag.span_label(lifetime_ref.span, "expected named lifetime parameter");
+ diag.multipart_suggestion("consider introducing a named lifetime parameter",
+ multi_sugg_vec,
+ rustc_errors::Applicability::MaybeIncorrect);
+
+ },
+ None => { }
+ }
+
+ diag.emit();
+ return;
+ }
+ scope = s;
+ }
+
+ Scope::Elision { s, .. }
+ | Scope::ObjectLifetimeDefault { s, .. }
+ | Scope::Supertrait { s, .. }
+ | Scope::TraitRefBoundary { s, .. } => {
+ scope = s;
+ }
+ }
+ };
+
+ if let Some(mut def) = result {
+ if let Region::EarlyBound(..) = def {
+ // Do not free early-bound regions, only late-bound ones.
+ } else if let Some(body_id) = outermost_body {
+ let fn_id = self.tcx.hir().body_owner(body_id);
+ match self.tcx.hir().get(fn_id) {
+ Node::Item(&hir::Item { kind: hir::ItemKind::Fn(..), .. })
+ | Node::TraitItem(&hir::TraitItem {
+ kind: hir::TraitItemKind::Fn(..), ..
+ })
+ | Node::ImplItem(&hir::ImplItem { kind: hir::ImplItemKind::Fn(..), .. }) => {
+ let scope = self.tcx.hir().local_def_id(fn_id);
+ def = Region::Free(scope.to_def_id(), def.id().unwrap());
+ }
+ _ => {}
+ }
+ }
+
+ self.insert_lifetime(lifetime_ref, def);
+ return;
+ }
+
+ // We may fail to resolve higher-ranked lifetimes that are mentioned by APIT.
+ // AST-based resolution does not care for impl-trait desugaring, which are the
+ // responibility of lowering. This may create a mismatch between the resolution
+ // AST found (`region_def_id`) which points to HRTB, and what HIR allows.
+ // ```
+ // fn foo(x: impl for<'a> Trait<'a, Assoc = impl Copy + 'a>) {}
+ // ```
+ //
+ // In such case, walk back the binders to diagnose it properly.
+ let mut scope = self.scope;
+ loop {
+ match *scope {
+ Scope::Binder {
+ where_bound_origin: Some(hir::PredicateOrigin::ImplTrait), ..
+ } => {
+ let mut err = self.tcx.sess.struct_span_err(
+ lifetime_ref.span,
+ "`impl Trait` can only mention lifetimes bound at the fn or impl level",
+ );
+ err.span_note(self.tcx.def_span(region_def_id), "lifetime declared here");
+ err.emit();
+ return;
+ }
+ Scope::Root => break,
+ Scope::Binder { s, .. }
+ | Scope::Body { s, .. }
+ | Scope::Elision { s, .. }
+ | Scope::ObjectLifetimeDefault { s, .. }
+ | Scope::Supertrait { s, .. }
+ | Scope::TraitRefBoundary { s, .. } => {
+ scope = s;
+ }
+ }
+ }
+
+ self.tcx.sess.delay_span_bug(
+ lifetime_ref.span,
+ &format!("Could not resolve {:?} in scope {:#?}", lifetime_ref, self.scope,),
+ );
+ }
+
+ #[instrument(level = "debug", skip(self))]
+ fn visit_segment_args(
+ &mut self,
+ res: Res,
+ depth: usize,
+ generic_args: &'tcx hir::GenericArgs<'tcx>,
+ ) {
+ if generic_args.parenthesized {
+ self.visit_fn_like_elision(
+ generic_args.inputs(),
+ Some(generic_args.bindings[0].ty()),
+ false,
+ );
+ return;
+ }
+
+ for arg in generic_args.args {
+ if let hir::GenericArg::Lifetime(lt) = arg {
+ self.visit_lifetime(lt);
+ }
+ }
+
+ // Figure out if this is a type/trait segment,
+ // which requires object lifetime defaults.
+ let type_def_id = match res {
+ Res::Def(DefKind::AssocTy, def_id) if depth == 1 => Some(self.tcx.parent(def_id)),
+ Res::Def(DefKind::Variant, def_id) if depth == 0 => Some(self.tcx.parent(def_id)),
+ Res::Def(
+ DefKind::Struct
+ | DefKind::Union
+ | DefKind::Enum
+ | DefKind::TyAlias
+ | DefKind::Trait,
+ def_id,
+ ) if depth == 0 => Some(def_id),
+ _ => None,
+ };
+
+ debug!(?type_def_id);
+
+ // Compute a vector of defaults, one for each type parameter,
+ // per the rules given in RFCs 599 and 1156. Example:
+ //
+ // ```rust
+ // struct Foo<'a, T: 'a, U> { }
+ // ```
+ //
+ // If you have `Foo<'x, dyn Bar, dyn Baz>`, we want to default
+ // `dyn Bar` to `dyn Bar + 'x` (because of the `T: 'a` bound)
+ // and `dyn Baz` to `dyn Baz + 'static` (because there is no
+ // such bound).
+ //
+ // Therefore, we would compute `object_lifetime_defaults` to a
+ // vector like `['x, 'static]`. Note that the vector only
+ // includes type parameters.
+ let object_lifetime_defaults = type_def_id.map_or_else(Vec::new, |def_id| {
+ let in_body = {
+ let mut scope = self.scope;
+ loop {
+ match *scope {
+ Scope::Root => break false,
+
+ Scope::Body { .. } => break true,
+
+ Scope::Binder { s, .. }
+ | Scope::Elision { s, .. }
+ | Scope::ObjectLifetimeDefault { s, .. }
+ | Scope::Supertrait { s, .. }
+ | Scope::TraitRefBoundary { s, .. } => {
+ scope = s;
+ }
+ }
+ }
+ };
+
+ let map = &self.map;
+ let generics = self.tcx.generics_of(def_id);
+
+ // `type_def_id` points to an item, so there is nothing to inherit generics from.
+ debug_assert_eq!(generics.parent_count, 0);
+
+ let set_to_region = |set: ObjectLifetimeDefault| match set {
+ ObjectLifetimeDefault::Empty => {
+ if in_body {
+ None
+ } else {
+ Some(Region::Static)
+ }
+ }
+ ObjectLifetimeDefault::Static => Some(Region::Static),
+ ObjectLifetimeDefault::Param(param_def_id) => {
+ // This index can be used with `generic_args` since `parent_count == 0`.
+ let index = generics.param_def_id_to_index[&param_def_id] as usize;
+ generic_args.args.get(index).and_then(|arg| match arg {
+ GenericArg::Lifetime(lt) => map.defs.get(&lt.hir_id).copied(),
+ _ => None,
+ })
+ }
+ ObjectLifetimeDefault::Ambiguous => None,
+ };
+ generics
+ .params
+ .iter()
+ .filter_map(|param| {
+ match self.tcx.def_kind(param.def_id) {
+ // Generic consts don't impose any constraints.
+ //
+ // We still store a dummy value here to allow generic parameters
+ // in an arbitrary order.
+ DefKind::ConstParam => Some(ObjectLifetimeDefault::Empty),
+ DefKind::TyParam => Some(self.tcx.object_lifetime_default(param.def_id)),
+ // We may also get a `Trait` or `TraitAlias` because of how generics `Self` parameter
+ // works. Ignore it because it can't have a meaningful lifetime default.
+ DefKind::LifetimeParam | DefKind::Trait | DefKind::TraitAlias => None,
+ dk => bug!("unexpected def_kind {:?}", dk),
+ }
+ })
+ .map(set_to_region)
+ .collect()
+ });
+
+ debug!(?object_lifetime_defaults);
+
+ let mut i = 0;
+ for arg in generic_args.args {
+ match arg {
+ GenericArg::Lifetime(_) => {}
+ GenericArg::Type(ty) => {
+ if let Some(&lt) = object_lifetime_defaults.get(i) {
+ let scope = Scope::ObjectLifetimeDefault { lifetime: lt, s: self.scope };
+ self.with(scope, |this| this.visit_ty(ty));
+ } else {
+ self.visit_ty(ty);
+ }
+ i += 1;
+ }
+ GenericArg::Const(ct) => {
+ self.visit_anon_const(&ct.value);
+ i += 1;
+ }
+ GenericArg::Infer(inf) => {
+ self.visit_id(inf.hir_id);
+ i += 1;
+ }
+ }
+ }
+
+ // Hack: when resolving the type `XX` in binding like `dyn
+ // Foo<'b, Item = XX>`, the current object-lifetime default
+ // would be to examine the trait `Foo` to check whether it has
+ // a lifetime bound declared on `Item`. e.g., if `Foo` is
+ // declared like so, then the default object lifetime bound in
+ // `XX` should be `'b`:
+ //
+ // ```rust
+ // trait Foo<'a> {
+ // type Item: 'a;
+ // }
+ // ```
+ //
+ // but if we just have `type Item;`, then it would be
+ // `'static`. However, we don't get all of this logic correct.
+ //
+ // Instead, we do something hacky: if there are no lifetime parameters
+ // to the trait, then we simply use a default object lifetime
+ // bound of `'static`, because there is no other possibility. On the other hand,
+ // if there ARE lifetime parameters, then we require the user to give an
+ // explicit bound for now.
+ //
+ // This is intended to leave room for us to implement the
+ // correct behavior in the future.
+ let has_lifetime_parameter =
+ generic_args.args.iter().any(|arg| matches!(arg, GenericArg::Lifetime(_)));
+
+ // Resolve lifetimes found in the bindings, so either in the type `XX` in `Item = XX` or
+ // in the trait ref `YY<...>` in `Item: YY<...>`.
+ for binding in generic_args.bindings {
+ let scope = Scope::ObjectLifetimeDefault {
+ lifetime: if has_lifetime_parameter { None } else { Some(Region::Static) },
+ s: self.scope,
+ };
+ if let Some(type_def_id) = type_def_id {
+ let lifetimes = LifetimeContext::supertrait_hrtb_lifetimes(
+ self.tcx,
+ type_def_id,
+ binding.ident,
+ );
+ self.with(scope, |this| {
+ let scope = Scope::Supertrait {
+ lifetimes: lifetimes.unwrap_or_default(),
+ s: this.scope,
+ };
+ this.with(scope, |this| this.visit_assoc_type_binding(binding));
+ });
+ } else {
+ self.with(scope, |this| this.visit_assoc_type_binding(binding));
+ }
+ }
+ }
+
+ /// Returns all the late-bound vars that come into scope from supertrait HRTBs, based on the
+ /// associated type name and starting trait.
+ /// For example, imagine we have
+ /// ```ignore (illustrative)
+ /// trait Foo<'a, 'b> {
+ /// type As;
+ /// }
+ /// trait Bar<'b>: for<'a> Foo<'a, 'b> {}
+ /// trait Bar: for<'b> Bar<'b> {}
+ /// ```
+ /// In this case, if we wanted to the supertrait HRTB lifetimes for `As` on
+ /// the starting trait `Bar`, we would return `Some(['b, 'a])`.
+ fn supertrait_hrtb_lifetimes(
+ tcx: TyCtxt<'tcx>,
+ def_id: DefId,
+ assoc_name: Ident,
+ ) -> Option<Vec<ty::BoundVariableKind>> {
+ let trait_defines_associated_type_named = |trait_def_id: DefId| {
+ tcx.associated_items(trait_def_id)
+ .find_by_name_and_kind(tcx, assoc_name, ty::AssocKind::Type, trait_def_id)
+ .is_some()
+ };
+
+ use smallvec::{smallvec, SmallVec};
+ let mut stack: SmallVec<[(DefId, SmallVec<[ty::BoundVariableKind; 8]>); 8]> =
+ smallvec![(def_id, smallvec![])];
+ let mut visited: FxHashSet<DefId> = FxHashSet::default();
+ loop {
+ let Some((def_id, bound_vars)) = stack.pop() else {
+ break None;
+ };
+ // See issue #83753. If someone writes an associated type on a non-trait, just treat it as
+ // there being no supertrait HRTBs.
+ match tcx.def_kind(def_id) {
+ DefKind::Trait | DefKind::TraitAlias | DefKind::Impl => {}
+ _ => break None,
+ }
+
+ if trait_defines_associated_type_named(def_id) {
+ break Some(bound_vars.into_iter().collect());
+ }
+ let predicates =
+ tcx.super_predicates_that_define_assoc_type((def_id, Some(assoc_name)));
+ let obligations = predicates.predicates.iter().filter_map(|&(pred, _)| {
+ let bound_predicate = pred.kind();
+ match bound_predicate.skip_binder() {
+ ty::PredicateKind::Trait(data) => {
+ // The order here needs to match what we would get from `subst_supertrait`
+ let pred_bound_vars = bound_predicate.bound_vars();
+ let mut all_bound_vars = bound_vars.clone();
+ all_bound_vars.extend(pred_bound_vars.iter());
+ let super_def_id = data.trait_ref.def_id;
+ Some((super_def_id, all_bound_vars))
+ }
+ _ => None,
+ }
+ });
+
+ let obligations = obligations.filter(|o| visited.insert(o.0));
+ stack.extend(obligations);
+ }
+ }
+
+ #[instrument(level = "debug", skip(self))]
+ fn visit_fn_like_elision(
+ &mut self,
+ inputs: &'tcx [hir::Ty<'tcx>],
+ output: Option<&'tcx hir::Ty<'tcx>>,
+ in_closure: bool,
+ ) {
+ self.with(Scope::Elision { s: self.scope }, |this| {
+ for input in inputs {
+ this.visit_ty(input);
+ }
+ if !in_closure && let Some(output) = output {
+ this.visit_ty(output);
+ }
+ });
+ if in_closure && let Some(output) = output {
+ self.visit_ty(output);
+ }
+ }
+
+ fn resolve_object_lifetime_default(&mut self, lifetime_ref: &'tcx hir::Lifetime) {
+ debug!("resolve_object_lifetime_default(lifetime_ref={:?})", lifetime_ref);
+ let mut late_depth = 0;
+ let mut scope = self.scope;
+ let lifetime = loop {
+ match *scope {
+ Scope::Binder { s, scope_type, .. } => {
+ match scope_type {
+ BinderScopeType::Normal => late_depth += 1,
+ BinderScopeType::Concatenating => {}
+ }
+ scope = s;
+ }
+
+ Scope::Root | Scope::Elision { .. } => break Region::Static,
+
+ Scope::Body { .. } | Scope::ObjectLifetimeDefault { lifetime: None, .. } => return,
+
+ Scope::ObjectLifetimeDefault { lifetime: Some(l), .. } => break l,
+
+ Scope::Supertrait { s, .. } | Scope::TraitRefBoundary { s, .. } => {
+ scope = s;
+ }
+ }
+ };
+ self.insert_lifetime(lifetime_ref, lifetime.shifted(late_depth));
+ }
+
+ #[instrument(level = "debug", skip(self))]
+ fn insert_lifetime(&mut self, lifetime_ref: &'tcx hir::Lifetime, def: Region) {
+ debug!(
+ node = ?self.tcx.hir().node_to_string(lifetime_ref.hir_id),
+ span = ?self.tcx.sess.source_map().span_to_diagnostic_string(lifetime_ref.span)
+ );
+ self.map.defs.insert(lifetime_ref.hir_id, def);
+ }
+
+ /// Sometimes we resolve a lifetime, but later find that it is an
+ /// error (esp. around impl trait). In that case, we remove the
+ /// entry into `map.defs` so as not to confuse later code.
+ fn uninsert_lifetime_on_error(&mut self, lifetime_ref: &'tcx hir::Lifetime, bad_def: Region) {
+ let old_value = self.map.defs.remove(&lifetime_ref.hir_id);
+ assert_eq!(old_value, Some(bad_def));
+ }
+}
+
+/// Detects late-bound lifetimes and inserts them into
+/// `late_bound`.
+///
+/// A region declared on a fn is **late-bound** if:
+/// - it is constrained by an argument type;
+/// - it does not appear in a where-clause.
+///
+/// "Constrained" basically means that it appears in any type but
+/// not amongst the inputs to a projection. In other words, `<&'a
+/// T as Trait<''b>>::Foo` does not constrain `'a` or `'b`.
+fn is_late_bound_map(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<&FxIndexSet<LocalDefId>> {
+ let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
+ let decl = tcx.hir().fn_decl_by_hir_id(hir_id)?;
+ let generics = tcx.hir().get_generics(def_id)?;
+
+ let mut late_bound = FxIndexSet::default();
+
+ let mut constrained_by_input = ConstrainedCollector::default();
+ for arg_ty in decl.inputs {
+ constrained_by_input.visit_ty(arg_ty);
+ }
+
+ let mut appears_in_output = AllCollector::default();
+ intravisit::walk_fn_ret_ty(&mut appears_in_output, &decl.output);
+
+ debug!(?constrained_by_input.regions);
+
+ // Walk the lifetimes that appear in where clauses.
+ //
+ // Subtle point: because we disallow nested bindings, we can just
+ // ignore binders here and scrape up all names we see.
+ let mut appears_in_where_clause = AllCollector::default();
+ appears_in_where_clause.visit_generics(generics);
+ debug!(?appears_in_where_clause.regions);
+
+ // Late bound regions are those that:
+ // - appear in the inputs
+ // - do not appear in the where-clauses
+ // - are not implicitly captured by `impl Trait`
+ for param in generics.params {
+ match param.kind {
+ hir::GenericParamKind::Lifetime { .. } => { /* fall through */ }
+
+ // Neither types nor consts are late-bound.
+ hir::GenericParamKind::Type { .. } | hir::GenericParamKind::Const { .. } => continue,
+ }
+
+ let param_def_id = tcx.hir().local_def_id(param.hir_id);
+
+ // appears in the where clauses? early-bound.
+ if appears_in_where_clause.regions.contains(&param_def_id) {
+ continue;
+ }
+
+ // does not appear in the inputs, but appears in the return type? early-bound.
+ if !constrained_by_input.regions.contains(&param_def_id)
+ && appears_in_output.regions.contains(&param_def_id)
+ {
+ continue;
+ }
+
+ debug!("lifetime {:?} with id {:?} is late-bound", param.name.ident(), param.hir_id);
+
+ let inserted = late_bound.insert(param_def_id);
+ assert!(inserted, "visited lifetime {:?} twice", param.hir_id);
+ }
+
+ debug!(?late_bound);
+ return Some(tcx.arena.alloc(late_bound));
+
+ #[derive(Default)]
+ struct ConstrainedCollector {
+ regions: FxHashSet<LocalDefId>,
+ }
+
+ impl<'v> Visitor<'v> for ConstrainedCollector {
+ fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) {
+ match ty.kind {
+ hir::TyKind::Path(
+ hir::QPath::Resolved(Some(_), _) | hir::QPath::TypeRelative(..),
+ ) => {
+ // ignore lifetimes appearing in associated type
+ // projections, as they are not *constrained*
+ // (defined above)
+ }
+
+ hir::TyKind::Path(hir::QPath::Resolved(None, ref path)) => {
+ // consider only the lifetimes on the final
+ // segment; I am not sure it's even currently
+ // valid to have them elsewhere, but even if it
+ // is, those would be potentially inputs to
+ // projections
+ if let Some(last_segment) = path.segments.last() {
+ self.visit_path_segment(last_segment);
+ }
+ }
+
+ _ => {
+ intravisit::walk_ty(self, ty);
+ }
+ }
+ }
+
+ fn visit_lifetime(&mut self, lifetime_ref: &'v hir::Lifetime) {
+ if let hir::LifetimeName::Param(def_id, _) = lifetime_ref.name {
+ self.regions.insert(def_id);
+ }
+ }
+ }
+
+ #[derive(Default)]
+ struct AllCollector {
+ regions: FxHashSet<LocalDefId>,
+ }
+
+ impl<'v> Visitor<'v> for AllCollector {
+ fn visit_lifetime(&mut self, lifetime_ref: &'v hir::Lifetime) {
+ if let hir::LifetimeName::Param(def_id, _) = lifetime_ref.name {
+ self.regions.insert(def_id);
+ }
+ }
+ }
+}
diff --git a/compiler/rustc_hir_analysis/src/collect/predicates_of.rs b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs
new file mode 100644
index 000000000..2e84e1d01
--- /dev/null
+++ b/compiler/rustc_hir_analysis/src/collect/predicates_of.rs
@@ -0,0 +1,707 @@
+use crate::astconv::AstConv;
+use crate::bounds::Bounds;
+use crate::collect::ItemCtxt;
+use crate::constrained_generic_params as cgp;
+use hir::{HirId, Node};
+use rustc_data_structures::fx::FxIndexSet;
+use rustc_hir as hir;
+use rustc_hir::def::DefKind;
+use rustc_hir::def_id::{DefId, LocalDefId};
+use rustc_hir::intravisit::{self, Visitor};
+use rustc_middle::ty::subst::InternalSubsts;
+use rustc_middle::ty::ToPredicate;
+use rustc_middle::ty::{self, Ty, TyCtxt};
+use rustc_span::symbol::{sym, Ident};
+use rustc_span::{Span, DUMMY_SP};
+
+#[derive(Debug)]
+struct OnlySelfBounds(bool);
+
+/// Returns a list of all type predicates (explicit and implicit) for the definition with
+/// ID `def_id`. This includes all predicates returned by `predicates_defined_on`, plus
+/// `Self: Trait` predicates for traits.
+pub(super) fn predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicates<'_> {
+ let mut result = tcx.predicates_defined_on(def_id);
+
+ if tcx.is_trait(def_id) {
+ // For traits, add `Self: Trait` predicate. This is
+ // not part of the predicates that a user writes, but it
+ // is something that one must prove in order to invoke a
+ // method or project an associated type.
+ //
+ // In the chalk setup, this predicate is not part of the
+ // "predicates" for a trait item. But it is useful in
+ // rustc because if you directly (e.g.) invoke a trait
+ // method like `Trait::method(...)`, you must naturally
+ // prove that the trait applies to the types that were
+ // used, and adding the predicate into this list ensures
+ // that this is done.
+ //
+ // We use a DUMMY_SP here as a way to signal trait bounds that come
+ // from the trait itself that *shouldn't* be shown as the source of
+ // an obligation and instead be skipped. Otherwise we'd use
+ // `tcx.def_span(def_id);`
+
+ let constness = if tcx.has_attr(def_id, sym::const_trait) {
+ ty::BoundConstness::ConstIfConst
+ } else {
+ ty::BoundConstness::NotConst
+ };
+
+ let span = rustc_span::DUMMY_SP;
+ result.predicates =
+ tcx.arena.alloc_from_iter(result.predicates.iter().copied().chain(std::iter::once((
+ ty::TraitRef::identity(tcx, def_id).with_constness(constness).to_predicate(tcx),
+ span,
+ ))));
+ }
+ debug!("predicates_of(def_id={:?}) = {:?}", def_id, result);
+ result
+}
+
+/// Returns a list of user-specified type predicates for the definition with ID `def_id`.
+/// N.B., this does not include any implied/inferred constraints.
+#[instrument(level = "trace", skip(tcx), ret)]
+fn gather_explicit_predicates_of(tcx: TyCtxt<'_>, def_id: DefId) -> ty::GenericPredicates<'_> {
+ use rustc_hir::*;
+
+ let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local());
+ let node = tcx.hir().get(hir_id);
+
+ let mut is_trait = None;
+ let mut is_default_impl_trait = None;
+
+ let icx = ItemCtxt::new(tcx, def_id);
+
+ const NO_GENERICS: &hir::Generics<'_> = hir::Generics::empty();
+
+ // We use an `IndexSet` to preserves order of insertion.
+ // Preserving the order of insertion is important here so as not to break UI tests.
+ let mut predicates: FxIndexSet<(ty::Predicate<'_>, Span)> = FxIndexSet::default();
+
+ let ast_generics = match node {
+ Node::TraitItem(item) => item.generics,
+
+ Node::ImplItem(item) => item.generics,
+
+ Node::Item(item) => {
+ match item.kind {
+ ItemKind::Impl(ref impl_) => {
+ if impl_.defaultness.is_default() {
+ is_default_impl_trait = tcx.impl_trait_ref(def_id).map(ty::Binder::dummy);
+ }
+ &impl_.generics
+ }
+ ItemKind::Fn(.., ref generics, _)
+ | ItemKind::TyAlias(_, ref generics)
+ | ItemKind::Enum(_, ref generics)
+ | ItemKind::Struct(_, ref generics)
+ | ItemKind::Union(_, ref generics) => *generics,
+
+ ItemKind::Trait(_, _, ref generics, ..) => {
+ is_trait = Some(ty::TraitRef::identity(tcx, def_id));
+ *generics
+ }
+ ItemKind::TraitAlias(ref generics, _) => {
+ is_trait = Some(ty::TraitRef::identity(tcx, def_id));
+ *generics
+ }
+ ItemKind::OpaqueTy(OpaqueTy {
+ origin: hir::OpaqueTyOrigin::AsyncFn(..) | hir::OpaqueTyOrigin::FnReturn(..),
+ ..
+ }) => {
+ // return-position impl trait
+ //
+ // We don't inherit predicates from the parent here:
+ // If we have, say `fn f<'a, T: 'a>() -> impl Sized {}`
+ // then the return type is `f::<'static, T>::{{opaque}}`.
+ //
+ // If we inherited the predicates of `f` then we would
+ // require that `T: 'static` to show that the return
+ // type is well-formed.
+ //
+ // The only way to have something with this opaque type
+ // is from the return type of the containing function,
+ // which will ensure that the function's predicates
+ // hold.
+ return ty::GenericPredicates { parent: None, predicates: &[] };
+ }
+ ItemKind::OpaqueTy(OpaqueTy {
+ ref generics,
+ origin: hir::OpaqueTyOrigin::TyAlias,
+ ..
+ }) => {
+ // type-alias impl trait
+ generics
+ }
+
+ _ => NO_GENERICS,
+ }
+ }
+
+ Node::ForeignItem(item) => match item.kind {
+ ForeignItemKind::Static(..) => NO_GENERICS,
+ ForeignItemKind::Fn(_, _, ref generics) => *generics,
+ ForeignItemKind::Type => NO_GENERICS,
+ },
+
+ _ => NO_GENERICS,
+ };
+
+ let generics = tcx.generics_of(def_id);
+ let parent_count = generics.parent_count as u32;
+ let has_own_self = generics.has_self && parent_count == 0;
+
+ // Below we'll consider the bounds on the type parameters (including `Self`)
+ // and the explicit where-clauses, but to get the full set of predicates
+ // on a trait we need to add in the supertrait bounds and bounds found on
+ // associated types.
+ if let Some(_trait_ref) = is_trait {
+ predicates.extend(tcx.super_predicates_of(def_id).predicates.iter().cloned());
+ }
+
+ // In default impls, we can assume that the self type implements
+ // the trait. So in:
+ //
+ // default impl Foo for Bar { .. }
+ //
+ // we add a default where clause `Foo: Bar`. We do a similar thing for traits
+ // (see below). Recall that a default impl is not itself an impl, but rather a
+ // set of defaults that can be incorporated into another impl.
+ if let Some(trait_ref) = is_default_impl_trait {
+ predicates.insert((trait_ref.without_const().to_predicate(tcx), tcx.def_span(def_id)));
+ }
+
+ // Collect the region predicates that were declared inline as
+ // well. In the case of parameters declared on a fn or method, we
+ // have to be careful to only iterate over early-bound regions.
+ let mut index = parent_count
+ + has_own_self as u32
+ + super::early_bound_lifetimes_from_generics(tcx, ast_generics).count() as u32;
+
+ trace!(?predicates);
+ trace!(?ast_generics);
+
+ // Collect the predicates that were written inline by the user on each
+ // type parameter (e.g., `<T: Foo>`).
+ for param in ast_generics.params {
+ match param.kind {
+ // We already dealt with early bound lifetimes above.
+ GenericParamKind::Lifetime { .. } => (),
+ GenericParamKind::Type { .. } => {
+ let name = param.name.ident().name;
+ let param_ty = ty::ParamTy::new(index, name).to_ty(tcx);
+ index += 1;
+
+ let mut bounds = Bounds::default();
+ // Params are implicitly sized unless a `?Sized` bound is found
+ <dyn AstConv<'_>>::add_implicitly_sized(
+ &icx,
+ &mut bounds,
+ &[],
+ Some((param.hir_id, ast_generics.predicates)),
+ param.span,
+ );
+ trace!(?bounds);
+ predicates.extend(bounds.predicates(tcx, param_ty));
+ trace!(?predicates);
+ }
+ GenericParamKind::Const { .. } => {
+ // Bounds on const parameters are currently not possible.
+ index += 1;
+ }
+ }
+ }
+
+ trace!(?predicates);
+ // Add in the bounds that appear in the where-clause.
+ for predicate in ast_generics.predicates {
+ match predicate {
+ hir::WherePredicate::BoundPredicate(bound_pred) => {
+ let ty = icx.to_ty(bound_pred.bounded_ty);
+ let bound_vars = icx.tcx.late_bound_vars(bound_pred.hir_id);
+
+ // Keep the type around in a dummy predicate, in case of no bounds.
+ // That way, `where Ty:` is not a complete noop (see #53696) and `Ty`
+ // is still checked for WF.
+ if bound_pred.bounds.is_empty() {
+ if let ty::Param(_) = ty.kind() {
+ // This is a `where T:`, which can be in the HIR from the
+ // transformation that moves `?Sized` to `T`'s declaration.
+ // We can skip the predicate because type parameters are
+ // trivially WF, but also we *should*, to avoid exposing
+ // users who never wrote `where Type:,` themselves, to
+ // compiler/tooling bugs from not handling WF predicates.
+ } else {
+ let span = bound_pred.bounded_ty.span;
+ let predicate = ty::Binder::bind_with_vars(
+ ty::PredicateKind::WellFormed(ty.into()),
+ bound_vars,
+ );
+ predicates.insert((predicate.to_predicate(tcx), span));
+ }
+ }
+
+ let mut bounds = Bounds::default();
+ <dyn AstConv<'_>>::add_bounds(
+ &icx,
+ ty,
+ bound_pred.bounds.iter(),
+ &mut bounds,
+ bound_vars,
+ );
+ predicates.extend(bounds.predicates(tcx, ty));
+ }
+
+ hir::WherePredicate::RegionPredicate(region_pred) => {
+ let r1 = <dyn AstConv<'_>>::ast_region_to_region(&icx, &region_pred.lifetime, None);
+ predicates.extend(region_pred.bounds.iter().map(|bound| {
+ let (r2, span) = match bound {
+ hir::GenericBound::Outlives(lt) => {
+ (<dyn AstConv<'_>>::ast_region_to_region(&icx, lt, None), lt.span)
+ }
+ _ => bug!(),
+ };
+ let pred = ty::Binder::dummy(ty::PredicateKind::RegionOutlives(
+ ty::OutlivesPredicate(r1, r2),
+ ))
+ .to_predicate(icx.tcx);
+
+ (pred, span)
+ }))
+ }
+
+ hir::WherePredicate::EqPredicate(..) => {
+ // FIXME(#20041)
+ }
+ }
+ }
+
+ if tcx.features().generic_const_exprs {
+ predicates.extend(const_evaluatable_predicates_of(tcx, def_id.expect_local()));
+ }
+
+ let mut predicates: Vec<_> = predicates.into_iter().collect();
+
+ // Subtle: before we store the predicates into the tcx, we
+ // sort them so that predicates like `T: Foo<Item=U>` come
+ // before uses of `U`. This avoids false ambiguity errors
+ // in trait checking. See `setup_constraining_predicates`
+ // for details.
+ if let Node::Item(&Item { kind: ItemKind::Impl { .. }, .. }) = node {
+ let self_ty = tcx.type_of(def_id);
+ let trait_ref = tcx.impl_trait_ref(def_id);
+ cgp::setup_constraining_predicates(
+ tcx,
+ &mut predicates,
+ trait_ref,
+ &mut cgp::parameters_for_impl(self_ty, trait_ref),
+ );
+ }
+
+ ty::GenericPredicates {
+ parent: generics.parent,
+ predicates: tcx.arena.alloc_from_iter(predicates),
+ }
+}
+
+fn const_evaluatable_predicates_of<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ def_id: LocalDefId,
+) -> FxIndexSet<(ty::Predicate<'tcx>, Span)> {
+ struct ConstCollector<'tcx> {
+ tcx: TyCtxt<'tcx>,
+ preds: FxIndexSet<(ty::Predicate<'tcx>, Span)>,
+ }
+
+ impl<'tcx> intravisit::Visitor<'tcx> for ConstCollector<'tcx> {
+ fn visit_anon_const(&mut self, c: &'tcx hir::AnonConst) {
+ let def_id = self.tcx.hir().local_def_id(c.hir_id);
+ let ct = ty::Const::from_anon_const(self.tcx, def_id);
+ if let ty::ConstKind::Unevaluated(_) = ct.kind() {
+ let span = self.tcx.hir().span(c.hir_id);
+ self.preds.insert((
+ ty::Binder::dummy(ty::PredicateKind::ConstEvaluatable(ct))
+ .to_predicate(self.tcx),
+ span,
+ ));
+ }
+ }
+
+ fn visit_const_param_default(&mut self, _param: HirId, _ct: &'tcx hir::AnonConst) {
+ // Do not look into const param defaults,
+ // these get checked when they are actually instantiated.
+ //
+ // We do not want the following to error:
+ //
+ // struct Foo<const N: usize, const M: usize = { N + 1 }>;
+ // struct Bar<const N: usize>(Foo<N, 3>);
+ }
+ }
+
+ let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
+ let node = tcx.hir().get(hir_id);
+
+ let mut collector = ConstCollector { tcx, preds: FxIndexSet::default() };
+ if let hir::Node::Item(item) = node && let hir::ItemKind::Impl(ref impl_) = item.kind {
+ if let Some(of_trait) = &impl_.of_trait {
+ debug!("const_evaluatable_predicates_of({:?}): visit impl trait_ref", def_id);
+ collector.visit_trait_ref(of_trait);
+ }
+
+ debug!("const_evaluatable_predicates_of({:?}): visit_self_ty", def_id);
+ collector.visit_ty(impl_.self_ty);
+ }
+
+ if let Some(generics) = node.generics() {
+ debug!("const_evaluatable_predicates_of({:?}): visit_generics", def_id);
+ collector.visit_generics(generics);
+ }
+
+ if let Some(fn_sig) = tcx.hir().fn_sig_by_hir_id(hir_id) {
+ debug!("const_evaluatable_predicates_of({:?}): visit_fn_decl", def_id);
+ collector.visit_fn_decl(fn_sig.decl);
+ }
+ debug!("const_evaluatable_predicates_of({:?}) = {:?}", def_id, collector.preds);
+
+ collector.preds
+}
+
+pub(super) fn trait_explicit_predicates_and_bounds(
+ tcx: TyCtxt<'_>,
+ def_id: LocalDefId,
+) -> ty::GenericPredicates<'_> {
+ assert_eq!(tcx.def_kind(def_id), DefKind::Trait);
+ gather_explicit_predicates_of(tcx, def_id.to_def_id())
+}
+
+pub(super) fn explicit_predicates_of<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ def_id: DefId,
+) -> ty::GenericPredicates<'tcx> {
+ let def_kind = tcx.def_kind(def_id);
+ if let DefKind::Trait = def_kind {
+ // Remove bounds on associated types from the predicates, they will be
+ // returned by `explicit_item_bounds`.
+ let predicates_and_bounds = tcx.trait_explicit_predicates_and_bounds(def_id.expect_local());
+ let trait_identity_substs = InternalSubsts::identity_for_item(tcx, def_id);
+
+ let is_assoc_item_ty = |ty: Ty<'tcx>| {
+ // For a predicate from a where clause to become a bound on an
+ // associated type:
+ // * It must use the identity substs of the item.
+ // * Since any generic parameters on the item are not in scope,
+ // this means that the item is not a GAT, and its identity
+ // substs are the same as the trait's.
+ // * It must be an associated type for this trait (*not* a
+ // supertrait).
+ if let ty::Projection(projection) = ty.kind() {
+ projection.substs == trait_identity_substs
+ && tcx.associated_item(projection.item_def_id).container_id(tcx) == def_id
+ } else {
+ false
+ }
+ };
+
+ let predicates: Vec<_> = predicates_and_bounds
+ .predicates
+ .iter()
+ .copied()
+ .filter(|(pred, _)| match pred.kind().skip_binder() {
+ ty::PredicateKind::Trait(tr) => !is_assoc_item_ty(tr.self_ty()),
+ ty::PredicateKind::Projection(proj) => {
+ !is_assoc_item_ty(proj.projection_ty.self_ty())
+ }
+ ty::PredicateKind::TypeOutlives(outlives) => !is_assoc_item_ty(outlives.0),
+ _ => true,
+ })
+ .collect();
+ if predicates.len() == predicates_and_bounds.predicates.len() {
+ predicates_and_bounds
+ } else {
+ ty::GenericPredicates {
+ parent: predicates_and_bounds.parent,
+ predicates: tcx.arena.alloc_slice(&predicates),
+ }
+ }
+ } else {
+ if matches!(def_kind, DefKind::AnonConst) && tcx.lazy_normalization() {
+ let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local());
+ if tcx.hir().opt_const_param_default_param_hir_id(hir_id).is_some() {
+ // In `generics_of` we set the generics' parent to be our parent's parent which means that
+ // we lose out on the predicates of our actual parent if we dont return those predicates here.
+ // (See comment in `generics_of` for more information on why the parent shenanigans is necessary)
+ //
+ // struct Foo<T, const N: usize = { <T as Trait>::ASSOC }>(T) where T: Trait;
+ // ^^^ ^^^^^^^^^^^^^^^^^^^^^^^ the def id we are calling
+ // ^^^ explicit_predicates_of on
+ // parent item we dont have set as the
+ // parent of generics returned by `generics_of`
+ //
+ // In the above code we want the anon const to have predicates in its param env for `T: Trait`
+ let item_def_id = tcx.hir().get_parent_item(hir_id);
+ // In the above code example we would be calling `explicit_predicates_of(Foo)` here
+ return tcx.explicit_predicates_of(item_def_id);
+ }
+ }
+ gather_explicit_predicates_of(tcx, def_id)
+ }
+}
+
+/// Ensures that the super-predicates of the trait with a `DefId`
+/// of `trait_def_id` are converted and stored. This also ensures that
+/// the transitive super-predicates are converted.
+pub(super) fn super_predicates_of(
+ tcx: TyCtxt<'_>,
+ trait_def_id: DefId,
+) -> ty::GenericPredicates<'_> {
+ tcx.super_predicates_that_define_assoc_type((trait_def_id, None))
+}
+
+/// Ensures that the super-predicates of the trait with a `DefId`
+/// of `trait_def_id` are converted and stored. This also ensures that
+/// the transitive super-predicates are converted.
+pub(super) fn super_predicates_that_define_assoc_type(
+ tcx: TyCtxt<'_>,
+ (trait_def_id, assoc_name): (DefId, Option<Ident>),
+) -> ty::GenericPredicates<'_> {
+ if trait_def_id.is_local() {
+ debug!("local trait");
+ let trait_hir_id = tcx.hir().local_def_id_to_hir_id(trait_def_id.expect_local());
+
+ let Node::Item(item) = tcx.hir().get(trait_hir_id) else {
+ bug!("trait_node_id {} is not an item", trait_hir_id);
+ };
+
+ let (generics, bounds) = match item.kind {
+ hir::ItemKind::Trait(.., ref generics, ref supertraits, _) => (generics, supertraits),
+ hir::ItemKind::TraitAlias(ref generics, ref supertraits) => (generics, supertraits),
+ _ => span_bug!(item.span, "super_predicates invoked on non-trait"),
+ };
+
+ let icx = ItemCtxt::new(tcx, trait_def_id);
+
+ // Convert the bounds that follow the colon, e.g., `Bar + Zed` in `trait Foo: Bar + Zed`.
+ let self_param_ty = tcx.types.self_param;
+ let superbounds1 = if let Some(assoc_name) = assoc_name {
+ <dyn AstConv<'_>>::compute_bounds_that_match_assoc_type(
+ &icx,
+ self_param_ty,
+ bounds,
+ assoc_name,
+ )
+ } else {
+ <dyn AstConv<'_>>::compute_bounds(&icx, self_param_ty, bounds)
+ };
+
+ let superbounds1 = superbounds1.predicates(tcx, self_param_ty);
+
+ // Convert any explicit superbounds in the where-clause,
+ // e.g., `trait Foo where Self: Bar`.
+ // In the case of trait aliases, however, we include all bounds in the where-clause,
+ // so e.g., `trait Foo = where u32: PartialEq<Self>` would include `u32: PartialEq<Self>`
+ // as one of its "superpredicates".
+ let is_trait_alias = tcx.is_trait_alias(trait_def_id);
+ let superbounds2 = icx.type_parameter_bounds_in_generics(
+ generics,
+ item.hir_id(),
+ self_param_ty,
+ OnlySelfBounds(!is_trait_alias),
+ assoc_name,
+ );
+
+ // Combine the two lists to form the complete set of superbounds:
+ let superbounds = &*tcx.arena.alloc_from_iter(superbounds1.into_iter().chain(superbounds2));
+ debug!(?superbounds);
+
+ // Now require that immediate supertraits are converted,
+ // which will, in turn, reach indirect supertraits.
+ if assoc_name.is_none() {
+ // Now require that immediate supertraits are converted,
+ // which will, in turn, reach indirect supertraits.
+ for &(pred, span) in superbounds {
+ debug!("superbound: {:?}", pred);
+ if let ty::PredicateKind::Trait(bound) = pred.kind().skip_binder() {
+ tcx.at(span).super_predicates_of(bound.def_id());
+ }
+ }
+ }
+
+ ty::GenericPredicates { parent: None, predicates: superbounds }
+ } else {
+ // if `assoc_name` is None, then the query should've been redirected to an
+ // external provider
+ assert!(assoc_name.is_some());
+ tcx.super_predicates_of(trait_def_id)
+ }
+}
+
+/// Returns the predicates defined on `item_def_id` of the form
+/// `X: Foo` where `X` is the type parameter `def_id`.
+#[instrument(level = "trace", skip(tcx))]
+pub(super) fn type_param_predicates(
+ tcx: TyCtxt<'_>,
+ (item_def_id, def_id, assoc_name): (DefId, LocalDefId, Ident),
+) -> ty::GenericPredicates<'_> {
+ use rustc_hir::*;
+
+ // In the AST, bounds can derive from two places. Either
+ // written inline like `<T: Foo>` or in a where-clause like
+ // `where T: Foo`.
+
+ let param_id = tcx.hir().local_def_id_to_hir_id(def_id);
+ let param_owner = tcx.hir().ty_param_owner(def_id);
+ let generics = tcx.generics_of(param_owner);
+ let index = generics.param_def_id_to_index[&def_id.to_def_id()];
+ let ty = tcx.mk_ty_param(index, tcx.hir().ty_param_name(def_id));
+
+ // Don't look for bounds where the type parameter isn't in scope.
+ let parent = if item_def_id == param_owner.to_def_id() {
+ None
+ } else {
+ tcx.generics_of(item_def_id).parent
+ };
+
+ let mut result = parent
+ .map(|parent| {
+ let icx = ItemCtxt::new(tcx, parent);
+ icx.get_type_parameter_bounds(DUMMY_SP, def_id.to_def_id(), assoc_name)
+ })
+ .unwrap_or_default();
+ let mut extend = None;
+
+ let item_hir_id = tcx.hir().local_def_id_to_hir_id(item_def_id.expect_local());
+ let ast_generics = match tcx.hir().get(item_hir_id) {
+ Node::TraitItem(item) => &item.generics,
+
+ Node::ImplItem(item) => &item.generics,
+
+ Node::Item(item) => {
+ match item.kind {
+ ItemKind::Fn(.., ref generics, _)
+ | ItemKind::Impl(hir::Impl { ref generics, .. })
+ | ItemKind::TyAlias(_, ref generics)
+ | ItemKind::OpaqueTy(OpaqueTy {
+ ref generics,
+ origin: hir::OpaqueTyOrigin::TyAlias,
+ ..
+ })
+ | ItemKind::Enum(_, ref generics)
+ | ItemKind::Struct(_, ref generics)
+ | ItemKind::Union(_, ref generics) => generics,
+ ItemKind::Trait(_, _, ref generics, ..) => {
+ // Implied `Self: Trait` and supertrait bounds.
+ if param_id == item_hir_id {
+ let identity_trait_ref = ty::TraitRef::identity(tcx, item_def_id);
+ extend =
+ Some((identity_trait_ref.without_const().to_predicate(tcx), item.span));
+ }
+ generics
+ }
+ _ => return result,
+ }
+ }
+
+ Node::ForeignItem(item) => match item.kind {
+ ForeignItemKind::Fn(_, _, ref generics) => generics,
+ _ => return result,
+ },
+
+ _ => return result,
+ };
+
+ let icx = ItemCtxt::new(tcx, item_def_id);
+ let extra_predicates = extend.into_iter().chain(
+ icx.type_parameter_bounds_in_generics(
+ ast_generics,
+ param_id,
+ ty,
+ OnlySelfBounds(true),
+ Some(assoc_name),
+ )
+ .into_iter()
+ .filter(|(predicate, _)| match predicate.kind().skip_binder() {
+ ty::PredicateKind::Trait(data) => data.self_ty().is_param(index),
+ _ => false,
+ }),
+ );
+ result.predicates =
+ tcx.arena.alloc_from_iter(result.predicates.iter().copied().chain(extra_predicates));
+ result
+}
+
+impl<'tcx> ItemCtxt<'tcx> {
+ /// Finds bounds from `hir::Generics`. This requires scanning through the
+ /// AST. We do this to avoid having to convert *all* the bounds, which
+ /// would create artificial cycles. Instead, we can only convert the
+ /// bounds for a type parameter `X` if `X::Foo` is used.
+ #[instrument(level = "trace", skip(self, ast_generics))]
+ fn type_parameter_bounds_in_generics(
+ &self,
+ ast_generics: &'tcx hir::Generics<'tcx>,
+ param_id: hir::HirId,
+ ty: Ty<'tcx>,
+ only_self_bounds: OnlySelfBounds,
+ assoc_name: Option<Ident>,
+ ) -> Vec<(ty::Predicate<'tcx>, Span)> {
+ let param_def_id = self.tcx.hir().local_def_id(param_id).to_def_id();
+ trace!(?param_def_id);
+ ast_generics
+ .predicates
+ .iter()
+ .filter_map(|wp| match *wp {
+ hir::WherePredicate::BoundPredicate(ref bp) => Some(bp),
+ _ => None,
+ })
+ .flat_map(|bp| {
+ let bt = if bp.is_param_bound(param_def_id) {
+ Some(ty)
+ } else if !only_self_bounds.0 {
+ Some(self.to_ty(bp.bounded_ty))
+ } else {
+ None
+ };
+ let bvars = self.tcx.late_bound_vars(bp.hir_id);
+
+ bp.bounds.iter().filter_map(move |b| bt.map(|bt| (bt, b, bvars))).filter(
+ |(_, b, _)| match assoc_name {
+ Some(assoc_name) => self.bound_defines_assoc_item(b, assoc_name),
+ None => true,
+ },
+ )
+ })
+ .flat_map(|(bt, b, bvars)| predicates_from_bound(self, bt, b, bvars))
+ .collect()
+ }
+
+ #[instrument(level = "trace", skip(self))]
+ fn bound_defines_assoc_item(&self, b: &hir::GenericBound<'_>, assoc_name: Ident) -> bool {
+ match b {
+ hir::GenericBound::Trait(poly_trait_ref, _) => {
+ let trait_ref = &poly_trait_ref.trait_ref;
+ if let Some(trait_did) = trait_ref.trait_def_id() {
+ self.tcx.trait_may_define_assoc_type(trait_did, assoc_name)
+ } else {
+ false
+ }
+ }
+ _ => false,
+ }
+ }
+}
+
+/// Converts a specific `GenericBound` from the AST into a set of
+/// predicates that apply to the self type. A vector is returned
+/// because this can be anywhere from zero predicates (`T: ?Sized` adds no
+/// predicates) to one (`T: Foo`) to many (`T: Bar<X = i32>` adds `T: Bar`
+/// and `<T as Bar>::X == i32`).
+fn predicates_from_bound<'tcx>(
+ astconv: &dyn AstConv<'tcx>,
+ param_ty: Ty<'tcx>,
+ bound: &'tcx hir::GenericBound<'tcx>,
+ bound_vars: &'tcx ty::List<ty::BoundVariableKind>,
+) -> Vec<(ty::Predicate<'tcx>, Span)> {
+ let mut bounds = Bounds::default();
+ astconv.add_bounds(param_ty, [bound].into_iter(), &mut bounds, bound_vars);
+ bounds.predicates(astconv.tcx(), param_ty).collect()
+}
diff --git a/compiler/rustc_typeck/src/collect/type_of.rs b/compiler/rustc_hir_analysis/src/collect/type_of.rs
index 534ddfa95..c29a645eb 100644
--- a/compiler/rustc_typeck/src/collect/type_of.rs
+++ b/compiler/rustc_hir_analysis/src/collect/type_of.rs
@@ -1,6 +1,5 @@
use rustc_errors::{Applicability, StashKey};
use rustc_hir as hir;
-use rustc_hir::def::Res;
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::intravisit;
use rustc_hir::intravisit::Visitor;
@@ -19,7 +18,6 @@ use crate::errors::UnconstrainedOpaqueType;
/// Computes the relevant generic parameter for a potential generic const argument.
///
/// This should be called using the query `tcx.opt_const_param_of`.
-#[instrument(level = "debug", skip(tcx))]
pub(super) fn opt_const_param_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<DefId> {
use hir::*;
let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
@@ -67,8 +65,8 @@ pub(super) fn opt_const_param_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<
let ty = item_ctxt.ast_ty_to_ty(hir_ty);
// Iterate through the generics of the projection to find the one that corresponds to
- // the def_id that this query was called with. We filter to only const args here as a
- // precaution for if it's ever allowed to elide lifetimes in GAT's. It currently isn't
+ // the def_id that this query was called with. We filter to only type and const args here
+ // as a precaution for if it's ever allowed to elide lifetimes in GAT's. It currently isn't
// but it can't hurt to be safe ^^
if let ty::Projection(projection) = ty.kind() {
let generics = tcx.generics_of(projection.item_def_id);
@@ -79,7 +77,7 @@ pub(super) fn opt_const_param_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<
args.args
.iter()
.filter(|arg| arg.is_ty_or_const())
- .position(|arg| arg.id() == hir_id)
+ .position(|arg| arg.hir_id() == hir_id)
})
.unwrap_or_else(|| {
bug!("no arg matching AnonConst in segment");
@@ -112,7 +110,7 @@ pub(super) fn opt_const_param_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<
args.args
.iter()
.filter(|arg| arg.is_ty_or_const())
- .position(|arg| arg.id() == hir_id)
+ .position(|arg| arg.hir_id() == hir_id)
})
.unwrap_or_else(|| {
bug!("no arg matching AnonConst in segment");
@@ -166,7 +164,7 @@ pub(super) fn opt_const_param_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<
args.args
.iter()
.filter(|arg| arg.is_ty_or_const())
- .position(|arg| arg.id() == hir_id)
+ .position(|arg| arg.hir_id() == hir_id)
.map(|index| (index, seg)).or_else(|| args.bindings
.iter()
.filter_map(TypeBinding::opt_const)
@@ -180,15 +178,12 @@ pub(super) fn opt_const_param_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Option<
return None;
};
- // Try to use the segment resolution if it is valid, otherwise we
- // default to the path resolution.
- let res = segment.res.filter(|&r| r != Res::Err).unwrap_or(path.res);
- let generics = match tcx.res_generics_def_id(res) {
+ let generics = match tcx.res_generics_def_id(segment.res) {
Some(def_id) => tcx.generics_of(def_id),
None => {
tcx.sess.delay_span_bug(
tcx.def_span(def_id),
- &format!("unexpected anon const res {:?} in path: {:?}", res, path),
+ &format!("unexpected anon const res {:?} in path: {:?}", segment.res, path),
);
return None;
}
@@ -229,7 +224,7 @@ fn get_path_containing_arg_in_pat<'hir>(
.iter()
.filter_map(|seg| seg.args)
.flat_map(|args| args.args)
- .any(|arg| arg.id() == arg_id)
+ .any(|arg| arg.hir_id() == arg_id)
};
let mut arg_path = None;
pat.walk(|pat| match pat.kind {
@@ -289,7 +284,7 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> {
icx.to_ty(ty)
}
}
- ImplItemKind::TyAlias(ty) => {
+ ImplItemKind::Type(ty) => {
if tcx.impl_trait_ref(tcx.hir().get_parent_item(hir_id)).is_none() {
check_feature_inherent_assoc_ty(tcx, item.span);
}
@@ -324,7 +319,15 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> {
}
}
ItemKind::TyAlias(self_ty, _) => icx.to_ty(self_ty),
- ItemKind::Impl(hir::Impl { self_ty, .. }) => icx.to_ty(*self_ty),
+ ItemKind::Impl(hir::Impl { self_ty, .. }) => {
+ match self_ty.find_self_aliases() {
+ spans if spans.len() > 0 => {
+ tcx.sess.emit_err(crate::errors::SelfInImplSelf { span: spans.into(), note: (), });
+ tcx.ty_error()
+ },
+ _ => icx.to_ty(*self_ty),
+ }
+ },
ItemKind::Fn(..) => {
let substs = InternalSubsts::identity_for_item(tcx, def_id.to_def_id());
tcx.mk_fn_def(def_id.to_def_id(), substs)
@@ -338,7 +341,15 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> {
find_opaque_ty_constraints_for_tait(tcx, def_id)
}
// Opaque types desugared from `impl Trait`.
- ItemKind::OpaqueTy(OpaqueTy { origin: hir::OpaqueTyOrigin::FnReturn(owner) | hir::OpaqueTyOrigin::AsyncFn(owner), .. }) => {
+ ItemKind::OpaqueTy(OpaqueTy {
+ origin:
+ hir::OpaqueTyOrigin::FnReturn(owner) | hir::OpaqueTyOrigin::AsyncFn(owner),
+ in_trait,
+ ..
+ }) => {
+ if in_trait {
+ assert!(tcx.impl_defaultness(owner).has_value());
+ }
find_opaque_ty_constraints_for_rpit(tcx, def_id, owner)
}
ItemKind::Trait(..)
@@ -379,7 +390,9 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> {
Node::Field(field) => icx.to_ty(field.ty),
- Node::Expr(&Expr { kind: ExprKind::Closure{..}, .. }) => tcx.typeck(def_id).node_type(hir_id),
+ Node::Expr(&Expr { kind: ExprKind::Closure { .. }, .. }) => {
+ tcx.typeck(def_id).node_type(hir_id)
+ }
Node::AnonConst(_) if let Some(param) = tcx.opt_const_param_of(def_id) => {
// We defer to `type_of` of the corresponding parameter
@@ -411,40 +424,93 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> {
| Node::Item(&Item { kind: ItemKind::GlobalAsm(asm), .. })
if asm.operands.iter().any(|(op, _op_sp)| match op {
hir::InlineAsmOperand::Const { anon_const }
- | hir::InlineAsmOperand::SymFn { anon_const } => anon_const.hir_id == hir_id,
+ | hir::InlineAsmOperand::SymFn { anon_const } => {
+ anon_const.hir_id == hir_id
+ }
_ => false,
}) =>
{
tcx.typeck(def_id).node_type(hir_id)
}
- Node::Variant(Variant { disr_expr: Some(ref e), .. }) if e.hir_id == hir_id => tcx
- .adt_def(tcx.hir().get_parent_item(hir_id))
- .repr()
- .discr_type()
- .to_ty(tcx),
+ Node::Variant(Variant { disr_expr: Some(ref e), .. }) if e.hir_id == hir_id => {
+ tcx.adt_def(tcx.hir().get_parent_item(hir_id)).repr().discr_type().to_ty(tcx)
+ }
- Node::TypeBinding(binding @ &TypeBinding { hir_id: binding_id, .. })
- if let Node::TraitRef(trait_ref) = tcx.hir().get(
- tcx.hir().get_parent_node(binding_id)
- ) =>
+ Node::TypeBinding(
+ binding @ &TypeBinding {
+ hir_id: binding_id,
+ kind: TypeBindingKind::Equality { term: Term::Const(ref e) },
+ ..
+ },
+ ) if let Node::TraitRef(trait_ref) =
+ tcx.hir().get(tcx.hir().get_parent_node(binding_id))
+ && e.hir_id == hir_id =>
{
- let Some(trait_def_id) = trait_ref.trait_def_id() else {
- return tcx.ty_error_with_message(DUMMY_SP, "Could not find trait");
- };
- let assoc_items = tcx.associated_items(trait_def_id);
- let assoc_item = assoc_items.find_by_name_and_kind(
- tcx, binding.ident, ty::AssocKind::Const, def_id.to_def_id(),
- );
- if let Some(assoc_item) = assoc_item {
- tcx.type_of(assoc_item.def_id)
- } else {
- // FIXME(associated_const_equality): add a useful error message here.
- tcx.ty_error_with_message(
- DUMMY_SP,
- "Could not find associated const on trait",
- )
- }
+ let Some(trait_def_id) = trait_ref.trait_def_id() else {
+ return tcx.ty_error_with_message(DUMMY_SP, "Could not find trait");
+ };
+ let assoc_items = tcx.associated_items(trait_def_id);
+ let assoc_item = assoc_items.find_by_name_and_kind(
+ tcx,
+ binding.ident,
+ ty::AssocKind::Const,
+ def_id.to_def_id(),
+ );
+ if let Some(assoc_item) = assoc_item {
+ tcx.type_of(assoc_item.def_id)
+ } else {
+ // FIXME(associated_const_equality): add a useful error message here.
+ tcx.ty_error_with_message(
+ DUMMY_SP,
+ "Could not find associated const on trait",
+ )
+ }
+ }
+
+ Node::TypeBinding(
+ binding @ &TypeBinding { hir_id: binding_id, gen_args, ref kind, .. },
+ ) if let Node::TraitRef(trait_ref) =
+ tcx.hir().get(tcx.hir().get_parent_node(binding_id))
+ && let Some((idx, _)) =
+ gen_args.args.iter().enumerate().find(|(_, arg)| {
+ if let GenericArg::Const(ct) = arg {
+ ct.value.hir_id == hir_id
+ } else {
+ false
+ }
+ }) =>
+ {
+ let Some(trait_def_id) = trait_ref.trait_def_id() else {
+ return tcx.ty_error_with_message(DUMMY_SP, "Could not find trait");
+ };
+ let assoc_items = tcx.associated_items(trait_def_id);
+ let assoc_item = assoc_items.find_by_name_and_kind(
+ tcx,
+ binding.ident,
+ match kind {
+ // I think `<A: T>` type bindings requires that `A` is a type
+ TypeBindingKind::Constraint { .. }
+ | TypeBindingKind::Equality { term: Term::Ty(..) } => {
+ ty::AssocKind::Type
+ }
+ TypeBindingKind::Equality { term: Term::Const(..) } => {
+ ty::AssocKind::Const
+ }
+ },
+ def_id.to_def_id(),
+ );
+ if let Some(param)
+ = assoc_item.map(|item| &tcx.generics_of(item.def_id).params[idx]).filter(|param| param.kind.is_ty_or_const())
+ {
+ tcx.type_of(param.def_id)
+ } else {
+ // FIXME(associated_const_equality): add a useful error message here.
+ tcx.ty_error_with_message(
+ DUMMY_SP,
+ "Could not find associated const on trait",
+ )
+ }
}
Node::GenericParam(&GenericParam {
@@ -453,8 +519,7 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> {
..
}) if ct.hir_id == hir_id => tcx.type_of(tcx.hir().local_def_id(param_hir_id)),
- x =>
- tcx.ty_error_with_message(
+ x => tcx.ty_error_with_message(
DUMMY_SP,
&format!("unexpected const parent in type_of(): {x:?}"),
),
@@ -508,6 +573,11 @@ fn find_opaque_ty_constraints_for_tait(tcx: TyCtxt<'_>, def_id: LocalDefId) -> T
/// checked against it (we also carry the span of that first
/// type).
found: Option<ty::OpaqueHiddenType<'tcx>>,
+
+ /// In the presence of dead code, typeck may figure out a hidden type
+ /// while borrowck will now. We collect these cases here and check at
+ /// the end that we actually found a type that matches (modulo regions).
+ typeck_types: Vec<ty::OpaqueHiddenType<'tcx>>,
}
impl ConstraintLocator<'_> {
@@ -534,18 +604,23 @@ fn find_opaque_ty_constraints_for_tait(tcx: TyCtxt<'_>, def_id: LocalDefId) -> T
self.found = Some(ty::OpaqueHiddenType { span: DUMMY_SP, ty: self.tcx.ty_error() });
return;
}
- if !tables.concrete_opaque_types.contains_key(&self.def_id) {
+ let Some(&typeck_hidden_ty) = tables.concrete_opaque_types.get(&self.def_id) else {
debug!("no constraints in typeck results");
return;
+ };
+ if self.typeck_types.iter().all(|prev| prev.ty != typeck_hidden_ty.ty) {
+ self.typeck_types.push(typeck_hidden_ty);
}
+
// Use borrowck to get the type with unerased regions.
let concrete_opaque_types = &self.tcx.mir_borrowck(item_def_id).concrete_opaque_types;
debug!(?concrete_opaque_types);
if let Some(&concrete_type) = concrete_opaque_types.get(&self.def_id) {
debug!(?concrete_type, "found constraint");
- if let Some(prev) = self.found {
- if concrete_type.ty != prev.ty && !(concrete_type, prev).references_error() {
+ if let Some(prev) = &mut self.found {
+ if concrete_type.ty != prev.ty && !(concrete_type, prev.ty).references_error() {
prev.report_mismatch(&concrete_type, self.tcx);
+ prev.ty = self.tcx.ty_error();
}
} else {
self.found = Some(concrete_type);
@@ -568,31 +643,31 @@ fn find_opaque_ty_constraints_for_tait(tcx: TyCtxt<'_>, def_id: LocalDefId) -> T
intravisit::walk_expr(self, ex);
}
fn visit_item(&mut self, it: &'tcx Item<'tcx>) {
- trace!(?it.def_id);
+ trace!(?it.owner_id);
// The opaque type itself or its children are not within its reveal scope.
- if it.def_id != self.def_id {
- self.check(it.def_id);
+ if it.owner_id.def_id != self.def_id {
+ self.check(it.owner_id.def_id);
intravisit::walk_item(self, it);
}
}
fn visit_impl_item(&mut self, it: &'tcx ImplItem<'tcx>) {
- trace!(?it.def_id);
+ trace!(?it.owner_id);
// The opaque type itself or its children are not within its reveal scope.
- if it.def_id != self.def_id {
- self.check(it.def_id);
+ if it.owner_id.def_id != self.def_id {
+ self.check(it.owner_id.def_id);
intravisit::walk_impl_item(self, it);
}
}
fn visit_trait_item(&mut self, it: &'tcx TraitItem<'tcx>) {
- trace!(?it.def_id);
- self.check(it.def_id);
+ trace!(?it.owner_id);
+ self.check(it.owner_id.def_id);
intravisit::walk_trait_item(self, it);
}
}
let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
let scope = tcx.hir().get_defining_scope(hir_id);
- let mut locator = ConstraintLocator { def_id: def_id, tcx, found: None };
+ let mut locator = ConstraintLocator { def_id: def_id, tcx, found: None, typeck_types: vec![] };
debug!(?scope);
@@ -622,16 +697,32 @@ fn find_opaque_ty_constraints_for_tait(tcx: TyCtxt<'_>, def_id: LocalDefId) -> T
}
}
- match locator.found {
- Some(hidden) => hidden.ty,
- None => {
- tcx.sess.emit_err(UnconstrainedOpaqueType {
- span: tcx.def_span(def_id),
- name: tcx.item_name(tcx.local_parent(def_id).to_def_id()),
- });
- tcx.ty_error()
+ let Some(hidden) = locator.found else {
+ tcx.sess.emit_err(UnconstrainedOpaqueType {
+ span: tcx.def_span(def_id),
+ name: tcx.item_name(tcx.local_parent(def_id).to_def_id()),
+ what: match tcx.hir().get(scope) {
+ _ if scope == hir::CRATE_HIR_ID => "module",
+ Node::Item(hir::Item { kind: hir::ItemKind::Mod(_), .. }) => "module",
+ Node::Item(hir::Item { kind: hir::ItemKind::Impl(_), .. }) => "impl",
+ _ => "item",
+ },
+ });
+ return tcx.ty_error();
+ };
+
+ // Only check against typeck if we didn't already error
+ if !hidden.ty.references_error() {
+ for concrete_type in locator.typeck_types {
+ if tcx.erase_regions(concrete_type.ty) != tcx.erase_regions(hidden.ty)
+ && !(concrete_type, hidden).references_error()
+ {
+ hidden.report_mismatch(&concrete_type, tcx);
+ }
}
}
+
+ hidden.ty
}
fn find_opaque_ty_constraints_for_rpit(
@@ -687,24 +778,24 @@ fn find_opaque_ty_constraints_for_rpit(
intravisit::walk_expr(self, ex);
}
fn visit_item(&mut self, it: &'tcx Item<'tcx>) {
- trace!(?it.def_id);
+ trace!(?it.owner_id);
// The opaque type itself or its children are not within its reveal scope.
- if it.def_id != self.def_id {
- self.check(it.def_id);
+ if it.owner_id.def_id != self.def_id {
+ self.check(it.owner_id.def_id);
intravisit::walk_item(self, it);
}
}
fn visit_impl_item(&mut self, it: &'tcx ImplItem<'tcx>) {
- trace!(?it.def_id);
+ trace!(?it.owner_id);
// The opaque type itself or its children are not within its reveal scope.
- if it.def_id != self.def_id {
- self.check(it.def_id);
+ if it.owner_id.def_id != self.def_id {
+ self.check(it.owner_id.def_id);
intravisit::walk_impl_item(self, it);
}
}
fn visit_trait_item(&mut self, it: &'tcx TraitItem<'tcx>) {
- trace!(?it.def_id);
- self.check(it.def_id);
+ trace!(?it.owner_id);
+ self.check(it.owner_id.def_id);
intravisit::walk_trait_item(self, it);
}
}
@@ -732,20 +823,15 @@ fn find_opaque_ty_constraints_for_rpit(
// the `concrete_opaque_types` table.
tcx.ty_error()
} else {
- table
- .concrete_opaque_types
- .get(&def_id)
- .copied()
- .unwrap_or_else(|| {
- // We failed to resolve the opaque type or it
- // resolves to itself. We interpret this as the
- // no values of the hidden type ever being constructed,
- // so we can just make the hidden type be `!`.
- // For backwards compatibility reasons, we fall back to
- // `()` until we the diverging default is changed.
- Some(tcx.mk_diverging_default())
- })
- .expect("RPIT always have a hidden type from typeck")
+ table.concrete_opaque_types.get(&def_id).map(|ty| ty.ty).unwrap_or_else(|| {
+ // We failed to resolve the opaque type or it
+ // resolves to itself. We interpret this as the
+ // no values of the hidden type ever being constructed,
+ // so we can just make the hidden type be `!`.
+ // For backwards compatibility reasons, we fall back to
+ // `()` until we the diverging default is changed.
+ tcx.mk_diverging_default()
+ })
}
})
}
@@ -801,6 +887,9 @@ fn infer_placeholder_type<'a>(
match tcx.sess.diagnostic().steal_diagnostic(span, StashKey::ItemNoType) {
Some(mut err) => {
if !ty.references_error() {
+ // Only suggest adding `:` if it was missing (and suggested by parsing diagnostic)
+ let colon = if span == item_ident.span.shrink_to_hi() { ":" } else { "" };
+
// 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.
// FIXME(eddyb) this looks like it should be functionality on `Diagnostic`.
@@ -816,7 +905,7 @@ fn infer_placeholder_type<'a>(
err.span_suggestion(
span,
&format!("provide a type for the {item}", item = kind),
- format!("{}: {}", item_ident, sugg_ty),
+ format!("{colon} {sugg_ty}"),
Applicability::MachineApplicable,
);
} else {
diff --git a/compiler/rustc_typeck/src/constrained_generic_params.rs b/compiler/rustc_hir_analysis/src/constrained_generic_params.rs
index 8428e4664..213b89fc7 100644
--- a/compiler/rustc_typeck/src/constrained_generic_params.rs
+++ b/compiler/rustc_hir_analysis/src/constrained_generic_params.rs
@@ -114,9 +114,9 @@ pub fn identify_constrained_generic_params<'tcx>(
/// ```
/// The impl's predicates are collected from left to right. Ignoring
/// the implicit `Sized` bounds, these are
-/// * T: Debug
-/// * U: Iterator
-/// * <U as Iterator>::Item = T -- a desugared ProjectionPredicate
+/// * `T: Debug`
+/// * `U: Iterator`
+/// * `<U as Iterator>::Item = T` -- a desugared ProjectionPredicate
///
/// When we, for example, try to go over the trait-reference
/// `IntoIter<u32> as Trait`, we substitute the impl parameters with fresh
@@ -132,12 +132,16 @@ pub fn identify_constrained_generic_params<'tcx>(
///
/// We *do* have to be somewhat careful when projection targets contain
/// projections themselves, for example in
+///
+/// ```ignore (illustrative)
/// impl<S,U,V,W> Trait for U where
/// /* 0 */ S: Iterator<Item = U>,
/// /* - */ U: Iterator,
/// /* 1 */ <U as Iterator>::Item: ToOwned<Owned=(W,<V as Iterator>::Item)>
/// /* 2 */ W: Iterator<Item = V>
/// /* 3 */ V: Debug
+/// ```
+///
/// we have to evaluate the projections in the order I wrote them:
/// `V: Debug` requires `V` to be evaluated. The only projection that
/// *determines* `V` is 2 (1 contains it, but *does not determine it*,
diff --git a/compiler/rustc_hir_analysis/src/errors.rs b/compiler/rustc_hir_analysis/src/errors.rs
new file mode 100644
index 000000000..d5b1a7ce1
--- /dev/null
+++ b/compiler/rustc_hir_analysis/src/errors.rs
@@ -0,0 +1,282 @@
+//! Errors emitted by `rustc_hir_analysis`.
+
+use rustc_errors::{error_code, Applicability, DiagnosticBuilder, ErrorGuaranteed, Handler};
+use rustc_errors::{IntoDiagnostic, MultiSpan};
+use rustc_macros::{Diagnostic, LintDiagnostic};
+use rustc_middle::ty::Ty;
+use rustc_span::{symbol::Ident, Span, Symbol};
+
+#[derive(Diagnostic)]
+#[diag(hir_analysis_unrecognized_atomic_operation, code = "E0092")]
+pub struct UnrecognizedAtomicOperation<'a> {
+ #[primary_span]
+ #[label]
+ pub span: Span,
+ pub op: &'a str,
+}
+
+#[derive(Diagnostic)]
+#[diag(hir_analysis_wrong_number_of_generic_arguments_to_intrinsic, code = "E0094")]
+pub struct WrongNumberOfGenericArgumentsToIntrinsic<'a> {
+ #[primary_span]
+ #[label]
+ pub span: Span,
+ pub found: usize,
+ pub expected: usize,
+ pub descr: &'a str,
+}
+
+#[derive(Diagnostic)]
+#[diag(hir_analysis_unrecognized_intrinsic_function, code = "E0093")]
+pub struct UnrecognizedIntrinsicFunction {
+ #[primary_span]
+ #[label]
+ pub span: Span,
+ pub name: Symbol,
+}
+
+#[derive(Diagnostic)]
+#[diag(hir_analysis_lifetimes_or_bounds_mismatch_on_trait, code = "E0195")]
+pub struct LifetimesOrBoundsMismatchOnTrait {
+ #[primary_span]
+ #[label]
+ pub span: Span,
+ #[label(generics_label)]
+ pub generics_span: Option<Span>,
+ pub item_kind: &'static str,
+ pub ident: Ident,
+}
+
+#[derive(Diagnostic)]
+#[diag(hir_analysis_drop_impl_on_wrong_item, code = "E0120")]
+pub struct DropImplOnWrongItem {
+ #[primary_span]
+ #[label]
+ pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(hir_analysis_field_already_declared, code = "E0124")]
+pub struct FieldAlreadyDeclared {
+ pub field_name: Ident,
+ #[primary_span]
+ #[label]
+ pub span: Span,
+ #[label(previous_decl_label)]
+ pub prev_span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(hir_analysis_copy_impl_on_type_with_dtor, code = "E0184")]
+pub struct CopyImplOnTypeWithDtor {
+ #[primary_span]
+ #[label]
+ pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(hir_analysis_multiple_relaxed_default_bounds, code = "E0203")]
+pub struct MultipleRelaxedDefaultBounds {
+ #[primary_span]
+ pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(hir_analysis_copy_impl_on_non_adt, code = "E0206")]
+pub struct CopyImplOnNonAdt {
+ #[primary_span]
+ #[label]
+ pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(hir_analysis_trait_object_declared_with_no_traits, code = "E0224")]
+pub struct TraitObjectDeclaredWithNoTraits {
+ #[primary_span]
+ pub span: Span,
+ #[label(alias_span)]
+ pub trait_alias_span: Option<Span>,
+}
+
+#[derive(Diagnostic)]
+#[diag(hir_analysis_ambiguous_lifetime_bound, code = "E0227")]
+pub struct AmbiguousLifetimeBound {
+ #[primary_span]
+ pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(hir_analysis_assoc_type_binding_not_allowed, code = "E0229")]
+pub struct AssocTypeBindingNotAllowed {
+ #[primary_span]
+ #[label]
+ pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(hir_analysis_typeof_reserved_keyword_used, code = "E0516")]
+pub struct TypeofReservedKeywordUsed<'tcx> {
+ pub ty: Ty<'tcx>,
+ #[primary_span]
+ #[label]
+ pub span: Span,
+ #[suggestion_verbose(code = "{ty}")]
+ pub opt_sugg: Option<(Span, Applicability)>,
+}
+
+#[derive(Diagnostic)]
+#[diag(hir_analysis_value_of_associated_struct_already_specified, code = "E0719")]
+pub struct ValueOfAssociatedStructAlreadySpecified {
+ #[primary_span]
+ #[label]
+ pub span: Span,
+ #[label(previous_bound_label)]
+ pub prev_span: Span,
+ pub item_name: Ident,
+ pub def_path: String,
+}
+
+#[derive(Diagnostic)]
+#[diag(hir_analysis_unconstrained_opaque_type)]
+#[note]
+pub struct UnconstrainedOpaqueType {
+ #[primary_span]
+ pub span: Span,
+ pub name: Symbol,
+ pub what: &'static str,
+}
+
+pub struct MissingTypeParams {
+ pub span: Span,
+ pub def_span: Span,
+ pub span_snippet: Option<String>,
+ pub missing_type_params: Vec<Symbol>,
+ pub empty_generic_args: bool,
+}
+
+// Manual implementation of `IntoDiagnostic` to be able to call `span_to_snippet`.
+impl<'a> IntoDiagnostic<'a> for MissingTypeParams {
+ fn into_diagnostic(self, handler: &'a Handler) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
+ let mut err = handler.struct_span_err_with_code(
+ self.span,
+ rustc_errors::fluent::hir_analysis_missing_type_params,
+ error_code!(E0393),
+ );
+ err.set_arg("parameterCount", self.missing_type_params.len());
+ err.set_arg(
+ "parameters",
+ self.missing_type_params
+ .iter()
+ .map(|n| format!("`{}`", n))
+ .collect::<Vec<_>>()
+ .join(", "),
+ );
+
+ err.span_label(self.def_span, rustc_errors::fluent::label);
+
+ let mut suggested = false;
+ // Don't suggest setting the type params if there are some already: the order is
+ // tricky to get right and the user will already know what the syntax is.
+ if let Some(snippet) = self.span_snippet && self.empty_generic_args {
+ if snippet.ends_with('>') {
+ // The user wrote `Trait<'a, T>` or similar. To provide an accurate suggestion
+ // we would have to preserve the right order. For now, as clearly the user is
+ // aware of the syntax, we do nothing.
+ } else {
+ // The user wrote `Iterator`, so we don't have a type we can suggest, but at
+ // least we can clue them to the correct syntax `Iterator<Type>`.
+ err.span_suggestion(
+ self.span,
+ rustc_errors::fluent::suggestion,
+ format!(
+ "{}<{}>",
+ snippet,
+ self.missing_type_params
+ .iter()
+ .map(|n| n.to_string())
+ .collect::<Vec<_>>()
+ .join(", ")
+ ),
+ Applicability::HasPlaceholders,
+ );
+ suggested = true;
+ }
+ }
+ if !suggested {
+ err.span_label(self.span, rustc_errors::fluent::no_suggestion_label);
+ }
+
+ err.note(rustc_errors::fluent::note);
+ err
+ }
+}
+
+#[derive(Diagnostic)]
+#[diag(hir_analysis_manual_implementation, code = "E0183")]
+#[help]
+pub struct ManualImplementation {
+ #[primary_span]
+ #[label]
+ pub span: Span,
+ pub trait_name: String,
+}
+
+#[derive(Diagnostic)]
+#[diag(hir_analysis_substs_on_overridden_impl)]
+pub struct SubstsOnOverriddenImpl {
+ #[primary_span]
+ pub span: Span,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(hir_analysis_unused_extern_crate)]
+pub struct UnusedExternCrate {
+ #[suggestion(applicability = "machine-applicable", code = "")]
+ pub span: Span,
+}
+
+#[derive(LintDiagnostic)]
+#[diag(hir_analysis_extern_crate_not_idiomatic)]
+pub struct ExternCrateNotIdiomatic {
+ #[suggestion_short(applicability = "machine-applicable", code = "{suggestion_code}")]
+ pub span: Span,
+ pub msg_code: String,
+ pub suggestion_code: String,
+}
+
+#[derive(Diagnostic)]
+#[diag(hir_analysis_expected_used_symbol)]
+pub struct ExpectedUsedSymbol {
+ #[primary_span]
+ pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(hir_analysis_const_impl_for_non_const_trait)]
+pub struct ConstImplForNonConstTrait {
+ #[primary_span]
+ pub trait_ref_span: Span,
+ pub trait_name: String,
+ #[suggestion(applicability = "machine-applicable", code = "#[const_trait]")]
+ pub local_trait_span: Option<Span>,
+ #[note]
+ pub marking: (),
+ #[note(adding)]
+ pub adding: (),
+}
+
+#[derive(Diagnostic)]
+#[diag(hir_analysis_const_bound_for_non_const_trait)]
+pub struct ConstBoundForNonConstTrait {
+ #[primary_span]
+ pub span: Span,
+}
+
+#[derive(Diagnostic)]
+#[diag(hir_analysis_self_in_impl_self)]
+pub struct SelfInImplSelf {
+ #[primary_span]
+ pub span: MultiSpan,
+ #[note]
+ pub note: (),
+}
diff --git a/compiler/rustc_typeck/src/hir_wf_check.rs b/compiler/rustc_hir_analysis/src/hir_wf_check.rs
index 55c7a15f9..b0fdfcf38 100644
--- a/compiler/rustc_typeck/src/hir_wf_check.rs
+++ b/compiler/rustc_hir_analysis/src/hir_wf_check.rs
@@ -3,11 +3,10 @@ use rustc_hir as hir;
use rustc_hir::intravisit::{self, Visitor};
use rustc_hir::{ForeignItem, ForeignItemKind, HirId};
use rustc_infer::infer::TyCtxtInferExt;
-use rustc_infer::traits::TraitEngine;
use rustc_infer::traits::{ObligationCause, WellFormedLoc};
use rustc_middle::ty::query::Providers;
use rustc_middle::ty::{self, Region, ToPredicate, TyCtxt, TypeFoldable, TypeFolder};
-use rustc_trait_selection::traits::{self, TraitEngineExt};
+use rustc_trait_selection::traits;
pub fn provide(providers: &mut Providers) {
*providers = Providers { diagnostic_hir_wf_check, ..*providers };
@@ -65,41 +64,36 @@ fn diagnostic_hir_wf_check<'tcx>(
impl<'tcx> Visitor<'tcx> for HirWfCheck<'tcx> {
fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx>) {
- self.tcx.infer_ctxt().enter(|infcx| {
- let mut fulfill = <dyn TraitEngine<'tcx>>::new(self.tcx);
- let tcx_ty =
- self.icx.to_ty(ty).fold_with(&mut EraseAllBoundRegions { tcx: self.tcx });
- let cause = traits::ObligationCause::new(
- ty.span,
- self.hir_id,
- traits::ObligationCauseCode::WellFormed(None),
- );
- fulfill.register_predicate_obligation(
- &infcx,
- traits::Obligation::new(
- cause,
- self.param_env,
- ty::Binder::dummy(ty::PredicateKind::WellFormed(tcx_ty.into()))
- .to_predicate(self.tcx),
- ),
- );
-
- let errors = fulfill.select_all_or_error(&infcx);
- if !errors.is_empty() {
- debug!("Wf-check got errors for {:?}: {:?}", ty, errors);
- for error in errors {
- if error.obligation.predicate == self.predicate {
- // Save the cause from the greatest depth - this corresponds
- // to picking more-specific types (e.g. `MyStruct<u8>`)
- // over less-specific types (e.g. `Option<MyStruct<u8>>`)
- if self.depth >= self.cause_depth {
- self.cause = Some(error.obligation.cause);
- self.cause_depth = self.depth
- }
+ let infcx = self.tcx.infer_ctxt().build();
+ let tcx_ty = self.icx.to_ty(ty).fold_with(&mut EraseAllBoundRegions { tcx: self.tcx });
+ let cause = traits::ObligationCause::new(
+ ty.span,
+ self.hir_id,
+ traits::ObligationCauseCode::WellFormed(None),
+ );
+ let errors = traits::fully_solve_obligation(
+ &infcx,
+ traits::Obligation::new(
+ cause,
+ self.param_env,
+ ty::Binder::dummy(ty::PredicateKind::WellFormed(tcx_ty.into()))
+ .to_predicate(self.tcx),
+ ),
+ );
+ if !errors.is_empty() {
+ debug!("Wf-check got errors for {:?}: {:?}", ty, errors);
+ for error in errors {
+ if error.obligation.predicate == self.predicate {
+ // Save the cause from the greatest depth - this corresponds
+ // to picking more-specific types (e.g. `MyStruct<u8>`)
+ // over less-specific types (e.g. `Option<MyStruct<u8>>`)
+ if self.depth >= self.cause_depth {
+ self.cause = Some(error.obligation.cause);
+ self.cause_depth = self.depth
}
}
}
- });
+ }
self.depth += 1;
intravisit::walk_ty(self, ty);
self.depth -= 1;
@@ -123,7 +117,7 @@ fn diagnostic_hir_wf_check<'tcx>(
let ty = match loc {
WellFormedLoc::Ty(_) => match hir.get(hir_id) {
hir::Node::ImplItem(item) => match item.kind {
- hir::ImplItemKind::TyAlias(ty) => Some(ty),
+ hir::ImplItemKind::Type(ty) => Some(ty),
hir::ImplItemKind::Const(ty, _) => Some(ty),
ref item => bug!("Unexpected ImplItem {:?}", item),
},
@@ -144,6 +138,10 @@ fn diagnostic_hir_wf_check<'tcx>(
hir::Node::ForeignItem(ForeignItem {
kind: ForeignItemKind::Static(ty, _), ..
}) => Some(*ty),
+ hir::Node::GenericParam(hir::GenericParam {
+ kind: hir::GenericParamKind::Type { default: Some(ty), .. },
+ ..
+ }) => Some(*ty),
ref node => bug!("Unexpected node {:?}", node),
},
WellFormedLoc::Param { function: _, param_idx } => {
diff --git a/compiler/rustc_typeck/src/impl_wf_check.rs b/compiler/rustc_hir_analysis/src/impl_wf_check.rs
index 9fee1eaae..136f61999 100644
--- a/compiler/rustc_typeck/src/impl_wf_check.rs
+++ b/compiler/rustc_hir_analysis/src/impl_wf_check.rs
@@ -11,7 +11,7 @@
use crate::constrained_generic_params as cgp;
use min_specialization::check_min_specialization;
-use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_data_structures::fx::FxHashSet;
use rustc_errors::struct_span_err;
use rustc_hir::def::DefKind;
use rustc_hir::def_id::LocalDefId;
@@ -19,8 +19,6 @@ use rustc_middle::ty::query::Providers;
use rustc_middle::ty::{self, TyCtxt, TypeVisitable};
use rustc_span::{Span, Symbol};
-use std::collections::hash_map::Entry::{Occupied, Vacant};
-
mod min_specialization;
/// Checks that all the type/lifetime parameters on an impl also
@@ -57,11 +55,10 @@ fn check_mod_impl_wf(tcx: TyCtxt<'_>, module_def_id: LocalDefId) {
let min_specialization = tcx.features().min_specialization;
let module = tcx.hir_module_items(module_def_id);
for id in module.items() {
- if matches!(tcx.def_kind(id.def_id), DefKind::Impl) {
- enforce_impl_params_are_constrained(tcx, id.def_id);
- enforce_impl_items_are_distinct(tcx, id.def_id);
+ if matches!(tcx.def_kind(id.owner_id), DefKind::Impl) {
+ enforce_impl_params_are_constrained(tcx, id.owner_id.def_id);
if min_specialization {
- check_min_specialization(tcx, id.def_id);
+ check_min_specialization(tcx, id.owner_id.def_id);
}
}
}
@@ -194,35 +191,3 @@ fn report_unused_parameter(tcx: TyCtxt<'_>, span: Span, kind: &str, name: Symbol
}
err.emit();
}
-
-/// Enforce that we do not have two items in an impl with the same name.
-fn enforce_impl_items_are_distinct(tcx: TyCtxt<'_>, impl_def_id: LocalDefId) {
- let mut seen_type_items = FxHashMap::default();
- let mut seen_value_items = FxHashMap::default();
- for &impl_item_ref in tcx.associated_item_def_ids(impl_def_id) {
- let impl_item = tcx.associated_item(impl_item_ref);
- let seen_items = match impl_item.kind {
- ty::AssocKind::Type => &mut seen_type_items,
- _ => &mut seen_value_items,
- };
- let span = tcx.def_span(impl_item_ref);
- let ident = impl_item.ident(tcx);
- match seen_items.entry(ident.normalize_to_macros_2_0()) {
- Occupied(entry) => {
- let mut err = struct_span_err!(
- tcx.sess,
- span,
- E0201,
- "duplicate definitions with name `{}`:",
- ident
- );
- err.span_label(*entry.get(), format!("previous definition of `{}` here", ident));
- err.span_label(span, "duplicate definition");
- err.emit();
- }
- Vacant(entry) => {
- entry.insert(span);
- }
- }
- }
-}
diff --git a/compiler/rustc_typeck/src/impl_wf_check/min_specialization.rs b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs
index 74abb71a1..e806e9487 100644
--- a/compiler/rustc_typeck/src/impl_wf_check/min_specialization.rs
+++ b/compiler/rustc_hir_analysis/src/impl_wf_check/min_specialization.rs
@@ -65,27 +65,25 @@
//! cause use after frees with purely safe code in the same way as specializing
//! on traits with methods can.
-use crate::check::regionck::OutlivesEnvironmentExt;
-use crate::check::wfcheck::impl_implied_bounds;
use crate::constrained_generic_params as cgp;
use crate::errors::SubstsOnOverriddenImpl;
use rustc_data_structures::fx::FxHashSet;
use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_infer::infer::outlives::env::OutlivesEnvironment;
-use rustc_infer::infer::{InferCtxt, TyCtxtInferExt};
+use rustc_infer::infer::TyCtxtInferExt;
use rustc_infer::traits::specialization_graph::Node;
use rustc_middle::ty::subst::{GenericArg, InternalSubsts, SubstsRef};
use rustc_middle::ty::trait_def::TraitSpecializationKind;
use rustc_middle::ty::{self, TyCtxt, TypeVisitable};
use rustc_span::Span;
-use rustc_trait_selection::traits::{self, translate_substs, wf};
+use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt;
+use rustc_trait_selection::traits::outlives_bounds::InferCtxtExt as _;
+use rustc_trait_selection::traits::{self, translate_substs, wf, ObligationCtxt};
pub(super) fn check_min_specialization(tcx: TyCtxt<'_>, impl_def_id: LocalDefId) {
if let Some(node) = parent_specialization_node(tcx, impl_def_id) {
- tcx.infer_ctxt().enter(|infcx| {
- check_always_applicable(&infcx, impl_def_id, node);
- });
+ check_always_applicable(tcx, impl_def_id, node);
}
}
@@ -105,16 +103,14 @@ fn parent_specialization_node(tcx: TyCtxt<'_>, impl1_def_id: LocalDefId) -> Opti
}
/// Check that `impl1` is a sound specialization
-fn check_always_applicable(infcx: &InferCtxt<'_, '_>, impl1_def_id: LocalDefId, impl2_node: Node) {
- if let Some((impl1_substs, impl2_substs)) = get_impl_substs(infcx, impl1_def_id, impl2_node) {
+fn check_always_applicable(tcx: TyCtxt<'_>, impl1_def_id: LocalDefId, impl2_node: Node) {
+ if let Some((impl1_substs, impl2_substs)) = get_impl_substs(tcx, impl1_def_id, impl2_node) {
let impl2_def_id = impl2_node.def_id();
debug!(
"check_always_applicable(\nimpl1_def_id={:?},\nimpl2_def_id={:?},\nimpl2_substs={:?}\n)",
impl1_def_id, impl2_def_id, impl2_substs
);
- let tcx = infcx.tcx;
-
let parent_substs = if impl2_node.is_from_trait() {
impl2_substs.to_vec()
} else {
@@ -124,7 +120,7 @@ fn check_always_applicable(infcx: &InferCtxt<'_, '_>, impl1_def_id: LocalDefId,
let span = tcx.def_span(impl1_def_id);
check_static_lifetimes(tcx, &parent_substs, span);
check_duplicate_params(tcx, impl1_substs, &parent_substs, span);
- check_predicates(infcx, impl1_def_id, impl1_substs, impl2_node, impl2_substs, span);
+ check_predicates(tcx, impl1_def_id, impl1_substs, impl2_node, impl2_substs, span);
}
}
@@ -134,30 +130,37 @@ fn check_always_applicable(infcx: &InferCtxt<'_, '_>, impl1_def_id: LocalDefId,
///
/// Example
///
+/// ```ignore (illustrative)
/// impl<A, B> Foo<A> for B { /* impl2 */ }
/// impl<C> Foo<Vec<C>> for C { /* impl1 */ }
+/// ```
///
/// Would return `S1 = [C]` and `S2 = [Vec<C>, C]`.
fn get_impl_substs<'tcx>(
- infcx: &InferCtxt<'_, 'tcx>,
+ tcx: TyCtxt<'tcx>,
impl1_def_id: LocalDefId,
impl2_node: Node,
) -> Option<(SubstsRef<'tcx>, SubstsRef<'tcx>)> {
- let tcx = infcx.tcx;
+ let infcx = &tcx.infer_ctxt().build();
+ let ocx = ObligationCtxt::new(infcx);
let param_env = tcx.param_env(impl1_def_id);
+ let impl1_hir_id = tcx.hir().local_def_id_to_hir_id(impl1_def_id);
+
+ let assumed_wf_types =
+ ocx.assumed_wf_types(param_env, tcx.def_span(impl1_def_id), impl1_def_id);
let impl1_substs = InternalSubsts::identity_for_item(tcx, impl1_def_id.to_def_id());
let impl2_substs =
translate_substs(infcx, param_env, impl1_def_id.to_def_id(), impl1_substs, impl2_node);
- let mut outlives_env = OutlivesEnvironment::new(param_env);
- let implied_bounds =
- impl_implied_bounds(infcx.tcx, param_env, impl1_def_id, tcx.def_span(impl1_def_id));
- outlives_env.add_implied_bounds(
- infcx,
- implied_bounds,
- tcx.hir().local_def_id_to_hir_id(impl1_def_id),
- );
+ let errors = ocx.select_all_or_error();
+ if !errors.is_empty() {
+ ocx.infcx.err_ctxt().report_fulfillment_errors(&errors, None, false);
+ return None;
+ }
+
+ let implied_bounds = infcx.implied_bounds_tys(param_env, impl1_hir_id, assumed_wf_types);
+ let outlives_env = OutlivesEnvironment::with_bounds(param_env, Some(infcx), implied_bounds);
infcx.check_region_obligations_and_report_errors(impl1_def_id, &outlives_env);
let Ok(impl2_substs) = infcx.fully_resolve(impl2_substs) else {
let span = tcx.def_span(impl1_def_id);
@@ -224,13 +227,17 @@ fn unconstrained_parent_impl_substs<'tcx>(
///
/// For example forbid the following:
///
+/// ```ignore (illustrative)
/// impl<A> Tr for A { }
/// impl<B> Tr for (B, B) { }
+/// ```
///
/// Note that only consider the unconstrained parameters of the base impl:
///
+/// ```ignore (illustrative)
/// impl<S, I: IntoIterator<Item = S>> Tr<S> for I { }
/// impl<T> Tr<T> for Vec<T> { }
+/// ```
///
/// The substs for the parent impl here are `[T, Vec<T>]`, which repeats `T`,
/// but `S` is constrained in the parent impl, so `parent_substs` is only
@@ -255,8 +262,10 @@ fn check_duplicate_params<'tcx>(
///
/// For example forbid the following:
///
+/// ```ignore (illustrative)
/// impl<A> Tr for A { }
/// impl Tr for &'static i32 { }
+/// ```
fn check_static_lifetimes<'tcx>(
tcx: TyCtxt<'tcx>,
parent_substs: &Vec<GenericArg<'tcx>>,
@@ -279,14 +288,13 @@ fn check_static_lifetimes<'tcx>(
/// * a well-formed predicate of a type argument of the trait being implemented,
/// including the `Self`-type.
fn check_predicates<'tcx>(
- infcx: &InferCtxt<'_, 'tcx>,
+ tcx: TyCtxt<'tcx>,
impl1_def_id: LocalDefId,
impl1_substs: SubstsRef<'tcx>,
impl2_node: Node,
impl2_substs: SubstsRef<'tcx>,
span: Span,
) {
- let tcx = infcx.tcx;
let instantiated = tcx.predicates_of(impl1_def_id).instantiate(tcx, impl1_substs);
let impl1_predicates: Vec<_> = traits::elaborate_predicates_with_span(
tcx,
@@ -343,19 +351,21 @@ fn check_predicates<'tcx>(
// Include the well-formed predicates of the type parameters of the impl.
for arg in tcx.impl_trait_ref(impl1_def_id).unwrap().substs {
- if let Some(obligations) = wf::obligations(
+ let infcx = &tcx.infer_ctxt().build();
+ let obligations = wf::obligations(
infcx,
tcx.param_env(impl1_def_id),
tcx.hir().local_def_id_to_hir_id(impl1_def_id),
0,
arg,
span,
- ) {
- impl2_predicates.extend(
- traits::elaborate_obligations(tcx, obligations)
- .map(|obligation| obligation.predicate),
- )
- }
+ )
+ .unwrap();
+
+ assert!(!obligations.needs_infer());
+ impl2_predicates.extend(
+ traits::elaborate_obligations(tcx, obligations).map(|obligation| obligation.predicate),
+ )
}
impl2_predicates.extend(
traits::elaborate_predicates_with_span(tcx, always_applicable_traits)
@@ -418,13 +428,10 @@ fn trait_predicate_kind<'tcx>(
predicate: ty::Predicate<'tcx>,
) -> Option<TraitSpecializationKind> {
match predicate.kind().skip_binder() {
- ty::PredicateKind::Trait(ty::TraitPredicate {
- trait_ref,
- constness: ty::BoundConstness::NotConst,
- polarity: _,
- }) => Some(tcx.trait_def(trait_ref.def_id).specialization_kind),
- ty::PredicateKind::Trait(_)
- | ty::PredicateKind::RegionOutlives(_)
+ ty::PredicateKind::Trait(ty::TraitPredicate { trait_ref, constness: _, polarity: _ }) => {
+ Some(tcx.trait_def(trait_ref.def_id).specialization_kind)
+ }
+ ty::PredicateKind::RegionOutlives(_)
| ty::PredicateKind::TypeOutlives(_)
| ty::PredicateKind::Projection(_)
| ty::PredicateKind::WellFormed(_)
diff --git a/compiler/rustc_hir_analysis/src/lib.rs b/compiler/rustc_hir_analysis/src/lib.rs
new file mode 100644
index 000000000..525cd2419
--- /dev/null
+++ b/compiler/rustc_hir_analysis/src/lib.rs
@@ -0,0 +1,552 @@
+/*!
+
+# typeck
+
+The type checker is responsible for:
+
+1. Determining the type of each expression.
+2. Resolving methods and traits.
+3. Guaranteeing that most type rules are met. ("Most?", you say, "why most?"
+ Well, dear reader, read on.)
+
+The main entry point is [`check_crate()`]. Type checking operates in
+several major phases:
+
+1. The collect phase first passes over all items and determines their
+ type, without examining their "innards".
+
+2. Variance inference then runs to compute the variance of each parameter.
+
+3. Coherence checks for overlapping or orphaned impls.
+
+4. Finally, the check phase then checks function bodies and so forth.
+ Within the check phase, we check each function body one at a time
+ (bodies of function expressions are checked as part of the
+ containing function). Inference is used to supply types wherever
+ they are unknown. The actual checking of a function itself has
+ several phases (check, regionck, writeback), as discussed in the
+ documentation for the [`check`] module.
+
+The type checker is defined into various submodules which are documented
+independently:
+
+- astconv: converts the AST representation of types
+ into the `ty` representation.
+
+- collect: computes the types of each top-level item and enters them into
+ the `tcx.types` table for later use.
+
+- coherence: enforces coherence rules, builds some tables.
+
+- variance: variance inference
+
+- outlives: outlives inference
+
+- check: walks over function bodies and type checks them, inferring types for
+ local variables, type parameters, etc as necessary.
+
+- infer: finds the types to use for each type variable such that
+ all subtyping and assignment constraints are met. In essence, the check
+ module specifies the constraints, and the infer module solves them.
+
+## Note
+
+This API is completely unstable and subject to change.
+
+*/
+
+#![allow(rustc::potential_query_instability)]
+#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
+#![feature(box_patterns)]
+#![feature(control_flow_enum)]
+#![feature(drain_filter)]
+#![feature(hash_drain_filter)]
+#![feature(if_let_guard)]
+#![feature(is_sorted)]
+#![feature(iter_intersperse)]
+#![feature(let_chains)]
+#![feature(min_specialization)]
+#![feature(never_type)]
+#![feature(once_cell)]
+#![feature(slice_partition_dedup)]
+#![feature(try_blocks)]
+#![feature(is_some_and)]
+#![feature(type_alias_impl_trait)]
+#![recursion_limit = "256"]
+
+#[macro_use]
+extern crate tracing;
+
+#[macro_use]
+extern crate rustc_middle;
+
+// These are used by Clippy.
+pub mod check;
+
+pub mod astconv;
+mod bounds;
+mod check_unused;
+mod coherence;
+// FIXME: This module shouldn't be public.
+pub mod collect;
+mod constrained_generic_params;
+mod errors;
+pub mod hir_wf_check;
+mod impl_wf_check;
+mod outlives;
+pub mod structured_errors;
+mod variance;
+
+use rustc_errors::{struct_span_err, ErrorGuaranteed};
+use rustc_hir as hir;
+use rustc_hir::def_id::DefId;
+use rustc_hir::{Node, CRATE_HIR_ID};
+use rustc_infer::infer::{InferOk, TyCtxtInferExt};
+use rustc_middle::middle;
+use rustc_middle::ty::query::Providers;
+use rustc_middle::ty::{self, Ty, TyCtxt};
+use rustc_middle::util;
+use rustc_session::config::EntryFnType;
+use rustc_span::{symbol::sym, Span, DUMMY_SP};
+use rustc_target::spec::abi::Abi;
+use rustc_trait_selection::traits::error_reporting::TypeErrCtxtExt as _;
+use rustc_trait_selection::traits::{self, ObligationCause, ObligationCauseCode};
+
+use std::iter;
+
+use astconv::AstConv;
+use bounds::Bounds;
+
+fn require_c_abi_if_c_variadic(tcx: TyCtxt<'_>, decl: &hir::FnDecl<'_>, abi: Abi, span: Span) {
+ match (decl.c_variadic, abi) {
+ // The function has the correct calling convention, or isn't a "C-variadic" function.
+ (false, _) | (true, Abi::C { .. }) | (true, Abi::Cdecl { .. }) => {}
+ // The function is a "C-variadic" function with an incorrect calling convention.
+ (true, _) => {
+ let mut err = struct_span_err!(
+ tcx.sess,
+ span,
+ E0045,
+ "C-variadic function must have C or cdecl calling convention"
+ );
+ err.span_label(span, "C-variadics require C or cdecl calling convention").emit();
+ }
+ }
+}
+
+fn require_same_types<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ cause: &ObligationCause<'tcx>,
+ expected: Ty<'tcx>,
+ actual: Ty<'tcx>,
+) -> bool {
+ let infcx = &tcx.infer_ctxt().build();
+ let param_env = ty::ParamEnv::empty();
+ let errors = match infcx.at(cause, param_env).eq(expected, actual) {
+ Ok(InferOk { obligations, .. }) => traits::fully_solve_obligations(infcx, obligations),
+ Err(err) => {
+ infcx.err_ctxt().report_mismatched_types(cause, expected, actual, err).emit();
+ return false;
+ }
+ };
+
+ match &errors[..] {
+ [] => true,
+ errors => {
+ infcx.err_ctxt().report_fulfillment_errors(errors, None, false);
+ false
+ }
+ }
+}
+
+fn check_main_fn_ty(tcx: TyCtxt<'_>, main_def_id: DefId) {
+ let main_fnsig = tcx.fn_sig(main_def_id);
+ let main_span = tcx.def_span(main_def_id);
+
+ fn main_fn_diagnostics_hir_id(tcx: TyCtxt<'_>, def_id: DefId, sp: Span) -> hir::HirId {
+ if let Some(local_def_id) = def_id.as_local() {
+ let hir_id = tcx.hir().local_def_id_to_hir_id(local_def_id);
+ let hir_type = tcx.type_of(local_def_id);
+ if !matches!(hir_type.kind(), ty::FnDef(..)) {
+ span_bug!(sp, "main has a non-function type: found `{}`", hir_type);
+ }
+ hir_id
+ } else {
+ CRATE_HIR_ID
+ }
+ }
+
+ fn main_fn_generics_params_span(tcx: TyCtxt<'_>, def_id: DefId) -> Option<Span> {
+ if !def_id.is_local() {
+ return None;
+ }
+ let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local());
+ match tcx.hir().find(hir_id) {
+ Some(Node::Item(hir::Item { kind: hir::ItemKind::Fn(_, ref generics, _), .. })) => {
+ if !generics.params.is_empty() {
+ Some(generics.span)
+ } else {
+ None
+ }
+ }
+ _ => {
+ span_bug!(tcx.def_span(def_id), "main has a non-function type");
+ }
+ }
+ }
+
+ fn main_fn_where_clauses_span(tcx: TyCtxt<'_>, def_id: DefId) -> Option<Span> {
+ if !def_id.is_local() {
+ return None;
+ }
+ let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local());
+ match tcx.hir().find(hir_id) {
+ Some(Node::Item(hir::Item { kind: hir::ItemKind::Fn(_, ref generics, _), .. })) => {
+ Some(generics.where_clause_span)
+ }
+ _ => {
+ span_bug!(tcx.def_span(def_id), "main has a non-function type");
+ }
+ }
+ }
+
+ fn main_fn_asyncness_span(tcx: TyCtxt<'_>, def_id: DefId) -> Option<Span> {
+ if !def_id.is_local() {
+ return None;
+ }
+ Some(tcx.def_span(def_id))
+ }
+
+ fn main_fn_return_type_span(tcx: TyCtxt<'_>, def_id: DefId) -> Option<Span> {
+ if !def_id.is_local() {
+ return None;
+ }
+ let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local());
+ match tcx.hir().find(hir_id) {
+ Some(Node::Item(hir::Item { kind: hir::ItemKind::Fn(ref fn_sig, _, _), .. })) => {
+ Some(fn_sig.decl.output.span())
+ }
+ _ => {
+ span_bug!(tcx.def_span(def_id), "main has a non-function type");
+ }
+ }
+ }
+
+ let mut error = false;
+ let main_diagnostics_hir_id = main_fn_diagnostics_hir_id(tcx, main_def_id, main_span);
+ let main_fn_generics = tcx.generics_of(main_def_id);
+ let main_fn_predicates = tcx.predicates_of(main_def_id);
+ if main_fn_generics.count() != 0 || !main_fnsig.bound_vars().is_empty() {
+ let generics_param_span = main_fn_generics_params_span(tcx, main_def_id);
+ let msg = "`main` function is not allowed to have generic \
+ parameters";
+ let mut diag =
+ struct_span_err!(tcx.sess, generics_param_span.unwrap_or(main_span), E0131, "{}", msg);
+ if let Some(generics_param_span) = generics_param_span {
+ let label = "`main` cannot have generic parameters";
+ diag.span_label(generics_param_span, label);
+ }
+ diag.emit();
+ error = true;
+ } else if !main_fn_predicates.predicates.is_empty() {
+ // generics may bring in implicit predicates, so we skip this check if generics is present.
+ let generics_where_clauses_span = main_fn_where_clauses_span(tcx, main_def_id);
+ let mut diag = struct_span_err!(
+ tcx.sess,
+ generics_where_clauses_span.unwrap_or(main_span),
+ E0646,
+ "`main` function is not allowed to have a `where` clause"
+ );
+ if let Some(generics_where_clauses_span) = generics_where_clauses_span {
+ diag.span_label(generics_where_clauses_span, "`main` cannot have a `where` clause");
+ }
+ diag.emit();
+ error = true;
+ }
+
+ let main_asyncness = tcx.asyncness(main_def_id);
+ if let hir::IsAsync::Async = main_asyncness {
+ let mut diag = struct_span_err!(
+ tcx.sess,
+ main_span,
+ E0752,
+ "`main` function is not allowed to be `async`"
+ );
+ let asyncness_span = main_fn_asyncness_span(tcx, main_def_id);
+ if let Some(asyncness_span) = asyncness_span {
+ diag.span_label(asyncness_span, "`main` function is not allowed to be `async`");
+ }
+ diag.emit();
+ error = true;
+ }
+
+ for attr in tcx.get_attrs(main_def_id, sym::track_caller) {
+ tcx.sess
+ .struct_span_err(attr.span, "`main` function is not allowed to be `#[track_caller]`")
+ .span_label(main_span, "`main` function is not allowed to be `#[track_caller]`")
+ .emit();
+ error = true;
+ }
+
+ if error {
+ return;
+ }
+
+ let expected_return_type;
+ if let Some(term_did) = tcx.lang_items().termination() {
+ let return_ty = main_fnsig.output();
+ let return_ty_span = main_fn_return_type_span(tcx, main_def_id).unwrap_or(main_span);
+ if !return_ty.bound_vars().is_empty() {
+ let msg = "`main` function return type is not allowed to have generic \
+ parameters";
+ struct_span_err!(tcx.sess, return_ty_span, E0131, "{}", msg).emit();
+ error = true;
+ }
+ let return_ty = return_ty.skip_binder();
+ let infcx = tcx.infer_ctxt().build();
+ // Main should have no WC, so empty param env is OK here.
+ let param_env = ty::ParamEnv::empty();
+ let cause = traits::ObligationCause::new(
+ return_ty_span,
+ main_diagnostics_hir_id,
+ ObligationCauseCode::MainFunctionType,
+ );
+ let ocx = traits::ObligationCtxt::new(&infcx);
+ let norm_return_ty = ocx.normalize(cause.clone(), param_env, return_ty);
+ ocx.register_bound(cause, param_env, norm_return_ty, term_did);
+ let errors = ocx.select_all_or_error();
+ if !errors.is_empty() {
+ infcx.err_ctxt().report_fulfillment_errors(&errors, None, false);
+ error = true;
+ }
+ // now we can take the return type of the given main function
+ expected_return_type = main_fnsig.output();
+ } else {
+ // standard () main return type
+ expected_return_type = ty::Binder::dummy(tcx.mk_unit());
+ }
+
+ if error {
+ return;
+ }
+
+ let se_ty = tcx.mk_fn_ptr(expected_return_type.map_bound(|expected_return_type| {
+ tcx.mk_fn_sig(iter::empty(), expected_return_type, false, hir::Unsafety::Normal, Abi::Rust)
+ }));
+
+ require_same_types(
+ tcx,
+ &ObligationCause::new(
+ main_span,
+ main_diagnostics_hir_id,
+ ObligationCauseCode::MainFunctionType,
+ ),
+ se_ty,
+ tcx.mk_fn_ptr(main_fnsig),
+ );
+}
+fn check_start_fn_ty(tcx: TyCtxt<'_>, start_def_id: DefId) {
+ let start_def_id = start_def_id.expect_local();
+ let start_id = tcx.hir().local_def_id_to_hir_id(start_def_id);
+ let start_span = tcx.def_span(start_def_id);
+ let start_t = tcx.type_of(start_def_id);
+ match start_t.kind() {
+ ty::FnDef(..) => {
+ if let Some(Node::Item(it)) = tcx.hir().find(start_id) {
+ if let hir::ItemKind::Fn(ref sig, ref generics, _) = it.kind {
+ let mut error = false;
+ if !generics.params.is_empty() {
+ struct_span_err!(
+ tcx.sess,
+ generics.span,
+ E0132,
+ "start function is not allowed to have type parameters"
+ )
+ .span_label(generics.span, "start function cannot have type parameters")
+ .emit();
+ error = true;
+ }
+ if generics.has_where_clause_predicates {
+ struct_span_err!(
+ tcx.sess,
+ generics.where_clause_span,
+ E0647,
+ "start function is not allowed to have a `where` clause"
+ )
+ .span_label(
+ generics.where_clause_span,
+ "start function cannot have a `where` clause",
+ )
+ .emit();
+ error = true;
+ }
+ if let hir::IsAsync::Async = sig.header.asyncness {
+ let span = tcx.def_span(it.owner_id);
+ struct_span_err!(
+ tcx.sess,
+ span,
+ E0752,
+ "`start` is not allowed to be `async`"
+ )
+ .span_label(span, "`start` is not allowed to be `async`")
+ .emit();
+ error = true;
+ }
+
+ let attrs = tcx.hir().attrs(start_id);
+ for attr in attrs {
+ if attr.has_name(sym::track_caller) {
+ tcx.sess
+ .struct_span_err(
+ attr.span,
+ "`start` is not allowed to be `#[track_caller]`",
+ )
+ .span_label(
+ start_span,
+ "`start` is not allowed to be `#[track_caller]`",
+ )
+ .emit();
+ error = true;
+ }
+ }
+
+ if error {
+ return;
+ }
+ }
+ }
+
+ let se_ty = tcx.mk_fn_ptr(ty::Binder::dummy(tcx.mk_fn_sig(
+ [tcx.types.isize, tcx.mk_imm_ptr(tcx.mk_imm_ptr(tcx.types.u8))].iter().cloned(),
+ tcx.types.isize,
+ false,
+ hir::Unsafety::Normal,
+ Abi::Rust,
+ )));
+
+ require_same_types(
+ tcx,
+ &ObligationCause::new(start_span, start_id, ObligationCauseCode::StartFunctionType),
+ se_ty,
+ tcx.mk_fn_ptr(tcx.fn_sig(start_def_id)),
+ );
+ }
+ _ => {
+ span_bug!(start_span, "start has a non-function type: found `{}`", start_t);
+ }
+ }
+}
+
+fn check_for_entry_fn(tcx: TyCtxt<'_>) {
+ match tcx.entry_fn(()) {
+ Some((def_id, EntryFnType::Main { .. })) => check_main_fn_ty(tcx, def_id),
+ Some((def_id, EntryFnType::Start)) => check_start_fn_ty(tcx, def_id),
+ _ => {}
+ }
+}
+
+pub fn provide(providers: &mut Providers) {
+ collect::provide(providers);
+ coherence::provide(providers);
+ check::provide(providers);
+ variance::provide(providers);
+ outlives::provide(providers);
+ impl_wf_check::provide(providers);
+ hir_wf_check::provide(providers);
+}
+
+pub fn check_crate(tcx: TyCtxt<'_>) -> Result<(), ErrorGuaranteed> {
+ let _prof_timer = tcx.sess.timer("type_check_crate");
+
+ // this ensures that later parts of type checking can assume that items
+ // have valid types and not error
+ // FIXME(matthewjasper) We shouldn't need to use `track_errors`.
+ tcx.sess.track_errors(|| {
+ tcx.sess.time("type_collecting", || {
+ tcx.hir().for_each_module(|module| tcx.ensure().collect_mod_item_types(module))
+ });
+ })?;
+
+ if tcx.features().rustc_attrs {
+ tcx.sess.track_errors(|| {
+ tcx.sess.time("outlives_testing", || outlives::test::test_inferred_outlives(tcx));
+ })?;
+ }
+
+ tcx.sess.track_errors(|| {
+ tcx.sess.time("impl_wf_inference", || {
+ tcx.hir().for_each_module(|module| tcx.ensure().check_mod_impl_wf(module))
+ });
+ })?;
+
+ tcx.sess.track_errors(|| {
+ tcx.sess.time("coherence_checking", || {
+ for &trait_def_id in tcx.all_local_trait_impls(()).keys() {
+ tcx.ensure().coherent_trait(trait_def_id);
+ }
+
+ // these queries are executed for side-effects (error reporting):
+ tcx.ensure().crate_inherent_impls(());
+ tcx.ensure().crate_inherent_impls_overlap_check(());
+ });
+ })?;
+
+ if tcx.features().rustc_attrs {
+ tcx.sess.track_errors(|| {
+ tcx.sess.time("variance_testing", || variance::test::test_variance(tcx));
+ })?;
+ }
+
+ tcx.sess.track_errors(|| {
+ tcx.sess.time("wf_checking", || {
+ tcx.hir().par_for_each_module(|module| tcx.ensure().check_mod_type_wf(module))
+ });
+ })?;
+
+ // NOTE: This is copy/pasted in librustdoc/core.rs and should be kept in sync.
+ tcx.sess.time("item_types_checking", || {
+ tcx.hir().for_each_module(|module| tcx.ensure().check_mod_item_types(module))
+ });
+
+ tcx.sess.time("item_bodies_checking", || tcx.typeck_item_bodies(()));
+
+ check_unused::check_crate(tcx);
+ check_for_entry_fn(tcx);
+
+ if let Some(reported) = tcx.sess.has_errors() { Err(reported) } else { Ok(()) }
+}
+
+/// A quasi-deprecated helper used in rustdoc and clippy to get
+/// the type from a HIR node.
+pub fn hir_ty_to_ty<'tcx>(tcx: TyCtxt<'tcx>, hir_ty: &hir::Ty<'_>) -> Ty<'tcx> {
+ // In case there are any projections, etc., find the "environment"
+ // def-ID that will be used to determine the traits/predicates in
+ // scope. This is derived from the enclosing item-like thing.
+ let env_def_id = tcx.hir().get_parent_item(hir_ty.hir_id);
+ let item_cx = self::collect::ItemCtxt::new(tcx, env_def_id.to_def_id());
+ <dyn AstConv<'_>>::ast_ty_to_ty(&item_cx, hir_ty)
+}
+
+pub fn hir_trait_to_predicates<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ hir_trait: &hir::TraitRef<'_>,
+ self_ty: Ty<'tcx>,
+) -> Bounds<'tcx> {
+ // In case there are any projections, etc., find the "environment"
+ // def-ID that will be used to determine the traits/predicates in
+ // scope. This is derived from the enclosing item-like thing.
+ let env_def_id = tcx.hir().get_parent_item(hir_trait.hir_ref_id);
+ let item_cx = self::collect::ItemCtxt::new(tcx, env_def_id.to_def_id());
+ let mut bounds = Bounds::default();
+ let _ = <dyn AstConv<'_>>::instantiate_poly_trait_ref(
+ &item_cx,
+ hir_trait,
+ DUMMY_SP,
+ ty::BoundConstness::NotConst,
+ self_ty,
+ &mut bounds,
+ true,
+ );
+
+ bounds
+}
diff --git a/compiler/rustc_typeck/src/outlives/explicit.rs b/compiler/rustc_hir_analysis/src/outlives/explicit.rs
index 7534482cc..7534482cc 100644
--- a/compiler/rustc_typeck/src/outlives/explicit.rs
+++ b/compiler/rustc_hir_analysis/src/outlives/explicit.rs
diff --git a/compiler/rustc_typeck/src/outlives/implicit_infer.rs b/compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs
index 3b779280e..90c6edb65 100644
--- a/compiler/rustc_typeck/src/outlives/implicit_infer.rs
+++ b/compiler/rustc_hir_analysis/src/outlives/implicit_infer.rs
@@ -1,8 +1,8 @@
use rustc_data_structures::fx::FxHashMap;
use rustc_hir::def::DefKind;
use rustc_hir::def_id::DefId;
-use rustc_middle::ty::subst::{GenericArg, GenericArgKind, Subst};
use rustc_middle::ty::{self, DefIdTree, Ty, TyCtxt};
+use rustc_middle::ty::{GenericArg, GenericArgKind};
use rustc_span::Span;
use super::explicit::ExplicitPredicatesMap;
@@ -29,7 +29,7 @@ pub(super) fn infer_predicates<'tcx>(
// Visit all the crates and infer predicates
for id in tcx.hir().items() {
- let item_did = id.def_id;
+ let item_did = id.owner_id;
debug!("InferVisitor::visit_item(item={:?})", item_did);
diff --git a/compiler/rustc_hir_analysis/src/outlives/mod.rs b/compiler/rustc_hir_analysis/src/outlives/mod.rs
new file mode 100644
index 000000000..e50c26765
--- /dev/null
+++ b/compiler/rustc_hir_analysis/src/outlives/mod.rs
@@ -0,0 +1,129 @@
+use hir::Node;
+use rustc_hir as hir;
+use rustc_hir::def_id::DefId;
+use rustc_middle::ty::query::Providers;
+use rustc_middle::ty::subst::GenericArgKind;
+use rustc_middle::ty::{self, CratePredicatesMap, ToPredicate, TyCtxt};
+use rustc_span::symbol::sym;
+use rustc_span::Span;
+
+mod explicit;
+mod implicit_infer;
+/// Code to write unit test for outlives.
+pub mod test;
+mod utils;
+
+pub fn provide(providers: &mut Providers) {
+ *providers = Providers { inferred_outlives_of, inferred_outlives_crate, ..*providers };
+}
+
+fn inferred_outlives_of(tcx: TyCtxt<'_>, item_def_id: DefId) -> &[(ty::Predicate<'_>, Span)] {
+ let id = tcx.hir().local_def_id_to_hir_id(item_def_id.expect_local());
+
+ if matches!(tcx.def_kind(item_def_id), hir::def::DefKind::AnonConst) && tcx.lazy_normalization()
+ {
+ if tcx.hir().opt_const_param_default_param_hir_id(id).is_some() {
+ // In `generics_of` we set the generics' parent to be our parent's parent which means that
+ // we lose out on the predicates of our actual parent if we dont return those predicates here.
+ // (See comment in `generics_of` for more information on why the parent shenanigans is necessary)
+ //
+ // struct Foo<'a, 'b, const N: usize = { ... }>(&'a &'b ());
+ // ^^^ ^^^^^^^ the def id we are calling
+ // ^^^ inferred_outlives_of on
+ // parent item we dont have set as the
+ // parent of generics returned by `generics_of`
+ //
+ // In the above code we want the anon const to have predicates in its param env for `'b: 'a`
+ let item_def_id = tcx.hir().get_parent_item(id);
+ // In the above code example we would be calling `inferred_outlives_of(Foo)` here
+ return tcx.inferred_outlives_of(item_def_id);
+ }
+ }
+
+ match tcx.hir().get(id) {
+ Node::Item(item) => match item.kind {
+ hir::ItemKind::Struct(..) | hir::ItemKind::Enum(..) | hir::ItemKind::Union(..) => {
+ let crate_map = tcx.inferred_outlives_crate(());
+
+ let predicates = crate_map.predicates.get(&item_def_id).copied().unwrap_or(&[]);
+
+ if tcx.has_attr(item_def_id, sym::rustc_outlives) {
+ let mut pred: Vec<String> = predicates
+ .iter()
+ .map(|(out_pred, _)| match out_pred.kind().skip_binder() {
+ ty::PredicateKind::RegionOutlives(p) => p.to_string(),
+ ty::PredicateKind::TypeOutlives(p) => p.to_string(),
+ err => bug!("unexpected predicate {:?}", err),
+ })
+ .collect();
+ pred.sort();
+
+ let span = tcx.def_span(item_def_id);
+ let mut err = tcx.sess.struct_span_err(span, "rustc_outlives");
+ for p in &pred {
+ err.note(p);
+ }
+ err.emit();
+ }
+
+ debug!("inferred_outlives_of({:?}) = {:?}", item_def_id, predicates);
+
+ predicates
+ }
+
+ _ => &[],
+ },
+
+ _ => &[],
+ }
+}
+
+fn inferred_outlives_crate(tcx: TyCtxt<'_>, (): ()) -> CratePredicatesMap<'_> {
+ // Compute a map from each struct/enum/union S to the **explicit**
+ // outlives predicates (`T: 'a`, `'a: 'b`) that the user wrote.
+ // Typically there won't be many of these, except in older code where
+ // they were mandatory. Nonetheless, we have to ensure that every such
+ // predicate is satisfied, so they form a kind of base set of requirements
+ // for the type.
+
+ // Compute the inferred predicates
+ let global_inferred_outlives = implicit_infer::infer_predicates(tcx);
+
+ // Convert the inferred predicates into the "collected" form the
+ // global data structure expects.
+ //
+ // FIXME -- consider correcting impedance mismatch in some way,
+ // probably by updating the global data structure.
+ let predicates = global_inferred_outlives
+ .iter()
+ .map(|(&def_id, set)| {
+ let predicates = &*tcx.arena.alloc_from_iter(set.0.iter().filter_map(
+ |(ty::OutlivesPredicate(kind1, region2), &span)| {
+ match kind1.unpack() {
+ GenericArgKind::Type(ty1) => Some((
+ ty::Binder::dummy(ty::PredicateKind::TypeOutlives(
+ ty::OutlivesPredicate(ty1, *region2),
+ ))
+ .to_predicate(tcx),
+ span,
+ )),
+ GenericArgKind::Lifetime(region1) => Some((
+ ty::Binder::dummy(ty::PredicateKind::RegionOutlives(
+ ty::OutlivesPredicate(region1, *region2),
+ ))
+ .to_predicate(tcx),
+ span,
+ )),
+ GenericArgKind::Const(_) => {
+ // Generic consts don't impose any constraints.
+ None
+ }
+ }
+ },
+ ));
+ (def_id, predicates)
+ })
+ .collect();
+
+ ty::CratePredicatesMap { predicates }
+}
diff --git a/compiler/rustc_hir_analysis/src/outlives/test.rs b/compiler/rustc_hir_analysis/src/outlives/test.rs
new file mode 100644
index 000000000..fa2ac5659
--- /dev/null
+++ b/compiler/rustc_hir_analysis/src/outlives/test.rs
@@ -0,0 +1,21 @@
+use rustc_errors::struct_span_err;
+use rustc_middle::ty::TyCtxt;
+use rustc_span::symbol::sym;
+
+pub fn test_inferred_outlives(tcx: TyCtxt<'_>) {
+ for id in tcx.hir().items() {
+ // For unit testing: check for a special "rustc_outlives"
+ // attribute and report an error with various results if found.
+ if tcx.has_attr(id.owner_id.to_def_id(), sym::rustc_outlives) {
+ let inferred_outlives_of = tcx.inferred_outlives_of(id.owner_id);
+ struct_span_err!(
+ tcx.sess,
+ tcx.def_span(id.owner_id),
+ E0640,
+ "{:?}",
+ inferred_outlives_of
+ )
+ .emit();
+ }
+ }
+}
diff --git a/compiler/rustc_hir_analysis/src/outlives/utils.rs b/compiler/rustc_hir_analysis/src/outlives/utils.rs
new file mode 100644
index 000000000..0409c7081
--- /dev/null
+++ b/compiler/rustc_hir_analysis/src/outlives/utils.rs
@@ -0,0 +1,186 @@
+use rustc_infer::infer::outlives::components::{push_outlives_components, Component};
+use rustc_middle::ty::subst::{GenericArg, GenericArgKind};
+use rustc_middle::ty::{self, Region, Ty, TyCtxt};
+use rustc_span::Span;
+use smallvec::smallvec;
+use std::collections::BTreeMap;
+
+/// Tracks the `T: 'a` or `'a: 'a` predicates that we have inferred
+/// must be added to the struct header.
+pub(crate) type RequiredPredicates<'tcx> =
+ BTreeMap<ty::OutlivesPredicate<GenericArg<'tcx>, ty::Region<'tcx>>, Span>;
+
+/// Given a requirement `T: 'a` or `'b: 'a`, deduce the
+/// outlives_component and add it to `required_predicates`
+pub(crate) fn insert_outlives_predicate<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ kind: GenericArg<'tcx>,
+ outlived_region: Region<'tcx>,
+ span: Span,
+ required_predicates: &mut RequiredPredicates<'tcx>,
+) {
+ // If the `'a` region is bound within the field type itself, we
+ // don't want to propagate this constraint to the header.
+ if !is_free_region(outlived_region) {
+ return;
+ }
+
+ match kind.unpack() {
+ GenericArgKind::Type(ty) => {
+ // `T: 'outlived_region` for some type `T`
+ // But T could be a lot of things:
+ // e.g., if `T = &'b u32`, then `'b: 'outlived_region` is
+ // what we want to add.
+ //
+ // Or if within `struct Foo<U>` you had `T = Vec<U>`, then
+ // we would want to add `U: 'outlived_region`
+ let mut components = smallvec![];
+ push_outlives_components(tcx, ty, &mut components);
+ for component in components {
+ match component {
+ Component::Region(r) => {
+ // This would arise from something like:
+ //
+ // ```
+ // struct Foo<'a, 'b> {
+ // x: &'a &'b u32
+ // }
+ // ```
+ //
+ // Here `outlived_region = 'a` and `kind = &'b
+ // u32`. Decomposing `&'b u32` into
+ // components would yield `'b`, and we add the
+ // where clause that `'b: 'a`.
+ insert_outlives_predicate(
+ tcx,
+ r.into(),
+ outlived_region,
+ span,
+ required_predicates,
+ );
+ }
+
+ Component::Param(param_ty) => {
+ // param_ty: ty::ParamTy
+ // This would arise from something like:
+ //
+ // ```
+ // struct Foo<'a, U> {
+ // x: &'a Vec<U>
+ // }
+ // ```
+ //
+ // Here `outlived_region = 'a` and `kind =
+ // Vec<U>`. Decomposing `Vec<U>` into
+ // components would yield `U`, and we add the
+ // where clause that `U: 'a`.
+ let ty: Ty<'tcx> = param_ty.to_ty(tcx);
+ required_predicates
+ .entry(ty::OutlivesPredicate(ty.into(), outlived_region))
+ .or_insert(span);
+ }
+
+ Component::Projection(proj_ty) => {
+ // This would arise from something like:
+ //
+ // ```
+ // struct Foo<'a, T: Iterator> {
+ // x: &'a <T as Iterator>::Item
+ // }
+ // ```
+ //
+ // Here we want to add an explicit `where <T as Iterator>::Item: 'a`.
+ let ty: Ty<'tcx> = tcx.mk_projection(proj_ty.item_def_id, proj_ty.substs);
+ required_predicates
+ .entry(ty::OutlivesPredicate(ty.into(), outlived_region))
+ .or_insert(span);
+ }
+
+ Component::Opaque(def_id, substs) => {
+ // This would arise from something like:
+ //
+ // ```rust
+ // type Opaque<T> = impl Sized;
+ // fn defining<T>() -> Opaque<T> {}
+ // struct Ss<'a, T>(&'a Opaque<T>);
+ // ```
+ //
+ // Here we want to have an implied bound `Opaque<T>: 'a`
+
+ let ty = tcx.mk_opaque(def_id, substs);
+ required_predicates
+ .entry(ty::OutlivesPredicate(ty.into(), outlived_region))
+ .or_insert(span);
+ }
+
+ Component::EscapingProjection(_) => {
+ // As above, but the projection involves
+ // late-bound regions. Therefore, the WF
+ // requirement is not checked in type definition
+ // but at fn call site, so ignore it.
+ //
+ // ```
+ // struct Foo<'a, T: Iterator> {
+ // x: for<'b> fn(<&'b T as Iterator>::Item)
+ // // ^^^^^^^^^^^^^^^^^^^^^^^^^
+ // }
+ // ```
+ //
+ // Since `'b` is not in scope on `Foo`, can't
+ // do anything here, ignore it.
+ }
+
+ Component::UnresolvedInferenceVariable(_) => bug!("not using infcx"),
+ }
+ }
+ }
+
+ GenericArgKind::Lifetime(r) => {
+ if !is_free_region(r) {
+ return;
+ }
+ required_predicates.entry(ty::OutlivesPredicate(kind, outlived_region)).or_insert(span);
+ }
+
+ GenericArgKind::Const(_) => {
+ // Generic consts don't impose any constraints.
+ }
+ }
+}
+
+fn is_free_region(region: Region<'_>) -> bool {
+ // First, screen for regions that might appear in a type header.
+ match *region {
+ // These correspond to `T: 'a` relationships:
+ //
+ // struct Foo<'a, T> {
+ // field: &'a T, // this would generate a ReEarlyBound referencing `'a`
+ // }
+ //
+ // We care about these, so fall through.
+ ty::ReEarlyBound(_) => true,
+
+ // These correspond to `T: 'static` relationships which can be
+ // rather surprising.
+ //
+ // struct Foo<'a, T> {
+ // field: &'static T, // this would generate a ReStatic
+ // }
+ ty::ReStatic => false,
+
+ // Late-bound regions can appear in `fn` types:
+ //
+ // struct Foo<T> {
+ // field: for<'b> fn(&'b T) // e.g., 'b here
+ // }
+ //
+ // The type above might generate a `T: 'b` bound, but we can
+ // ignore it. We can't put it on the struct header anyway.
+ ty::ReLateBound(..) => false,
+
+ // These regions don't appear in types from type declarations:
+ ty::ReErased | ty::ReVar(..) | ty::RePlaceholder(..) | ty::ReFree(..) => {
+ bug!("unexpected region in outlives inference: {:?}", region);
+ }
+ }
+}
diff --git a/compiler/rustc_typeck/src/structured_errors.rs b/compiler/rustc_hir_analysis/src/structured_errors.rs
index 0b46fce17..0b46fce17 100644
--- a/compiler/rustc_typeck/src/structured_errors.rs
+++ b/compiler/rustc_hir_analysis/src/structured_errors.rs
diff --git a/compiler/rustc_typeck/src/structured_errors/missing_cast_for_variadic_arg.rs b/compiler/rustc_hir_analysis/src/structured_errors/missing_cast_for_variadic_arg.rs
index 324df313e..324df313e 100644
--- a/compiler/rustc_typeck/src/structured_errors/missing_cast_for_variadic_arg.rs
+++ b/compiler/rustc_hir_analysis/src/structured_errors/missing_cast_for_variadic_arg.rs
diff --git a/compiler/rustc_typeck/src/structured_errors/sized_unsized_cast.rs b/compiler/rustc_hir_analysis/src/structured_errors/sized_unsized_cast.rs
index bb6088054..bb6088054 100644
--- a/compiler/rustc_typeck/src/structured_errors/sized_unsized_cast.rs
+++ b/compiler/rustc_hir_analysis/src/structured_errors/sized_unsized_cast.rs
diff --git a/compiler/rustc_typeck/src/structured_errors/wrong_number_of_generic_args.rs b/compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs
index 99729391e..435912464 100644
--- a/compiler/rustc_typeck/src/structured_errors/wrong_number_of_generic_args.rs
+++ b/compiler/rustc_hir_analysis/src/structured_errors/wrong_number_of_generic_args.rs
@@ -4,7 +4,6 @@ use rustc_errors::{
MultiSpan,
};
use rustc_hir as hir;
-use rustc_middle::hir::map::fn_sig;
use rustc_middle::ty::{self as ty, AssocItems, AssocKind, TyCtxt};
use rustc_session::Session;
use rustc_span::def_id::DefId;
@@ -292,62 +291,60 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
// Creates lifetime name suggestions from the lifetime parameter names
fn get_lifetime_args_suggestions_from_param_names(
&self,
- path_hir_id: Option<hir::HirId>,
+ path_hir_id: hir::HirId,
num_params_to_take: usize,
) -> String {
debug!(?path_hir_id);
- if let Some(path_hir_id) = path_hir_id {
- let mut ret = Vec::new();
- for (id, node) in self.tcx.hir().parent_iter(path_hir_id) {
- debug!(?id);
- let params = if let Some(generics) = node.generics() {
- generics.params
- } else if let hir::Node::Ty(ty) = node
- && let hir::TyKind::BareFn(bare_fn) = ty.kind
- {
- bare_fn.generic_params
- } else {
- &[]
- };
- ret.extend(params.iter().filter_map(|p| {
- let hir::GenericParamKind::Lifetime { kind: hir::LifetimeParamKind::Explicit }
- = p.kind
- else { return None };
- let hir::ParamName::Plain(name) = p.name else { return None };
- Some(name.to_string())
- }));
- // Suggest `'static` when in const/static item-like.
- if let hir::Node::Item(hir::Item {
- kind: hir::ItemKind::Static { .. } | hir::ItemKind::Const { .. },
- ..
- })
- | hir::Node::TraitItem(hir::TraitItem {
- kind: hir::TraitItemKind::Const { .. },
- ..
- })
- | hir::Node::ImplItem(hir::ImplItem {
- kind: hir::ImplItemKind::Const { .. },
- ..
- })
- | hir::Node::ForeignItem(hir::ForeignItem {
- kind: hir::ForeignItemKind::Static { .. },
- ..
- })
- | hir::Node::AnonConst(..) = node
- {
- ret.extend(
- std::iter::repeat("'static".to_owned())
- .take(num_params_to_take.saturating_sub(ret.len())),
- );
- }
- if ret.len() >= num_params_to_take {
- return ret[..num_params_to_take].join(", ");
- }
- // We cannot refer to lifetimes defined in an outer function.
- if let hir::Node::Item(_) = node {
- break;
- }
+ let mut ret = Vec::new();
+ for (id, node) in self.tcx.hir().parent_iter(path_hir_id) {
+ debug!(?id);
+ let params = if let Some(generics) = node.generics() {
+ generics.params
+ } else if let hir::Node::Ty(ty) = node
+ && let hir::TyKind::BareFn(bare_fn) = ty.kind
+ {
+ bare_fn.generic_params
+ } else {
+ &[]
+ };
+ ret.extend(params.iter().filter_map(|p| {
+ let hir::GenericParamKind::Lifetime { kind: hir::LifetimeParamKind::Explicit }
+ = p.kind
+ else { return None };
+ let hir::ParamName::Plain(name) = p.name else { return None };
+ Some(name.to_string())
+ }));
+ // Suggest `'static` when in const/static item-like.
+ if let hir::Node::Item(hir::Item {
+ kind: hir::ItemKind::Static { .. } | hir::ItemKind::Const { .. },
+ ..
+ })
+ | hir::Node::TraitItem(hir::TraitItem {
+ kind: hir::TraitItemKind::Const { .. },
+ ..
+ })
+ | hir::Node::ImplItem(hir::ImplItem {
+ kind: hir::ImplItemKind::Const { .. },
+ ..
+ })
+ | hir::Node::ForeignItem(hir::ForeignItem {
+ kind: hir::ForeignItemKind::Static { .. },
+ ..
+ })
+ | hir::Node::AnonConst(..) = node
+ {
+ ret.extend(
+ std::iter::repeat("'static".to_owned())
+ .take(num_params_to_take.saturating_sub(ret.len())),
+ );
+ }
+ if ret.len() >= num_params_to_take {
+ return ret[..num_params_to_take].join(", ");
+ }
+ // We cannot refer to lifetimes defined in an outer function.
+ if let hir::Node::Item(_) = node {
+ break;
}
}
@@ -368,7 +365,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
&self,
num_params_to_take: usize,
) -> String {
- let fn_sig = self.tcx.hir().get_if_local(self.def_id).and_then(fn_sig);
+ let fn_sig = self.tcx.hir().get_if_local(self.def_id).and_then(hir::Node::fn_sig);
let is_used_in_input = |def_id| {
fn_sig.map_or(false, |fn_sig| {
fn_sig.decl.inputs.iter().any(|ty| match ty.kind {
@@ -524,6 +521,7 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
if self.not_enough_args_provided() {
self.suggest_adding_args(err);
} else if self.too_many_args_provided() {
+ self.suggest_moving_args_from_assoc_fn_to_trait(err);
self.suggest_removing_args_or_generics(err);
} else {
unreachable!();
@@ -654,6 +652,144 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
}
}
+ /// Suggests moving redundant argument(s) of an associate function to the
+ /// trait it belongs to.
+ ///
+ /// ```compile_fail
+ /// Into::into::<Option<_>>(42) // suggests considering `Into::<Option<_>>::into(42)`
+ /// ```
+ fn suggest_moving_args_from_assoc_fn_to_trait(&self, err: &mut Diagnostic) {
+ let trait_ = match self.tcx.trait_of_item(self.def_id) {
+ Some(def_id) => def_id,
+ None => return,
+ };
+
+ // Skip suggestion when the associated function is itself generic, it is unclear
+ // how to split the provided parameters between those to suggest to the trait and
+ // those to remain on the associated type.
+ let num_assoc_fn_expected_args =
+ self.num_expected_type_or_const_args() + self.num_expected_lifetime_args();
+ if num_assoc_fn_expected_args > 0 {
+ return;
+ }
+
+ let num_assoc_fn_excess_args =
+ self.num_excess_type_or_const_args() + self.num_excess_lifetime_args();
+
+ let trait_generics = self.tcx.generics_of(trait_);
+ let num_trait_generics_except_self =
+ trait_generics.count() - if trait_generics.has_self { 1 } else { 0 };
+
+ let msg = format!(
+ "consider moving {these} generic argument{s} to the `{name}` trait, which takes up to {num} argument{s}",
+ these = pluralize!("this", num_assoc_fn_excess_args),
+ s = pluralize!(num_assoc_fn_excess_args),
+ name = self.tcx.item_name(trait_),
+ num = num_trait_generics_except_self,
+ );
+
+ if let Some(parent_node) = self.tcx.hir().find_parent_node(self.path_segment.hir_id)
+ && let Some(parent_node) = self.tcx.hir().find(parent_node)
+ && let hir::Node::Expr(expr) = parent_node {
+ match expr.kind {
+ hir::ExprKind::Path(ref qpath) => {
+ self.suggest_moving_args_from_assoc_fn_to_trait_for_qualified_path(
+ err,
+ qpath,
+ msg,
+ num_assoc_fn_excess_args,
+ num_trait_generics_except_self
+ )
+ },
+ hir::ExprKind::MethodCall(..) => {
+ self.suggest_moving_args_from_assoc_fn_to_trait_for_method_call(
+ err,
+ trait_,
+ expr,
+ msg,
+ num_assoc_fn_excess_args,
+ num_trait_generics_except_self
+ )
+ },
+ _ => return,
+ }
+ }
+ }
+
+ fn suggest_moving_args_from_assoc_fn_to_trait_for_qualified_path(
+ &self,
+ err: &mut Diagnostic,
+ qpath: &'tcx hir::QPath<'tcx>,
+ msg: String,
+ num_assoc_fn_excess_args: usize,
+ num_trait_generics_except_self: usize,
+ ) {
+ if let hir::QPath::Resolved(_, path) = qpath
+ && let Some(trait_path_segment) = path.segments.get(0) {
+ let num_generic_args_supplied_to_trait = trait_path_segment.args().num_generic_params();
+
+ if num_assoc_fn_excess_args == num_trait_generics_except_self - num_generic_args_supplied_to_trait {
+ if let Some(span) = self.gen_args.span_ext()
+ && let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) {
+ let sugg = vec![
+ (self.path_segment.ident.span, format!("{}::{}", snippet, self.path_segment.ident)),
+ (span.with_lo(self.path_segment.ident.span.hi()), "".to_owned())
+ ];
+
+ err.multipart_suggestion(
+ msg,
+ sugg,
+ Applicability::MaybeIncorrect
+ );
+ }
+ }
+ }
+ }
+
+ fn suggest_moving_args_from_assoc_fn_to_trait_for_method_call(
+ &self,
+ err: &mut Diagnostic,
+ trait_def_id: DefId,
+ expr: &'tcx hir::Expr<'tcx>,
+ msg: String,
+ num_assoc_fn_excess_args: usize,
+ num_trait_generics_except_self: usize,
+ ) {
+ let sm = self.tcx.sess.source_map();
+ let hir::ExprKind::MethodCall(_, rcvr, args, _) = expr.kind else { return; };
+ if num_assoc_fn_excess_args != num_trait_generics_except_self {
+ return;
+ }
+ let Some(gen_args) = self.gen_args.span_ext() else { return; };
+ let Ok(generics) = sm.span_to_snippet(gen_args) else { return; };
+ let Ok(rcvr) = sm.span_to_snippet(
+ rcvr.span.find_ancestor_inside(expr.span).unwrap_or(rcvr.span)
+ ) else { return; };
+ let Ok(rest) =
+ (match args {
+ [] => Ok(String::new()),
+ [arg] => sm.span_to_snippet(
+ arg.span.find_ancestor_inside(expr.span).unwrap_or(arg.span),
+ ),
+ [first, .., last] => {
+ let first_span =
+ first.span.find_ancestor_inside(expr.span).unwrap_or(first.span);
+ let last_span =
+ last.span.find_ancestor_inside(expr.span).unwrap_or(last.span);
+ sm.span_to_snippet(first_span.to(last_span))
+ }
+ }) else { return; };
+ let comma = if args.len() > 0 { ", " } else { "" };
+ let trait_path = self.tcx.def_path_str(trait_def_id);
+ let method_name = self.tcx.item_name(self.def_id);
+ err.span_suggestion(
+ expr.span,
+ msg,
+ format!("{trait_path}::{generics}::{method_name}({rcvr}{comma}{rest})"),
+ Applicability::MaybeIncorrect,
+ );
+ }
+
/// Suggests to remove redundant argument(s):
///
/// ```text
@@ -763,16 +899,13 @@ impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> {
// If there is a single unbound associated type and a single excess generic param
// suggest replacing the generic param with the associated type bound
if provided_args_matches_unbound_traits && !unbound_types.is_empty() {
- let mut suggestions = vec![];
let unused_generics = &self.gen_args.args[self.num_expected_type_or_const_args()..];
- for (potential, name) in iter::zip(unused_generics, &unbound_types) {
- if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(potential.span()) {
- suggestions.push((potential.span(), format!("{} = {}", name, snippet)));
- }
- }
+ let suggestions = iter::zip(unused_generics, &unbound_types)
+ .map(|(potential, name)| (potential.span().shrink_to_lo(), format!("{name} = ")))
+ .collect::<Vec<_>>();
if !suggestions.is_empty() {
- err.multipart_suggestion(
+ err.multipart_suggestion_verbose(
&format!(
"replace the generic bound{s} with the associated type{s}",
s = pluralize!(unbound_types.len())
diff --git a/compiler/rustc_typeck/src/variance/constraints.rs b/compiler/rustc_hir_analysis/src/variance/constraints.rs
index d79450e1a..eaf0310d5 100644
--- a/compiler/rustc_typeck/src/variance/constraints.rs
+++ b/compiler/rustc_hir_analysis/src/variance/constraints.rs
@@ -257,7 +257,7 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
self.add_constraints_from_invariant_substs(current, substs, variance);
}
- ty::Dynamic(data, r) => {
+ ty::Dynamic(data, r, _) => {
// The type `Foo<T+'a>` is contravariant w/r/t `'a`:
let contra = self.contravariant(variance);
self.add_constraints_from_region(current, r, contra);
@@ -271,11 +271,11 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
}
for projection in data.projection_bounds() {
- match projection.skip_binder().term {
- ty::Term::Ty(ty) => {
+ match projection.skip_binder().term.unpack() {
+ ty::TermKind::Ty(ty) => {
self.add_constraints_from_ty(current, ty, self.invariant);
}
- ty::Term::Const(c) => {
+ ty::TermKind::Const(c) => {
self.add_constraints_from_const(current, c, self.invariant)
}
}
@@ -411,11 +411,7 @@ impl<'a, 'tcx> ConstraintContext<'a, 'tcx> {
// way early-bound regions do, so we skip them here.
}
- ty::ReFree(..)
- | ty::ReVar(..)
- | ty::RePlaceholder(..)
- | ty::ReEmpty(_)
- | ty::ReErased => {
+ ty::ReFree(..) | ty::ReVar(..) | ty::RePlaceholder(..) | ty::ReErased => {
// We don't expect to see anything but 'static or bound
// regions when visiting member types or method types.
bug!(
diff --git a/compiler/rustc_typeck/src/variance/mod.rs b/compiler/rustc_hir_analysis/src/variance/mod.rs
index 82103c5a0..82103c5a0 100644
--- a/compiler/rustc_typeck/src/variance/mod.rs
+++ b/compiler/rustc_hir_analysis/src/variance/mod.rs
diff --git a/compiler/rustc_typeck/src/variance/solve.rs b/compiler/rustc_hir_analysis/src/variance/solve.rs
index 97aca621a..97aca621a 100644
--- a/compiler/rustc_typeck/src/variance/solve.rs
+++ b/compiler/rustc_hir_analysis/src/variance/solve.rs
diff --git a/compiler/rustc_typeck/src/variance/terms.rs b/compiler/rustc_hir_analysis/src/variance/terms.rs
index 1f763011e..1f763011e 100644
--- a/compiler/rustc_typeck/src/variance/terms.rs
+++ b/compiler/rustc_hir_analysis/src/variance/terms.rs
diff --git a/compiler/rustc_hir_analysis/src/variance/test.rs b/compiler/rustc_hir_analysis/src/variance/test.rs
new file mode 100644
index 000000000..83ed3e44b
--- /dev/null
+++ b/compiler/rustc_hir_analysis/src/variance/test.rs
@@ -0,0 +1,15 @@
+use rustc_errors::struct_span_err;
+use rustc_middle::ty::TyCtxt;
+use rustc_span::symbol::sym;
+
+pub fn test_variance(tcx: TyCtxt<'_>) {
+ // For unit testing: check for a special "rustc_variance"
+ // attribute and report an error with various results if found.
+ for id in tcx.hir().items() {
+ if tcx.has_attr(id.owner_id.to_def_id(), sym::rustc_variance) {
+ let variances_of = tcx.variances_of(id.owner_id);
+ struct_span_err!(tcx.sess, tcx.def_span(id.owner_id), E0208, "{:?}", variances_of)
+ .emit();
+ }
+ }
+}
diff --git a/compiler/rustc_typeck/src/variance/xform.rs b/compiler/rustc_hir_analysis/src/variance/xform.rs
index 027f0859f..027f0859f 100644
--- a/compiler/rustc_typeck/src/variance/xform.rs
+++ b/compiler/rustc_hir_analysis/src/variance/xform.rs