diff options
Diffstat (limited to 'compiler/rustc_typeck')
82 files changed, 0 insertions, 56727 deletions
diff --git a/compiler/rustc_typeck/Cargo.toml b/compiler/rustc_typeck/Cargo.toml deleted file mode 100644 index cae29c1d3..000000000 --- a/compiler/rustc_typeck/Cargo.toml +++ /dev/null @@ -1,33 +0,0 @@ -[package] -name = "rustc_typeck" -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_ty_utils = { path = "../rustc_ty_utils" } -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_typeck/README.md deleted file mode 100644 index b61dbd8c9..000000000 --- a/compiler/rustc_typeck/README.md +++ /dev/null @@ -1,5 +0,0 @@ -For high-level intro to how type checking works in rustc, see the -[type checking] chapter of the [rustc dev guide]. - -[type checking]: https://rustc-dev-guide.rust-lang.org/type-checking.html -[rustc dev guide]: https://rustc-dev-guide.rust-lang.org/ diff --git a/compiler/rustc_typeck/src/astconv/errors.rs b/compiler/rustc_typeck/src/astconv/errors.rs deleted file mode 100644 index a9152bdc5..000000000 --- a/compiler/rustc_typeck/src/astconv/errors.rs +++ /dev/null @@ -1,411 +0,0 @@ -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_typeck/src/astconv/generics.rs b/compiler/rustc_typeck/src/astconv/generics.rs deleted file mode 100644 index afac75de2..000000000 --- a/compiler/rustc_typeck/src/astconv/generics.rs +++ /dev/null @@ -1,663 +0,0 @@ -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, ¶m.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 param_type = tcx.infer_ctxt().enter(|infcx| { - 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(¶m) = 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(¶m) = 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(¶m)) => { - match (arg, ¶m.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(¶m)) => { - // 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 && !gen_args.bindings.is_empty() { - Self::prohibit_assoc_ty_binding(tcx, gen_args.bindings[0].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, - |lint| { - lint.build(msg).emit(); - }, - ); - } - - ExplicitLateBound::Yes - } else { - ExplicitLateBound::No - } - } -} diff --git a/compiler/rustc_typeck/src/astconv/mod.rs b/compiler/rustc_typeck/src/astconv/mod.rs deleted file mode 100644 index 4bf9562e2..000000000 --- a/compiler/rustc_typeck/src/astconv/mod.rs +++ /dev/null @@ -1,3110 +0,0 @@ -//! 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, Subst, 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::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, - ); - let assoc_bindings = self.create_assoc_bindings_for_generic_args(item_segment.args()); - - if let Some(b) = assoc_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>>, - ) -> (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(&[]), 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 (¶m.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, - ); - - (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(crate) fn create_substs_for_associated_item( - &self, - tcx: TyCtxt<'tcx>, - 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 - ); - if tcx.generics_of(item_def_id).params.is_empty() { - self.prohibit_generics(slice::from_ref(item_segment).iter(), |_| {}); - - parent_substs - } else { - let (args, _) = self.create_substs_for_ast_path( - span, - item_def_id, - parent_substs, - item_segment, - item_segment.args(), - item_segment.infer_args, - None, - ); - - let assoc_bindings = self.create_assoc_bindings_for_generic_args(item_segment.args()); - if let Some(b) = assoc_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>, - ) -> 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, - ) - } - - 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), - ); - - 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), - ); - // 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, - ) -> ty::TraitRef<'tcx> { - let (substs, _) = self.create_substs_for_ast_trait_ref( - span, - trait_def_id, - self_ty, - trait_segment, - is_impl, - ); - let assoc_bindings = self.create_assoc_bindings_for_generic_args(trait_segment.args()); - if let Some(b) = assoc_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, - ) -> (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), - ) - } - - 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, - ) -> 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( - tcx, - path_span, - assoc_item.def_id, - &item_segment, - trait_ref.substs, - ); - - debug!( - "add_predicates_for_ast_type_binding: substs for trait-ref and assoc_item: {:?}", - 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 = {:?}", late_bound_in_trait_ref); - debug!("late_bound_in_ty = {:?}", 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 = ®ular_traits[0]; - let additional_trait = ®ular_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 ®ular_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. - 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!("one_bound_for_assoc_type: bound = {:?}", bound); - - if let Some(bound2) = next_cand { - debug!("one_bound_for_assoc_type: bound2 = {:?}", 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. - 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 - }; - - debug!("associated_path_to_ty: {:?}::{}", qself_ty, assoc_ident); - - // 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::SelfTy { trait_: Some(_), alias_to: Some((impl_def_id, _)) }) => { - // `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::SelfTy { trait_: Some(param_did), alias_to: None } - | 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, |lint| { - let mut err = lint.build("ambiguous associated item"); - 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) - ); - err.span_note(tcx.def_span(def_id), ¬e_msg); - }; - - could_refer_to(DefKind::Variant, variant_def_id, ""); - could_refer_to(kind, item.def_id, " also"); - - err.span_suggestion( - span, - "use fully-qualified syntax", - format!("<{} as {}>::{}", qself_ty, tcx.item_name(trait_did), assoc_ident), - Applicability::MachineApplicable, - ); - - err.emit(); - }); - } - 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<'_>, - ) -> 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); - - let item_substs = self.create_substs_for_associated_item( - tcx, - 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 [binding, ..] = segment.args().bindings { - Self::prohibit_assoc_ty_binding(self.tcx(), binding.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::SelfTy { trait_: Some(_), alias_to: None } => { - // `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::SelfTy { trait_: _, alias_to: Some((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(), |_| {}); - self.qpath_to_ty( - span, - opt_self_ty, - def_id, - &path.segments[path.segments.len() - 2], - path.segments.last().unwrap(), - ) - } - 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.def_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, - ); - 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), - } - } - - 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> { - debug!("ty_of_fn"); - - 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!("ty_of_fn: output_ty={:?}", 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)); - - 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); - 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 sugg = Vec::from_iter([ - ( - self_ty.span.shrink_to_lo(), - format!( - "{}dyn {}", - if needs_bracket { "<" } else { "" }, - if is_global { "(" } else { "" }, - ), - ), - ( - 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, - |lint| { - let mut diag = lint.build(msg); - diag.multipart_suggestion_verbose( - "use `dyn`", - sugg, - Applicability::MachineApplicable, - ); - self.maybe_lint_blanket_trait_impl(&self_ty, &mut diag); - diag.emit(); - }, - ); - } - } - } -} diff --git a/compiler/rustc_typeck/src/bounds.rs b/compiler/rustc_typeck/src/bounds.rs deleted file mode 100644 index 6a28bb16a..000000000 --- a/compiler/rustc_typeck/src/bounds.rs +++ /dev/null @@ -1,90 +0,0 @@ -//! Bounds are restrictions applied to some types after they've been converted into the -//! `ty` form from the HIR. - -use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt}; -use rustc_span::Span; - -/// Collects together a list of type bounds. These lists of bounds occur in many places -/// in Rust's syntax: -/// -/// ```text -/// trait Foo: Bar + Baz { } -/// ^^^^^^^^^ supertrait list bounding the `Self` type parameter -/// -/// fn foo<T: Bar + Baz>() { } -/// ^^^^^^^^^ bounding the type parameter `T` -/// -/// impl dyn Bar + Baz -/// ^^^^^^^^^ bounding the forgotten dynamic type -/// ``` -/// -/// Our representation is a bit mixed here -- in some cases, we -/// include the self type (e.g., `trait_bounds`) but in others we do not -#[derive(Default, PartialEq, Eq, Clone, Debug)] -pub struct Bounds<'tcx> { - /// A list of region bounds on the (implicit) self type. So if you - /// had `T: 'a + 'b` this might would be a list `['a, 'b]` (but - /// the `T` is not explicitly included). - pub region_bounds: Vec<(ty::Binder<'tcx, ty::Region<'tcx>>, Span)>, - - /// A list of trait bounds. So if you had `T: Debug` this would be - /// `T: Debug`. Note that the self-type is explicit here. - pub trait_bounds: Vec<(ty::PolyTraitRef<'tcx>, Span, ty::BoundConstness)>, - - /// A list of projection equality bounds. So if you had `T: - /// Iterator<Item = u32>` this would include `<T as - /// Iterator>::Item => u32`. Note that the self-type is explicit - /// here. - pub projection_bounds: Vec<(ty::PolyProjectionPredicate<'tcx>, Span)>, - - /// `Some` if there is *no* `?Sized` predicate. The `span` - /// is the location in the source of the `T` declaration which can - /// be cited as the source of the `T: Sized` requirement. - pub implicitly_sized: Option<Span>, -} - -impl<'tcx> Bounds<'tcx> { - /// Converts a bounds list into a flat set of predicates (like - /// where-clauses). Because some of our bounds listings (e.g., - /// regions) don't include the self-type, you must supply the - /// self-type here (the `param_ty` parameter). - pub fn predicates<'out, 's>( - &'s self, - tcx: TyCtxt<'tcx>, - param_ty: Ty<'tcx>, - // the output must live shorter than the duration of the borrow of self and 'tcx. - ) -> impl Iterator<Item = (ty::Predicate<'tcx>, Span)> + 'out - where - 'tcx: 'out, - 's: 'out, - { - // If it could be sized, and is, add the `Sized` predicate. - let sized_predicate = self.implicitly_sized.and_then(|span| { - tcx.lang_items().sized_trait().map(move |sized| { - let trait_ref = ty::Binder::dummy(ty::TraitRef { - def_id: sized, - substs: tcx.mk_substs_trait(param_ty, &[]), - }); - (trait_ref.without_const().to_predicate(tcx), span) - }) - }); - - let region_preds = self.region_bounds.iter().map(move |&(region_bound, span)| { - let pred = region_bound - .map_bound(|region_bound| ty::OutlivesPredicate(param_ty, region_bound)) - .to_predicate(tcx); - (pred, span) - }); - let trait_bounds = - self.trait_bounds.iter().map(move |&(bound_trait_ref, span, constness)| { - let predicate = bound_trait_ref.with_constness(constness).to_predicate(tcx); - (predicate, span) - }); - let projection_bounds = self - .projection_bounds - .iter() - .map(move |&(projection, span)| (projection.to_predicate(tcx), span)); - - sized_predicate.into_iter().chain(region_preds).chain(trait_bounds).chain(projection_bounds) - } -} diff --git a/compiler/rustc_typeck/src/check/_match.rs b/compiler/rustc_typeck/src/check/_match.rs deleted file mode 100644 index 20332e75c..000000000 --- a/compiler/rustc_typeck/src/check/_match.rs +++ /dev/null @@ -1,557 +0,0 @@ -use crate::check::coercion::{AsCoercionSite, CoerceMany}; -use crate::check::{Diverges, Expectation, FnCtxt, Needs}; -use rustc_errors::{Applicability, MultiSpan}; -use rustc_hir::{self as hir, ExprKind}; -use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; -use rustc_infer::traits::Obligation; -use rustc_middle::ty::{self, Subst, ToPredicate, Ty}; -use rustc_span::Span; -use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; -use rustc_trait_selection::traits::{ - IfExpressionCause, MatchExpressionArmCause, ObligationCause, ObligationCauseCode, -}; - -impl<'a, 'tcx> FnCtxt<'a, 'tcx> { - #[instrument(skip(self), level = "debug", ret)] - pub fn check_match( - &self, - expr: &'tcx hir::Expr<'tcx>, - scrut: &'tcx hir::Expr<'tcx>, - arms: &'tcx [hir::Arm<'tcx>], - orig_expected: Expectation<'tcx>, - match_src: hir::MatchSource, - ) -> Ty<'tcx> { - let tcx = self.tcx; - - let acrb = arms_contain_ref_bindings(arms); - let scrutinee_ty = self.demand_scrutinee_type(scrut, acrb, arms.is_empty()); - debug!(?scrutinee_ty); - - // If there are no arms, that is a diverging match; a special case. - if arms.is_empty() { - self.diverges.set(self.diverges.get() | Diverges::always(expr.span)); - return tcx.types.never; - } - - self.warn_arms_when_scrutinee_diverges(arms); - - // Otherwise, we have to union together the types that the arms produce and so forth. - let scrut_diverges = self.diverges.replace(Diverges::Maybe); - - // #55810: Type check patterns first so we get types for all bindings. - let scrut_span = scrut.span.find_ancestor_inside(expr.span).unwrap_or(scrut.span); - for arm in arms { - self.check_pat_top(&arm.pat, scrutinee_ty, Some(scrut_span), true); - } - - // Now typecheck the blocks. - // - // The result of the match is the common supertype of all the - // arms. Start out the value as bottom, since it's the, well, - // bottom the type lattice, and we'll be moving up the lattice as - // we process each arm. (Note that any match with 0 arms is matching - // on any empty type and is therefore unreachable; should the flow - // of execution reach it, we will panic, so bottom is an appropriate - // type in that case) - let mut all_arms_diverge = Diverges::WarnedAlways; - - let expected = orig_expected.adjust_for_branches(self); - debug!(?expected); - - let mut coercion = { - let coerce_first = match expected { - // We don't coerce to `()` so that if the match expression is a - // statement it's branches can have any consistent type. That allows - // us to give better error messages (pointing to a usually better - // arm for inconsistent arms or to the whole match when a `()` type - // is required). - Expectation::ExpectHasType(ety) if ety != self.tcx.mk_unit() => ety, - _ => self.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::MiscVariable, - span: expr.span, - }), - }; - CoerceMany::with_coercion_sites(coerce_first, arms) - }; - - let mut other_arms = vec![]; // Used only for diagnostics. - let mut prior_arm = None; - for arm in arms { - if let Some(g) = &arm.guard { - self.diverges.set(Diverges::Maybe); - match g { - hir::Guard::If(e) => { - self.check_expr_has_type_or_error(e, tcx.types.bool, |_| {}); - } - hir::Guard::IfLet(l) => { - self.check_expr_let(l); - } - }; - } - - self.diverges.set(Diverges::Maybe); - - let arm_ty = self.check_expr_with_expectation(&arm.body, expected); - all_arms_diverge &= self.diverges.get(); - - let opt_suggest_box_span = prior_arm.and_then(|(_, prior_arm_ty, _)| { - self.opt_suggest_box_span(prior_arm_ty, arm_ty, orig_expected) - }); - - let (arm_block_id, arm_span) = if let hir::ExprKind::Block(blk, _) = arm.body.kind { - (Some(blk.hir_id), self.find_block_span(blk)) - } else { - (None, arm.body.span) - }; - - let (span, code) = match prior_arm { - // The reason for the first arm to fail is not that the match arms diverge, - // but rather that there's a prior obligation that doesn't hold. - None => (arm_span, ObligationCauseCode::BlockTailExpression(arm.body.hir_id)), - Some((prior_arm_block_id, prior_arm_ty, prior_arm_span)) => ( - expr.span, - ObligationCauseCode::MatchExpressionArm(Box::new(MatchExpressionArmCause { - arm_block_id, - arm_span, - arm_ty, - prior_arm_block_id, - prior_arm_ty, - prior_arm_span, - scrut_span: scrut.span, - source: match_src, - prior_arms: other_arms.clone(), - scrut_hir_id: scrut.hir_id, - opt_suggest_box_span, - })), - ), - }; - let cause = self.cause(span, code); - - // This is the moral equivalent of `coercion.coerce(self, cause, arm.body, arm_ty)`. - // We use it this way to be able to expand on the potential error and detect when a - // `match` tail statement could be a tail expression instead. If so, we suggest - // removing the stray semicolon. - coercion.coerce_inner( - self, - &cause, - Some(&arm.body), - arm_ty, - Some(&mut |err| { - let Some(ret) = self - .tcx - .hir() - .find_by_def_id(self.body_id.owner) - .and_then(|owner| owner.fn_decl()) - .map(|decl| decl.output.span()) - else { return; }; - let Expectation::IsLast(stmt) = orig_expected else { - return - }; - let can_coerce_to_return_ty = match self.ret_coercion.as_ref() { - Some(ret_coercion) if self.in_tail_expr => { - let ret_ty = ret_coercion.borrow().expected_ty(); - let ret_ty = self.inh.infcx.shallow_resolve(ret_ty); - self.can_coerce(arm_ty, ret_ty) - && prior_arm.map_or(true, |(_, t, _)| self.can_coerce(t, ret_ty)) - // The match arms need to unify for the case of `impl Trait`. - && !matches!(ret_ty.kind(), ty::Opaque(..)) - } - _ => false, - }; - if !can_coerce_to_return_ty { - return; - } - - let semi_span = expr.span.shrink_to_hi().with_hi(stmt.hi()); - let mut ret_span: MultiSpan = semi_span.into(); - ret_span.push_span_label( - expr.span, - "this could be implicitly returned but it is a statement, not a \ - tail expression", - ); - ret_span - .push_span_label(ret, "the `match` arms can conform to this return type"); - ret_span.push_span_label( - semi_span, - "the `match` is a statement because of this semicolon, consider \ - removing it", - ); - err.span_note( - ret_span, - "you might have meant to return the `match` expression", - ); - err.tool_only_span_suggestion( - semi_span, - "remove this semicolon", - "", - Applicability::MaybeIncorrect, - ); - }), - false, - ); - - other_arms.push(arm_span); - if other_arms.len() > 5 { - other_arms.remove(0); - } - - prior_arm = Some((arm_block_id, arm_ty, arm_span)); - } - - // If all of the arms in the `match` diverge, - // and we're dealing with an actual `match` block - // (as opposed to a `match` desugared from something else'), - // we can emit a better note. Rather than pointing - // at a diverging expression in an arbitrary arm, - // we can point at the entire `match` expression - if let (Diverges::Always { .. }, hir::MatchSource::Normal) = (all_arms_diverge, match_src) { - all_arms_diverge = Diverges::Always { - span: expr.span, - custom_note: Some( - "any code following this `match` expression is unreachable, as all arms diverge", - ), - }; - } - - // We won't diverge unless the scrutinee or all arms diverge. - self.diverges.set(scrut_diverges | all_arms_diverge); - - coercion.complete(self) - } - - /// When the previously checked expression (the scrutinee) diverges, - /// warn the user about the match arms being unreachable. - fn warn_arms_when_scrutinee_diverges(&self, arms: &'tcx [hir::Arm<'tcx>]) { - for arm in arms { - self.warn_if_unreachable(arm.body.hir_id, arm.body.span, "arm"); - } - } - - /// Handle the fallback arm of a desugared if(-let) like a missing else. - /// - /// Returns `true` if there was an error forcing the coercion to the `()` type. - pub(super) fn if_fallback_coercion<T>( - &self, - span: Span, - then_expr: &'tcx hir::Expr<'tcx>, - coercion: &mut CoerceMany<'tcx, '_, T>, - ) -> bool - where - T: AsCoercionSite, - { - // If this `if` expr is the parent's function return expr, - // the cause of the type coercion is the return type, point at it. (#25228) - let ret_reason = self.maybe_get_coercion_reason(then_expr.hir_id, span); - let cause = self.cause(span, ObligationCauseCode::IfExpressionWithNoElse); - let mut error = false; - coercion.coerce_forced_unit( - self, - &cause, - &mut |err| { - if let Some((span, msg)) = &ret_reason { - err.span_label(*span, msg); - } else if let ExprKind::Block(block, _) = &then_expr.kind - && let Some(expr) = &block.expr - { - err.span_label(expr.span, "found here"); - } - err.note("`if` expressions without `else` evaluate to `()`"); - err.help("consider adding an `else` block that evaluates to the expected type"); - error = true; - }, - ret_reason.is_none(), - ); - error - } - - fn maybe_get_coercion_reason(&self, hir_id: hir::HirId, sp: Span) -> Option<(Span, String)> { - let node = { - let rslt = self.tcx.hir().get_parent_node(self.tcx.hir().get_parent_node(hir_id)); - self.tcx.hir().get(rslt) - }; - if let hir::Node::Block(block) = node { - // check that the body's parent is an fn - let parent = self - .tcx - .hir() - .get(self.tcx.hir().get_parent_node(self.tcx.hir().get_parent_node(block.hir_id))); - if let (Some(expr), hir::Node::Item(hir::Item { kind: hir::ItemKind::Fn(..), .. })) = - (&block.expr, parent) - { - // check that the `if` expr without `else` is the fn body's expr - if expr.span == sp { - return self.get_fn_decl(hir_id).and_then(|(fn_decl, _)| { - let span = fn_decl.output.span(); - let snippet = self.tcx.sess.source_map().span_to_snippet(span).ok()?; - Some((span, format!("expected `{snippet}` because of this return type"))) - }); - } - } - } - if let hir::Node::Local(hir::Local { ty: Some(_), pat, .. }) = node { - return Some((pat.span, "expected because of this assignment".to_string())); - } - None - } - - pub(crate) fn if_cause( - &self, - span: Span, - cond_span: Span, - then_expr: &'tcx hir::Expr<'tcx>, - else_expr: &'tcx hir::Expr<'tcx>, - then_ty: Ty<'tcx>, - else_ty: Ty<'tcx>, - opt_suggest_box_span: Option<Span>, - ) -> ObligationCause<'tcx> { - let mut outer_span = if self.tcx.sess.source_map().is_multiline(span) { - // The `if`/`else` isn't in one line in the output, include some context to make it - // clear it is an if/else expression: - // ``` - // LL | let x = if true { - // | _____________- - // LL || 10i32 - // || ----- expected because of this - // LL || } else { - // LL || 10u32 - // || ^^^^^ expected `i32`, found `u32` - // LL || }; - // ||_____- `if` and `else` have incompatible types - // ``` - Some(span) - } else { - // The entire expression is in one line, only point at the arms - // ``` - // LL | let x = if true { 10i32 } else { 10u32 }; - // | ----- ^^^^^ expected `i32`, found `u32` - // | | - // | expected because of this - // ``` - None - }; - - let (error_sp, else_id) = if let ExprKind::Block(block, _) = &else_expr.kind { - let block = block.innermost_block(); - - // Avoid overlapping spans that aren't as readable: - // ``` - // 2 | let x = if true { - // | _____________- - // 3 | | 3 - // | | - expected because of this - // 4 | | } else { - // | |____________^ - // 5 | || - // 6 | || }; - // | || ^ - // | ||_____| - // | |______if and else have incompatible types - // | expected integer, found `()` - // ``` - // by not pointing at the entire expression: - // ``` - // 2 | let x = if true { - // | ------- `if` and `else` have incompatible types - // 3 | 3 - // | - expected because of this - // 4 | } else { - // | ____________^ - // 5 | | - // 6 | | }; - // | |_____^ expected integer, found `()` - // ``` - if block.expr.is_none() && block.stmts.is_empty() - && let Some(outer_span) = &mut outer_span - && let Some(cond_span) = cond_span.find_ancestor_inside(*outer_span) - { - *outer_span = outer_span.with_hi(cond_span.hi()) - } - - (self.find_block_span(block), block.hir_id) - } else { - (else_expr.span, else_expr.hir_id) - }; - - let then_id = if let ExprKind::Block(block, _) = &then_expr.kind { - let block = block.innermost_block(); - // Exclude overlapping spans - if block.expr.is_none() && block.stmts.is_empty() { - outer_span = None; - } - block.hir_id - } else { - then_expr.hir_id - }; - - // Finally construct the cause: - self.cause( - error_sp, - ObligationCauseCode::IfExpression(Box::new(IfExpressionCause { - else_id, - then_id, - then_ty, - else_ty, - outer_span, - opt_suggest_box_span, - })), - ) - } - - pub(super) fn demand_scrutinee_type( - &self, - scrut: &'tcx hir::Expr<'tcx>, - contains_ref_bindings: Option<hir::Mutability>, - no_arms: bool, - ) -> Ty<'tcx> { - // Not entirely obvious: if matches may create ref bindings, we want to - // use the *precise* type of the scrutinee, *not* some supertype, as - // the "scrutinee type" (issue #23116). - // - // arielb1 [writes here in this comment thread][c] that there - // is certainly *some* potential danger, e.g., for an example - // like: - // - // [c]: https://github.com/rust-lang/rust/pull/43399#discussion_r130223956 - // - // ``` - // let Foo(x) = f()[0]; - // ``` - // - // Then if the pattern matches by reference, we want to match - // `f()[0]` as a lexpr, so we can't allow it to be - // coerced. But if the pattern matches by value, `f()[0]` is - // still syntactically a lexpr, but we *do* want to allow - // coercions. - // - // However, *likely* we are ok with allowing coercions to - // happen if there are no explicit ref mut patterns - all - // implicit ref mut patterns must occur behind a reference, so - // they will have the "correct" variance and lifetime. - // - // This does mean that the following pattern would be legal: - // - // ``` - // struct Foo(Bar); - // struct Bar(u32); - // impl Deref for Foo { - // type Target = Bar; - // fn deref(&self) -> &Bar { &self.0 } - // } - // impl DerefMut for Foo { - // fn deref_mut(&mut self) -> &mut Bar { &mut self.0 } - // } - // fn foo(x: &mut Foo) { - // { - // let Bar(z): &mut Bar = x; - // *z = 42; - // } - // assert_eq!(foo.0.0, 42); - // } - // ``` - // - // FIXME(tschottdorf): don't call contains_explicit_ref_binding, which - // is problematic as the HIR is being scraped, but ref bindings may be - // implicit after #42640. We need to make sure that pat_adjustments - // (once introduced) is populated by the time we get here. - // - // See #44848. - if let Some(m) = contains_ref_bindings { - self.check_expr_with_needs(scrut, Needs::maybe_mut_place(m)) - } else if no_arms { - self.check_expr(scrut) - } else { - // ...but otherwise we want to use any supertype of the - // scrutinee. This is sort of a workaround, see note (*) in - // `check_pat` for some details. - let scrut_ty = self.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::TypeInference, - span: scrut.span, - }); - self.check_expr_has_type_or_error(scrut, scrut_ty, |_| {}); - scrut_ty - } - } - - /// When we have a `match` as a tail expression in a `fn` with a returned `impl Trait` - /// we check if the different arms would work with boxed trait objects instead and - /// provide a structured suggestion in that case. - pub(crate) fn opt_suggest_box_span( - &self, - first_ty: Ty<'tcx>, - second_ty: Ty<'tcx>, - orig_expected: Expectation<'tcx>, - ) -> Option<Span> { - // FIXME(compiler-errors): This really shouldn't need to be done during the - // "good" path of typeck, but here we are. - match orig_expected { - Expectation::ExpectHasType(expected) => { - let TypeVariableOrigin { - span, - kind: TypeVariableOriginKind::OpaqueTypeInference(rpit_def_id), - .. - } = self.type_var_origin(expected)? else { return None; }; - - let sig = *self - .typeck_results - .borrow() - .liberated_fn_sigs() - .get(hir::HirId::make_owner(self.body_id.owner))?; - - let substs = sig.output().walk().find_map(|arg| { - if let ty::GenericArgKind::Type(ty) = arg.unpack() - && let ty::Opaque(def_id, substs) = *ty.kind() - && def_id == rpit_def_id - { - Some(substs) - } else { - None - } - })?; - let opaque_ty = self.tcx.mk_opaque(rpit_def_id, substs); - - if !self.can_coerce(first_ty, expected) || !self.can_coerce(second_ty, expected) { - return None; - } - - for ty in [first_ty, second_ty] { - for pred in self.tcx.bound_explicit_item_bounds(rpit_def_id).transpose_iter() { - let pred = pred.map_bound(|(pred, _)| *pred).subst(self.tcx, substs); - let pred = match pred.kind().skip_binder() { - ty::PredicateKind::Trait(mut trait_pred) => { - assert_eq!(trait_pred.trait_ref.self_ty(), opaque_ty); - trait_pred.trait_ref.substs = - self.tcx.mk_substs_trait(ty, &trait_pred.trait_ref.substs[1..]); - pred.kind().rebind(trait_pred).to_predicate(self.tcx) - } - ty::PredicateKind::Projection(mut proj_pred) => { - assert_eq!(proj_pred.projection_ty.self_ty(), opaque_ty); - proj_pred.projection_ty.substs = self - .tcx - .mk_substs_trait(ty, &proj_pred.projection_ty.substs[1..]); - pred.kind().rebind(proj_pred).to_predicate(self.tcx) - } - _ => continue, - }; - if !self.predicate_must_hold_modulo_regions(&Obligation::new( - ObligationCause::misc(span, self.body_id), - self.param_env, - pred, - )) { - return None; - } - } - } - - Some(span) - } - _ => None, - } - } -} - -fn arms_contain_ref_bindings<'tcx>(arms: &'tcx [hir::Arm<'tcx>]) -> Option<hir::Mutability> { - arms.iter().filter_map(|a| a.pat.contains_explicit_ref_binding()).max_by_key(|m| match *m { - hir::Mutability::Mut => 1, - hir::Mutability::Not => 0, - }) -} diff --git a/compiler/rustc_typeck/src/check/autoderef.rs b/compiler/rustc_typeck/src/check/autoderef.rs deleted file mode 100644 index 59c366ad7..000000000 --- a/compiler/rustc_typeck/src/check/autoderef.rs +++ /dev/null @@ -1,78 +0,0 @@ -//! Some helper functions for `AutoDeref` -use super::method::MethodCallee; -use super::{FnCtxt, PlaceOp}; - -use rustc_infer::infer::InferOk; -use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref}; -use rustc_middle::ty::{self, Ty}; -use rustc_span::Span; -use rustc_trait_selection::autoderef::{Autoderef, AutoderefKind}; - -use std::iter; - -impl<'a, 'tcx> FnCtxt<'a, 'tcx> { - pub fn autoderef(&'a self, span: Span, base_ty: Ty<'tcx>) -> Autoderef<'a, 'tcx> { - Autoderef::new(self, self.param_env, self.body_id, span, base_ty, span) - } - - /// Like `autoderef`, but provides a custom `Span` to use for calls to - /// an overloaded `Deref` operator - pub fn autoderef_overloaded_span( - &'a self, - span: Span, - base_ty: Ty<'tcx>, - overloaded_span: Span, - ) -> Autoderef<'a, 'tcx> { - Autoderef::new(self, self.param_env, self.body_id, span, base_ty, overloaded_span) - } - - pub fn try_overloaded_deref( - &self, - span: Span, - base_ty: Ty<'tcx>, - ) -> Option<InferOk<'tcx, MethodCallee<'tcx>>> { - self.try_overloaded_place_op(span, base_ty, &[], PlaceOp::Deref) - } - - /// Returns the adjustment steps. - pub fn adjust_steps(&self, autoderef: &Autoderef<'a, 'tcx>) -> Vec<Adjustment<'tcx>> { - self.register_infer_ok_obligations(self.adjust_steps_as_infer_ok(autoderef)) - } - - pub fn adjust_steps_as_infer_ok( - &self, - autoderef: &Autoderef<'a, 'tcx>, - ) -> InferOk<'tcx, Vec<Adjustment<'tcx>>> { - let mut obligations = vec![]; - let steps = autoderef.steps(); - let targets = - steps.iter().skip(1).map(|&(ty, _)| ty).chain(iter::once(autoderef.final_ty(false))); - let steps: Vec<_> = steps - .iter() - .map(|&(source, kind)| { - if let AutoderefKind::Overloaded = kind { - self.try_overloaded_deref(autoderef.span(), source).and_then( - |InferOk { value: method, obligations: o }| { - obligations.extend(o); - if let ty::Ref(region, _, mutbl) = *method.sig.output().kind() { - Some(OverloadedDeref { - region, - mutbl, - span: autoderef.overloaded_span(), - }) - } else { - None - } - }, - ) - } else { - None - } - }) - .zip(targets) - .map(|(autoderef, target)| Adjustment { kind: Adjust::Deref(autoderef), target }) - .collect(); - - InferOk { obligations, value: steps } - } -} diff --git a/compiler/rustc_typeck/src/check/callee.rs b/compiler/rustc_typeck/src/check/callee.rs deleted file mode 100644 index 0d35c2479..000000000 --- a/compiler/rustc_typeck/src/check/callee.rs +++ /dev/null @@ -1,694 +0,0 @@ -use super::method::MethodCallee; -use super::{DefIdOrName, Expectation, FnCtxt, TupleArgumentsFlag}; -use crate::type_error_struct; - -use rustc_errors::{struct_span_err, Applicability, Diagnostic}; -use rustc_hir as hir; -use rustc_hir::def::{self, Namespace, Res}; -use rustc_hir::def_id::DefId; -use rustc_infer::{ - infer, - traits::{self, Obligation}, -}; -use rustc_infer::{ - infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}, - traits::ObligationCause, -}; -use rustc_middle::ty::adjustment::{ - Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, -}; -use rustc_middle::ty::subst::{Subst, SubstsRef}; -use rustc_middle::ty::{self, Ty, TyCtxt, TypeVisitable}; -use rustc_span::def_id::LocalDefId; -use rustc_span::symbol::{sym, Ident}; -use rustc_span::Span; -use rustc_target::spec::abi; -use rustc_trait_selection::autoderef::Autoderef; -use rustc_trait_selection::infer::InferCtxtExt as _; -use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; - -use std::iter; - -/// Checks that it is legal to call methods of the trait corresponding -/// to `trait_id` (this only cares about the trait, not the specific -/// method that is called). -pub fn check_legal_trait_for_method_call( - tcx: TyCtxt<'_>, - span: Span, - receiver: Option<Span>, - expr_span: Span, - trait_id: DefId, -) { - if tcx.lang_items().drop_trait() == Some(trait_id) { - let mut err = struct_span_err!(tcx.sess, span, E0040, "explicit use of destructor method"); - err.span_label(span, "explicit destructor calls not allowed"); - - let (sp, suggestion) = receiver - .and_then(|s| tcx.sess.source_map().span_to_snippet(s).ok()) - .filter(|snippet| !snippet.is_empty()) - .map(|snippet| (expr_span, format!("drop({snippet})"))) - .unwrap_or_else(|| (span, "drop".to_string())); - - err.span_suggestion( - sp, - "consider using `drop` function", - suggestion, - Applicability::MaybeIncorrect, - ); - - err.emit(); - } -} - -enum CallStep<'tcx> { - Builtin(Ty<'tcx>), - DeferredClosure(LocalDefId, ty::FnSig<'tcx>), - /// E.g., enum variant constructors. - Overloaded(MethodCallee<'tcx>), -} - -impl<'a, 'tcx> FnCtxt<'a, 'tcx> { - pub fn check_call( - &self, - call_expr: &'tcx hir::Expr<'tcx>, - callee_expr: &'tcx hir::Expr<'tcx>, - arg_exprs: &'tcx [hir::Expr<'tcx>], - expected: Expectation<'tcx>, - ) -> Ty<'tcx> { - let original_callee_ty = match &callee_expr.kind { - hir::ExprKind::Path(hir::QPath::Resolved(..) | hir::QPath::TypeRelative(..)) => self - .check_expr_with_expectation_and_args( - callee_expr, - Expectation::NoExpectation, - arg_exprs, - ), - _ => self.check_expr(callee_expr), - }; - - let expr_ty = self.structurally_resolved_type(call_expr.span, original_callee_ty); - - let mut autoderef = self.autoderef(callee_expr.span, expr_ty); - let mut result = None; - while result.is_none() && autoderef.next().is_some() { - result = self.try_overloaded_call_step(call_expr, callee_expr, arg_exprs, &autoderef); - } - self.register_predicates(autoderef.into_obligations()); - - let output = match result { - None => { - // this will report an error since original_callee_ty is not a fn - self.confirm_builtin_call( - call_expr, - callee_expr, - original_callee_ty, - arg_exprs, - expected, - ) - } - - Some(CallStep::Builtin(callee_ty)) => { - self.confirm_builtin_call(call_expr, callee_expr, callee_ty, arg_exprs, expected) - } - - Some(CallStep::DeferredClosure(def_id, fn_sig)) => { - self.confirm_deferred_closure_call(call_expr, arg_exprs, expected, def_id, fn_sig) - } - - Some(CallStep::Overloaded(method_callee)) => { - self.confirm_overloaded_call(call_expr, arg_exprs, expected, method_callee) - } - }; - - // we must check that return type of called functions is WF: - self.register_wf_obligation(output.into(), call_expr.span, traits::WellFormed(None)); - - output - } - - fn try_overloaded_call_step( - &self, - call_expr: &'tcx hir::Expr<'tcx>, - callee_expr: &'tcx hir::Expr<'tcx>, - arg_exprs: &'tcx [hir::Expr<'tcx>], - autoderef: &Autoderef<'a, 'tcx>, - ) -> Option<CallStep<'tcx>> { - let adjusted_ty = - self.structurally_resolved_type(autoderef.span(), autoderef.final_ty(false)); - debug!( - "try_overloaded_call_step(call_expr={:?}, adjusted_ty={:?})", - call_expr, adjusted_ty - ); - - // If the callee is a bare function or a closure, then we're all set. - match *adjusted_ty.kind() { - ty::FnDef(..) | ty::FnPtr(_) => { - let adjustments = self.adjust_steps(autoderef); - self.apply_adjustments(callee_expr, adjustments); - return Some(CallStep::Builtin(adjusted_ty)); - } - - ty::Closure(def_id, substs) => { - let def_id = def_id.expect_local(); - - // Check whether this is a call to a closure where we - // haven't yet decided on whether the closure is fn vs - // fnmut vs fnonce. If so, we have to defer further processing. - if self.closure_kind(substs).is_none() { - let closure_sig = substs.as_closure().sig(); - let closure_sig = self.replace_bound_vars_with_fresh_vars( - call_expr.span, - infer::FnCall, - closure_sig, - ); - let adjustments = self.adjust_steps(autoderef); - self.record_deferred_call_resolution( - def_id, - DeferredCallResolution { - call_expr, - callee_expr, - adjusted_ty, - adjustments, - fn_sig: closure_sig, - closure_substs: substs, - }, - ); - return Some(CallStep::DeferredClosure(def_id, closure_sig)); - } - } - - // Hack: we know that there are traits implementing Fn for &F - // where F:Fn and so forth. In the particular case of types - // like `x: &mut FnMut()`, if there is a call `x()`, we would - // normally translate to `FnMut::call_mut(&mut x, ())`, but - // that winds up requiring `mut x: &mut FnMut()`. A little - // over the top. The simplest fix by far is to just ignore - // this case and deref again, so we wind up with - // `FnMut::call_mut(&mut *x, ())`. - ty::Ref(..) if autoderef.step_count() == 0 => { - return None; - } - - _ => {} - } - - // Now, we look for the implementation of a Fn trait on the object's type. - // We first do it with the explicit instruction to look for an impl of - // `Fn<Tuple>`, with the tuple `Tuple` having an arity corresponding - // to the number of call parameters. - // If that fails (or_else branch), we try again without specifying the - // shape of the tuple (hence the None). This allows to detect an Fn trait - // is implemented, and use this information for diagnostic. - self.try_overloaded_call_traits(call_expr, adjusted_ty, Some(arg_exprs)) - .or_else(|| self.try_overloaded_call_traits(call_expr, adjusted_ty, None)) - .map(|(autoref, method)| { - let mut adjustments = self.adjust_steps(autoderef); - adjustments.extend(autoref); - self.apply_adjustments(callee_expr, adjustments); - CallStep::Overloaded(method) - }) - } - - fn try_overloaded_call_traits( - &self, - call_expr: &hir::Expr<'_>, - adjusted_ty: Ty<'tcx>, - opt_arg_exprs: Option<&'tcx [hir::Expr<'tcx>]>, - ) -> Option<(Option<Adjustment<'tcx>>, MethodCallee<'tcx>)> { - // Try the options that are least restrictive on the caller first. - for (opt_trait_def_id, method_name, borrow) in [ - (self.tcx.lang_items().fn_trait(), Ident::with_dummy_span(sym::call), true), - (self.tcx.lang_items().fn_mut_trait(), Ident::with_dummy_span(sym::call_mut), true), - (self.tcx.lang_items().fn_once_trait(), Ident::with_dummy_span(sym::call_once), false), - ] { - let Some(trait_def_id) = opt_trait_def_id else { continue }; - - let opt_input_types = opt_arg_exprs.map(|arg_exprs| { - [self.tcx.mk_tup(arg_exprs.iter().map(|e| { - self.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::TypeInference, - span: e.span, - }) - }))] - }); - let opt_input_types = opt_input_types.as_ref().map(AsRef::as_ref); - - if let Some(ok) = self.lookup_method_in_trait( - call_expr.span, - method_name, - trait_def_id, - adjusted_ty, - opt_input_types, - ) { - let method = self.register_infer_ok_obligations(ok); - let mut autoref = None; - if borrow { - // Check for &self vs &mut self in the method signature. Since this is either - // the Fn or FnMut trait, it should be one of those. - let ty::Ref(region, _, mutbl) = method.sig.inputs()[0].kind() else { - // The `fn`/`fn_mut` lang item is ill-formed, which should have - // caused an error elsewhere. - self.tcx - .sess - .delay_span_bug(call_expr.span, "input to call/call_mut is not a ref?"); - return None; - }; - - let mutbl = match mutbl { - hir::Mutability::Not => AutoBorrowMutability::Not, - hir::Mutability::Mut => AutoBorrowMutability::Mut { - // For initial two-phase borrow - // deployment, conservatively omit - // overloaded function call ops. - allow_two_phase_borrow: AllowTwoPhase::No, - }, - }; - autoref = Some(Adjustment { - kind: Adjust::Borrow(AutoBorrow::Ref(*region, mutbl)), - target: method.sig.inputs()[0], - }); - } - return Some((autoref, method)); - } - } - - None - } - - /// Give appropriate suggestion when encountering `||{/* not callable */}()`, where the - /// likely intention is to call the closure, suggest `(||{})()`. (#55851) - fn identify_bad_closure_def_and_call( - &self, - err: &mut Diagnostic, - hir_id: hir::HirId, - callee_node: &hir::ExprKind<'_>, - callee_span: Span, - ) { - let hir = self.tcx.hir(); - let parent_hir_id = hir.get_parent_node(hir_id); - let parent_node = hir.get(parent_hir_id); - if let ( - hir::Node::Expr(hir::Expr { - kind: hir::ExprKind::Closure(&hir::Closure { fn_decl_span, body, .. }), - .. - }), - hir::ExprKind::Block(..), - ) = (parent_node, callee_node) - { - let fn_decl_span = if hir.body(body).generator_kind - == Some(hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Closure)) - { - // Actually need to unwrap a few more layers of HIR to get to - // the _real_ closure... - let async_closure = hir.get_parent_node(hir.get_parent_node(parent_hir_id)); - if let hir::Node::Expr(hir::Expr { - kind: hir::ExprKind::Closure(&hir::Closure { fn_decl_span, .. }), - .. - }) = hir.get(async_closure) - { - fn_decl_span - } else { - return; - } - } else { - fn_decl_span - }; - - let start = fn_decl_span.shrink_to_lo(); - let end = callee_span.shrink_to_hi(); - err.multipart_suggestion( - "if you meant to create this closure and immediately call it, surround the \ - closure with parentheses", - vec![(start, "(".to_string()), (end, ")".to_string())], - Applicability::MaybeIncorrect, - ); - } - } - - /// Give appropriate suggestion when encountering `[("a", 0) ("b", 1)]`, where the - /// likely intention is to create an array containing tuples. - fn maybe_suggest_bad_array_definition( - &self, - err: &mut Diagnostic, - call_expr: &'tcx hir::Expr<'tcx>, - callee_expr: &'tcx hir::Expr<'tcx>, - ) -> bool { - let hir_id = self.tcx.hir().get_parent_node(call_expr.hir_id); - let parent_node = self.tcx.hir().get(hir_id); - if let ( - hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Array(_), .. }), - hir::ExprKind::Tup(exp), - hir::ExprKind::Call(_, args), - ) = (parent_node, &callee_expr.kind, &call_expr.kind) - && args.len() == exp.len() - { - let start = callee_expr.span.shrink_to_hi(); - err.span_suggestion( - start, - "consider separating array elements with a comma", - ",", - Applicability::MaybeIncorrect, - ); - return true; - } - false - } - - fn confirm_builtin_call( - &self, - call_expr: &'tcx hir::Expr<'tcx>, - callee_expr: &'tcx hir::Expr<'tcx>, - callee_ty: Ty<'tcx>, - arg_exprs: &'tcx [hir::Expr<'tcx>], - expected: Expectation<'tcx>, - ) -> Ty<'tcx> { - let (fn_sig, def_id) = match *callee_ty.kind() { - ty::FnDef(def_id, subst) => { - let fn_sig = self.tcx.bound_fn_sig(def_id).subst(self.tcx, subst); - - // Unit testing: function items annotated with - // `#[rustc_evaluate_where_clauses]` trigger special output - // to let us test the trait evaluation system. - if self.tcx.has_attr(def_id, sym::rustc_evaluate_where_clauses) { - let predicates = self.tcx.predicates_of(def_id); - let predicates = predicates.instantiate(self.tcx, subst); - for (predicate, predicate_span) in - predicates.predicates.iter().zip(&predicates.spans) - { - let obligation = Obligation::new( - ObligationCause::dummy_with_span(callee_expr.span), - self.param_env, - *predicate, - ); - let result = self.evaluate_obligation(&obligation); - self.tcx - .sess - .struct_span_err( - callee_expr.span, - &format!("evaluate({:?}) = {:?}", predicate, result), - ) - .span_label(*predicate_span, "predicate") - .emit(); - } - } - (fn_sig, Some(def_id)) - } - ty::FnPtr(sig) => (sig, None), - _ => { - let mut unit_variant = None; - if let hir::ExprKind::Path(qpath) = &callee_expr.kind - && let Res::Def(def::DefKind::Ctor(kind, def::CtorKind::Const), _) - = self.typeck_results.borrow().qpath_res(qpath, callee_expr.hir_id) - // Only suggest removing parens if there are no arguments - && arg_exprs.is_empty() - { - let descr = match kind { - def::CtorOf::Struct => "struct", - def::CtorOf::Variant => "enum variant", - }; - let removal_span = - callee_expr.span.shrink_to_hi().to(call_expr.span.shrink_to_hi()); - unit_variant = - Some((removal_span, descr, rustc_hir_pretty::qpath_to_string(qpath))); - } - - let callee_ty = self.resolve_vars_if_possible(callee_ty); - let mut err = type_error_struct!( - self.tcx.sess, - callee_expr.span, - callee_ty, - E0618, - "expected function, found {}", - match &unit_variant { - Some((_, kind, path)) => format!("{kind} `{path}`"), - None => format!("`{callee_ty}`"), - } - ); - - self.identify_bad_closure_def_and_call( - &mut err, - call_expr.hir_id, - &callee_expr.kind, - callee_expr.span, - ); - - if let Some((removal_span, kind, path)) = &unit_variant { - err.span_suggestion_verbose( - *removal_span, - &format!( - "`{path}` is a unit {kind}, and does not take parentheses to be constructed", - ), - "", - Applicability::MachineApplicable, - ); - } - - let mut inner_callee_path = None; - let def = match callee_expr.kind { - hir::ExprKind::Path(ref qpath) => { - self.typeck_results.borrow().qpath_res(qpath, callee_expr.hir_id) - } - hir::ExprKind::Call(ref inner_callee, _) => { - // If the call spans more than one line and the callee kind is - // itself another `ExprCall`, that's a clue that we might just be - // missing a semicolon (Issue #51055) - let call_is_multiline = - self.tcx.sess.source_map().is_multiline(call_expr.span); - if call_is_multiline { - err.span_suggestion( - callee_expr.span.shrink_to_hi(), - "consider using a semicolon here", - ";", - Applicability::MaybeIncorrect, - ); - } - if let hir::ExprKind::Path(ref inner_qpath) = inner_callee.kind { - inner_callee_path = Some(inner_qpath); - self.typeck_results.borrow().qpath_res(inner_qpath, inner_callee.hir_id) - } else { - Res::Err - } - } - _ => Res::Err, - }; - - if !self.maybe_suggest_bad_array_definition(&mut err, call_expr, callee_expr) { - if let Some((maybe_def, output_ty, _)) = self.extract_callable_info(callee_expr, callee_ty) - && !self.type_is_sized_modulo_regions(self.param_env, output_ty, callee_expr.span) - { - let descr = match maybe_def { - DefIdOrName::DefId(def_id) => self.tcx.def_kind(def_id).descr(def_id), - DefIdOrName::Name(name) => name, - }; - err.span_label( - callee_expr.span, - format!("this {descr} returns an unsized value `{output_ty}`, so it cannot be called") - ); - if let DefIdOrName::DefId(def_id) = maybe_def - && let Some(def_span) = self.tcx.hir().span_if_local(def_id) - { - err.span_label(def_span, "the callable type is defined here"); - } - } else { - err.span_label(call_expr.span, "call expression requires function"); - } - } - - if let Some(span) = self.tcx.hir().res_span(def) { - let callee_ty = callee_ty.to_string(); - let label = match (unit_variant, inner_callee_path) { - (Some((_, kind, path)), _) => Some(format!("{kind} `{path}` defined here")), - (_, Some(hir::QPath::Resolved(_, path))) => self - .tcx - .sess - .source_map() - .span_to_snippet(path.span) - .ok() - .map(|p| format!("`{p}` defined here returns `{callee_ty}`")), - _ => { - match def { - // Emit a different diagnostic for local variables, as they are not - // type definitions themselves, but rather variables *of* that type. - Res::Local(hir_id) => Some(format!( - "`{}` has type `{}`", - self.tcx.hir().name(hir_id), - callee_ty - )), - Res::Def(kind, def_id) if kind.ns() == Some(Namespace::ValueNS) => { - Some(format!( - "`{}` defined here", - self.tcx.def_path_str(def_id), - )) - } - _ => Some(format!("`{callee_ty}` defined here")), - } - } - }; - if let Some(label) = label { - err.span_label(span, label); - } - } - err.emit(); - - // This is the "default" function signature, used in case of error. - // In that case, we check each argument against "error" in order to - // set up all the node type bindings. - ( - ty::Binder::dummy(self.tcx.mk_fn_sig( - self.err_args(arg_exprs.len()).into_iter(), - self.tcx.ty_error(), - false, - hir::Unsafety::Normal, - abi::Abi::Rust, - )), - None, - ) - } - }; - - // Replace any late-bound regions that appear in the function - // signature with region variables. We also have to - // renormalize the associated types at this point, since they - // previously appeared within a `Binder<>` and hence would not - // have been normalized before. - let fn_sig = self.replace_bound_vars_with_fresh_vars(call_expr.span, infer::FnCall, fn_sig); - let fn_sig = self.normalize_associated_types_in(call_expr.span, fn_sig); - - // Call the generic checker. - let expected_arg_tys = self.expected_inputs_for_expected_output( - call_expr.span, - expected, - fn_sig.output(), - fn_sig.inputs(), - ); - self.check_argument_types( - call_expr.span, - call_expr, - fn_sig.inputs(), - expected_arg_tys, - arg_exprs, - fn_sig.c_variadic, - TupleArgumentsFlag::DontTupleArguments, - def_id, - ); - - fn_sig.output() - } - - fn confirm_deferred_closure_call( - &self, - call_expr: &'tcx hir::Expr<'tcx>, - arg_exprs: &'tcx [hir::Expr<'tcx>], - expected: Expectation<'tcx>, - closure_def_id: LocalDefId, - fn_sig: ty::FnSig<'tcx>, - ) -> Ty<'tcx> { - // `fn_sig` is the *signature* of the closure being called. We - // don't know the full details yet (`Fn` vs `FnMut` etc), but we - // do know the types expected for each argument and the return - // type. - - let expected_arg_tys = self.expected_inputs_for_expected_output( - call_expr.span, - expected, - fn_sig.output(), - fn_sig.inputs(), - ); - - self.check_argument_types( - call_expr.span, - call_expr, - fn_sig.inputs(), - expected_arg_tys, - arg_exprs, - fn_sig.c_variadic, - TupleArgumentsFlag::TupleArguments, - Some(closure_def_id.to_def_id()), - ); - - fn_sig.output() - } - - fn confirm_overloaded_call( - &self, - call_expr: &'tcx hir::Expr<'tcx>, - arg_exprs: &'tcx [hir::Expr<'tcx>], - expected: Expectation<'tcx>, - method_callee: MethodCallee<'tcx>, - ) -> Ty<'tcx> { - let output_type = self.check_method_argument_types( - call_expr.span, - call_expr, - Ok(method_callee), - arg_exprs, - TupleArgumentsFlag::TupleArguments, - expected, - ); - - self.write_method_call(call_expr.hir_id, method_callee); - output_type - } -} - -#[derive(Debug)] -pub struct DeferredCallResolution<'tcx> { - call_expr: &'tcx hir::Expr<'tcx>, - callee_expr: &'tcx hir::Expr<'tcx>, - adjusted_ty: Ty<'tcx>, - adjustments: Vec<Adjustment<'tcx>>, - fn_sig: ty::FnSig<'tcx>, - closure_substs: SubstsRef<'tcx>, -} - -impl<'a, 'tcx> DeferredCallResolution<'tcx> { - pub fn resolve(self, fcx: &FnCtxt<'a, 'tcx>) { - debug!("DeferredCallResolution::resolve() {:?}", self); - - // we should not be invoked until the closure kind has been - // determined by upvar inference - assert!(fcx.closure_kind(self.closure_substs).is_some()); - - // We may now know enough to figure out fn vs fnmut etc. - match fcx.try_overloaded_call_traits(self.call_expr, self.adjusted_ty, None) { - Some((autoref, method_callee)) => { - // One problem is that when we get here, we are going - // to have a newly instantiated function signature - // from the call trait. This has to be reconciled with - // the older function signature we had before. In - // principle we *should* be able to fn_sigs(), but we - // can't because of the annoying need for a TypeTrace. - // (This always bites me, should find a way to - // refactor it.) - let method_sig = method_callee.sig; - - debug!("attempt_resolution: method_callee={:?}", method_callee); - - for (method_arg_ty, self_arg_ty) in - iter::zip(method_sig.inputs().iter().skip(1), self.fn_sig.inputs()) - { - fcx.demand_eqtype(self.call_expr.span, *self_arg_ty, *method_arg_ty); - } - - fcx.demand_eqtype(self.call_expr.span, method_sig.output(), self.fn_sig.output()); - - let mut adjustments = self.adjustments; - adjustments.extend(autoref); - fcx.apply_adjustments(self.callee_expr, adjustments); - - fcx.write_method_call(self.call_expr.hir_id, method_callee); - } - None => { - // This can happen if `#![no_core]` is used and the `fn/fn_mut/fn_once` - // lang items are not defined (issue #86238). - let mut err = fcx.inh.tcx.sess.struct_span_err( - self.call_expr.span, - "failed to find an overloaded call trait for closure call", - ); - err.help( - "make sure the `fn`/`fn_mut`/`fn_once` lang items are defined \ - and have associated `call`/`call_mut`/`call_once` functions", - ); - err.emit(); - } - } - } -} diff --git a/compiler/rustc_typeck/src/check/cast.rs b/compiler/rustc_typeck/src/check/cast.rs deleted file mode 100644 index 81a979865..000000000 --- a/compiler/rustc_typeck/src/check/cast.rs +++ /dev/null @@ -1,1163 +0,0 @@ -//! Code for type-checking cast expressions. -//! -//! A cast `e as U` is valid if one of the following holds: -//! * `e` has type `T` and `T` coerces to `U`; *coercion-cast* -//! * `e` has type `*T`, `U` is `*U_0`, and either `U_0: Sized` or -//! pointer_kind(`T`) = pointer_kind(`U_0`); *ptr-ptr-cast* -//! * `e` has type `*T` and `U` is a numeric type, while `T: Sized`; *ptr-addr-cast* -//! * `e` is an integer and `U` is `*U_0`, while `U_0: Sized`; *addr-ptr-cast* -//! * `e` has type `T` and `T` and `U` are any numeric types; *numeric-cast* -//! * `e` is a C-like enum and `U` is an integer type; *enum-cast* -//! * `e` has type `bool` or `char` and `U` is an integer; *prim-int-cast* -//! * `e` has type `u8` and `U` is `char`; *u8-char-cast* -//! * `e` has type `&[T; n]` and `U` is `*const T`; *array-ptr-cast* -//! * `e` is a function pointer type and `U` has type `*T`, -//! while `T: Sized`; *fptr-ptr-cast* -//! * `e` is a function pointer type and `U` is an integer; *fptr-addr-cast* -//! -//! where `&.T` and `*T` are references of either mutability, -//! and where pointer_kind(`T`) is the kind of the unsize info -//! in `T` - the vtable for a trait definition (e.g., `fmt::Display` or -//! `Iterator`, not `Iterator<Item=u8>`) or a length (or `()` if `T: Sized`). -//! -//! Note that lengths are not adjusted when casting raw slices - -//! `T: *const [u16] as *const [u8]` creates a slice that only includes -//! half of the original memory. -//! -//! Casting is not transitive, that is, even if `e as U1 as U2` is a valid -//! expression, `e as U2` is not necessarily so (in fact it will only be valid if -//! `U1` coerces to `U2`). - -use super::FnCtxt; - -use crate::hir::def_id::DefId; -use crate::type_error_struct; -use hir::def_id::LOCAL_CRATE; -use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed}; -use rustc_hir as hir; -use rustc_infer::traits::{Obligation, ObligationCause, ObligationCauseCode}; -use rustc_middle::mir::Mutability; -use rustc_middle::ty::adjustment::AllowTwoPhase; -use rustc_middle::ty::cast::{CastKind, CastTy}; -use rustc_middle::ty::error::TypeError; -use rustc_middle::ty::subst::SubstsRef; -use rustc_middle::ty::{self, Binder, Ty, TypeAndMut, TypeVisitable, VariantDef}; -use rustc_session::lint; -use rustc_session::Session; -use rustc_span::symbol::sym; -use rustc_span::Span; -use rustc_trait_selection::infer::InferCtxtExt; -use rustc_trait_selection::traits::error_reporting::report_object_safety_error; - -/// Reifies a cast check to be checked once we have full type information for -/// a function context. -#[derive(Debug)] -pub struct CastCheck<'tcx> { - /// The expression whose value is being casted - expr: &'tcx hir::Expr<'tcx>, - /// The source type for the cast expression - expr_ty: Ty<'tcx>, - expr_span: Span, - /// The target type. That is, the type we are casting to. - cast_ty: Ty<'tcx>, - cast_span: Span, - span: Span, -} - -/// The kind of pointer and associated metadata (thin, length or vtable) - we -/// only allow casts between fat pointers if their metadata have the same -/// kind. -#[derive(Copy, Clone, PartialEq, Eq)] -enum PointerKind<'tcx> { - /// No metadata attached, ie pointer to sized type or foreign type - Thin, - /// A trait object - VTable(Option<DefId>), - /// Slice - Length, - /// The unsize info of this projection - OfProjection(&'tcx ty::ProjectionTy<'tcx>), - /// The unsize info of this opaque ty - OfOpaque(DefId, SubstsRef<'tcx>), - /// The unsize info of this parameter - OfParam(&'tcx ty::ParamTy), -} - -impl<'a, 'tcx> FnCtxt<'a, 'tcx> { - /// Returns the kind of unsize information of t, or None - /// if t is unknown. - fn pointer_kind( - &self, - t: Ty<'tcx>, - span: Span, - ) -> Result<Option<PointerKind<'tcx>>, ErrorGuaranteed> { - debug!("pointer_kind({:?}, {:?})", t, span); - - let t = self.resolve_vars_if_possible(t); - - if let Some(reported) = t.error_reported() { - return Err(reported); - } - - if self.type_is_sized_modulo_regions(self.param_env, t, span) { - return Ok(Some(PointerKind::Thin)); - } - - Ok(match *t.kind() { - ty::Slice(_) | ty::Str => Some(PointerKind::Length), - ty::Dynamic(ref tty, _, ty::Dyn) => Some(PointerKind::VTable(tty.principal_def_id())), - ty::Adt(def, substs) if def.is_struct() => match def.non_enum_variant().fields.last() { - None => Some(PointerKind::Thin), - Some(f) => { - let field_ty = self.field_ty(span, f, substs); - self.pointer_kind(field_ty, span)? - } - }, - ty::Tuple(fields) => match fields.last() { - None => Some(PointerKind::Thin), - Some(&f) => self.pointer_kind(f, span)?, - }, - - // Pointers to foreign types are thin, despite being unsized - ty::Foreign(..) => Some(PointerKind::Thin), - // We should really try to normalize here. - ty::Projection(ref pi) => Some(PointerKind::OfProjection(pi)), - ty::Opaque(def_id, substs) => Some(PointerKind::OfOpaque(def_id, substs)), - ty::Param(ref p) => Some(PointerKind::OfParam(p)), - // Insufficient type information. - ty::Placeholder(..) | ty::Bound(..) | ty::Infer(_) => None, - - ty::Bool - | ty::Char - | ty::Int(..) - | ty::Uint(..) - | ty::Float(_) - | ty::Array(..) - | ty::GeneratorWitness(..) - | ty::RawPtr(_) - | ty::Ref(..) - | ty::FnDef(..) - | ty::FnPtr(..) - | ty::Closure(..) - | ty::Generator(..) - | ty::Adt(..) - | ty::Never - | ty::Dynamic(_, _, ty::DynStar) - | ty::Error(_) => { - let reported = self - .tcx - .sess - .delay_span_bug(span, &format!("`{:?}` should be sized but is not?", t)); - return Err(reported); - } - }) - } -} - -#[derive(Copy, Clone)] -pub enum CastError { - ErrorGuaranteed, - - CastToBool, - CastToChar, - DifferingKinds, - /// Cast of thin to fat raw ptr (e.g., `*const () as *const [u8]`). - SizedUnsizedCast, - IllegalCast, - NeedDeref, - NeedViaPtr, - NeedViaThinPtr, - NeedViaInt, - NonScalar, - UnknownExprPtrKind, - UnknownCastPtrKind, - /// Cast of int to (possibly) fat raw pointer. - /// - /// Argument is the specific name of the metadata in plain words, such as "a vtable" - /// or "a length". If this argument is None, then the metadata is unknown, for example, - /// when we're typechecking a type parameter with a ?Sized bound. - IntToFatCast(Option<&'static str>), - ForeignNonExhaustiveAdt, -} - -impl From<ErrorGuaranteed> for CastError { - fn from(_: ErrorGuaranteed) -> Self { - CastError::ErrorGuaranteed - } -} - -fn make_invalid_casting_error<'a, 'tcx>( - sess: &'a Session, - span: Span, - expr_ty: Ty<'tcx>, - cast_ty: Ty<'tcx>, - fcx: &FnCtxt<'a, 'tcx>, -) -> DiagnosticBuilder<'a, ErrorGuaranteed> { - type_error_struct!( - sess, - span, - expr_ty, - E0606, - "casting `{}` as `{}` is invalid", - fcx.ty_to_string(expr_ty), - fcx.ty_to_string(cast_ty) - ) -} - -pub enum CastCheckResult<'tcx> { - Ok, - Deferred(CastCheck<'tcx>), - Err(ErrorGuaranteed), -} - -pub fn check_cast<'tcx>( - fcx: &FnCtxt<'_, 'tcx>, - expr: &'tcx hir::Expr<'tcx>, - expr_ty: Ty<'tcx>, - cast_ty: Ty<'tcx>, - cast_span: Span, - span: Span, -) -> CastCheckResult<'tcx> { - if cast_ty.is_dyn_star() { - check_dyn_star_cast(fcx, expr, expr_ty, cast_ty) - } else { - match CastCheck::new(fcx, expr, expr_ty, cast_ty, cast_span, span) { - Ok(check) => CastCheckResult::Deferred(check), - Err(e) => CastCheckResult::Err(e), - } - } -} - -fn check_dyn_star_cast<'tcx>( - fcx: &FnCtxt<'_, 'tcx>, - expr: &'tcx hir::Expr<'tcx>, - expr_ty: Ty<'tcx>, - cast_ty: Ty<'tcx>, -) -> CastCheckResult<'tcx> { - // Find the bounds in the dyn*. For eaxmple, if we have - // - // let x = 22_usize as dyn* (Clone + Debug + 'static) - // - // this would return `existential_predicates = [?Self: Clone, ?Self: Debug]` and `region = 'static`. - let (existential_predicates, region) = match cast_ty.kind() { - ty::Dynamic(predicates, region, ty::DynStar) => (predicates, region), - _ => panic!("Invalid dyn* cast_ty"), - }; - - let cause = ObligationCause::new( - expr.span, - fcx.body_id, - // FIXME(dyn-star): Use a better obligation cause code - ObligationCauseCode::MiscObligation, - ); - - // For each existential predicate (e.g., `?Self: Clone`) substitute - // the type of the expression (e.g., `usize` in our example above) - // and then require that the resulting predicate (e.g., `usize: Clone`) - // holds (it does). - for existential_predicate in existential_predicates.iter() { - let predicate = existential_predicate.with_self_ty(fcx.tcx, expr_ty); - fcx.register_predicate(Obligation::new(cause.clone(), fcx.param_env, predicate)); - } - - // Enforce the region bound `'static` (e.g., `usize: 'static`, in our example). - fcx.register_predicate(Obligation::new( - cause, - fcx.param_env, - fcx.tcx.mk_predicate(Binder::dummy(ty::PredicateKind::TypeOutlives( - ty::OutlivesPredicate(expr_ty, *region), - ))), - )); - - CastCheckResult::Ok -} - -impl<'a, 'tcx> CastCheck<'tcx> { - fn new( - fcx: &FnCtxt<'a, 'tcx>, - expr: &'tcx hir::Expr<'tcx>, - expr_ty: Ty<'tcx>, - cast_ty: Ty<'tcx>, - cast_span: Span, - span: Span, - ) -> Result<CastCheck<'tcx>, ErrorGuaranteed> { - let expr_span = expr.span.find_ancestor_inside(span).unwrap_or(expr.span); - let check = CastCheck { expr, expr_ty, expr_span, cast_ty, cast_span, span }; - - // For better error messages, check for some obviously unsized - // cases now. We do a more thorough check at the end, once - // inference is more completely known. - match cast_ty.kind() { - ty::Dynamic(_, _, ty::Dyn) | ty::Slice(..) => { - let reported = check.report_cast_to_unsized_type(fcx); - Err(reported) - } - _ => Ok(check), - } - } - - fn report_cast_error(&self, fcx: &FnCtxt<'a, 'tcx>, e: CastError) { - match e { - CastError::ErrorGuaranteed => { - // an error has already been reported - } - CastError::NeedDeref => { - let error_span = self.span; - let mut err = make_invalid_casting_error( - fcx.tcx.sess, - self.span, - self.expr_ty, - self.cast_ty, - fcx, - ); - let cast_ty = fcx.ty_to_string(self.cast_ty); - err.span_label( - error_span, - format!("cannot cast `{}` as `{}`", fcx.ty_to_string(self.expr_ty), cast_ty), - ); - if let Ok(snippet) = fcx.sess().source_map().span_to_snippet(self.expr_span) { - err.span_suggestion( - self.expr_span, - "dereference the expression", - format!("*{}", snippet), - Applicability::MaybeIncorrect, - ); - } else { - err.span_help(self.expr_span, "dereference the expression with `*`"); - } - err.emit(); - } - CastError::NeedViaThinPtr | CastError::NeedViaPtr => { - let mut err = make_invalid_casting_error( - fcx.tcx.sess, - self.span, - self.expr_ty, - self.cast_ty, - fcx, - ); - if self.cast_ty.is_integral() { - err.help(&format!( - "cast through {} first", - match e { - CastError::NeedViaPtr => "a raw pointer", - CastError::NeedViaThinPtr => "a thin pointer", - _ => bug!(), - } - )); - } - err.emit(); - } - CastError::NeedViaInt => { - make_invalid_casting_error( - fcx.tcx.sess, - self.span, - self.expr_ty, - self.cast_ty, - fcx, - ) - .help(&format!( - "cast through {} first", - match e { - CastError::NeedViaInt => "an integer", - _ => bug!(), - } - )) - .emit(); - } - CastError::IllegalCast => { - make_invalid_casting_error( - fcx.tcx.sess, - self.span, - self.expr_ty, - self.cast_ty, - fcx, - ) - .emit(); - } - CastError::DifferingKinds => { - make_invalid_casting_error( - fcx.tcx.sess, - self.span, - self.expr_ty, - self.cast_ty, - fcx, - ) - .note("vtable kinds may not match") - .emit(); - } - CastError::CastToBool => { - let mut err = - struct_span_err!(fcx.tcx.sess, self.span, E0054, "cannot cast as `bool`"); - - if self.expr_ty.is_numeric() { - match fcx.tcx.sess.source_map().span_to_snippet(self.expr_span) { - Ok(snippet) => { - err.span_suggestion( - self.span, - "compare with zero instead", - format!("{snippet} != 0"), - Applicability::MachineApplicable, - ); - } - Err(_) => { - err.span_help(self.span, "compare with zero instead"); - } - } - } else { - err.span_label(self.span, "unsupported cast"); - } - - err.emit(); - } - CastError::CastToChar => { - let mut err = type_error_struct!( - fcx.tcx.sess, - self.span, - self.expr_ty, - E0604, - "only `u8` can be cast as `char`, not `{}`", - self.expr_ty - ); - err.span_label(self.span, "invalid cast"); - if self.expr_ty.is_numeric() { - if self.expr_ty == fcx.tcx.types.u32 { - match fcx.tcx.sess.source_map().span_to_snippet(self.expr.span) { - Ok(snippet) => err.span_suggestion( - self.span, - "try `char::from_u32` instead", - format!("char::from_u32({snippet})"), - Applicability::MachineApplicable, - ), - - Err(_) => err.span_help(self.span, "try `char::from_u32` instead"), - }; - } else if self.expr_ty == fcx.tcx.types.i8 { - err.span_help(self.span, "try casting from `u8` instead"); - } else { - err.span_help(self.span, "try `char::from_u32` instead (via a `u32`)"); - }; - } - err.emit(); - } - CastError::NonScalar => { - let mut err = type_error_struct!( - fcx.tcx.sess, - self.span, - self.expr_ty, - E0605, - "non-primitive cast: `{}` as `{}`", - self.expr_ty, - fcx.ty_to_string(self.cast_ty) - ); - let mut sugg = None; - let mut sugg_mutref = false; - if let ty::Ref(reg, cast_ty, mutbl) = *self.cast_ty.kind() { - if let ty::RawPtr(TypeAndMut { ty: expr_ty, .. }) = *self.expr_ty.kind() - && fcx - .try_coerce( - self.expr, - fcx.tcx.mk_ref( - fcx.tcx.lifetimes.re_erased, - TypeAndMut { ty: expr_ty, mutbl }, - ), - self.cast_ty, - AllowTwoPhase::No, - None, - ) - .is_ok() - { - sugg = Some((format!("&{}*", mutbl.prefix_str()), cast_ty == expr_ty)); - } else if let ty::Ref(expr_reg, expr_ty, expr_mutbl) = *self.expr_ty.kind() - && expr_mutbl == Mutability::Not - && mutbl == Mutability::Mut - && fcx - .try_coerce( - self.expr, - fcx.tcx.mk_ref( - expr_reg, - TypeAndMut { ty: expr_ty, mutbl: Mutability::Mut }, - ), - self.cast_ty, - AllowTwoPhase::No, - None, - ) - .is_ok() - { - sugg_mutref = true; - } - - if !sugg_mutref - && sugg == None - && fcx - .try_coerce( - self.expr, - fcx.tcx.mk_ref(reg, TypeAndMut { ty: self.expr_ty, mutbl }), - self.cast_ty, - AllowTwoPhase::No, - None, - ) - .is_ok() - { - sugg = Some((format!("&{}", mutbl.prefix_str()), false)); - } - } else if let ty::RawPtr(TypeAndMut { mutbl, .. }) = *self.cast_ty.kind() - && fcx - .try_coerce( - self.expr, - fcx.tcx.mk_ref( - fcx.tcx.lifetimes.re_erased, - TypeAndMut { ty: self.expr_ty, mutbl }, - ), - self.cast_ty, - AllowTwoPhase::No, - None, - ) - .is_ok() - { - sugg = Some((format!("&{}", mutbl.prefix_str()), false)); - } - if sugg_mutref { - err.span_label(self.span, "invalid cast"); - err.span_note(self.expr_span, "this reference is immutable"); - err.span_note(self.cast_span, "trying to cast to a mutable reference type"); - } else if let Some((sugg, remove_cast)) = sugg { - err.span_label(self.span, "invalid cast"); - - let has_parens = fcx - .tcx - .sess - .source_map() - .span_to_snippet(self.expr_span) - .map_or(false, |snip| snip.starts_with('(')); - - // Very crude check to see whether the expression must be wrapped - // in parentheses for the suggestion to work (issue #89497). - // Can/should be extended in the future. - let needs_parens = - !has_parens && matches!(self.expr.kind, hir::ExprKind::Cast(..)); - - let mut suggestion = vec![(self.expr_span.shrink_to_lo(), sugg)]; - if needs_parens { - suggestion[0].1 += "("; - suggestion.push((self.expr_span.shrink_to_hi(), ")".to_string())); - } - if remove_cast { - suggestion.push(( - self.expr_span.shrink_to_hi().to(self.cast_span), - String::new(), - )); - } - - err.multipart_suggestion_verbose( - "consider borrowing the value", - suggestion, - Applicability::MachineApplicable, - ); - } else if !matches!( - self.cast_ty.kind(), - ty::FnDef(..) | ty::FnPtr(..) | ty::Closure(..) - ) { - let mut label = true; - // Check `impl From<self.expr_ty> for self.cast_ty {}` for accurate suggestion: - if let Ok(snippet) = fcx.tcx.sess.source_map().span_to_snippet(self.expr_span) - && let Some(from_trait) = fcx.tcx.get_diagnostic_item(sym::From) - { - let ty = fcx.resolve_vars_if_possible(self.cast_ty); - // Erase regions to avoid panic in `prove_value` when calling - // `type_implements_trait`. - let ty = fcx.tcx.erase_regions(ty); - let expr_ty = fcx.resolve_vars_if_possible(self.expr_ty); - let expr_ty = fcx.tcx.erase_regions(expr_ty); - let ty_params = fcx.tcx.mk_substs_trait(expr_ty, &[]); - if fcx - .infcx - .type_implements_trait(from_trait, ty, ty_params, fcx.param_env) - .must_apply_modulo_regions() - { - label = false; - err.span_suggestion( - self.span, - "consider using the `From` trait instead", - format!("{}::from({})", self.cast_ty, snippet), - Applicability::MaybeIncorrect, - ); - } - } - let msg = "an `as` expression can only be used to convert between primitive \ - types or to coerce to a specific trait object"; - if label { - err.span_label(self.span, msg); - } else { - err.note(msg); - } - } else { - err.span_label(self.span, "invalid cast"); - } - err.emit(); - } - CastError::SizedUnsizedCast => { - use crate::structured_errors::{SizedUnsizedCast, StructuredDiagnostic}; - - SizedUnsizedCast { - sess: &fcx.tcx.sess, - span: self.span, - expr_ty: self.expr_ty, - cast_ty: fcx.ty_to_string(self.cast_ty), - } - .diagnostic() - .emit(); - } - CastError::IntToFatCast(known_metadata) => { - let mut err = struct_span_err!( - fcx.tcx.sess, - self.cast_span, - E0606, - "cannot cast `{}` to a pointer that {} wide", - fcx.ty_to_string(self.expr_ty), - if known_metadata.is_some() { "is" } else { "may be" } - ); - - err.span_label( - self.cast_span, - format!( - "creating a `{}` requires both an address and {}", - self.cast_ty, - known_metadata.unwrap_or("type-specific metadata"), - ), - ); - - if fcx.tcx.sess.is_nightly_build() { - err.span_label( - self.expr_span, - "consider casting this expression to `*const ()`, \ - then using `core::ptr::from_raw_parts`", - ); - } - - err.emit(); - } - CastError::UnknownCastPtrKind | CastError::UnknownExprPtrKind => { - let unknown_cast_to = match e { - CastError::UnknownCastPtrKind => true, - CastError::UnknownExprPtrKind => false, - _ => bug!(), - }; - let mut err = struct_span_err!( - fcx.tcx.sess, - if unknown_cast_to { self.cast_span } else { self.span }, - E0641, - "cannot cast {} a pointer of an unknown kind", - if unknown_cast_to { "to" } else { "from" } - ); - if unknown_cast_to { - err.span_label(self.cast_span, "needs more type information"); - err.note( - "the type information given here is insufficient to check whether \ - the pointer cast is valid", - ); - } else { - err.span_label( - self.span, - "the type information given here is insufficient to check whether \ - the pointer cast is valid", - ); - } - err.emit(); - } - CastError::ForeignNonExhaustiveAdt => { - make_invalid_casting_error( - fcx.tcx.sess, - self.span, - self.expr_ty, - self.cast_ty, - fcx, - ) - .note("cannot cast an enum with a non-exhaustive variant when it's defined in another crate") - .emit(); - } - } - } - - fn report_cast_to_unsized_type(&self, fcx: &FnCtxt<'a, 'tcx>) -> ErrorGuaranteed { - if let Some(reported) = - self.cast_ty.error_reported().or_else(|| self.expr_ty.error_reported()) - { - return reported; - } - - let tstr = fcx.ty_to_string(self.cast_ty); - let mut err = type_error_struct!( - fcx.tcx.sess, - self.span, - self.expr_ty, - E0620, - "cast to unsized type: `{}` as `{}`", - fcx.resolve_vars_if_possible(self.expr_ty), - tstr - ); - match self.expr_ty.kind() { - ty::Ref(_, _, mt) => { - let mtstr = mt.prefix_str(); - if self.cast_ty.is_trait() { - match fcx.tcx.sess.source_map().span_to_snippet(self.cast_span) { - Ok(s) => { - err.span_suggestion( - self.cast_span, - "try casting to a reference instead", - format!("&{}{}", mtstr, s), - Applicability::MachineApplicable, - ); - } - Err(_) => { - let msg = &format!("did you mean `&{}{}`?", mtstr, tstr); - err.span_help(self.cast_span, msg); - } - } - } else { - let msg = - &format!("consider using an implicit coercion to `&{mtstr}{tstr}` instead"); - err.span_help(self.span, msg); - } - } - ty::Adt(def, ..) if def.is_box() => { - match fcx.tcx.sess.source_map().span_to_snippet(self.cast_span) { - Ok(s) => { - err.span_suggestion( - self.cast_span, - "you can cast to a `Box` instead", - format!("Box<{s}>"), - Applicability::MachineApplicable, - ); - } - Err(_) => { - err.span_help( - self.cast_span, - &format!("you might have meant `Box<{tstr}>`"), - ); - } - } - } - _ => { - err.span_help(self.expr_span, "consider using a box or reference as appropriate"); - } - } - err.emit() - } - - fn trivial_cast_lint(&self, fcx: &FnCtxt<'a, 'tcx>) { - let t_cast = self.cast_ty; - let t_expr = self.expr_ty; - let type_asc_or = - if fcx.tcx.features().type_ascription { "type ascription or " } else { "" }; - let (adjective, lint) = if t_cast.is_numeric() && t_expr.is_numeric() { - ("numeric ", lint::builtin::TRIVIAL_NUMERIC_CASTS) - } else { - ("", lint::builtin::TRIVIAL_CASTS) - }; - fcx.tcx.struct_span_lint_hir(lint, self.expr.hir_id, self.span, |err| { - err.build(&format!( - "trivial {}cast: `{}` as `{}`", - adjective, - fcx.ty_to_string(t_expr), - fcx.ty_to_string(t_cast) - )) - .help(&format!( - "cast can be replaced by coercion; this might \ - require {type_asc_or}a temporary variable" - )) - .emit(); - }); - } - - #[instrument(skip(fcx), level = "debug")] - pub fn check(mut self, fcx: &FnCtxt<'a, 'tcx>) { - self.expr_ty = fcx.structurally_resolved_type(self.expr_span, self.expr_ty); - self.cast_ty = fcx.structurally_resolved_type(self.cast_span, self.cast_ty); - - debug!("check_cast({}, {:?} as {:?})", self.expr.hir_id, self.expr_ty, self.cast_ty); - - if !fcx.type_is_sized_modulo_regions(fcx.param_env, self.cast_ty, self.span) - && !self.cast_ty.has_infer_types() - { - self.report_cast_to_unsized_type(fcx); - } else if self.expr_ty.references_error() || self.cast_ty.references_error() { - // No sense in giving duplicate error messages - } else { - match self.try_coercion_cast(fcx) { - Ok(()) => { - self.trivial_cast_lint(fcx); - debug!(" -> CoercionCast"); - fcx.typeck_results.borrow_mut().set_coercion_cast(self.expr.hir_id.local_id); - } - Err(ty::error::TypeError::ObjectUnsafeCoercion(did)) => { - self.report_object_unsafe_cast(&fcx, did); - } - Err(_) => { - match self.do_check(fcx) { - Ok(k) => { - debug!(" -> {:?}", k); - } - Err(e) => self.report_cast_error(fcx, e), - }; - } - }; - } - } - - fn report_object_unsafe_cast(&self, fcx: &FnCtxt<'a, 'tcx>, did: DefId) { - let violations = fcx.tcx.object_safety_violations(did); - let mut err = report_object_safety_error(fcx.tcx, self.cast_span, did, violations); - err.note(&format!("required by cast to type '{}'", fcx.ty_to_string(self.cast_ty))); - err.emit(); - } - - /// Checks a cast, and report an error if one exists. In some cases, this - /// can return Ok and create type errors in the fcx rather than returning - /// directly. coercion-cast is handled in check instead of here. - pub fn do_check(&self, fcx: &FnCtxt<'a, 'tcx>) -> Result<CastKind, CastError> { - use rustc_middle::ty::cast::CastTy::*; - use rustc_middle::ty::cast::IntTy::*; - - let (t_from, t_cast) = match (CastTy::from_ty(self.expr_ty), CastTy::from_ty(self.cast_ty)) - { - (Some(t_from), Some(t_cast)) => (t_from, t_cast), - // Function item types may need to be reified before casts. - (None, Some(t_cast)) => { - match *self.expr_ty.kind() { - ty::FnDef(..) => { - // Attempt a coercion to a fn pointer type. - let f = fcx.normalize_associated_types_in( - self.expr_span, - self.expr_ty.fn_sig(fcx.tcx), - ); - let res = fcx.try_coerce( - self.expr, - self.expr_ty, - fcx.tcx.mk_fn_ptr(f), - AllowTwoPhase::No, - None, - ); - if let Err(TypeError::IntrinsicCast) = res { - return Err(CastError::IllegalCast); - } - if res.is_err() { - return Err(CastError::NonScalar); - } - (FnPtr, t_cast) - } - // Special case some errors for references, and check for - // array-ptr-casts. `Ref` is not a CastTy because the cast - // is split into a coercion to a pointer type, followed by - // a cast. - ty::Ref(_, inner_ty, mutbl) => { - return match t_cast { - Int(_) | Float => match *inner_ty.kind() { - ty::Int(_) - | ty::Uint(_) - | ty::Float(_) - | ty::Infer(ty::InferTy::IntVar(_) | ty::InferTy::FloatVar(_)) => { - Err(CastError::NeedDeref) - } - _ => Err(CastError::NeedViaPtr), - }, - // array-ptr-cast - Ptr(mt) => { - self.check_ref_cast(fcx, TypeAndMut { mutbl, ty: inner_ty }, mt) - } - _ => Err(CastError::NonScalar), - }; - } - _ => return Err(CastError::NonScalar), - } - } - _ => return Err(CastError::NonScalar), - }; - - if let ty::Adt(adt_def, _) = *self.expr_ty.kind() { - if adt_def.did().krate != LOCAL_CRATE { - if adt_def.variants().iter().any(VariantDef::is_field_list_non_exhaustive) { - return Err(CastError::ForeignNonExhaustiveAdt); - } - } - } - - match (t_from, t_cast) { - // These types have invariants! can't cast into them. - (_, Int(CEnum) | FnPtr) => Err(CastError::NonScalar), - - // * -> Bool - (_, Int(Bool)) => Err(CastError::CastToBool), - - // * -> Char - (Int(U(ty::UintTy::U8)), Int(Char)) => Ok(CastKind::U8CharCast), // u8-char-cast - (_, Int(Char)) => Err(CastError::CastToChar), - - // prim -> float,ptr - (Int(Bool) | Int(CEnum) | Int(Char), Float) => Err(CastError::NeedViaInt), - - (Int(Bool) | Int(CEnum) | Int(Char) | Float, Ptr(_)) | (Ptr(_) | FnPtr, Float) => { - Err(CastError::IllegalCast) - } - - // ptr -> * - (Ptr(m_e), Ptr(m_c)) => self.check_ptr_ptr_cast(fcx, m_e, m_c), // ptr-ptr-cast - - // ptr-addr-cast - (Ptr(m_expr), Int(t_c)) => { - self.lossy_provenance_ptr2int_lint(fcx, t_c); - self.check_ptr_addr_cast(fcx, m_expr) - } - (FnPtr, Int(_)) => { - // FIXME(#95489): there should eventually be a lint for these casts - Ok(CastKind::FnPtrAddrCast) - } - // addr-ptr-cast - (Int(_), Ptr(mt)) => { - self.fuzzy_provenance_int2ptr_lint(fcx); - self.check_addr_ptr_cast(fcx, mt) - } - // fn-ptr-cast - (FnPtr, Ptr(mt)) => self.check_fptr_ptr_cast(fcx, mt), - - // prim -> prim - (Int(CEnum), Int(_)) => { - self.cenum_impl_drop_lint(fcx); - Ok(CastKind::EnumCast) - } - (Int(Char) | Int(Bool), Int(_)) => Ok(CastKind::PrimIntCast), - - (Int(_) | Float, Int(_) | Float) => Ok(CastKind::NumericCast), - - // FIXME(dyn-star): this needs more conditions... - (_, DynStar) => Ok(CastKind::DynStarCast), - - // FIXME(dyn-star): do we want to allow dyn* upcasting or other casts? - (DynStar, _) => Err(CastError::IllegalCast), - } - } - - fn check_ptr_ptr_cast( - &self, - fcx: &FnCtxt<'a, 'tcx>, - m_expr: ty::TypeAndMut<'tcx>, - m_cast: ty::TypeAndMut<'tcx>, - ) -> Result<CastKind, CastError> { - debug!("check_ptr_ptr_cast m_expr={:?} m_cast={:?}", m_expr, m_cast); - // ptr-ptr cast. vtables must match. - - let expr_kind = fcx.pointer_kind(m_expr.ty, self.span)?; - let cast_kind = fcx.pointer_kind(m_cast.ty, self.span)?; - - let Some(cast_kind) = cast_kind else { - // We can't cast if target pointer kind is unknown - return Err(CastError::UnknownCastPtrKind); - }; - - // Cast to thin pointer is OK - if cast_kind == PointerKind::Thin { - return Ok(CastKind::PtrPtrCast); - } - - let Some(expr_kind) = expr_kind else { - // We can't cast to fat pointer if source pointer kind is unknown - return Err(CastError::UnknownExprPtrKind); - }; - - // thin -> fat? report invalid cast (don't complain about vtable kinds) - if expr_kind == PointerKind::Thin { - return Err(CastError::SizedUnsizedCast); - } - - // vtable kinds must match - if cast_kind == expr_kind { - Ok(CastKind::PtrPtrCast) - } else { - Err(CastError::DifferingKinds) - } - } - - fn check_fptr_ptr_cast( - &self, - fcx: &FnCtxt<'a, 'tcx>, - m_cast: ty::TypeAndMut<'tcx>, - ) -> Result<CastKind, CastError> { - // fptr-ptr cast. must be to thin ptr - - match fcx.pointer_kind(m_cast.ty, self.span)? { - None => Err(CastError::UnknownCastPtrKind), - Some(PointerKind::Thin) => Ok(CastKind::FnPtrPtrCast), - _ => Err(CastError::IllegalCast), - } - } - - fn check_ptr_addr_cast( - &self, - fcx: &FnCtxt<'a, 'tcx>, - m_expr: ty::TypeAndMut<'tcx>, - ) -> Result<CastKind, CastError> { - // ptr-addr cast. must be from thin ptr - - match fcx.pointer_kind(m_expr.ty, self.span)? { - None => Err(CastError::UnknownExprPtrKind), - Some(PointerKind::Thin) => Ok(CastKind::PtrAddrCast), - _ => Err(CastError::NeedViaThinPtr), - } - } - - fn check_ref_cast( - &self, - fcx: &FnCtxt<'a, 'tcx>, - m_expr: ty::TypeAndMut<'tcx>, - m_cast: ty::TypeAndMut<'tcx>, - ) -> Result<CastKind, CastError> { - // array-ptr-cast: allow mut-to-mut, mut-to-const, const-to-const - if m_expr.mutbl == hir::Mutability::Mut || m_cast.mutbl == hir::Mutability::Not { - if let ty::Array(ety, _) = m_expr.ty.kind() { - // Due to the limitations of LLVM global constants, - // region pointers end up pointing at copies of - // vector elements instead of the original values. - // To allow raw pointers to work correctly, we - // need to special-case obtaining a raw pointer - // from a region pointer to a vector. - - // Coerce to a raw pointer so that we generate AddressOf in MIR. - let array_ptr_type = fcx.tcx.mk_ptr(m_expr); - fcx.try_coerce(self.expr, self.expr_ty, array_ptr_type, AllowTwoPhase::No, None) - .unwrap_or_else(|_| { - bug!( - "could not cast from reference to array to pointer to array ({:?} to {:?})", - self.expr_ty, - array_ptr_type, - ) - }); - - // this will report a type mismatch if needed - fcx.demand_eqtype(self.span, *ety, m_cast.ty); - return Ok(CastKind::ArrayPtrCast); - } - } - - Err(CastError::IllegalCast) - } - - fn check_addr_ptr_cast( - &self, - fcx: &FnCtxt<'a, 'tcx>, - m_cast: TypeAndMut<'tcx>, - ) -> Result<CastKind, CastError> { - // ptr-addr cast. pointer must be thin. - match fcx.pointer_kind(m_cast.ty, self.span)? { - None => Err(CastError::UnknownCastPtrKind), - Some(PointerKind::Thin) => Ok(CastKind::AddrPtrCast), - Some(PointerKind::VTable(_)) => Err(CastError::IntToFatCast(Some("a vtable"))), - Some(PointerKind::Length) => Err(CastError::IntToFatCast(Some("a length"))), - Some( - PointerKind::OfProjection(_) - | PointerKind::OfOpaque(_, _) - | PointerKind::OfParam(_), - ) => Err(CastError::IntToFatCast(None)), - } - } - - fn try_coercion_cast(&self, fcx: &FnCtxt<'a, 'tcx>) -> Result<(), ty::error::TypeError<'tcx>> { - match fcx.try_coerce(self.expr, self.expr_ty, self.cast_ty, AllowTwoPhase::No, None) { - Ok(_) => Ok(()), - Err(err) => Err(err), - } - } - - fn cenum_impl_drop_lint(&self, fcx: &FnCtxt<'a, 'tcx>) { - if let ty::Adt(d, _) = self.expr_ty.kind() - && d.has_dtor(fcx.tcx) - { - fcx.tcx.struct_span_lint_hir( - lint::builtin::CENUM_IMPL_DROP_CAST, - self.expr.hir_id, - self.span, - |err| { - err.build(&format!( - "cannot cast enum `{}` into integer `{}` because it implements `Drop`", - self.expr_ty, self.cast_ty - )) - .emit(); - }, - ); - } - } - - fn lossy_provenance_ptr2int_lint(&self, fcx: &FnCtxt<'a, 'tcx>, t_c: ty::cast::IntTy) { - fcx.tcx.struct_span_lint_hir( - lint::builtin::LOSSY_PROVENANCE_CASTS, - self.expr.hir_id, - self.span, - |err| { - let mut err = err.build(&format!( - "under strict provenance it is considered bad style to cast pointer `{}` to integer `{}`", - self.expr_ty, self.cast_ty - )); - - let msg = "use `.addr()` to obtain the address of a pointer"; - - let expr_prec = self.expr.precedence().order(); - let needs_parens = expr_prec < rustc_ast::util::parser::PREC_POSTFIX; - - let scalar_cast = match t_c { - ty::cast::IntTy::U(ty::UintTy::Usize) => String::new(), - _ => format!(" as {}", self.cast_ty), - }; - - let cast_span = self.expr_span.shrink_to_hi().to(self.cast_span); - - if needs_parens { - let suggestions = vec![ - (self.expr_span.shrink_to_lo(), String::from("(")), - (cast_span, format!(").addr(){scalar_cast}")), - ]; - - err.multipart_suggestion(msg, suggestions, Applicability::MaybeIncorrect); - } else { - err.span_suggestion( - cast_span, - msg, - format!(".addr(){scalar_cast}"), - Applicability::MaybeIncorrect, - ); - } - - err.help( - "if you can't comply with strict provenance and need to expose the pointer \ - provenance you can use `.expose_addr()` instead" - ); - - err.emit(); - }, - ); - } - - fn fuzzy_provenance_int2ptr_lint(&self, fcx: &FnCtxt<'a, 'tcx>) { - fcx.tcx.struct_span_lint_hir( - lint::builtin::FUZZY_PROVENANCE_CASTS, - self.expr.hir_id, - self.span, - |err| { - let mut err = err.build(&format!( - "strict provenance disallows casting integer `{}` to pointer `{}`", - self.expr_ty, self.cast_ty - )); - let msg = "use `.with_addr()` to adjust a valid pointer in the same allocation, to this address"; - let suggestions = vec![ - (self.expr_span.shrink_to_lo(), String::from("(...).with_addr(")), - (self.expr_span.shrink_to_hi().to(self.cast_span), String::from(")")), - ]; - - err.multipart_suggestion(msg, suggestions, Applicability::MaybeIncorrect); - err.help( - "if you can't comply with strict provenance and don't have a pointer with \ - the correct provenance you can use `std::ptr::from_exposed_addr()` instead" - ); - - err.emit(); - }, - ); - } -} diff --git a/compiler/rustc_typeck/src/check/check.rs b/compiler/rustc_typeck/src/check/check.rs deleted file mode 100644 index d6fa74c87..000000000 --- a/compiler/rustc_typeck/src/check/check.rs +++ /dev/null @@ -1,1753 +0,0 @@ -use crate::check::intrinsicck::InlineAsmCtxt; - -use super::coercion::CoerceMany; -use super::compare_method::check_type_bounds; -use super::compare_method::{compare_const_impl, 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::lang_items::LangItem; -use rustc_hir::{ItemKind, Node, PathSegment}; -use rustc_infer::infer::outlives::env::OutlivesEnvironment; -use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; -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::InferCtxtExt as _; -use rustc_trait_selection::traits::{self, ObligationCtxt}; -use rustc_ty_utils::representability::{self, Representability}; - -use std::ops::ControlFlow; - -pub(super) 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, |lint| { - lint.build("use of calling convention not supported on this target").emit(); - }); - } - } - - // 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(); - } -} - -/// Helper used for fns and closures. Does the grungy work of checking a function -/// body and returns the function context used for that purpose, since in the case of a fn item -/// there is still a bit more to do. -/// -/// * ... -/// * inherited: other fields inherited from the enclosing fn (if any) -#[instrument(skip(inherited, body), level = "debug")] -pub(super) fn check_fn<'a, 'tcx>( - inherited: &'a Inherited<'a, 'tcx>, - param_env: ty::ParamEnv<'tcx>, - fn_sig: ty::FnSig<'tcx>, - decl: &'tcx hir::FnDecl<'tcx>, - fn_id: hir::HirId, - body: &'tcx hir::Body<'tcx>, - can_be_generator: Option<hir::Movability>, - return_type_pre_known: bool, -) -> (FnCtxt<'a, 'tcx>, Option<GeneratorTypes<'tcx>>) { - // Create the function context. This is either derived from scratch or, - // in the case of closures, based on the outer context. - let mut fcx = FnCtxt::new(inherited, param_env, body.value.hir_id); - fcx.ps.set(UnsafetyState::function(fn_sig.unsafety, fn_id)); - fcx.return_type_pre_known = return_type_pre_known; - - let tcx = fcx.tcx; - let hir = tcx.hir(); - - let declared_ret_ty = fn_sig.output(); - - let ret_ty = - fcx.register_infer_ok_obligations(fcx.infcx.replace_opaque_types_with_inference_vars( - declared_ret_ty, - body.value.hir_id, - decl.output.span(), - param_env, - )); - // If we replaced declared_ret_ty with infer vars, then we must be inferring - // an opaque type, so set a flag so we can improve diagnostics. - fcx.return_type_has_opaque = ret_ty != declared_ret_ty; - - fcx.ret_coercion = Some(RefCell::new(CoerceMany::new(ret_ty))); - - let span = body.value.span; - - fn_maybe_err(tcx, span, fn_sig.abi); - - if fn_sig.abi == Abi::RustCall { - let expected_args = if let ImplicitSelfKind::None = decl.implicit_self { 1 } else { 2 }; - - let err = || { - let item = match tcx.hir().get(fn_id) { - Node::Item(hir::Item { kind: ItemKind::Fn(header, ..), .. }) => Some(header), - Node::ImplItem(hir::ImplItem { - kind: hir::ImplItemKind::Fn(header, ..), .. - }) => Some(header), - Node::TraitItem(hir::TraitItem { - kind: hir::TraitItemKind::Fn(header, ..), - .. - }) => Some(header), - // Closures are RustCall, but they tuple their arguments, so shouldn't be checked - Node::Expr(hir::Expr { kind: hir::ExprKind::Closure { .. }, .. }) => None, - node => bug!("Item being checked wasn't a function/closure: {:?}", node), - }; - - if let Some(header) = item { - tcx.sess.span_err(header.span, "functions with the \"rust-call\" ABI must take a single non-self argument that is a tuple"); - } - }; - - if fn_sig.inputs().len() != expected_args { - err() - } else { - // FIXME(CraftSpider) Add a check on parameter expansion, so we don't just make the ICE happen later on - // This will probably require wide-scale changes to support a TupleKind obligation - // We can't resolve this without knowing the type of the param - if !matches!(fn_sig.inputs()[expected_args - 1].kind(), ty::Tuple(_) | ty::Param(_)) { - err() - } - } - } - - if body.generator_kind.is_some() && can_be_generator.is_some() { - let yield_ty = fcx - .next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::TypeInference, span }); - fcx.require_type_is_sized(yield_ty, span, traits::SizedYieldType); - - // Resume type defaults to `()` if the generator has no argument. - let resume_ty = fn_sig.inputs().get(0).copied().unwrap_or_else(|| tcx.mk_unit()); - - fcx.resume_yield_tys = Some((resume_ty, yield_ty)); - } - - GatherLocalsVisitor::new(&fcx).visit_body(body); - - // C-variadic fns also have a `VaList` input that's not listed in `fn_sig` - // (as it's created inside the body itself, not passed in from outside). - let maybe_va_list = if fn_sig.c_variadic { - let span = body.params.last().unwrap().span; - let va_list_did = tcx.require_lang_item(LangItem::VaList, Some(span)); - let region = fcx.next_region_var(RegionVariableOrigin::MiscVariable(span)); - - Some(tcx.bound_type_of(va_list_did).subst(tcx, &[region.into()])) - } else { - None - }; - - // Add formal parameters. - let inputs_hir = hir.fn_decl_by_hir_id(fn_id).map(|decl| &decl.inputs); - let inputs_fn = fn_sig.inputs().iter().copied(); - for (idx, (param_ty, param)) in inputs_fn.chain(maybe_va_list).zip(body.params).enumerate() { - // Check the pattern. - let ty_span = try { inputs_hir?.get(idx)?.span }; - fcx.check_pat_top(¶m.pat, param_ty, ty_span, false); - - // Check that argument is Sized. - // The check for a non-trivial pattern is a hack to avoid duplicate warnings - // for simple cases like `fn foo(x: Trait)`, - // where we would error once on the parameter as a whole, and once on the binding `x`. - if param.pat.simple_ident().is_none() && !tcx.features().unsized_fn_params { - fcx.require_type_is_sized(param_ty, param.pat.span, traits::SizedArgumentType(ty_span)); - } - - fcx.write_ty(param.hir_id, param_ty); - } - - inherited.typeck_results.borrow_mut().liberated_fn_sigs_mut().insert(fn_id, fn_sig); - - fcx.in_tail_expr = true; - if let ty::Dynamic(..) = declared_ret_ty.kind() { - // FIXME: We need to verify that the return type is `Sized` after the return expression has - // been evaluated so that we have types available for all the nodes being returned, but that - // requires the coerced evaluated type to be stored. Moving `check_return_expr` before this - // causes unsized errors caused by the `declared_ret_ty` to point at the return expression, - // while keeping the current ordering we will ignore the tail expression's type because we - // don't know it yet. We can't do `check_expr_kind` while keeping `check_return_expr` - // because we will trigger "unreachable expression" lints unconditionally. - // Because of all of this, we perform a crude check to know whether the simplest `!Sized` - // case that a newcomer might make, returning a bare trait, and in that case we populate - // the tail expression's type so that the suggestion will be correct, but ignore all other - // possible cases. - fcx.check_expr(&body.value); - fcx.require_type_is_sized(declared_ret_ty, decl.output.span(), traits::SizedReturnType); - } else { - fcx.require_type_is_sized(declared_ret_ty, decl.output.span(), traits::SizedReturnType); - fcx.check_return_expr(&body.value, false); - } - fcx.in_tail_expr = false; - - // We insert the deferred_generator_interiors entry after visiting the body. - // This ensures that all nested generators appear before the entry of this generator. - // resolve_generator_interiors relies on this property. - let gen_ty = if let (Some(_), Some(gen_kind)) = (can_be_generator, body.generator_kind) { - let interior = fcx - .next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::MiscVariable, span }); - fcx.deferred_generator_interiors.borrow_mut().push((body.id(), interior, gen_kind)); - - let (resume_ty, yield_ty) = fcx.resume_yield_tys.unwrap(); - Some(GeneratorTypes { - resume_ty, - yield_ty, - interior, - movability: can_be_generator.unwrap(), - }) - } else { - None - }; - - // Finalize the return check by taking the LUB of the return types - // we saw and assigning it to the expected return type. This isn't - // really expected to fail, since the coercions would have failed - // earlier when trying to find a LUB. - let coercion = fcx.ret_coercion.take().unwrap().into_inner(); - let mut actual_return_ty = coercion.complete(&fcx); - debug!("actual_return_ty = {:?}", actual_return_ty); - if let ty::Dynamic(..) = declared_ret_ty.kind() { - // We have special-cased the case where the function is declared - // `-> dyn Foo` and we don't actually relate it to the - // `fcx.ret_coercion`, so just substitute a type variable. - actual_return_ty = - fcx.next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::DynReturnFn, span }); - debug!("actual_return_ty replaced with {:?}", actual_return_ty); - } - - // HACK(oli-obk, compiler-errors): We should be comparing this against - // `declared_ret_ty`, but then anything uninferred would be inferred to - // the opaque type itself. That again would cause writeback to assume - // we have a recursive call site and do the sadly stabilized fallback to `()`. - fcx.demand_suptype(span, ret_ty, actual_return_ty); - - // Check that a function marked as `#[panic_handler]` has signature `fn(&PanicInfo) -> !` - if let Some(panic_impl_did) = tcx.lang_items().panic_impl() - && panic_impl_did == hir.local_def_id(fn_id).to_def_id() - { - check_panic_info_fn(tcx, panic_impl_did.expect_local(), fn_sig, decl, declared_ret_ty); - } - - // Check that a function marked as `#[alloc_error_handler]` has signature `fn(Layout) -> !` - if let Some(alloc_error_handler_did) = tcx.lang_items().oom() - && alloc_error_handler_did == hir.local_def_id(fn_id).to_def_id() - { - check_alloc_error_fn(tcx, alloc_error_handler_did.expect_local(), fn_sig, decl, declared_ret_ty); - } - - (fcx, gen_ty) -} - -fn check_panic_info_fn( - tcx: TyCtxt<'_>, - fn_id: LocalDefId, - fn_sig: ty::FnSig<'_>, - decl: &hir::FnDecl<'_>, - declared_ret_ty: Ty<'_>, -) { - let Some(panic_info_did) = tcx.lang_items().panic_info() else { - tcx.sess.err("language item required, but not found: `panic_info`"); - return; - }; - - if *declared_ret_ty.kind() != ty::Never { - tcx.sess.span_err(decl.output.span(), "return type should be `!`"); - } - - let inputs = fn_sig.inputs(); - if inputs.len() != 1 { - tcx.sess.span_err(tcx.def_span(fn_id), "function should have one argument"); - return; - } - - let arg_is_panic_info = match *inputs[0].kind() { - ty::Ref(region, ty, mutbl) => match *ty.kind() { - ty::Adt(ref adt, _) => { - adt.did() == panic_info_did && mutbl == hir::Mutability::Not && !region.is_static() - } - _ => false, - }, - _ => false, - }; - - if !arg_is_panic_info { - tcx.sess.span_err(decl.inputs[0].span, "argument should be `&PanicInfo`"); - } - - let DefKind::Fn = tcx.def_kind(fn_id) else { - let span = tcx.def_span(fn_id); - tcx.sess.span_err(span, "should be a function"); - return; - }; - - let generic_counts = tcx.generics_of(fn_id).own_counts(); - if generic_counts.types != 0 { - let span = tcx.def_span(fn_id); - tcx.sess.span_err(span, "should have no type parameters"); - } - if generic_counts.consts != 0 { - let span = tcx.def_span(fn_id); - tcx.sess.span_err(span, "should have no const parameters"); - } -} - -fn check_alloc_error_fn( - tcx: TyCtxt<'_>, - fn_id: LocalDefId, - fn_sig: ty::FnSig<'_>, - decl: &hir::FnDecl<'_>, - declared_ret_ty: Ty<'_>, -) { - let Some(alloc_layout_did) = tcx.lang_items().alloc_layout() else { - tcx.sess.err("language item required, but not found: `alloc_layout`"); - return; - }; - - if *declared_ret_ty.kind() != ty::Never { - tcx.sess.span_err(decl.output.span(), "return type should be `!`"); - } - - let inputs = fn_sig.inputs(); - if inputs.len() != 1 { - tcx.sess.span_err(tcx.def_span(fn_id), "function should have one argument"); - return; - } - - let arg_is_alloc_layout = match inputs[0].kind() { - ty::Adt(ref adt, _) => adt.did() == alloc_layout_did, - _ => false, - }; - - if !arg_is_alloc_layout { - tcx.sess.span_err(decl.inputs[0].span, "argument should be `Layout`"); - } - - let DefKind::Fn = tcx.def_kind(fn_id) else { - let span = tcx.def_span(fn_id); - tcx.sess.span_err(span, "`#[alloc_error_handler]` should be a function"); - return; - }; - - let generic_counts = tcx.generics_of(fn_id).own_counts(); - if generic_counts.types != 0 { - let span = tcx.def_span(fn_id); - tcx.sess.span_err(span, "`#[alloc_error_handler]` function should have no type parameters"); - } - if generic_counts.consts != 0 { - let span = tcx.def_span(fn_id); - tcx.sess - .span_err(span, "`#[alloc_error_handler]` function should have no const parameters"); - } -} - -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 - check_representable(tcx, span, def_id); - - 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_representable(tcx, span, def_id); - 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.at(span), 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, - |lint| { - lint.build("static of uninhabited type") - .note("uninhabited statics cannot be initialized, and any access would be an immediate error") - .emit(); - }, - ); - } -} - -/// Checks that an opaque type does not contain cycles and does not use `Self` or `T::Foo` -/// projections that would result in "inheriting lifetimes". -pub(super) fn check_opaque<'tcx>( - tcx: TyCtxt<'tcx>, - def_id: LocalDefId, - substs: SubstsRef<'tcx>, - origin: &hir::OpaqueTyOrigin, -) { - let span = tcx.def_span(def_id); - check_opaque_for_inheriting_lifetimes(tcx, def_id, span); - if tcx.type_of(def_id).references_error() { - return; - } - if check_opaque_for_cycles(tcx, def_id, substs, span, origin).is_err() { - return; - } - check_opaque_meets_bounds(tcx, 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::SelfTy { trait_: _, alias_to: impl_ref }, .. }] => { - let impl_ty_name = - impl_ref.map(|(def_id, _)| 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: -/// -/// 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 hidden_type = tcx.bound_type_of(def_id.to_def_id()).subst(tcx, substs); - - 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); - - tcx.infer_ctxt().with_opaque_type_inference(DefiningAnchor::Bind(defining_use_anchor)).enter( - move |infcx| { - let ocx = ObligationCtxt::new(&infcx); - let opaque_ty = tcx.mk_opaque(def_id.to_def_id(), substs); - - let misc_cause = traits::ObligationCause::misc(span, hir_id); - - match infcx.at(&misc_cause, param_env).eq(opaque_ty, hidden_type) { - Ok(infer_ok) => ocx.register_infer_ok_obligations(infer_ok), - Err(ty_err) => { - tcx.sess.delay_span_bug( - span, - &format!("could not unify `{hidden_type}` 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_type.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.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.def_id, - tcx.def_path_str(id.def_id.to_def_id()) - ); - let _indenter = indenter(); - match tcx.def_kind(id.def_id) { - DefKind::Static(..) => { - tcx.ensure().typeck(id.def_id); - maybe_check_static_with_link_section(tcx, id.def_id); - check_static_inhabited(tcx, id.def_id); - } - DefKind::Const => { - tcx.ensure().typeck(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.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.def_id); - if let Some(impl_trait_ref) = tcx.impl_trait_ref(it.def_id) { - check_impl_items_against_trait( - tcx, - it.span, - it.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.def_id); - let trait_substs = - InternalSubsts::identity_for_item(tcx, it.def_id.to_def_id()); - let _: Result<_, rustc_errors::ErrorGuaranteed> = check_type_bounds( - tcx, - assoc_item, - assoc_item, - default.span, - ty::TraitRef { def_id: it.def_id.to_def_id(), substs: trait_substs }, - ); - } - _ => {} - } - } - } - DefKind::Struct => { - check_struct(tcx, id.def_id); - } - DefKind::Union => { - check_union(tcx, id.def_id); - } - DefKind::OpaqueTy => { - let item = tcx.hir().item(id); - let hir::ItemKind::OpaqueTy(hir::OpaqueTy { origin, .. }) = item.kind else { - 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 { - let substs = InternalSubsts::identity_for_item(tcx, item.def_id.to_def_id()); - check_opaque(tcx, item.def_id, substs, &origin); - } - } - DefKind::TyAlias => { - let pty_ty = tcx.type_of(id.def_id); - let generics = tcx.generics_of(id.def_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.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.def_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.def_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(..) => { - // Find associated const definition. - compare_const_impl( - tcx, - &ty_impl_item, - impl_item.span, - &ty_trait_item, - impl_trait_ref, - ); - } - 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::TyAlias(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, - ); - } - } -} - -/// Checks whether a type can be represented in memory. In particular, it -/// identifies types that contain themselves without indirection through a -/// pointer, which would mean their size is unbounded. -pub(super) fn check_representable(tcx: TyCtxt<'_>, sp: Span, item_def_id: LocalDefId) -> bool { - let rty = tcx.type_of(item_def_id); - - // Check that it is possible to represent this type. This call identifies - // (1) types that contain themselves and (2) types that contain a different - // recursive type. It is only necessary to throw an error on those that - // contain themselves. For case 2, there must be an inner type that will be - // caught by case 1. - match representability::ty_is_representable(tcx, rty, sp, None) { - Representability::SelfRecursive(spans) => { - recursive_type_with_infinite_size_error(tcx, item_def_id.to_def_id(), spans); - return false; - } - Representability::Representable | Representability::ContainsRecursive => (), - } - true -} - -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, - |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.build("zero-sized fields in repr(transparent) cannot contain external non-exhaustive types") - .note(format!("this {descr} contains `{field_ty}`, which {note}, \ - and makes it not a breaking change to become non-zero-sized in the future.")) - .emit(); - }, - ) - } - } -} - -#[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() && tcx.features().arbitrary_enum_discriminant { - 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_representable(tcx, sp, def_id); - 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_typeck/src/check/closure.rs b/compiler/rustc_typeck/src/check/closure.rs deleted file mode 100644 index 9b943b160..000000000 --- a/compiler/rustc_typeck/src/check/closure.rs +++ /dev/null @@ -1,825 +0,0 @@ -//! Code for type-checking closure expressions. - -use super::{check_fn, Expectation, FnCtxt, GeneratorTypes}; - -use crate::astconv::AstConv; -use crate::rustc_middle::ty::subst::Subst; -use hir::def::DefKind; -use rustc_hir as hir; -use rustc_hir::def_id::DefId; -use rustc_hir::lang_items::LangItem; -use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; -use rustc_infer::infer::LateBoundRegionConversionTime; -use rustc_infer::infer::{InferOk, InferResult}; -use rustc_middle::ty::subst::InternalSubsts; -use rustc_middle::ty::visit::TypeVisitable; -use rustc_middle::ty::{self, Ty}; -use rustc_span::source_map::Span; -use rustc_target::spec::abi::Abi; -use rustc_trait_selection::traits::error_reporting::ArgKind; -use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _; -use std::cmp; -use std::iter; - -/// What signature do we *expect* the closure to have from context? -#[derive(Debug)] -struct ExpectedSig<'tcx> { - /// Span that gave us this expectation, if we know that. - cause_span: Option<Span>, - sig: ty::PolyFnSig<'tcx>, -} - -struct ClosureSignatures<'tcx> { - /// The signature users of the closure see. - bound_sig: ty::PolyFnSig<'tcx>, - /// The signature within the function body. - /// This mostly differs in the sense that lifetimes are now early bound and any - /// opaque types from the signature expectation are overriden in case there are - /// explicit hidden types written by the user in the closure signature. - liberated_sig: ty::FnSig<'tcx>, -} - -impl<'a, 'tcx> FnCtxt<'a, 'tcx> { - #[instrument(skip(self, expr, _capture, decl, body_id), level = "debug")] - pub fn check_expr_closure( - &self, - expr: &hir::Expr<'_>, - _capture: hir::CaptureBy, - decl: &'tcx hir::FnDecl<'tcx>, - body_id: hir::BodyId, - gen: Option<hir::Movability>, - expected: Expectation<'tcx>, - ) -> Ty<'tcx> { - trace!("decl = {:#?}", decl); - trace!("expr = {:#?}", expr); - - // It's always helpful for inference if we know the kind of - // closure sooner rather than later, so first examine the expected - // type, and see if can glean a closure kind from there. - let (expected_sig, expected_kind) = match expected.to_option(self) { - Some(ty) => self.deduce_expectations_from_expected_type(ty), - None => (None, None), - }; - let body = self.tcx.hir().body(body_id); - self.check_closure(expr, expected_kind, decl, body, gen, expected_sig) - } - - #[instrument(skip(self, expr, body, decl), level = "debug", ret)] - fn check_closure( - &self, - expr: &hir::Expr<'_>, - opt_kind: Option<ty::ClosureKind>, - decl: &'tcx hir::FnDecl<'tcx>, - body: &'tcx hir::Body<'tcx>, - gen: Option<hir::Movability>, - expected_sig: Option<ExpectedSig<'tcx>>, - ) -> Ty<'tcx> { - trace!("decl = {:#?}", decl); - let expr_def_id = self.tcx.hir().local_def_id(expr.hir_id); - debug!(?expr_def_id); - - let ClosureSignatures { bound_sig, liberated_sig } = - self.sig_of_closure(expr.hir_id, expr_def_id.to_def_id(), decl, body, expected_sig); - - debug!(?bound_sig, ?liberated_sig); - - let return_type_pre_known = !liberated_sig.output().is_ty_infer(); - - let generator_types = check_fn( - self, - self.param_env.without_const(), - liberated_sig, - decl, - expr.hir_id, - body, - gen, - return_type_pre_known, - ) - .1; - - let parent_substs = InternalSubsts::identity_for_item( - self.tcx, - self.tcx.typeck_root_def_id(expr_def_id.to_def_id()), - ); - - let tupled_upvars_ty = self.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::ClosureSynthetic, - span: self.tcx.hir().span(expr.hir_id), - }); - - if let Some(GeneratorTypes { resume_ty, yield_ty, interior, movability }) = generator_types - { - let generator_substs = ty::GeneratorSubsts::new( - self.tcx, - ty::GeneratorSubstsParts { - parent_substs, - resume_ty, - yield_ty, - return_ty: liberated_sig.output(), - witness: interior, - tupled_upvars_ty, - }, - ); - - return self.tcx.mk_generator( - expr_def_id.to_def_id(), - generator_substs.substs, - movability, - ); - } - - // Tuple up the arguments and insert the resulting function type into - // the `closures` table. - let sig = bound_sig.map_bound(|sig| { - self.tcx.mk_fn_sig( - iter::once(self.tcx.intern_tup(sig.inputs())), - sig.output(), - sig.c_variadic, - sig.unsafety, - sig.abi, - ) - }); - - debug!(?sig, ?opt_kind); - - let closure_kind_ty = match opt_kind { - Some(kind) => kind.to_ty(self.tcx), - - // Create a type variable (for now) to represent the closure kind. - // It will be unified during the upvar inference phase (`upvar.rs`) - None => self.next_ty_var(TypeVariableOrigin { - // FIXME(eddyb) distinguish closure kind inference variables from the rest. - kind: TypeVariableOriginKind::ClosureSynthetic, - span: expr.span, - }), - }; - - let closure_substs = ty::ClosureSubsts::new( - self.tcx, - ty::ClosureSubstsParts { - parent_substs, - closure_kind_ty, - closure_sig_as_fn_ptr_ty: self.tcx.mk_fn_ptr(sig), - tupled_upvars_ty, - }, - ); - - self.tcx.mk_closure(expr_def_id.to_def_id(), closure_substs.substs) - } - - /// Given the expected type, figures out what it can about this closure we - /// are about to type check: - #[instrument(skip(self), level = "debug")] - fn deduce_expectations_from_expected_type( - &self, - expected_ty: Ty<'tcx>, - ) -> (Option<ExpectedSig<'tcx>>, Option<ty::ClosureKind>) { - match *expected_ty.kind() { - ty::Opaque(def_id, substs) => { - let bounds = self.tcx.bound_explicit_item_bounds(def_id); - let sig = bounds - .transpose_iter() - .map(|e| e.map_bound(|e| *e).transpose_tuple2()) - .find_map(|(pred, span)| match pred.0.kind().skip_binder() { - ty::PredicateKind::Projection(proj_predicate) => self - .deduce_sig_from_projection( - Some(span.0), - pred.0 - .kind() - .rebind(pred.rebind(proj_predicate).subst(self.tcx, substs)), - ), - _ => None, - }); - - let kind = bounds - .transpose_iter() - .map(|e| e.map_bound(|e| *e).transpose_tuple2()) - .filter_map(|(pred, _)| match pred.0.kind().skip_binder() { - ty::PredicateKind::Trait(tp) => { - self.tcx.fn_trait_kind_from_lang_item(tp.def_id()) - } - _ => None, - }) - .fold(None, |best, cur| Some(best.map_or(cur, |best| cmp::min(best, cur)))); - trace!(?sig, ?kind); - (sig, kind) - } - ty::Dynamic(ref object_type, ..) => { - let sig = object_type.projection_bounds().find_map(|pb| { - let pb = pb.with_self_ty(self.tcx, self.tcx.types.trait_object_dummy_self); - self.deduce_sig_from_projection(None, pb) - }); - let kind = object_type - .principal_def_id() - .and_then(|did| self.tcx.fn_trait_kind_from_lang_item(did)); - (sig, kind) - } - ty::Infer(ty::TyVar(vid)) => self.deduce_expectations_from_obligations(vid), - ty::FnPtr(sig) => { - let expected_sig = ExpectedSig { cause_span: None, sig }; - (Some(expected_sig), Some(ty::ClosureKind::Fn)) - } - _ => (None, None), - } - } - - fn deduce_expectations_from_obligations( - &self, - expected_vid: ty::TyVid, - ) -> (Option<ExpectedSig<'tcx>>, Option<ty::ClosureKind>) { - let expected_sig = - self.obligations_for_self_ty(expected_vid).find_map(|(_, obligation)| { - debug!(?obligation.predicate); - - let bound_predicate = obligation.predicate.kind(); - if let ty::PredicateKind::Projection(proj_predicate) = - obligation.predicate.kind().skip_binder() - { - // Given a Projection predicate, we can potentially infer - // the complete signature. - self.deduce_sig_from_projection( - Some(obligation.cause.span), - bound_predicate.rebind(proj_predicate), - ) - } else { - None - } - }); - - // Even if we can't infer the full signature, we may be able to - // infer the kind. This can occur when we elaborate a predicate - // like `F : Fn<A>`. Note that due to subtyping we could encounter - // many viable options, so pick the most restrictive. - let expected_kind = self - .obligations_for_self_ty(expected_vid) - .filter_map(|(tr, _)| self.tcx.fn_trait_kind_from_lang_item(tr.def_id())) - .fold(None, |best, cur| Some(best.map_or(cur, |best| cmp::min(best, cur)))); - - (expected_sig, expected_kind) - } - - /// Given a projection like "<F as Fn(X)>::Result == Y", we can deduce - /// everything we need to know about a closure or generator. - /// - /// The `cause_span` should be the span that caused us to - /// have this expected signature, or `None` if we can't readily - /// know that. - #[instrument(level = "debug", skip(self, cause_span), ret)] - fn deduce_sig_from_projection( - &self, - cause_span: Option<Span>, - projection: ty::PolyProjectionPredicate<'tcx>, - ) -> Option<ExpectedSig<'tcx>> { - let tcx = self.tcx; - - let trait_def_id = projection.trait_def_id(tcx); - - let is_fn = tcx.fn_trait_kind_from_lang_item(trait_def_id).is_some(); - let gen_trait = tcx.require_lang_item(LangItem::Generator, cause_span); - let is_gen = gen_trait == trait_def_id; - if !is_fn && !is_gen { - debug!("not fn or generator"); - return None; - } - - if is_gen { - // Check that we deduce the signature from the `<_ as std::ops::Generator>::Return` - // associated item and not yield. - let return_assoc_item = self.tcx.associated_item_def_ids(gen_trait)[1]; - if return_assoc_item != projection.projection_def_id() { - debug!("not return assoc item of generator"); - return None; - } - } - - let input_tys = if is_fn { - let arg_param_ty = projection.skip_binder().projection_ty.substs.type_at(1); - let arg_param_ty = self.resolve_vars_if_possible(arg_param_ty); - debug!(?arg_param_ty); - - match arg_param_ty.kind() { - &ty::Tuple(tys) => tys, - _ => return None, - } - } else { - // Generators with a `()` resume type may be defined with 0 or 1 explicit arguments, - // else they must have exactly 1 argument. For now though, just give up in this case. - return None; - }; - - // Since this is a return parameter type it is safe to unwrap. - let ret_param_ty = projection.skip_binder().term.ty().unwrap(); - let ret_param_ty = self.resolve_vars_if_possible(ret_param_ty); - debug!(?ret_param_ty); - - let sig = projection.rebind(self.tcx.mk_fn_sig( - input_tys.iter(), - ret_param_ty, - false, - hir::Unsafety::Normal, - Abi::Rust, - )); - - Some(ExpectedSig { cause_span, sig }) - } - - fn sig_of_closure( - &self, - hir_id: hir::HirId, - expr_def_id: DefId, - decl: &hir::FnDecl<'_>, - body: &hir::Body<'_>, - expected_sig: Option<ExpectedSig<'tcx>>, - ) -> ClosureSignatures<'tcx> { - if let Some(e) = expected_sig { - self.sig_of_closure_with_expectation(hir_id, expr_def_id, decl, body, e) - } else { - self.sig_of_closure_no_expectation(hir_id, expr_def_id, decl, body) - } - } - - /// If there is no expected signature, then we will convert the - /// types that the user gave into a signature. - #[instrument(skip(self, hir_id, expr_def_id, decl, body), level = "debug")] - fn sig_of_closure_no_expectation( - &self, - hir_id: hir::HirId, - expr_def_id: DefId, - decl: &hir::FnDecl<'_>, - body: &hir::Body<'_>, - ) -> ClosureSignatures<'tcx> { - let bound_sig = self.supplied_sig_of_closure(hir_id, expr_def_id, decl, body); - - self.closure_sigs(expr_def_id, body, bound_sig) - } - - /// Invoked to compute the signature of a closure expression. This - /// combines any user-provided type annotations (e.g., `|x: u32| - /// -> u32 { .. }`) with the expected signature. - /// - /// The approach is as follows: - /// - /// - Let `S` be the (higher-ranked) signature that we derive from the user's annotations. - /// - Let `E` be the (higher-ranked) signature that we derive from the expectations, if any. - /// - If we have no expectation `E`, then the signature of the closure is `S`. - /// - Otherwise, the signature of the closure is E. Moreover: - /// - Skolemize the late-bound regions in `E`, yielding `E'`. - /// - Instantiate all the late-bound regions bound in the closure within `S` - /// with fresh (existential) variables, yielding `S'` - /// - Require that `E' = S'` - /// - We could use some kind of subtyping relationship here, - /// I imagine, but equality is easier and works fine for - /// our purposes. - /// - /// The key intuition here is that the user's types must be valid - /// from "the inside" of the closure, but the expectation - /// ultimately drives the overall signature. - /// - /// # Examples - /// - /// ```ignore (illustrative) - /// fn with_closure<F>(_: F) - /// where F: Fn(&u32) -> &u32 { .. } - /// - /// with_closure(|x: &u32| { ... }) - /// ``` - /// - /// Here: - /// - E would be `fn(&u32) -> &u32`. - /// - S would be `fn(&u32) -> - /// - E' is `&'!0 u32 -> &'!0 u32` - /// - S' is `&'?0 u32 -> ?T` - /// - /// S' can be unified with E' with `['?0 = '!0, ?T = &'!10 u32]`. - /// - /// # Arguments - /// - /// - `expr_def_id`: the `DefId` of the closure expression - /// - `decl`: the HIR declaration of the closure - /// - `body`: the body of the closure - /// - `expected_sig`: the expected signature (if any). Note that - /// this is missing a binder: that is, there may be late-bound - /// regions with depth 1, which are bound then by the closure. - #[instrument(skip(self, hir_id, expr_def_id, decl, body), level = "debug")] - fn sig_of_closure_with_expectation( - &self, - hir_id: hir::HirId, - expr_def_id: DefId, - decl: &hir::FnDecl<'_>, - body: &hir::Body<'_>, - expected_sig: ExpectedSig<'tcx>, - ) -> ClosureSignatures<'tcx> { - // Watch out for some surprises and just ignore the - // expectation if things don't see to match up with what we - // expect. - if expected_sig.sig.c_variadic() != decl.c_variadic { - return self.sig_of_closure_no_expectation(hir_id, expr_def_id, decl, body); - } else if expected_sig.sig.skip_binder().inputs_and_output.len() != decl.inputs.len() + 1 { - return self.sig_of_closure_with_mismatched_number_of_arguments( - expr_def_id, - decl, - body, - expected_sig, - ); - } - - // Create a `PolyFnSig`. Note the oddity that late bound - // regions appearing free in `expected_sig` are now bound up - // in this binder we are creating. - assert!(!expected_sig.sig.skip_binder().has_vars_bound_above(ty::INNERMOST)); - let bound_sig = expected_sig.sig.map_bound(|sig| { - self.tcx.mk_fn_sig( - sig.inputs().iter().cloned(), - sig.output(), - sig.c_variadic, - hir::Unsafety::Normal, - Abi::RustCall, - ) - }); - - // `deduce_expectations_from_expected_type` introduces - // late-bound lifetimes defined elsewhere, which we now - // anonymize away, so as not to confuse the user. - let bound_sig = self.tcx.anonymize_late_bound_regions(bound_sig); - - let closure_sigs = self.closure_sigs(expr_def_id, body, bound_sig); - - // Up till this point, we have ignored the annotations that the user - // gave. This function will check that they unify successfully. - // Along the way, it also writes out entries for types that the user - // wrote into our typeck results, which are then later used by the privacy - // check. - match self.merge_supplied_sig_with_expectation( - hir_id, - expr_def_id, - decl, - body, - closure_sigs, - ) { - Ok(infer_ok) => self.register_infer_ok_obligations(infer_ok), - Err(_) => self.sig_of_closure_no_expectation(hir_id, expr_def_id, decl, body), - } - } - - fn sig_of_closure_with_mismatched_number_of_arguments( - &self, - expr_def_id: DefId, - decl: &hir::FnDecl<'_>, - body: &hir::Body<'_>, - expected_sig: ExpectedSig<'tcx>, - ) -> ClosureSignatures<'tcx> { - let hir = self.tcx.hir(); - let expr_map_node = hir.get_if_local(expr_def_id).unwrap(); - let expected_args: Vec<_> = expected_sig - .sig - .skip_binder() - .inputs() - .iter() - .map(|ty| ArgKind::from_expected_ty(*ty, None)) - .collect(); - let (closure_span, found_args) = match self.get_fn_like_arguments(expr_map_node) { - Some((sp, args)) => (Some(sp), args), - None => (None, Vec::new()), - }; - let expected_span = - expected_sig.cause_span.unwrap_or_else(|| hir.span_if_local(expr_def_id).unwrap()); - self.report_arg_count_mismatch( - expected_span, - closure_span, - expected_args, - found_args, - true, - ) - .emit(); - - let error_sig = self.error_sig_of_closure(decl); - - self.closure_sigs(expr_def_id, body, error_sig) - } - - /// Enforce the user's types against the expectation. See - /// `sig_of_closure_with_expectation` for details on the overall - /// strategy. - #[instrument(level = "debug", skip(self, hir_id, expr_def_id, decl, body, expected_sigs))] - fn merge_supplied_sig_with_expectation( - &self, - hir_id: hir::HirId, - expr_def_id: DefId, - decl: &hir::FnDecl<'_>, - body: &hir::Body<'_>, - mut expected_sigs: ClosureSignatures<'tcx>, - ) -> InferResult<'tcx, ClosureSignatures<'tcx>> { - // Get the signature S that the user gave. - // - // (See comment on `sig_of_closure_with_expectation` for the - // meaning of these letters.) - let supplied_sig = self.supplied_sig_of_closure(hir_id, expr_def_id, decl, body); - - debug!(?supplied_sig); - - // FIXME(#45727): As discussed in [this comment][c1], naively - // forcing equality here actually results in suboptimal error - // messages in some cases. For now, if there would have been - // an obvious error, we fallback to declaring the type of the - // closure to be the one the user gave, which allows other - // error message code to trigger. - // - // However, I think [there is potential to do even better - // here][c2], since in *this* code we have the precise span of - // the type parameter in question in hand when we report the - // error. - // - // [c1]: https://github.com/rust-lang/rust/pull/45072#issuecomment-341089706 - // [c2]: https://github.com/rust-lang/rust/pull/45072#issuecomment-341096796 - self.commit_if_ok(|_| { - let mut all_obligations = vec![]; - let inputs: Vec<_> = iter::zip( - decl.inputs, - supplied_sig.inputs().skip_binder(), // binder moved to (*) below - ) - .map(|(hir_ty, &supplied_ty)| { - // Instantiate (this part of..) S to S', i.e., with fresh variables. - self.replace_bound_vars_with_fresh_vars( - hir_ty.span, - LateBoundRegionConversionTime::FnCall, - // (*) binder moved to here - supplied_sig.inputs().rebind(supplied_ty), - ) - }) - .collect(); - - // The liberated version of this signature should be a subtype - // of the liberated form of the expectation. - for ((hir_ty, &supplied_ty), expected_ty) in iter::zip( - iter::zip(decl.inputs, &inputs), - expected_sigs.liberated_sig.inputs(), // `liberated_sig` is E'. - ) { - // Check that E' = S'. - let cause = self.misc(hir_ty.span); - let InferOk { value: (), obligations } = - self.at(&cause, self.param_env).eq(*expected_ty, supplied_ty)?; - all_obligations.extend(obligations); - } - - let supplied_output_ty = self.replace_bound_vars_with_fresh_vars( - decl.output.span(), - LateBoundRegionConversionTime::FnCall, - supplied_sig.output(), - ); - let cause = &self.misc(decl.output.span()); - let InferOk { value: (), obligations } = self - .at(cause, self.param_env) - .eq(expected_sigs.liberated_sig.output(), supplied_output_ty)?; - all_obligations.extend(obligations); - - let inputs = inputs.into_iter().map(|ty| self.resolve_vars_if_possible(ty)); - - expected_sigs.liberated_sig = self.tcx.mk_fn_sig( - inputs, - supplied_output_ty, - expected_sigs.liberated_sig.c_variadic, - hir::Unsafety::Normal, - Abi::RustCall, - ); - - Ok(InferOk { value: expected_sigs, obligations: all_obligations }) - }) - } - - /// If there is no expected signature, then we will convert the - /// types that the user gave into a signature. - /// - /// Also, record this closure signature for later. - #[instrument(skip(self, decl, body), level = "debug", ret)] - fn supplied_sig_of_closure( - &self, - hir_id: hir::HirId, - expr_def_id: DefId, - decl: &hir::FnDecl<'_>, - body: &hir::Body<'_>, - ) -> ty::PolyFnSig<'tcx> { - let astconv: &dyn AstConv<'_> = self; - - trace!("decl = {:#?}", decl); - debug!(?body.generator_kind); - - let bound_vars = self.tcx.late_bound_vars(hir_id); - - // First, convert the types that the user supplied (if any). - let supplied_arguments = decl.inputs.iter().map(|a| astconv.ast_ty_to_ty(a)); - let supplied_return = match decl.output { - hir::FnRetTy::Return(ref output) => astconv.ast_ty_to_ty(&output), - hir::FnRetTy::DefaultReturn(_) => match body.generator_kind { - // In the case of the async block that we create for a function body, - // we expect the return type of the block to match that of the enclosing - // function. - Some(hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Fn)) => { - debug!("closure is async fn body"); - self.deduce_future_output_from_obligations(expr_def_id, body.id().hir_id) - .unwrap_or_else(|| { - // AFAIK, deducing the future output - // always succeeds *except* in error cases - // like #65159. I'd like to return Error - // here, but I can't because I can't - // easily (and locally) prove that we - // *have* reported an - // error. --nikomatsakis - astconv.ty_infer(None, decl.output.span()) - }) - } - - _ => astconv.ty_infer(None, decl.output.span()), - }, - }; - - let result = ty::Binder::bind_with_vars( - self.tcx.mk_fn_sig( - supplied_arguments, - supplied_return, - decl.c_variadic, - hir::Unsafety::Normal, - Abi::RustCall, - ), - bound_vars, - ); - - let c_result = self.inh.infcx.canonicalize_response(result); - self.typeck_results.borrow_mut().user_provided_sigs.insert(expr_def_id, c_result); - - result - } - - /// Invoked when we are translating the generator that results - /// from desugaring an `async fn`. Returns the "sugared" return - /// type of the `async fn` -- that is, the return type that the - /// user specified. The "desugared" return type is an `impl - /// Future<Output = T>`, so we do this by searching through the - /// obligations to extract the `T`. - #[instrument(skip(self), level = "debug", ret)] - fn deduce_future_output_from_obligations( - &self, - expr_def_id: DefId, - body_id: hir::HirId, - ) -> Option<Ty<'tcx>> { - let ret_coercion = self.ret_coercion.as_ref().unwrap_or_else(|| { - span_bug!(self.tcx.def_span(expr_def_id), "async fn generator outside of a fn") - }); - - let ret_ty = ret_coercion.borrow().expected_ty(); - let ret_ty = self.inh.infcx.shallow_resolve(ret_ty); - - let get_future_output = |predicate: ty::Predicate<'tcx>, span| { - // Search for a pending obligation like - // - // `<R as Future>::Output = T` - // - // where R is the return type we are expecting. This type `T` - // will be our output. - let bound_predicate = predicate.kind(); - if let ty::PredicateKind::Projection(proj_predicate) = bound_predicate.skip_binder() { - self.deduce_future_output_from_projection( - span, - bound_predicate.rebind(proj_predicate), - ) - } else { - None - } - }; - - let output_ty = match *ret_ty.kind() { - ty::Infer(ty::TyVar(ret_vid)) => { - self.obligations_for_self_ty(ret_vid).find_map(|(_, obligation)| { - get_future_output(obligation.predicate, obligation.cause.span) - })? - } - ty::Opaque(def_id, substs) => self - .tcx - .bound_explicit_item_bounds(def_id) - .transpose_iter() - .map(|e| e.map_bound(|e| *e).transpose_tuple2()) - .find_map(|(p, s)| get_future_output(p.subst(self.tcx, substs), s.0))?, - ty::Error(_) => return None, - ty::Projection(proj) - if self.tcx.def_kind(proj.item_def_id) == DefKind::ImplTraitPlaceholder => - { - self.tcx - .bound_explicit_item_bounds(proj.item_def_id) - .transpose_iter() - .map(|e| e.map_bound(|e| *e).transpose_tuple2()) - .find_map(|(p, s)| get_future_output(p.subst(self.tcx, proj.substs), s.0))? - } - _ => span_bug!( - self.tcx.def_span(expr_def_id), - "async fn generator return type not an inference variable: {ret_ty}" - ), - }; - - // async fn that have opaque types in their return type need to redo the conversion to inference variables - // as they fetch the still opaque version from the signature. - let InferOk { value: output_ty, obligations } = self - .replace_opaque_types_with_inference_vars( - output_ty, - body_id, - self.tcx.def_span(expr_def_id), - self.param_env, - ); - self.register_predicates(obligations); - - Some(output_ty) - } - - /// Given a projection like - /// - /// `<X as Future>::Output = T` - /// - /// where `X` is some type that has no late-bound regions, returns - /// `Some(T)`. If the projection is for some other trait, returns - /// `None`. - fn deduce_future_output_from_projection( - &self, - cause_span: Span, - predicate: ty::PolyProjectionPredicate<'tcx>, - ) -> Option<Ty<'tcx>> { - debug!("deduce_future_output_from_projection(predicate={:?})", predicate); - - // We do not expect any bound regions in our predicate, so - // skip past the bound vars. - let Some(predicate) = predicate.no_bound_vars() else { - debug!("deduce_future_output_from_projection: has late-bound regions"); - return None; - }; - - // Check that this is a projection from the `Future` trait. - let trait_def_id = predicate.projection_ty.trait_def_id(self.tcx); - let future_trait = self.tcx.require_lang_item(LangItem::Future, Some(cause_span)); - if trait_def_id != future_trait { - debug!("deduce_future_output_from_projection: not a future"); - return None; - } - - // The `Future` trait has only one associated item, `Output`, - // so check that this is what we see. - let output_assoc_item = self.tcx.associated_item_def_ids(future_trait)[0]; - if output_assoc_item != predicate.projection_ty.item_def_id { - span_bug!( - cause_span, - "projecting associated item `{:?}` from future, which is not Output `{:?}`", - predicate.projection_ty.item_def_id, - output_assoc_item, - ); - } - - // Extract the type from the projection. Note that there can - // be no bound variables in this type because the "self type" - // does not have any regions in it. - let output_ty = self.resolve_vars_if_possible(predicate.term); - debug!("deduce_future_output_from_projection: output_ty={:?}", output_ty); - // This is a projection on a Fn trait so will always be a type. - Some(output_ty.ty().unwrap()) - } - - /// Converts the types that the user supplied, in case that doing - /// so should yield an error, but returns back a signature where - /// all parameters are of type `TyErr`. - fn error_sig_of_closure(&self, decl: &hir::FnDecl<'_>) -> ty::PolyFnSig<'tcx> { - let astconv: &dyn AstConv<'_> = self; - - let supplied_arguments = decl.inputs.iter().map(|a| { - // Convert the types that the user supplied (if any), but ignore them. - astconv.ast_ty_to_ty(a); - self.tcx.ty_error() - }); - - if let hir::FnRetTy::Return(ref output) = decl.output { - astconv.ast_ty_to_ty(&output); - } - - let result = ty::Binder::dummy(self.tcx.mk_fn_sig( - supplied_arguments, - self.tcx.ty_error(), - decl.c_variadic, - hir::Unsafety::Normal, - Abi::RustCall, - )); - - debug!("supplied_sig_of_closure: result={:?}", result); - - result - } - - fn closure_sigs( - &self, - expr_def_id: DefId, - body: &hir::Body<'_>, - bound_sig: ty::PolyFnSig<'tcx>, - ) -> ClosureSignatures<'tcx> { - let liberated_sig = self.tcx().liberate_late_bound_regions(expr_def_id, bound_sig); - let liberated_sig = self.inh.normalize_associated_types_in( - body.value.span, - body.value.hir_id, - self.param_env, - liberated_sig, - ); - ClosureSignatures { bound_sig, liberated_sig } - } -} diff --git a/compiler/rustc_typeck/src/check/coercion.rs b/compiler/rustc_typeck/src/check/coercion.rs deleted file mode 100644 index def592c46..000000000 --- a/compiler/rustc_typeck/src/check/coercion.rs +++ /dev/null @@ -1,1885 +0,0 @@ -//! # Type Coercion -//! -//! Under certain circumstances we will coerce from one type to another, -//! for example by auto-borrowing. This occurs in situations where the -//! compiler has a firm 'expected type' that was supplied from the user, -//! and where the actual type is similar to that expected type in purpose -//! but not in representation (so actual subtyping is inappropriate). -//! -//! ## Reborrowing -//! -//! Note that if we are expecting a reference, we will *reborrow* -//! even if the argument provided was already a reference. This is -//! useful for freezing mut things (that is, when the expected type is &T -//! but you have &mut T) and also for avoiding the linearity -//! of mut things (when the expected is &mut T and you have &mut T). See -//! the various `src/test/ui/coerce/*.rs` tests for -//! examples of where this is useful. -//! -//! ## Subtle note -//! -//! When inferring the generic arguments of functions, the argument -//! order is relevant, which can lead to the following edge case: -//! -//! ```ignore (illustrative) -//! fn foo<T>(a: T, b: T) { -//! // ... -//! } -//! -//! foo(&7i32, &mut 7i32); -//! // This compiles, as we first infer `T` to be `&i32`, -//! // and then coerce `&mut 7i32` to `&7i32`. -//! -//! foo(&mut 7i32, &7i32); -//! // This does not compile, as we first infer `T` to be `&mut i32` -//! // and are then unable to coerce `&7i32` to `&mut i32`. -//! ``` - -use crate::astconv::AstConv; -use crate::check::FnCtxt; -use rustc_errors::{ - struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, MultiSpan, -}; -use rustc_hir as hir; -use rustc_hir::def_id::DefId; -use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::Expr; -use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; -use rustc_infer::infer::{Coercion, InferOk, InferResult}; -use rustc_infer::traits::{Obligation, TraitEngine, TraitEngineExt}; -use rustc_middle::lint::in_external_macro; -use rustc_middle::ty::adjustment::{ - Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, PointerCast, -}; -use rustc_middle::ty::error::TypeError; -use rustc_middle::ty::relate::RelateResult; -use rustc_middle::ty::subst::SubstsRef; -use rustc_middle::ty::visit::TypeVisitable; -use rustc_middle::ty::{self, ToPredicate, Ty, TypeAndMut}; -use rustc_session::parse::feature_err; -use rustc_span::symbol::sym; -use rustc_span::{self, BytePos, DesugaringKind, Span}; -use rustc_target::spec::abi::Abi; -use rustc_trait_selection::infer::InferCtxtExt as _; -use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _; -use rustc_trait_selection::traits::{self, ObligationCause, ObligationCauseCode}; - -use smallvec::{smallvec, SmallVec}; -use std::ops::Deref; - -struct Coerce<'a, 'tcx> { - fcx: &'a FnCtxt<'a, 'tcx>, - cause: ObligationCause<'tcx>, - use_lub: bool, - /// Determines whether or not allow_two_phase_borrow is set on any - /// autoref adjustments we create while coercing. We don't want to - /// allow deref coercions to create two-phase borrows, at least initially, - /// but we do need two-phase borrows for function argument reborrows. - /// See #47489 and #48598 - /// See docs on the "AllowTwoPhase" type for a more detailed discussion - allow_two_phase: AllowTwoPhase, -} - -impl<'a, 'tcx> Deref for Coerce<'a, 'tcx> { - type Target = FnCtxt<'a, 'tcx>; - fn deref(&self) -> &Self::Target { - &self.fcx - } -} - -type CoerceResult<'tcx> = InferResult<'tcx, (Vec<Adjustment<'tcx>>, Ty<'tcx>)>; - -struct CollectRetsVisitor<'tcx> { - ret_exprs: Vec<&'tcx hir::Expr<'tcx>>, -} - -impl<'tcx> Visitor<'tcx> for CollectRetsVisitor<'tcx> { - fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { - if let hir::ExprKind::Ret(_) = expr.kind { - self.ret_exprs.push(expr); - } - intravisit::walk_expr(self, expr); - } -} - -/// Coercing a mutable reference to an immutable works, while -/// coercing `&T` to `&mut T` should be forbidden. -fn coerce_mutbls<'tcx>( - from_mutbl: hir::Mutability, - to_mutbl: hir::Mutability, -) -> RelateResult<'tcx, ()> { - match (from_mutbl, to_mutbl) { - (hir::Mutability::Mut, hir::Mutability::Mut | hir::Mutability::Not) - | (hir::Mutability::Not, hir::Mutability::Not) => Ok(()), - (hir::Mutability::Not, hir::Mutability::Mut) => Err(TypeError::Mutability), - } -} - -/// Do not require any adjustments, i.e. coerce `x -> x`. -fn identity(_: Ty<'_>) -> Vec<Adjustment<'_>> { - vec![] -} - -fn simple<'tcx>(kind: Adjust<'tcx>) -> impl FnOnce(Ty<'tcx>) -> Vec<Adjustment<'tcx>> { - move |target| vec![Adjustment { kind, target }] -} - -/// This always returns `Ok(...)`. -fn success<'tcx>( - adj: Vec<Adjustment<'tcx>>, - target: Ty<'tcx>, - obligations: traits::PredicateObligations<'tcx>, -) -> CoerceResult<'tcx> { - Ok(InferOk { value: (adj, target), obligations }) -} - -impl<'f, 'tcx> Coerce<'f, 'tcx> { - fn new( - fcx: &'f FnCtxt<'f, 'tcx>, - cause: ObligationCause<'tcx>, - allow_two_phase: AllowTwoPhase, - ) -> Self { - Coerce { fcx, cause, allow_two_phase, use_lub: false } - } - - fn unify(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> InferResult<'tcx, Ty<'tcx>> { - debug!("unify(a: {:?}, b: {:?}, use_lub: {})", a, b, self.use_lub); - self.commit_if_ok(|_| { - if self.use_lub { - self.at(&self.cause, self.fcx.param_env).lub(b, a) - } else { - self.at(&self.cause, self.fcx.param_env) - .sup(b, a) - .map(|InferOk { value: (), obligations }| InferOk { value: a, obligations }) - } - }) - } - - /// Unify two types (using sub or lub) and produce a specific coercion. - fn unify_and<F>(&self, a: Ty<'tcx>, b: Ty<'tcx>, f: F) -> CoerceResult<'tcx> - where - F: FnOnce(Ty<'tcx>) -> Vec<Adjustment<'tcx>>, - { - self.unify(a, b) - .and_then(|InferOk { value: ty, obligations }| success(f(ty), ty, obligations)) - } - - #[instrument(skip(self))] - fn coerce(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> { - // First, remove any resolved type variables (at the top level, at least): - let a = self.shallow_resolve(a); - let b = self.shallow_resolve(b); - debug!("Coerce.tys({:?} => {:?})", a, b); - - // Just ignore error types. - if a.references_error() || b.references_error() { - return success(vec![], self.fcx.tcx.ty_error(), vec![]); - } - - // Coercing from `!` to any type is allowed: - if a.is_never() { - return success(simple(Adjust::NeverToAny)(b), b, vec![]); - } - - // Coercing *from* an unresolved inference variable means that - // we have no information about the source type. This will always - // ultimately fall back to some form of subtyping. - if a.is_ty_var() { - return self.coerce_from_inference_variable(a, b, identity); - } - - // Consider coercing the subtype to a DST - // - // NOTE: this is wrapped in a `commit_if_ok` because it creates - // a "spurious" type variable, and we don't want to have that - // type variable in memory if the coercion fails. - let unsize = self.commit_if_ok(|_| self.coerce_unsized(a, b)); - match unsize { - Ok(_) => { - debug!("coerce: unsize successful"); - return unsize; - } - Err(TypeError::ObjectUnsafeCoercion(did)) => { - debug!("coerce: unsize not object safe"); - return Err(TypeError::ObjectUnsafeCoercion(did)); - } - Err(error) => { - debug!(?error, "coerce: unsize failed"); - } - } - - // Examine the supertype and consider auto-borrowing. - match *b.kind() { - ty::RawPtr(mt_b) => { - return self.coerce_unsafe_ptr(a, b, mt_b.mutbl); - } - ty::Ref(r_b, _, mutbl_b) => { - return self.coerce_borrowed_pointer(a, b, r_b, mutbl_b); - } - _ => {} - } - - match *a.kind() { - ty::FnDef(..) => { - // Function items are coercible to any closure - // type; function pointers are not (that would - // require double indirection). - // Additionally, we permit coercion of function - // items to drop the unsafe qualifier. - self.coerce_from_fn_item(a, b) - } - ty::FnPtr(a_f) => { - // We permit coercion of fn pointers to drop the - // unsafe qualifier. - self.coerce_from_fn_pointer(a, a_f, b) - } - ty::Closure(closure_def_id_a, substs_a) => { - // Non-capturing closures are coercible to - // function pointers or unsafe function pointers. - // It cannot convert closures that require unsafe. - self.coerce_closure_to_fn(a, closure_def_id_a, substs_a, b) - } - _ => { - // Otherwise, just use unification rules. - self.unify_and(a, b, identity) - } - } - } - - /// Coercing *from* an inference variable. In this case, we have no information - /// about the source type, so we can't really do a true coercion and we always - /// fall back to subtyping (`unify_and`). - fn coerce_from_inference_variable( - &self, - a: Ty<'tcx>, - b: Ty<'tcx>, - make_adjustments: impl FnOnce(Ty<'tcx>) -> Vec<Adjustment<'tcx>>, - ) -> CoerceResult<'tcx> { - debug!("coerce_from_inference_variable(a={:?}, b={:?})", a, b); - assert!(a.is_ty_var() && self.shallow_resolve(a) == a); - assert!(self.shallow_resolve(b) == b); - - if b.is_ty_var() { - // Two unresolved type variables: create a `Coerce` predicate. - let target_ty = if self.use_lub { - self.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::LatticeVariable, - span: self.cause.span, - }) - } else { - b - }; - - let mut obligations = Vec::with_capacity(2); - for &source_ty in &[a, b] { - if source_ty != target_ty { - obligations.push(Obligation::new( - self.cause.clone(), - self.param_env, - ty::Binder::dummy(ty::PredicateKind::Coerce(ty::CoercePredicate { - a: source_ty, - b: target_ty, - })) - .to_predicate(self.tcx()), - )); - } - } - - debug!( - "coerce_from_inference_variable: two inference variables, target_ty={:?}, obligations={:?}", - target_ty, obligations - ); - let adjustments = make_adjustments(target_ty); - InferResult::Ok(InferOk { value: (adjustments, target_ty), obligations }) - } else { - // One unresolved type variable: just apply subtyping, we may be able - // to do something useful. - self.unify_and(a, b, make_adjustments) - } - } - - /// Reborrows `&mut A` to `&mut B` and `&(mut) A` to `&B`. - /// To match `A` with `B`, autoderef will be performed, - /// calling `deref`/`deref_mut` where necessary. - fn coerce_borrowed_pointer( - &self, - a: Ty<'tcx>, - b: Ty<'tcx>, - r_b: ty::Region<'tcx>, - mutbl_b: hir::Mutability, - ) -> CoerceResult<'tcx> { - debug!("coerce_borrowed_pointer(a={:?}, b={:?})", a, b); - - // If we have a parameter of type `&M T_a` and the value - // provided is `expr`, we will be adding an implicit borrow, - // meaning that we convert `f(expr)` to `f(&M *expr)`. Therefore, - // to type check, we will construct the type that `&M*expr` would - // yield. - - let (r_a, mt_a) = match *a.kind() { - ty::Ref(r_a, ty, mutbl) => { - let mt_a = ty::TypeAndMut { ty, mutbl }; - coerce_mutbls(mt_a.mutbl, mutbl_b)?; - (r_a, mt_a) - } - _ => return self.unify_and(a, b, identity), - }; - - let span = self.cause.span; - - let mut first_error = None; - let mut r_borrow_var = None; - let mut autoderef = self.autoderef(span, a); - let mut found = None; - - for (referent_ty, autoderefs) in autoderef.by_ref() { - if autoderefs == 0 { - // Don't let this pass, otherwise it would cause - // &T to autoref to &&T. - continue; - } - - // At this point, we have deref'd `a` to `referent_ty`. So - // imagine we are coercing from `&'a mut Vec<T>` to `&'b mut [T]`. - // In the autoderef loop for `&'a mut Vec<T>`, we would get - // three callbacks: - // - // - `&'a mut Vec<T>` -- 0 derefs, just ignore it - // - `Vec<T>` -- 1 deref - // - `[T]` -- 2 deref - // - // At each point after the first callback, we want to - // check to see whether this would match out target type - // (`&'b mut [T]`) if we autoref'd it. We can't just - // compare the referent types, though, because we still - // have to consider the mutability. E.g., in the case - // we've been considering, we have an `&mut` reference, so - // the `T` in `[T]` needs to be unified with equality. - // - // Therefore, we construct reference types reflecting what - // the types will be after we do the final auto-ref and - // compare those. Note that this means we use the target - // mutability [1], since it may be that we are coercing - // from `&mut T` to `&U`. - // - // One fine point concerns the region that we use. We - // choose the region such that the region of the final - // type that results from `unify` will be the region we - // want for the autoref: - // - // - if in sub mode, that means we want to use `'b` (the - // region from the target reference) for both - // pointers [2]. This is because sub mode (somewhat - // arbitrarily) returns the subtype region. In the case - // where we are coercing to a target type, we know we - // want to use that target type region (`'b`) because -- - // for the program to type-check -- it must be the - // smaller of the two. - // - One fine point. It may be surprising that we can - // use `'b` without relating `'a` and `'b`. The reason - // that this is ok is that what we produce is - // effectively a `&'b *x` expression (if you could - // annotate the region of a borrow), and regionck has - // code that adds edges from the region of a borrow - // (`'b`, here) into the regions in the borrowed - // expression (`*x`, here). (Search for "link".) - // - if in lub mode, things can get fairly complicated. The - // easiest thing is just to make a fresh - // region variable [4], which effectively means we defer - // the decision to region inference (and regionck, which will add - // some more edges to this variable). However, this can wind up - // creating a crippling number of variables in some cases -- - // e.g., #32278 -- so we optimize one particular case [3]. - // Let me try to explain with some examples: - // - The "running example" above represents the simple case, - // where we have one `&` reference at the outer level and - // ownership all the rest of the way down. In this case, - // we want `LUB('a, 'b)` as the resulting region. - // - However, if there are nested borrows, that region is - // too strong. Consider a coercion from `&'a &'x Rc<T>` to - // `&'b T`. In this case, `'a` is actually irrelevant. - // The pointer we want is `LUB('x, 'b`). If we choose `LUB('a,'b)` - // we get spurious errors (`ui/regions-lub-ref-ref-rc.rs`). - // (The errors actually show up in borrowck, typically, because - // this extra edge causes the region `'a` to be inferred to something - // too big, which then results in borrowck errors.) - // - We could track the innermost shared reference, but there is already - // code in regionck that has the job of creating links between - // the region of a borrow and the regions in the thing being - // borrowed (here, `'a` and `'x`), and it knows how to handle - // all the various cases. So instead we just make a region variable - // and let regionck figure it out. - let r = if !self.use_lub { - r_b // [2] above - } else if autoderefs == 1 { - r_a // [3] above - } else { - if r_borrow_var.is_none() { - // create var lazily, at most once - let coercion = Coercion(span); - let r = self.next_region_var(coercion); - r_borrow_var = Some(r); // [4] above - } - r_borrow_var.unwrap() - }; - let derefd_ty_a = self.tcx.mk_ref( - r, - TypeAndMut { - ty: referent_ty, - mutbl: mutbl_b, // [1] above - }, - ); - match self.unify(derefd_ty_a, b) { - Ok(ok) => { - found = Some(ok); - break; - } - Err(err) => { - if first_error.is_none() { - first_error = Some(err); - } - } - } - } - - // Extract type or return an error. We return the first error - // we got, which should be from relating the "base" type - // (e.g., in example above, the failure from relating `Vec<T>` - // to the target type), since that should be the least - // confusing. - let Some(InferOk { value: ty, mut obligations }) = found else { - let err = first_error.expect("coerce_borrowed_pointer had no error"); - debug!("coerce_borrowed_pointer: failed with err = {:?}", err); - return Err(err); - }; - - if ty == a && mt_a.mutbl == hir::Mutability::Not && autoderef.step_count() == 1 { - // As a special case, if we would produce `&'a *x`, that's - // a total no-op. We end up with the type `&'a T` just as - // we started with. In that case, just skip it - // altogether. This is just an optimization. - // - // Note that for `&mut`, we DO want to reborrow -- - // otherwise, this would be a move, which might be an - // error. For example `foo(self.x)` where `self` and - // `self.x` both have `&mut `type would be a move of - // `self.x`, but we auto-coerce it to `foo(&mut *self.x)`, - // which is a borrow. - assert_eq!(mutbl_b, hir::Mutability::Not); // can only coerce &T -> &U - return success(vec![], ty, obligations); - } - - let InferOk { value: mut adjustments, obligations: o } = - self.adjust_steps_as_infer_ok(&autoderef); - obligations.extend(o); - obligations.extend(autoderef.into_obligations()); - - // Now apply the autoref. We have to extract the region out of - // the final ref type we got. - let ty::Ref(r_borrow, _, _) = ty.kind() else { - span_bug!(span, "expected a ref type, got {:?}", ty); - }; - let mutbl = match mutbl_b { - hir::Mutability::Not => AutoBorrowMutability::Not, - hir::Mutability::Mut => { - AutoBorrowMutability::Mut { allow_two_phase_borrow: self.allow_two_phase } - } - }; - adjustments.push(Adjustment { - kind: Adjust::Borrow(AutoBorrow::Ref(*r_borrow, mutbl)), - target: ty, - }); - - debug!("coerce_borrowed_pointer: succeeded ty={:?} adjustments={:?}", ty, adjustments); - - success(adjustments, ty, obligations) - } - - // &[T; n] or &mut [T; n] -> &[T] - // or &mut [T; n] -> &mut [T] - // or &Concrete -> &Trait, etc. - #[instrument(skip(self), level = "debug")] - fn coerce_unsized(&self, mut source: Ty<'tcx>, mut target: Ty<'tcx>) -> CoerceResult<'tcx> { - source = self.shallow_resolve(source); - target = self.shallow_resolve(target); - debug!(?source, ?target); - - // These 'if' statements require some explanation. - // The `CoerceUnsized` trait is special - it is only - // possible to write `impl CoerceUnsized<B> for A` where - // A and B have 'matching' fields. This rules out the following - // two types of blanket impls: - // - // `impl<T> CoerceUnsized<T> for SomeType` - // `impl<T> CoerceUnsized<SomeType> for T` - // - // Both of these trigger a special `CoerceUnsized`-related error (E0376) - // - // We can take advantage of this fact to avoid performing unnecessary work. - // If either `source` or `target` is a type variable, then any applicable impl - // would need to be generic over the self-type (`impl<T> CoerceUnsized<SomeType> for T`) - // or generic over the `CoerceUnsized` type parameter (`impl<T> CoerceUnsized<T> for - // SomeType`). - // - // However, these are exactly the kinds of impls which are forbidden by - // the compiler! Therefore, we can be sure that coercion will always fail - // when either the source or target type is a type variable. This allows us - // to skip performing any trait selection, and immediately bail out. - if source.is_ty_var() { - debug!("coerce_unsized: source is a TyVar, bailing out"); - return Err(TypeError::Mismatch); - } - if target.is_ty_var() { - debug!("coerce_unsized: target is a TyVar, bailing out"); - return Err(TypeError::Mismatch); - } - - let traits = - (self.tcx.lang_items().unsize_trait(), self.tcx.lang_items().coerce_unsized_trait()); - let (Some(unsize_did), Some(coerce_unsized_did)) = traits else { - debug!("missing Unsize or CoerceUnsized traits"); - return Err(TypeError::Mismatch); - }; - - // Note, we want to avoid unnecessary unsizing. We don't want to coerce to - // a DST unless we have to. This currently comes out in the wash since - // we can't unify [T] with U. But to properly support DST, we need to allow - // that, at which point we will need extra checks on the target here. - - // Handle reborrows before selecting `Source: CoerceUnsized<Target>`. - let reborrow = match (source.kind(), target.kind()) { - (&ty::Ref(_, ty_a, mutbl_a), &ty::Ref(_, _, mutbl_b)) => { - coerce_mutbls(mutbl_a, mutbl_b)?; - - let coercion = Coercion(self.cause.span); - let r_borrow = self.next_region_var(coercion); - let mutbl = match mutbl_b { - hir::Mutability::Not => AutoBorrowMutability::Not, - hir::Mutability::Mut => AutoBorrowMutability::Mut { - // We don't allow two-phase borrows here, at least for initial - // implementation. If it happens that this coercion is a function argument, - // the reborrow in coerce_borrowed_ptr will pick it up. - allow_two_phase_borrow: AllowTwoPhase::No, - }, - }; - Some(( - Adjustment { kind: Adjust::Deref(None), target: ty_a }, - Adjustment { - kind: Adjust::Borrow(AutoBorrow::Ref(r_borrow, mutbl)), - target: self - .tcx - .mk_ref(r_borrow, ty::TypeAndMut { mutbl: mutbl_b, ty: ty_a }), - }, - )) - } - (&ty::Ref(_, ty_a, mt_a), &ty::RawPtr(ty::TypeAndMut { mutbl: mt_b, .. })) => { - coerce_mutbls(mt_a, mt_b)?; - - Some(( - Adjustment { kind: Adjust::Deref(None), target: ty_a }, - Adjustment { - kind: Adjust::Borrow(AutoBorrow::RawPtr(mt_b)), - target: self.tcx.mk_ptr(ty::TypeAndMut { mutbl: mt_b, ty: ty_a }), - }, - )) - } - _ => None, - }; - let coerce_source = reborrow.as_ref().map_or(source, |&(_, ref r)| r.target); - - // Setup either a subtyping or a LUB relationship between - // the `CoerceUnsized` target type and the expected type. - // We only have the latter, so we use an inference variable - // for the former and let type inference do the rest. - let origin = TypeVariableOrigin { - kind: TypeVariableOriginKind::MiscVariable, - span: self.cause.span, - }; - let coerce_target = self.next_ty_var(origin); - let mut coercion = self.unify_and(coerce_target, target, |target| { - let unsize = Adjustment { kind: Adjust::Pointer(PointerCast::Unsize), target }; - match reborrow { - None => vec![unsize], - Some((ref deref, ref autoref)) => vec![deref.clone(), autoref.clone(), unsize], - } - })?; - - let mut selcx = traits::SelectionContext::new(self); - - // Create an obligation for `Source: CoerceUnsized<Target>`. - let cause = ObligationCause::new( - self.cause.span, - self.body_id, - ObligationCauseCode::Coercion { source, target }, - ); - - // Use a FIFO queue for this custom fulfillment procedure. - // - // A Vec (or SmallVec) is not a natural choice for a queue. However, - // this code path is hot, and this queue usually has a max length of 1 - // and almost never more than 3. By using a SmallVec we avoid an - // allocation, at the (very small) cost of (occasionally) having to - // shift subsequent elements down when removing the front element. - let mut queue: SmallVec<[_; 4]> = smallvec![traits::predicate_for_trait_def( - self.tcx, - self.fcx.param_env, - cause, - coerce_unsized_did, - 0, - coerce_source, - &[coerce_target.into()] - )]; - - let mut has_unsized_tuple_coercion = false; - let mut has_trait_upcasting_coercion = None; - - // Keep resolving `CoerceUnsized` and `Unsize` predicates to avoid - // emitting a coercion in cases like `Foo<$1>` -> `Foo<$2>`, where - // inference might unify those two inner type variables later. - let traits = [coerce_unsized_did, unsize_did]; - while !queue.is_empty() { - let obligation = queue.remove(0); - debug!("coerce_unsized resolve step: {:?}", obligation); - let bound_predicate = obligation.predicate.kind(); - let trait_pred = match bound_predicate.skip_binder() { - ty::PredicateKind::Trait(trait_pred) if traits.contains(&trait_pred.def_id()) => { - if unsize_did == trait_pred.def_id() { - let self_ty = trait_pred.self_ty(); - let unsize_ty = trait_pred.trait_ref.substs[1].expect_ty(); - if let (ty::Dynamic(ref data_a, ..), ty::Dynamic(ref data_b, ..)) = - (self_ty.kind(), unsize_ty.kind()) - && data_a.principal_def_id() != data_b.principal_def_id() - { - debug!("coerce_unsized: found trait upcasting coercion"); - has_trait_upcasting_coercion = Some((self_ty, unsize_ty)); - } - if let ty::Tuple(..) = unsize_ty.kind() { - debug!("coerce_unsized: found unsized tuple coercion"); - has_unsized_tuple_coercion = true; - } - } - bound_predicate.rebind(trait_pred) - } - _ => { - coercion.obligations.push(obligation); - continue; - } - }; - match selcx.select(&obligation.with(trait_pred)) { - // Uncertain or unimplemented. - Ok(None) => { - if trait_pred.def_id() == unsize_did { - let trait_pred = self.resolve_vars_if_possible(trait_pred); - let self_ty = trait_pred.skip_binder().self_ty(); - let unsize_ty = trait_pred.skip_binder().trait_ref.substs[1].expect_ty(); - debug!("coerce_unsized: ambiguous unsize case for {:?}", trait_pred); - match (&self_ty.kind(), &unsize_ty.kind()) { - (ty::Infer(ty::TyVar(v)), ty::Dynamic(..)) - if self.type_var_is_sized(*v) => - { - debug!("coerce_unsized: have sized infer {:?}", v); - coercion.obligations.push(obligation); - // `$0: Unsize<dyn Trait>` where we know that `$0: Sized`, try going - // for unsizing. - } - _ => { - // Some other case for `$0: Unsize<Something>`. Note that we - // hit this case even if `Something` is a sized type, so just - // don't do the coercion. - debug!("coerce_unsized: ambiguous unsize"); - return Err(TypeError::Mismatch); - } - } - } else { - debug!("coerce_unsized: early return - ambiguous"); - return Err(TypeError::Mismatch); - } - } - Err(traits::Unimplemented) => { - debug!("coerce_unsized: early return - can't prove obligation"); - return Err(TypeError::Mismatch); - } - - // Object safety violations or miscellaneous. - Err(err) => { - self.report_selection_error(obligation.clone(), &obligation, &err, false); - // Treat this like an obligation and follow through - // with the unsizing - the lack of a coercion should - // be silent, as it causes a type mismatch later. - } - - Ok(Some(impl_source)) => queue.extend(impl_source.nested_obligations()), - } - } - - if has_unsized_tuple_coercion && !self.tcx.features().unsized_tuple_coercion { - feature_err( - &self.tcx.sess.parse_sess, - sym::unsized_tuple_coercion, - self.cause.span, - "unsized tuple coercion is not stable enough for use and is subject to change", - ) - .emit(); - } - - if let Some((sub, sup)) = has_trait_upcasting_coercion - && !self.tcx().features().trait_upcasting - { - // Renders better when we erase regions, since they're not really the point here. - let (sub, sup) = self.tcx.erase_regions((sub, sup)); - let mut err = feature_err( - &self.tcx.sess.parse_sess, - sym::trait_upcasting, - self.cause.span, - &format!("cannot cast `{sub}` to `{sup}`, trait upcasting coercion is experimental"), - ); - err.note(&format!("required when coercing `{source}` into `{target}`")); - err.emit(); - } - - Ok(coercion) - } - - fn coerce_from_safe_fn<F, G>( - &self, - a: Ty<'tcx>, - fn_ty_a: ty::PolyFnSig<'tcx>, - b: Ty<'tcx>, - to_unsafe: F, - normal: G, - ) -> CoerceResult<'tcx> - where - F: FnOnce(Ty<'tcx>) -> Vec<Adjustment<'tcx>>, - G: FnOnce(Ty<'tcx>) -> Vec<Adjustment<'tcx>>, - { - self.commit_if_ok(|snapshot| { - let result = if let ty::FnPtr(fn_ty_b) = b.kind() - && let (hir::Unsafety::Normal, hir::Unsafety::Unsafe) = - (fn_ty_a.unsafety(), fn_ty_b.unsafety()) - { - let unsafe_a = self.tcx.safe_to_unsafe_fn_ty(fn_ty_a); - self.unify_and(unsafe_a, b, to_unsafe) - } else { - self.unify_and(a, b, normal) - }; - - // FIXME(#73154): This is a hack. Currently LUB can generate - // unsolvable constraints. Additionally, it returns `a` - // unconditionally, even when the "LUB" is `b`. In the future, we - // want the coerced type to be the actual supertype of these two, - // but for now, we want to just error to ensure we don't lock - // ourselves into a specific behavior with NLL. - self.leak_check(false, snapshot)?; - - result - }) - } - - fn coerce_from_fn_pointer( - &self, - a: Ty<'tcx>, - fn_ty_a: ty::PolyFnSig<'tcx>, - b: Ty<'tcx>, - ) -> CoerceResult<'tcx> { - //! Attempts to coerce from the type of a Rust function item - //! into a closure or a `proc`. - //! - - let b = self.shallow_resolve(b); - debug!("coerce_from_fn_pointer(a={:?}, b={:?})", a, b); - - self.coerce_from_safe_fn( - a, - fn_ty_a, - b, - simple(Adjust::Pointer(PointerCast::UnsafeFnPointer)), - identity, - ) - } - - fn coerce_from_fn_item(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> CoerceResult<'tcx> { - //! Attempts to coerce from the type of a Rust function item - //! into a closure or a `proc`. - - let b = self.shallow_resolve(b); - let InferOk { value: b, mut obligations } = - self.normalize_associated_types_in_as_infer_ok(self.cause.span, b); - debug!("coerce_from_fn_item(a={:?}, b={:?})", a, b); - - match b.kind() { - ty::FnPtr(b_sig) => { - let a_sig = a.fn_sig(self.tcx); - if let ty::FnDef(def_id, _) = *a.kind() { - // Intrinsics are not coercible to function pointers - if self.tcx.is_intrinsic(def_id) { - return Err(TypeError::IntrinsicCast); - } - - // Safe `#[target_feature]` functions are not assignable to safe fn pointers (RFC 2396). - - if b_sig.unsafety() == hir::Unsafety::Normal - && !self.tcx.codegen_fn_attrs(def_id).target_features.is_empty() - { - return Err(TypeError::TargetFeatureCast(def_id)); - } - } - - let InferOk { value: a_sig, obligations: o1 } = - self.normalize_associated_types_in_as_infer_ok(self.cause.span, a_sig); - obligations.extend(o1); - - let a_fn_pointer = self.tcx.mk_fn_ptr(a_sig); - let InferOk { value, obligations: o2 } = self.coerce_from_safe_fn( - a_fn_pointer, - a_sig, - b, - |unsafe_ty| { - vec![ - Adjustment { - kind: Adjust::Pointer(PointerCast::ReifyFnPointer), - target: a_fn_pointer, - }, - Adjustment { - kind: Adjust::Pointer(PointerCast::UnsafeFnPointer), - target: unsafe_ty, - }, - ] - }, - simple(Adjust::Pointer(PointerCast::ReifyFnPointer)), - )?; - - obligations.extend(o2); - Ok(InferOk { value, obligations }) - } - _ => self.unify_and(a, b, identity), - } - } - - fn coerce_closure_to_fn( - &self, - a: Ty<'tcx>, - closure_def_id_a: DefId, - substs_a: SubstsRef<'tcx>, - b: Ty<'tcx>, - ) -> CoerceResult<'tcx> { - //! Attempts to coerce from the type of a non-capturing closure - //! into a function pointer. - //! - - let b = self.shallow_resolve(b); - - match b.kind() { - // At this point we haven't done capture analysis, which means - // that the ClosureSubsts just contains an inference variable instead - // of tuple of captured types. - // - // All we care here is if any variable is being captured and not the exact paths, - // so we check `upvars_mentioned` for root variables being captured. - ty::FnPtr(fn_ty) - if self - .tcx - .upvars_mentioned(closure_def_id_a.expect_local()) - .map_or(true, |u| u.is_empty()) => - { - // We coerce the closure, which has fn type - // `extern "rust-call" fn((arg0,arg1,...)) -> _` - // to - // `fn(arg0,arg1,...) -> _` - // or - // `unsafe fn(arg0,arg1,...) -> _` - let closure_sig = substs_a.as_closure().sig(); - let unsafety = fn_ty.unsafety(); - let pointer_ty = - self.tcx.mk_fn_ptr(self.tcx.signature_unclosure(closure_sig, unsafety)); - debug!("coerce_closure_to_fn(a={:?}, b={:?}, pty={:?})", a, b, pointer_ty); - self.unify_and( - pointer_ty, - b, - simple(Adjust::Pointer(PointerCast::ClosureFnPointer(unsafety))), - ) - } - _ => self.unify_and(a, b, identity), - } - } - - fn coerce_unsafe_ptr( - &self, - a: Ty<'tcx>, - b: Ty<'tcx>, - mutbl_b: hir::Mutability, - ) -> CoerceResult<'tcx> { - debug!("coerce_unsafe_ptr(a={:?}, b={:?})", a, b); - - let (is_ref, mt_a) = match *a.kind() { - ty::Ref(_, ty, mutbl) => (true, ty::TypeAndMut { ty, mutbl }), - ty::RawPtr(mt) => (false, mt), - _ => return self.unify_and(a, b, identity), - }; - coerce_mutbls(mt_a.mutbl, mutbl_b)?; - - // Check that the types which they point at are compatible. - let a_unsafe = self.tcx.mk_ptr(ty::TypeAndMut { mutbl: mutbl_b, ty: mt_a.ty }); - // Although references and unsafe ptrs have the same - // representation, we still register an Adjust::DerefRef so that - // regionck knows that the region for `a` must be valid here. - if is_ref { - self.unify_and(a_unsafe, b, |target| { - vec![ - Adjustment { kind: Adjust::Deref(None), target: mt_a.ty }, - Adjustment { kind: Adjust::Borrow(AutoBorrow::RawPtr(mutbl_b)), target }, - ] - }) - } else if mt_a.mutbl != mutbl_b { - self.unify_and(a_unsafe, b, simple(Adjust::Pointer(PointerCast::MutToConstPointer))) - } else { - self.unify_and(a_unsafe, b, identity) - } - } -} - -impl<'a, 'tcx> FnCtxt<'a, 'tcx> { - /// Attempt to coerce an expression to a type, and return the - /// adjusted type of the expression, if successful. - /// Adjustments are only recorded if the coercion succeeded. - /// The expressions *must not* have any pre-existing adjustments. - pub fn try_coerce( - &self, - expr: &hir::Expr<'_>, - expr_ty: Ty<'tcx>, - target: Ty<'tcx>, - allow_two_phase: AllowTwoPhase, - cause: Option<ObligationCause<'tcx>>, - ) -> RelateResult<'tcx, Ty<'tcx>> { - let source = self.resolve_vars_with_obligations(expr_ty); - debug!("coercion::try({:?}: {:?} -> {:?})", expr, source, target); - - let cause = - cause.unwrap_or_else(|| self.cause(expr.span, ObligationCauseCode::ExprAssignable)); - let coerce = Coerce::new(self, cause, allow_two_phase); - let ok = self.commit_if_ok(|_| coerce.coerce(source, target))?; - - let (adjustments, _) = self.register_infer_ok_obligations(ok); - self.apply_adjustments(expr, adjustments); - Ok(if expr_ty.references_error() { self.tcx.ty_error() } else { target }) - } - - /// Same as `try_coerce()`, but without side-effects. - /// - /// Returns false if the coercion creates any obligations that result in - /// errors. - pub fn can_coerce(&self, expr_ty: Ty<'tcx>, target: Ty<'tcx>) -> bool { - let source = self.resolve_vars_with_obligations(expr_ty); - debug!("coercion::can_with_predicates({:?} -> {:?})", source, target); - - let cause = self.cause(rustc_span::DUMMY_SP, ObligationCauseCode::ExprAssignable); - // We don't ever need two-phase here since we throw out the result of the coercion - let coerce = Coerce::new(self, cause, AllowTwoPhase::No); - self.probe(|_| { - let Ok(ok) = coerce.coerce(source, target) else { - return false; - }; - let mut fcx = traits::FulfillmentContext::new_in_snapshot(); - fcx.register_predicate_obligations(self, ok.obligations); - fcx.select_where_possible(&self).is_empty() - }) - } - - /// Given a type and a target type, this function will calculate and return - /// how many dereference steps needed to achieve `expr_ty <: target`. If - /// it's not possible, return `None`. - pub fn deref_steps(&self, expr_ty: Ty<'tcx>, target: Ty<'tcx>) -> Option<usize> { - let cause = self.cause(rustc_span::DUMMY_SP, ObligationCauseCode::ExprAssignable); - // We don't ever need two-phase here since we throw out the result of the coercion - let coerce = Coerce::new(self, cause, AllowTwoPhase::No); - coerce - .autoderef(rustc_span::DUMMY_SP, expr_ty) - .find_map(|(ty, steps)| self.probe(|_| coerce.unify(ty, target)).ok().map(|_| steps)) - } - - /// Given a type, this function will calculate and return the type given - /// for `<Ty as Deref>::Target` only if `Ty` also implements `DerefMut`. - /// - /// This function is for diagnostics only, since it does not register - /// trait or region sub-obligations. (presumably we could, but it's not - /// particularly important for diagnostics...) - pub fn deref_once_mutably_for_diagnostic(&self, expr_ty: Ty<'tcx>) -> Option<Ty<'tcx>> { - self.autoderef(rustc_span::DUMMY_SP, expr_ty).nth(1).and_then(|(deref_ty, _)| { - self.infcx - .type_implements_trait( - self.tcx.lang_items().deref_mut_trait()?, - expr_ty, - ty::List::empty(), - self.param_env, - ) - .may_apply() - .then(|| deref_ty) - }) - } - - /// Given some expressions, their known unified type and another expression, - /// tries to unify the types, potentially inserting coercions on any of the - /// provided expressions and returns their LUB (aka "common supertype"). - /// - /// This is really an internal helper. From outside the coercion - /// module, you should instantiate a `CoerceMany` instance. - fn try_find_coercion_lub<E>( - &self, - cause: &ObligationCause<'tcx>, - exprs: &[E], - prev_ty: Ty<'tcx>, - new: &hir::Expr<'_>, - new_ty: Ty<'tcx>, - ) -> RelateResult<'tcx, Ty<'tcx>> - where - E: AsCoercionSite, - { - let prev_ty = self.resolve_vars_with_obligations(prev_ty); - let new_ty = self.resolve_vars_with_obligations(new_ty); - debug!( - "coercion::try_find_coercion_lub({:?}, {:?}, exprs={:?} exprs)", - prev_ty, - new_ty, - exprs.len() - ); - - // The following check fixes #88097, where the compiler erroneously - // attempted to coerce a closure type to itself via a function pointer. - if prev_ty == new_ty { - return Ok(prev_ty); - } - - // Special-case that coercion alone cannot handle: - // Function items or non-capturing closures of differing IDs or InternalSubsts. - let (a_sig, b_sig) = { - #[allow(rustc::usage_of_ty_tykind)] - let is_capturing_closure = |ty: &ty::TyKind<'tcx>| { - if let &ty::Closure(closure_def_id, _substs) = ty { - self.tcx.upvars_mentioned(closure_def_id.expect_local()).is_some() - } else { - false - } - }; - if is_capturing_closure(prev_ty.kind()) || is_capturing_closure(new_ty.kind()) { - (None, None) - } else { - match (prev_ty.kind(), new_ty.kind()) { - (ty::FnDef(..), ty::FnDef(..)) => { - // Don't reify if the function types have a LUB, i.e., they - // are the same function and their parameters have a LUB. - match self - .commit_if_ok(|_| self.at(cause, self.param_env).lub(prev_ty, new_ty)) - { - // We have a LUB of prev_ty and new_ty, just return it. - Ok(ok) => return Ok(self.register_infer_ok_obligations(ok)), - Err(_) => { - (Some(prev_ty.fn_sig(self.tcx)), Some(new_ty.fn_sig(self.tcx))) - } - } - } - (ty::Closure(_, substs), ty::FnDef(..)) => { - let b_sig = new_ty.fn_sig(self.tcx); - let a_sig = self - .tcx - .signature_unclosure(substs.as_closure().sig(), b_sig.unsafety()); - (Some(a_sig), Some(b_sig)) - } - (ty::FnDef(..), ty::Closure(_, substs)) => { - let a_sig = prev_ty.fn_sig(self.tcx); - let b_sig = self - .tcx - .signature_unclosure(substs.as_closure().sig(), a_sig.unsafety()); - (Some(a_sig), Some(b_sig)) - } - (ty::Closure(_, substs_a), ty::Closure(_, substs_b)) => ( - Some(self.tcx.signature_unclosure( - substs_a.as_closure().sig(), - hir::Unsafety::Normal, - )), - Some(self.tcx.signature_unclosure( - substs_b.as_closure().sig(), - hir::Unsafety::Normal, - )), - ), - _ => (None, None), - } - } - }; - if let (Some(a_sig), Some(b_sig)) = (a_sig, b_sig) { - // Intrinsics are not coercible to function pointers. - if a_sig.abi() == Abi::RustIntrinsic - || a_sig.abi() == Abi::PlatformIntrinsic - || b_sig.abi() == Abi::RustIntrinsic - || b_sig.abi() == Abi::PlatformIntrinsic - { - return Err(TypeError::IntrinsicCast); - } - // The signature must match. - let a_sig = self.normalize_associated_types_in(new.span, a_sig); - let b_sig = self.normalize_associated_types_in(new.span, b_sig); - let sig = self - .at(cause, self.param_env) - .trace(prev_ty, new_ty) - .lub(a_sig, b_sig) - .map(|ok| self.register_infer_ok_obligations(ok))?; - - // Reify both sides and return the reified fn pointer type. - let fn_ptr = self.tcx.mk_fn_ptr(sig); - let prev_adjustment = match prev_ty.kind() { - ty::Closure(..) => Adjust::Pointer(PointerCast::ClosureFnPointer(a_sig.unsafety())), - ty::FnDef(..) => Adjust::Pointer(PointerCast::ReifyFnPointer), - _ => unreachable!(), - }; - let next_adjustment = match new_ty.kind() { - ty::Closure(..) => Adjust::Pointer(PointerCast::ClosureFnPointer(b_sig.unsafety())), - ty::FnDef(..) => Adjust::Pointer(PointerCast::ReifyFnPointer), - _ => unreachable!(), - }; - for expr in exprs.iter().map(|e| e.as_coercion_site()) { - self.apply_adjustments( - expr, - vec![Adjustment { kind: prev_adjustment.clone(), target: fn_ptr }], - ); - } - self.apply_adjustments(new, vec![Adjustment { kind: next_adjustment, target: fn_ptr }]); - return Ok(fn_ptr); - } - - // Configure a Coerce instance to compute the LUB. - // We don't allow two-phase borrows on any autorefs this creates since we - // probably aren't processing function arguments here and even if we were, - // they're going to get autorefed again anyway and we can apply 2-phase borrows - // at that time. - let mut coerce = Coerce::new(self, cause.clone(), AllowTwoPhase::No); - coerce.use_lub = true; - - // First try to coerce the new expression to the type of the previous ones, - // but only if the new expression has no coercion already applied to it. - let mut first_error = None; - if !self.typeck_results.borrow().adjustments().contains_key(new.hir_id) { - let result = self.commit_if_ok(|_| coerce.coerce(new_ty, prev_ty)); - match result { - Ok(ok) => { - let (adjustments, target) = self.register_infer_ok_obligations(ok); - self.apply_adjustments(new, adjustments); - debug!( - "coercion::try_find_coercion_lub: was able to coerce from new type {:?} to previous type {:?} ({:?})", - new_ty, prev_ty, target - ); - return Ok(target); - } - Err(e) => first_error = Some(e), - } - } - - // Then try to coerce the previous expressions to the type of the new one. - // This requires ensuring there are no coercions applied to *any* of the - // previous expressions, other than noop reborrows (ignoring lifetimes). - for expr in exprs { - let expr = expr.as_coercion_site(); - let noop = match self.typeck_results.borrow().expr_adjustments(expr) { - &[ - Adjustment { kind: Adjust::Deref(_), .. }, - Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(_, mutbl_adj)), .. }, - ] => { - match *self.node_ty(expr.hir_id).kind() { - ty::Ref(_, _, mt_orig) => { - let mutbl_adj: hir::Mutability = mutbl_adj.into(); - // Reborrow that we can safely ignore, because - // the next adjustment can only be a Deref - // which will be merged into it. - mutbl_adj == mt_orig - } - _ => false, - } - } - &[Adjustment { kind: Adjust::NeverToAny, .. }] | &[] => true, - _ => false, - }; - - if !noop { - debug!( - "coercion::try_find_coercion_lub: older expression {:?} had adjustments, requiring LUB", - expr, - ); - - return self - .commit_if_ok(|_| self.at(cause, self.param_env).lub(prev_ty, new_ty)) - .map(|ok| self.register_infer_ok_obligations(ok)); - } - } - - match self.commit_if_ok(|_| coerce.coerce(prev_ty, new_ty)) { - Err(_) => { - // Avoid giving strange errors on failed attempts. - if let Some(e) = first_error { - Err(e) - } else { - self.commit_if_ok(|_| self.at(cause, self.param_env).lub(prev_ty, new_ty)) - .map(|ok| self.register_infer_ok_obligations(ok)) - } - } - Ok(ok) => { - let (adjustments, target) = self.register_infer_ok_obligations(ok); - for expr in exprs { - let expr = expr.as_coercion_site(); - self.apply_adjustments(expr, adjustments.clone()); - } - debug!( - "coercion::try_find_coercion_lub: was able to coerce previous type {:?} to new type {:?} ({:?})", - prev_ty, new_ty, target - ); - Ok(target) - } - } - } -} - -/// CoerceMany encapsulates the pattern you should use when you have -/// many expressions that are all getting coerced to a common -/// type. This arises, for example, when you have a match (the result -/// of each arm is coerced to a common type). It also arises in less -/// obvious places, such as when you have many `break foo` expressions -/// that target the same loop, or the various `return` expressions in -/// a function. -/// -/// The basic protocol is as follows: -/// -/// - Instantiate the `CoerceMany` with an initial `expected_ty`. -/// This will also serve as the "starting LUB". The expectation is -/// that this type is something which all of the expressions *must* -/// be coercible to. Use a fresh type variable if needed. -/// - For each expression whose result is to be coerced, invoke `coerce()` with. -/// - In some cases we wish to coerce "non-expressions" whose types are implicitly -/// unit. This happens for example if you have a `break` with no expression, -/// or an `if` with no `else`. In that case, invoke `coerce_forced_unit()`. -/// - `coerce()` and `coerce_forced_unit()` may report errors. They hide this -/// from you so that you don't have to worry your pretty head about it. -/// But if an error is reported, the final type will be `err`. -/// - Invoking `coerce()` may cause us to go and adjust the "adjustments" on -/// previously coerced expressions. -/// - When all done, invoke `complete()`. This will return the LUB of -/// all your expressions. -/// - WARNING: I don't believe this final type is guaranteed to be -/// related to your initial `expected_ty` in any particular way, -/// although it will typically be a subtype, so you should check it. -/// - Invoking `complete()` may cause us to go and adjust the "adjustments" on -/// previously coerced expressions. -/// -/// Example: -/// -/// ```ignore (illustrative) -/// let mut coerce = CoerceMany::new(expected_ty); -/// for expr in exprs { -/// let expr_ty = fcx.check_expr_with_expectation(expr, expected); -/// coerce.coerce(fcx, &cause, expr, expr_ty); -/// } -/// let final_ty = coerce.complete(fcx); -/// ``` -pub struct CoerceMany<'tcx, 'exprs, E: AsCoercionSite> { - expected_ty: Ty<'tcx>, - final_ty: Option<Ty<'tcx>>, - expressions: Expressions<'tcx, 'exprs, E>, - pushed: usize, -} - -/// The type of a `CoerceMany` that is storing up the expressions into -/// a buffer. We use this in `check/mod.rs` for things like `break`. -pub type DynamicCoerceMany<'tcx> = CoerceMany<'tcx, 'tcx, &'tcx hir::Expr<'tcx>>; - -enum Expressions<'tcx, 'exprs, E: AsCoercionSite> { - Dynamic(Vec<&'tcx hir::Expr<'tcx>>), - UpFront(&'exprs [E]), -} - -impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> { - /// The usual case; collect the set of expressions dynamically. - /// If the full set of coercion sites is known before hand, - /// consider `with_coercion_sites()` instead to avoid allocation. - pub fn new(expected_ty: Ty<'tcx>) -> Self { - Self::make(expected_ty, Expressions::Dynamic(vec![])) - } - - /// As an optimization, you can create a `CoerceMany` with a - /// pre-existing slice of expressions. In this case, you are - /// expected to pass each element in the slice to `coerce(...)` in - /// order. This is used with arrays in particular to avoid - /// needlessly cloning the slice. - pub fn with_coercion_sites(expected_ty: Ty<'tcx>, coercion_sites: &'exprs [E]) -> Self { - Self::make(expected_ty, Expressions::UpFront(coercion_sites)) - } - - fn make(expected_ty: Ty<'tcx>, expressions: Expressions<'tcx, 'exprs, E>) -> Self { - CoerceMany { expected_ty, final_ty: None, expressions, pushed: 0 } - } - - /// Returns the "expected type" with which this coercion was - /// constructed. This represents the "downward propagated" type - /// that was given to us at the start of typing whatever construct - /// we are typing (e.g., the match expression). - /// - /// Typically, this is used as the expected type when - /// type-checking each of the alternative expressions whose types - /// we are trying to merge. - pub fn expected_ty(&self) -> Ty<'tcx> { - self.expected_ty - } - - /// Returns the current "merged type", representing our best-guess - /// at the LUB of the expressions we've seen so far (if any). This - /// isn't *final* until you call `self.complete()`, which will return - /// the merged type. - pub fn merged_ty(&self) -> Ty<'tcx> { - self.final_ty.unwrap_or(self.expected_ty) - } - - /// Indicates that the value generated by `expression`, which is - /// of type `expression_ty`, is one of the possibilities that we - /// could coerce from. This will record `expression`, and later - /// calls to `coerce` may come back and add adjustments and things - /// if necessary. - pub fn coerce<'a>( - &mut self, - fcx: &FnCtxt<'a, 'tcx>, - cause: &ObligationCause<'tcx>, - expression: &'tcx hir::Expr<'tcx>, - expression_ty: Ty<'tcx>, - ) { - self.coerce_inner(fcx, cause, Some(expression), expression_ty, None, false) - } - - /// Indicates that one of the inputs is a "forced unit". This - /// occurs in a case like `if foo { ... };`, where the missing else - /// generates a "forced unit". Another example is a `loop { break; - /// }`, where the `break` has no argument expression. We treat - /// these cases slightly differently for error-reporting - /// purposes. Note that these tend to correspond to cases where - /// the `()` expression is implicit in the source, and hence we do - /// not take an expression argument. - /// - /// The `augment_error` gives you a chance to extend the error - /// message, in case any results (e.g., we use this to suggest - /// removing a `;`). - pub fn coerce_forced_unit<'a>( - &mut self, - fcx: &FnCtxt<'a, 'tcx>, - cause: &ObligationCause<'tcx>, - augment_error: &mut dyn FnMut(&mut Diagnostic), - label_unit_as_expected: bool, - ) { - self.coerce_inner( - fcx, - cause, - None, - fcx.tcx.mk_unit(), - Some(augment_error), - label_unit_as_expected, - ) - } - - /// The inner coercion "engine". If `expression` is `None`, this - /// is a forced-unit case, and hence `expression_ty` must be - /// `Nil`. - #[instrument(skip(self, fcx, augment_error, label_expression_as_expected), level = "debug")] - pub(crate) fn coerce_inner<'a>( - &mut self, - fcx: &FnCtxt<'a, 'tcx>, - cause: &ObligationCause<'tcx>, - expression: Option<&'tcx hir::Expr<'tcx>>, - mut expression_ty: Ty<'tcx>, - augment_error: Option<&mut dyn FnMut(&mut Diagnostic)>, - label_expression_as_expected: bool, - ) { - // Incorporate whatever type inference information we have - // until now; in principle we might also want to process - // pending obligations, but doing so should only improve - // compatibility (hopefully that is true) by helping us - // uncover never types better. - if expression_ty.is_ty_var() { - expression_ty = fcx.infcx.shallow_resolve(expression_ty); - } - - // If we see any error types, just propagate that error - // upwards. - if expression_ty.references_error() || self.merged_ty().references_error() { - self.final_ty = Some(fcx.tcx.ty_error()); - return; - } - - // Handle the actual type unification etc. - let result = if let Some(expression) = expression { - if self.pushed == 0 { - // Special-case the first expression we are coercing. - // To be honest, I'm not entirely sure why we do this. - // We don't allow two-phase borrows, see comment in try_find_coercion_lub for why - fcx.try_coerce( - expression, - expression_ty, - self.expected_ty, - AllowTwoPhase::No, - Some(cause.clone()), - ) - } else { - match self.expressions { - Expressions::Dynamic(ref exprs) => fcx.try_find_coercion_lub( - cause, - exprs, - self.merged_ty(), - expression, - expression_ty, - ), - Expressions::UpFront(ref coercion_sites) => fcx.try_find_coercion_lub( - cause, - &coercion_sites[0..self.pushed], - self.merged_ty(), - expression, - expression_ty, - ), - } - } - } else { - // this is a hack for cases where we default to `()` because - // the expression etc has been omitted from the source. An - // example is an `if let` without an else: - // - // if let Some(x) = ... { } - // - // we wind up with a second match arm that is like `_ => - // ()`. That is the case we are considering here. We take - // a different path to get the right "expected, found" - // message and so forth (and because we know that - // `expression_ty` will be unit). - // - // Another example is `break` with no argument expression. - assert!(expression_ty.is_unit(), "if let hack without unit type"); - fcx.at(cause, fcx.param_env) - .eq_exp(label_expression_as_expected, expression_ty, self.merged_ty()) - .map(|infer_ok| { - fcx.register_infer_ok_obligations(infer_ok); - expression_ty - }) - }; - - debug!(?result); - match result { - Ok(v) => { - self.final_ty = Some(v); - if let Some(e) = expression { - match self.expressions { - Expressions::Dynamic(ref mut buffer) => buffer.push(e), - Expressions::UpFront(coercion_sites) => { - // if the user gave us an array to validate, check that we got - // the next expression in the list, as expected - assert_eq!( - coercion_sites[self.pushed].as_coercion_site().hir_id, - e.hir_id - ); - } - } - self.pushed += 1; - } - } - Err(coercion_error) => { - // Mark that we've failed to coerce the types here to suppress - // any superfluous errors we might encounter while trying to - // emit or provide suggestions on how to fix the initial error. - fcx.set_tainted_by_errors(); - let (expected, found) = if label_expression_as_expected { - // In the case where this is a "forced unit", like - // `break`, we want to call the `()` "expected" - // since it is implied by the syntax. - // (Note: not all force-units work this way.)" - (expression_ty, self.merged_ty()) - } else { - // Otherwise, the "expected" type for error - // reporting is the current unification type, - // which is basically the LUB of the expressions - // we've seen so far (combined with the expected - // type) - (self.merged_ty(), expression_ty) - }; - let (expected, found) = fcx.resolve_vars_if_possible((expected, found)); - - let mut err; - let mut unsized_return = false; - let mut visitor = CollectRetsVisitor { ret_exprs: vec![] }; - match *cause.code() { - ObligationCauseCode::ReturnNoExpression => { - err = struct_span_err!( - fcx.tcx.sess, - cause.span, - E0069, - "`return;` in a function whose return type is not `()`" - ); - err.span_label(cause.span, "return type is not `()`"); - } - ObligationCauseCode::BlockTailExpression(blk_id) => { - let parent_id = fcx.tcx.hir().get_parent_node(blk_id); - err = self.report_return_mismatched_types( - cause, - expected, - found, - coercion_error.clone(), - fcx, - parent_id, - expression, - Some(blk_id), - ); - if !fcx.tcx.features().unsized_locals { - unsized_return = self.is_return_ty_unsized(fcx, blk_id); - } - if let Some(expression) = expression - && let hir::ExprKind::Loop(loop_blk, ..) = expression.kind { - intravisit::walk_block(& mut visitor, loop_blk); - } - } - ObligationCauseCode::ReturnValue(id) => { - err = self.report_return_mismatched_types( - cause, - expected, - found, - coercion_error.clone(), - fcx, - id, - expression, - None, - ); - if !fcx.tcx.features().unsized_locals { - let id = fcx.tcx.hir().get_parent_node(id); - unsized_return = self.is_return_ty_unsized(fcx, id); - } - } - _ => { - err = fcx.report_mismatched_types( - cause, - expected, - found, - coercion_error.clone(), - ); - } - } - - if let Some(augment_error) = augment_error { - augment_error(&mut err); - } - - let is_insufficiently_polymorphic = - matches!(coercion_error, TypeError::RegionsInsufficientlyPolymorphic(..)); - - if !is_insufficiently_polymorphic && let Some(expr) = expression { - fcx.emit_coerce_suggestions( - &mut err, - expr, - found, - expected, - None, - Some(coercion_error), - ); - } - - if visitor.ret_exprs.len() > 0 && let Some(expr) = expression { - self.note_unreachable_loop_return(&mut err, &expr, &visitor.ret_exprs); - } - err.emit_unless(unsized_return); - - self.final_ty = Some(fcx.tcx.ty_error()); - } - } - } - fn note_unreachable_loop_return( - &self, - err: &mut Diagnostic, - expr: &hir::Expr<'tcx>, - ret_exprs: &Vec<&'tcx hir::Expr<'tcx>>, - ) { - let hir::ExprKind::Loop(_, _, _, loop_span) = expr.kind else { return;}; - let mut span: MultiSpan = vec![loop_span].into(); - span.push_span_label(loop_span, "this might have zero elements to iterate on"); - const MAXITER: usize = 3; - let iter = ret_exprs.iter().take(MAXITER); - for ret_expr in iter { - span.push_span_label( - ret_expr.span, - "if the loop doesn't execute, this value would never get returned", - ); - } - err.span_note( - span, - "the function expects a value to always be returned, but loops might run zero times", - ); - if MAXITER < ret_exprs.len() { - err.note(&format!( - "if the loop doesn't execute, {} other values would never get returned", - ret_exprs.len() - MAXITER - )); - } - err.help( - "return a value for the case when the loop has zero elements to iterate on, or \ - consider changing the return type to account for that possibility", - ); - } - - fn report_return_mismatched_types<'a>( - &self, - cause: &ObligationCause<'tcx>, - expected: Ty<'tcx>, - found: Ty<'tcx>, - ty_err: TypeError<'tcx>, - fcx: &FnCtxt<'a, 'tcx>, - id: hir::HirId, - expression: Option<&'tcx hir::Expr<'tcx>>, - blk_id: Option<hir::HirId>, - ) -> DiagnosticBuilder<'a, ErrorGuaranteed> { - let mut err = fcx.report_mismatched_types(cause, expected, found, ty_err); - - let mut pointing_at_return_type = false; - let mut fn_output = None; - - let parent_id = fcx.tcx.hir().get_parent_node(id); - let parent = fcx.tcx.hir().get(parent_id); - if let Some(expr) = expression - && let hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Closure(&hir::Closure { body, .. }), .. }) = parent - && !matches!(fcx.tcx.hir().body(body).value.kind, hir::ExprKind::Block(..)) - { - fcx.suggest_missing_semicolon(&mut err, expr, expected, true); - } - // Verify that this is a tail expression of a function, otherwise the - // label pointing out the cause for the type coercion will be wrong - // as prior return coercions would not be relevant (#57664). - let fn_decl = if let (Some(expr), Some(blk_id)) = (expression, blk_id) { - pointing_at_return_type = - fcx.suggest_mismatched_types_on_tail(&mut err, expr, expected, found, blk_id); - if let (Some(cond_expr), true, false) = ( - fcx.tcx.hir().get_if_cause(expr.hir_id), - expected.is_unit(), - pointing_at_return_type, - ) - // If the block is from an external macro or try (`?`) desugaring, then - // do not suggest adding a semicolon, because there's nowhere to put it. - // See issues #81943 and #87051. - && matches!( - cond_expr.span.desugaring_kind(), - None | Some(DesugaringKind::WhileLoop) - ) && !in_external_macro(fcx.tcx.sess, cond_expr.span) - && !matches!( - cond_expr.kind, - hir::ExprKind::Match(.., hir::MatchSource::TryDesugar) - ) - { - err.span_label(cond_expr.span, "expected this to be `()`"); - if expr.can_have_side_effects() { - fcx.suggest_semicolon_at_end(cond_expr.span, &mut err); - } - } - fcx.get_node_fn_decl(parent).map(|(fn_decl, _, is_main)| (fn_decl, is_main)) - } else { - fcx.get_fn_decl(parent_id) - }; - - if let Some((fn_decl, can_suggest)) = fn_decl { - if blk_id.is_none() { - pointing_at_return_type |= fcx.suggest_missing_return_type( - &mut err, - &fn_decl, - expected, - found, - can_suggest, - fcx.tcx.hir().local_def_id_to_hir_id(fcx.tcx.hir().get_parent_item(id)), - ); - } - if !pointing_at_return_type { - fn_output = Some(&fn_decl.output); // `impl Trait` return type - } - } - - let parent_id = fcx.tcx.hir().get_parent_item(id); - let parent_item = fcx.tcx.hir().get_by_def_id(parent_id); - - if let (Some(expr), Some(_), Some((fn_decl, _, _))) = - (expression, blk_id, fcx.get_node_fn_decl(parent_item)) - { - fcx.suggest_missing_break_or_return_expr( - &mut err, - expr, - fn_decl, - expected, - found, - id, - fcx.tcx.hir().local_def_id_to_hir_id(parent_id), - ); - } - - let ret_coercion_span = fcx.ret_coercion_span.get(); - - if let Some(sp) = ret_coercion_span - // If the closure has an explicit return type annotation, or if - // the closure's return type has been inferred from outside - // requirements (such as an Fn* trait bound), then a type error - // may occur at the first return expression we see in the closure - // (if it conflicts with the declared return type). Skip adding a - // note in this case, since it would be incorrect. - && !fcx.return_type_pre_known - { - err.span_note( - sp, - &format!( - "return type inferred to be `{}` here", - expected - ), - ); - } - - if let (Some(sp), Some(fn_output)) = (ret_coercion_span, fn_output) { - self.add_impl_trait_explanation(&mut err, cause, fcx, expected, sp, fn_output); - } - - err - } - - fn add_impl_trait_explanation<'a>( - &self, - err: &mut Diagnostic, - cause: &ObligationCause<'tcx>, - fcx: &FnCtxt<'a, 'tcx>, - expected: Ty<'tcx>, - sp: Span, - fn_output: &hir::FnRetTy<'_>, - ) { - let return_sp = fn_output.span(); - err.span_label(return_sp, "expected because this return type..."); - err.span_label( - sp, - format!("...is found to be `{}` here", fcx.resolve_vars_with_obligations(expected)), - ); - let impl_trait_msg = "for information on `impl Trait`, see \ - <https://doc.rust-lang.org/book/ch10-02-traits.html\ - #returning-types-that-implement-traits>"; - let trait_obj_msg = "for information on trait objects, see \ - <https://doc.rust-lang.org/book/ch17-02-trait-objects.html\ - #using-trait-objects-that-allow-for-values-of-different-types>"; - err.note("to return `impl Trait`, all returned values must be of the same type"); - err.note(impl_trait_msg); - let snippet = fcx - .tcx - .sess - .source_map() - .span_to_snippet(return_sp) - .unwrap_or_else(|_| "dyn Trait".to_string()); - let mut snippet_iter = snippet.split_whitespace(); - let has_impl = snippet_iter.next().map_or(false, |s| s == "impl"); - // Only suggest `Box<dyn Trait>` if `Trait` in `impl Trait` is object safe. - let mut is_object_safe = false; - if let hir::FnRetTy::Return(ty) = fn_output - // Get the return type. - && let hir::TyKind::OpaqueDef(..) = ty.kind - { - let ty = <dyn AstConv<'_>>::ast_ty_to_ty(fcx, ty); - // Get the `impl Trait`'s `DefId`. - if let ty::Opaque(def_id, _) = ty.kind() - // Get the `impl Trait`'s `Item` so that we can get its trait bounds and - // get the `Trait`'s `DefId`. - && let hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds, .. }) = - fcx.tcx.hir().expect_item(def_id.expect_local()).kind - { - // Are of this `impl Trait`'s traits object safe? - is_object_safe = bounds.iter().all(|bound| { - bound - .trait_ref() - .and_then(|t| t.trait_def_id()) - .map_or(false, |def_id| { - fcx.tcx.object_safety_violations(def_id).is_empty() - }) - }) - } - }; - if has_impl { - if is_object_safe { - err.multipart_suggestion( - "you could change the return type to be a boxed trait object", - vec![ - (return_sp.with_hi(return_sp.lo() + BytePos(4)), "Box<dyn".to_string()), - (return_sp.shrink_to_hi(), ">".to_string()), - ], - Applicability::MachineApplicable, - ); - let sugg = [sp, cause.span] - .into_iter() - .flat_map(|sp| { - [ - (sp.shrink_to_lo(), "Box::new(".to_string()), - (sp.shrink_to_hi(), ")".to_string()), - ] - .into_iter() - }) - .collect::<Vec<_>>(); - err.multipart_suggestion( - "if you change the return type to expect trait objects, box the returned \ - expressions", - sugg, - Applicability::MaybeIncorrect, - ); - } else { - err.help(&format!( - "if the trait `{}` were object safe, you could return a boxed trait object", - &snippet[5..] - )); - } - err.note(trait_obj_msg); - } - err.help("you could instead create a new `enum` with a variant for each returned type"); - } - - fn is_return_ty_unsized<'a>(&self, fcx: &FnCtxt<'a, 'tcx>, blk_id: hir::HirId) -> bool { - if let Some((fn_decl, _)) = fcx.get_fn_decl(blk_id) - && let hir::FnRetTy::Return(ty) = fn_decl.output - && let ty = <dyn AstConv<'_>>::ast_ty_to_ty(fcx, ty) - && let ty::Dynamic(..) = ty.kind() - { - return true; - } - false - } - - pub fn complete<'a>(self, fcx: &FnCtxt<'a, 'tcx>) -> Ty<'tcx> { - if let Some(final_ty) = self.final_ty { - final_ty - } else { - // If we only had inputs that were of type `!` (or no - // inputs at all), then the final type is `!`. - assert_eq!(self.pushed, 0); - fcx.tcx.types.never - } - } -} - -/// Something that can be converted into an expression to which we can -/// apply a coercion. -pub trait AsCoercionSite { - fn as_coercion_site(&self) -> &hir::Expr<'_>; -} - -impl AsCoercionSite for hir::Expr<'_> { - fn as_coercion_site(&self) -> &hir::Expr<'_> { - self - } -} - -impl<'a, T> AsCoercionSite for &'a T -where - T: AsCoercionSite, -{ - fn as_coercion_site(&self) -> &hir::Expr<'_> { - (**self).as_coercion_site() - } -} - -impl AsCoercionSite for ! { - fn as_coercion_site(&self) -> &hir::Expr<'_> { - unreachable!() - } -} - -impl AsCoercionSite for hir::Arm<'_> { - fn as_coercion_site(&self) -> &hir::Expr<'_> { - &self.body - } -} diff --git a/compiler/rustc_typeck/src/check/compare_method.rs b/compiler/rustc_typeck/src/check/compare_method.rs deleted file mode 100644 index 59d591acd..000000000 --- a/compiler/rustc_typeck/src/check/compare_method.rs +++ /dev/null @@ -1,1792 +0,0 @@ -use super::potentially_plural_count; -use crate::errors::LifetimesOrBoundsMismatchOnTrait; -use hir::def_id::DefId; -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::subst::{InternalSubsts, Subst}; -use rustc_middle::ty::util::ExplicitSelf; -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::InferCtxtExt; -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); - - tcx.infer_ctxt().enter(|ref infcx| { - 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.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.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); - - tcx.infer_ctxt().enter(|ref infcx| { - let ocx = ObligationCtxt::new(infcx); - - let norm_cause = ObligationCause::misc(return_span, impl_m_hir_id); - let impl_return_ty = 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), - ) - .output(), - ); - - let mut collector = - ImplTraitInTraitCollector::new(&ocx, return_span, param_env, impl_m_hir_id); - let unnormalized_trait_return_ty = tcx - .liberate_late_bound_regions( - impl_m.def_id, - tcx.bound_fn_sig(trait_m.def_id).subst(tcx, trait_to_placeholder_substs), - ) - .output() - .fold_with(&mut collector); - let trait_return_ty = - ocx.normalize(norm_cause.clone(), param_env, unnormalized_trait_return_ty); - - let wf_tys = FxHashSet::from_iter([unnormalized_trait_return_ty, trait_return_ty]); - - 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.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()); - } - } - - // 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.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>> = substs - .iter() - .enumerate() - .map(|(index, arg)| (arg, id_substs[index])) - .collect(); - debug!(?map); - - let ty = tcx.fold_regions(ty, |region, _| { - if let ty::ReFree(_) = region.kind() { - map[®ion.into()].expect_region() - } else { - region - } - }); - 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 in self.tcx().bound_explicit_item_bounds(proj.item_def_id).transpose_iter() { - let pred_span = pred.0.1; - - let pred = pred.map_bound(|(pred, _)| *pred).subst(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<'a, 'tcx>( - infcx: &infer::InferCtxt<'a, '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(); - - tcx.infer_ctxt().enter(|infcx| { - 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 (¶m_impl.kind, ¶m_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(()) -} - -pub(crate) fn compare_const_impl<'tcx>( - tcx: TyCtxt<'tcx>, - impl_c: &ty::AssocItem, - impl_c_span: Span, - trait_c: &ty::AssocItem, - impl_trait_ref: ty::TraitRef<'tcx>, -) { - debug!("compare_const_impl(impl_trait_ref={:?})", impl_trait_ref); - - tcx.infer_ctxt().enter(|infcx| { - let param_env = tcx.param_env(impl_c.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_c.def_id.expect_local()); - - // Compute placeholder form of impl and trait const tys. - let impl_ty = tcx.type_of(impl_c.def_id); - let trait_ty = tcx.bound_type_of(trait_c.def_id).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_c.def_id.expect_local(), - trait_item_def_id: trait_c.def_id, - kind: impl_c.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_c.def_id.expect_local()).kind { - ImplItemKind::Const(ref ty, _) => cause.span = ty.span, - _ => bug!("{:?} is not a impl const", impl_c), - } - - let mut diag = struct_span_err!( - tcx.sess, - cause.span, - E0326, - "implemented const `{}` has an incompatible type for trait", - trait_c.name - ); - - let trait_c_span = trait_c.def_id.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_c), - } - }); - - infcx.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, - ); - diag.emit(); - } - - // Check that all obligations are satisfied by the implementation's - // version. - let errors = ocx.select_all_or_error(); - if !errors.is_empty() { - infcx.report_fulfillment_errors(&errors, None, false); - return; - } - - let outlives_environment = OutlivesEnvironment::new(param_env); - infcx.check_region_obligations_and_report_errors( - impl_c.def_id.expect_local(), - &outlives_environment, - ); - }); -} - -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); - tcx.infer_ctxt().enter(|infcx| { - 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.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); - - tcx.infer_ctxt().enter(move |infcx| { - 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) - .transpose_iter() - .map(|e| e.map_bound(|e| *e).transpose_tuple2()) - .map(|(bound, span)| { - debug!(?bound); - // this is where opaque type is found - let concrete_ty_bound = bound.subst(tcx, rebased_substs); - debug!("check_type_bounds: concrete_ty_bound = {:?}", concrete_ty_bound); - - traits::Obligation::new(mk_cause(span.0), 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.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 - .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/demand.rs b/compiler/rustc_typeck/src/check/demand.rs deleted file mode 100644 index e1d55ff82..000000000 --- a/compiler/rustc_typeck/src/check/demand.rs +++ /dev/null @@ -1,1415 +0,0 @@ -use crate::check::FnCtxt; -use rustc_infer::infer::InferOk; -use rustc_middle::middle::stability::EvalResult; -use rustc_trait_selection::infer::InferCtxtExt as _; -use rustc_trait_selection::traits::ObligationCause; - -use rustc_ast::util::parser::PREC_POSTFIX; -use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed}; -use rustc_hir as hir; -use rustc_hir::lang_items::LangItem; -use rustc_hir::{is_range_literal, Node}; -use rustc_middle::lint::in_external_macro; -use rustc_middle::ty::adjustment::AllowTwoPhase; -use rustc_middle::ty::error::{ExpectedFound, TypeError}; -use rustc_middle::ty::print::with_no_trimmed_paths; -use rustc_middle::ty::{self, Article, AssocItem, Ty, TypeAndMut}; -use rustc_span::symbol::{sym, Symbol}; -use rustc_span::{BytePos, Span}; - -use super::method::probe; - -use std::iter; - -impl<'a, 'tcx> FnCtxt<'a, 'tcx> { - pub fn emit_coerce_suggestions( - &self, - err: &mut Diagnostic, - expr: &hir::Expr<'tcx>, - expr_ty: Ty<'tcx>, - expected: Ty<'tcx>, - expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>, - error: Option<TypeError<'tcx>>, - ) { - self.annotate_expected_due_to_let_ty(err, expr, error); - self.suggest_deref_ref_or_into(err, expr, expected, expr_ty, expected_ty_expr); - self.suggest_compatible_variants(err, expr, expected, expr_ty); - self.suggest_non_zero_new_unwrap(err, expr, expected, expr_ty); - if self.suggest_calling_boxed_future_when_appropriate(err, expr, expected, expr_ty) { - return; - } - self.suggest_no_capture_closure(err, expected, expr_ty); - self.suggest_boxing_when_appropriate(err, expr, expected, expr_ty); - self.suggest_missing_parentheses(err, expr); - self.suggest_block_to_brackets_peeling_refs(err, expr, expr_ty, expected); - self.suggest_copied_or_cloned(err, expr, expr_ty, expected); - self.note_type_is_not_clone(err, expected, expr_ty, expr); - self.note_need_for_fn_pointer(err, expected, expr_ty); - self.note_internal_mutation_in_method(err, expr, expected, expr_ty); - } - - // Requires that the two types unify, and prints an error message if - // they don't. - pub fn demand_suptype(&self, sp: Span, expected: Ty<'tcx>, actual: Ty<'tcx>) { - if let Some(mut e) = self.demand_suptype_diag(sp, expected, actual) { - e.emit(); - } - } - - pub fn demand_suptype_diag( - &self, - sp: Span, - expected: Ty<'tcx>, - actual: Ty<'tcx>, - ) -> Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>> { - self.demand_suptype_with_origin(&self.misc(sp), expected, actual) - } - - #[instrument(skip(self), level = "debug")] - pub fn demand_suptype_with_origin( - &self, - cause: &ObligationCause<'tcx>, - expected: Ty<'tcx>, - actual: Ty<'tcx>, - ) -> Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>> { - match self.at(cause, self.param_env).sup(expected, actual) { - Ok(InferOk { obligations, value: () }) => { - self.register_predicates(obligations); - None - } - Err(e) => Some(self.report_mismatched_types(&cause, expected, actual, e)), - } - } - - pub fn demand_eqtype(&self, sp: Span, expected: Ty<'tcx>, actual: Ty<'tcx>) { - if let Some(mut err) = self.demand_eqtype_diag(sp, expected, actual) { - err.emit(); - } - } - - pub fn demand_eqtype_diag( - &self, - sp: Span, - expected: Ty<'tcx>, - actual: Ty<'tcx>, - ) -> Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>> { - self.demand_eqtype_with_origin(&self.misc(sp), expected, actual) - } - - pub fn demand_eqtype_with_origin( - &self, - cause: &ObligationCause<'tcx>, - expected: Ty<'tcx>, - actual: Ty<'tcx>, - ) -> Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>> { - match self.at(cause, self.param_env).eq(expected, actual) { - Ok(InferOk { obligations, value: () }) => { - self.register_predicates(obligations); - None - } - Err(e) => Some(self.report_mismatched_types(cause, expected, actual, e)), - } - } - - pub fn demand_coerce( - &self, - expr: &hir::Expr<'tcx>, - checked_ty: Ty<'tcx>, - expected: Ty<'tcx>, - expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>, - allow_two_phase: AllowTwoPhase, - ) -> Ty<'tcx> { - let (ty, err) = - self.demand_coerce_diag(expr, checked_ty, expected, expected_ty_expr, allow_two_phase); - if let Some(mut err) = err { - err.emit(); - } - ty - } - - /// Checks that the type of `expr` can be coerced to `expected`. - /// - /// N.B., this code relies on `self.diverges` to be accurate. In particular, assignments to `!` - /// will be permitted if the diverges flag is currently "always". - #[instrument(level = "debug", skip(self, expr, expected_ty_expr, allow_two_phase))] - pub fn demand_coerce_diag( - &self, - expr: &hir::Expr<'tcx>, - checked_ty: Ty<'tcx>, - expected: Ty<'tcx>, - expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>, - allow_two_phase: AllowTwoPhase, - ) -> (Ty<'tcx>, Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>>) { - let expected = self.resolve_vars_with_obligations(expected); - - let e = match self.try_coerce(expr, checked_ty, expected, allow_two_phase, None) { - Ok(ty) => return (ty, None), - Err(e) => e, - }; - - self.set_tainted_by_errors(); - let expr = expr.peel_drop_temps(); - let cause = self.misc(expr.span); - let expr_ty = self.resolve_vars_with_obligations(checked_ty); - let mut err = self.report_mismatched_types(&cause, expected, expr_ty, e.clone()); - - let is_insufficiently_polymorphic = - matches!(e, TypeError::RegionsInsufficientlyPolymorphic(..)); - - // FIXME(#73154): For now, we do leak check when coercing function - // pointers in typeck, instead of only during borrowck. This can lead - // to these `RegionsInsufficientlyPolymorphic` errors that aren't helpful. - if !is_insufficiently_polymorphic { - self.emit_coerce_suggestions( - &mut err, - expr, - expr_ty, - expected, - expected_ty_expr, - Some(e), - ); - } - - (expected, Some(err)) - } - - fn annotate_expected_due_to_let_ty( - &self, - err: &mut Diagnostic, - expr: &hir::Expr<'_>, - error: Option<TypeError<'_>>, - ) { - let parent = self.tcx.hir().get_parent_node(expr.hir_id); - match (self.tcx.hir().find(parent), error) { - (Some(hir::Node::Local(hir::Local { ty: Some(ty), init: Some(init), .. })), _) - if init.hir_id == expr.hir_id => - { - // Point at `let` assignment type. - err.span_label(ty.span, "expected due to this"); - } - ( - Some(hir::Node::Expr(hir::Expr { - kind: hir::ExprKind::Assign(lhs, rhs, _), .. - })), - Some(TypeError::Sorts(ExpectedFound { expected, .. })), - ) if rhs.hir_id == expr.hir_id && !expected.is_closure() => { - // We ignore closures explicitly because we already point at them elsewhere. - // Point at the assigned-to binding. - let mut primary_span = lhs.span; - let mut secondary_span = lhs.span; - let mut post_message = ""; - match lhs.kind { - hir::ExprKind::Path(hir::QPath::Resolved( - None, - hir::Path { - res: - hir::def::Res::Def( - hir::def::DefKind::Static(_) | hir::def::DefKind::Const, - def_id, - ), - .. - }, - )) => { - if let Some(hir::Node::Item(hir::Item { - ident, - kind: hir::ItemKind::Static(ty, ..) | hir::ItemKind::Const(ty, ..), - .. - })) = self.tcx.hir().get_if_local(*def_id) - { - primary_span = ty.span; - secondary_span = ident.span; - post_message = " type"; - } - } - hir::ExprKind::Path(hir::QPath::Resolved( - None, - hir::Path { res: hir::def::Res::Local(hir_id), .. }, - )) => { - if let Some(hir::Node::Pat(pat)) = self.tcx.hir().find(*hir_id) { - let parent = self.tcx.hir().get_parent_node(pat.hir_id); - primary_span = pat.span; - secondary_span = pat.span; - match self.tcx.hir().find(parent) { - Some(hir::Node::Local(hir::Local { ty: Some(ty), .. })) => { - primary_span = ty.span; - post_message = " type"; - } - Some(hir::Node::Local(hir::Local { init: Some(init), .. })) => { - primary_span = init.span; - post_message = " value"; - } - Some(hir::Node::Param(hir::Param { ty_span, .. })) => { - primary_span = *ty_span; - post_message = " parameter type"; - } - _ => {} - } - } - } - _ => {} - } - - if primary_span != secondary_span - && self - .tcx - .sess - .source_map() - .is_multiline(secondary_span.shrink_to_hi().until(primary_span)) - { - // We are pointing at the binding's type or initializer value, but it's pattern - // is in a different line, so we point at both. - err.span_label(secondary_span, "expected due to the type of this binding"); - err.span_label(primary_span, &format!("expected due to this{post_message}")); - } else if post_message == "" { - // We are pointing at either the assignment lhs or the binding def pattern. - err.span_label(primary_span, "expected due to the type of this binding"); - } else { - // We are pointing at the binding's type or initializer value. - err.span_label(primary_span, &format!("expected due to this{post_message}")); - } - - if !lhs.is_syntactic_place_expr() { - // We already emitted E0070 "invalid left-hand side of assignment", so we - // silence this. - err.downgrade_to_delayed_bug(); - } - } - _ => {} - } - } - - /// If the expected type is an enum (Issue #55250) with any variants whose - /// sole field is of the found type, suggest such variants. (Issue #42764) - fn suggest_compatible_variants( - &self, - err: &mut Diagnostic, - expr: &hir::Expr<'_>, - expected: Ty<'tcx>, - expr_ty: Ty<'tcx>, - ) { - if let ty::Adt(expected_adt, substs) = expected.kind() { - if let hir::ExprKind::Field(base, ident) = expr.kind { - let base_ty = self.typeck_results.borrow().expr_ty(base); - if self.can_eq(self.param_env, base_ty, expected).is_ok() - && let Some(base_span) = base.span.find_ancestor_inside(expr.span) - { - err.span_suggestion_verbose( - expr.span.with_lo(base_span.hi()), - format!("consider removing the tuple struct field `{ident}`"), - "", - Applicability::MaybeIncorrect, - ); - return - } - } - - // If the expression is of type () and it's the return expression of a block, - // we suggest adding a separate return expression instead. - // (To avoid things like suggesting `Ok(while .. { .. })`.) - if expr_ty.is_unit() { - let mut id = expr.hir_id; - let mut parent; - - // Unroll desugaring, to make sure this works for `for` loops etc. - loop { - parent = self.tcx.hir().get_parent_node(id); - if let Some(parent_span) = self.tcx.hir().opt_span(parent) { - if parent_span.find_ancestor_inside(expr.span).is_some() { - // The parent node is part of the same span, so is the result of the - // same expansion/desugaring and not the 'real' parent node. - id = parent; - continue; - } - } - break; - } - - if let Some(hir::Node::Block(&hir::Block { - span: block_span, expr: Some(e), .. - })) = self.tcx.hir().find(parent) - { - if e.hir_id == id { - if let Some(span) = expr.span.find_ancestor_inside(block_span) { - let return_suggestions = if self - .tcx - .is_diagnostic_item(sym::Result, expected_adt.did()) - { - vec!["Ok(())"] - } else if self.tcx.is_diagnostic_item(sym::Option, expected_adt.did()) { - vec!["None", "Some(())"] - } else { - return; - }; - if let Some(indent) = - self.tcx.sess.source_map().indentation_before(span.shrink_to_lo()) - { - // Add a semicolon, except after `}`. - let semicolon = - match self.tcx.sess.source_map().span_to_snippet(span) { - Ok(s) if s.ends_with('}') => "", - _ => ";", - }; - err.span_suggestions( - span.shrink_to_hi(), - "try adding an expression at the end of the block", - return_suggestions - .into_iter() - .map(|r| format!("{semicolon}\n{indent}{r}")), - Applicability::MaybeIncorrect, - ); - } - return; - } - } - } - } - - let compatible_variants: Vec<(String, _, _, Option<String>)> = expected_adt - .variants() - .iter() - .filter(|variant| { - variant.fields.len() == 1 - }) - .filter_map(|variant| { - let sole_field = &variant.fields[0]; - - let field_is_local = sole_field.did.is_local(); - let field_is_accessible = - sole_field.vis.is_accessible_from(expr.hir_id.owner, self.tcx) - // Skip suggestions for unstable public fields (for example `Pin::pointer`) - && matches!(self.tcx.eval_stability(sole_field.did, None, expr.span, None), EvalResult::Allow | EvalResult::Unmarked); - - if !field_is_local && !field_is_accessible { - return None; - } - - let note_about_variant_field_privacy = (field_is_local && !field_is_accessible) - .then(|| format!(" (its field is private, but it's local to this crate and its privacy can be changed)")); - - let sole_field_ty = sole_field.ty(self.tcx, substs); - if self.can_coerce(expr_ty, sole_field_ty) { - let variant_path = - with_no_trimmed_paths!(self.tcx.def_path_str(variant.def_id)); - // FIXME #56861: DRYer prelude filtering - if let Some(path) = variant_path.strip_prefix("std::prelude::") - && let Some((_, path)) = path.split_once("::") - { - return Some((path.to_string(), variant.ctor_kind, sole_field.name, note_about_variant_field_privacy)); - } - Some((variant_path, variant.ctor_kind, sole_field.name, note_about_variant_field_privacy)) - } else { - None - } - }) - .collect(); - - let suggestions_for = |variant: &_, ctor, field_name| { - let prefix = match self.maybe_get_struct_pattern_shorthand_field(expr) { - Some(ident) => format!("{ident}: "), - None => String::new(), - }; - - let (open, close) = match ctor { - hir::def::CtorKind::Fn => ("(".to_owned(), ")"), - hir::def::CtorKind::Fictive => (format!(" {{ {field_name}: "), " }"), - - // unit variants don't have fields - hir::def::CtorKind::Const => unreachable!(), - }; - - vec![ - (expr.span.shrink_to_lo(), format!("{prefix}{variant}{open}")), - (expr.span.shrink_to_hi(), close.to_owned()), - ] - }; - - match &compatible_variants[..] { - [] => { /* No variants to format */ } - [(variant, ctor_kind, field_name, note)] => { - // Just a single matching variant. - err.multipart_suggestion_verbose( - &format!( - "try wrapping the expression in `{variant}`{note}", - note = note.as_deref().unwrap_or("") - ), - suggestions_for(&**variant, *ctor_kind, *field_name), - Applicability::MaybeIncorrect, - ); - } - _ => { - // More than one matching variant. - err.multipart_suggestions( - &format!( - "try wrapping the expression in a variant of `{}`", - self.tcx.def_path_str(expected_adt.did()) - ), - compatible_variants.into_iter().map( - |(variant, ctor_kind, field_name, _)| { - suggestions_for(&variant, ctor_kind, field_name) - }, - ), - Applicability::MaybeIncorrect, - ); - } - } - } - } - - fn suggest_non_zero_new_unwrap( - &self, - err: &mut Diagnostic, - expr: &hir::Expr<'_>, - expected: Ty<'tcx>, - expr_ty: Ty<'tcx>, - ) { - let tcx = self.tcx; - let (adt, unwrap) = match expected.kind() { - // In case Option<NonZero*> is wanted, but * is provided, suggest calling new - ty::Adt(adt, substs) if tcx.is_diagnostic_item(sym::Option, adt.did()) => { - // Unwrap option - let ty::Adt(adt, _) = substs.type_at(0).kind() else { return }; - - (adt, "") - } - // In case NonZero* is wanted, but * is provided also add `.unwrap()` to satisfy types - ty::Adt(adt, _) => (adt, ".unwrap()"), - _ => return, - }; - - let map = [ - (sym::NonZeroU8, tcx.types.u8), - (sym::NonZeroU16, tcx.types.u16), - (sym::NonZeroU32, tcx.types.u32), - (sym::NonZeroU64, tcx.types.u64), - (sym::NonZeroU128, tcx.types.u128), - (sym::NonZeroI8, tcx.types.i8), - (sym::NonZeroI16, tcx.types.i16), - (sym::NonZeroI32, tcx.types.i32), - (sym::NonZeroI64, tcx.types.i64), - (sym::NonZeroI128, tcx.types.i128), - ]; - - let Some((s, _)) = map - .iter() - .find(|&&(s, t)| self.tcx.is_diagnostic_item(s, adt.did()) && self.can_coerce(expr_ty, t)) - else { return }; - - let path = self.tcx.def_path_str(adt.non_enum_variant().def_id); - - err.multipart_suggestion( - format!("consider calling `{s}::new`"), - vec![ - (expr.span.shrink_to_lo(), format!("{path}::new(")), - (expr.span.shrink_to_hi(), format!("){unwrap}")), - ], - Applicability::MaybeIncorrect, - ); - } - - pub fn get_conversion_methods( - &self, - span: Span, - expected: Ty<'tcx>, - checked_ty: Ty<'tcx>, - hir_id: hir::HirId, - ) -> Vec<AssocItem> { - let mut methods = - self.probe_for_return_type(span, probe::Mode::MethodCall, expected, checked_ty, hir_id); - methods.retain(|m| { - self.has_only_self_parameter(m) - && self - .tcx - // This special internal attribute is used to permit - // "identity-like" conversion methods to be suggested here. - // - // FIXME (#46459 and #46460): ideally - // `std::convert::Into::into` and `std::borrow:ToOwned` would - // also be `#[rustc_conversion_suggestion]`, if not for - // method-probing false-positives and -negatives (respectively). - // - // FIXME? Other potential candidate methods: `as_ref` and - // `as_mut`? - .has_attr(m.def_id, sym::rustc_conversion_suggestion) - }); - - methods - } - - /// This function checks whether the method is not static and does not accept other parameters than `self`. - fn has_only_self_parameter(&self, method: &AssocItem) -> bool { - match method.kind { - ty::AssocKind::Fn => { - method.fn_has_self_parameter - && self.tcx.fn_sig(method.def_id).inputs().skip_binder().len() == 1 - } - _ => false, - } - } - - /// Identify some cases where `as_ref()` would be appropriate and suggest it. - /// - /// Given the following code: - /// ```compile_fail,E0308 - /// struct Foo; - /// fn takes_ref(_: &Foo) {} - /// let ref opt = Some(Foo); - /// - /// opt.map(|param| takes_ref(param)); - /// ``` - /// Suggest using `opt.as_ref().map(|param| takes_ref(param));` instead. - /// - /// It only checks for `Option` and `Result` and won't work with - /// ```ignore (illustrative) - /// opt.map(|param| { takes_ref(param) }); - /// ``` - fn can_use_as_ref(&self, expr: &hir::Expr<'_>) -> Option<(Span, &'static str, String)> { - let hir::ExprKind::Path(hir::QPath::Resolved(_, ref path)) = expr.kind else { - return None; - }; - - let hir::def::Res::Local(local_id) = path.res else { - return None; - }; - - let local_parent = self.tcx.hir().get_parent_node(local_id); - let Some(Node::Param(hir::Param { hir_id: param_hir_id, .. })) = self.tcx.hir().find(local_parent) else { - return None; - }; - - let param_parent = self.tcx.hir().get_parent_node(*param_hir_id); - let Some(Node::Expr(hir::Expr { - hir_id: expr_hir_id, - kind: hir::ExprKind::Closure(hir::Closure { fn_decl: closure_fn_decl, .. }), - .. - })) = self.tcx.hir().find(param_parent) else { - return None; - }; - - let expr_parent = self.tcx.hir().get_parent_node(*expr_hir_id); - let hir = self.tcx.hir().find(expr_parent); - let closure_params_len = closure_fn_decl.inputs.len(); - let ( - Some(Node::Expr(hir::Expr { - kind: hir::ExprKind::MethodCall(method_path, receiver, ..), - .. - })), - 1, - ) = (hir, closure_params_len) else { - return None; - }; - - let self_ty = self.typeck_results.borrow().expr_ty(receiver); - let name = method_path.ident.name; - let is_as_ref_able = match self_ty.peel_refs().kind() { - ty::Adt(def, _) => { - (self.tcx.is_diagnostic_item(sym::Option, def.did()) - || self.tcx.is_diagnostic_item(sym::Result, def.did())) - && (name == sym::map || name == sym::and_then) - } - _ => false, - }; - match (is_as_ref_able, self.sess().source_map().span_to_snippet(method_path.ident.span)) { - (true, Ok(src)) => { - let suggestion = format!("as_ref().{}", src); - Some((method_path.ident.span, "consider using `as_ref` instead", suggestion)) - } - _ => None, - } - } - - pub(crate) fn maybe_get_struct_pattern_shorthand_field( - &self, - expr: &hir::Expr<'_>, - ) -> Option<Symbol> { - let hir = self.tcx.hir(); - let local = match expr { - hir::Expr { - kind: - hir::ExprKind::Path(hir::QPath::Resolved( - None, - hir::Path { - res: hir::def::Res::Local(_), - segments: [hir::PathSegment { ident, .. }], - .. - }, - )), - .. - } => Some(ident), - _ => None, - }?; - - match hir.find(hir.get_parent_node(expr.hir_id))? { - Node::ExprField(field) => { - if field.ident.name == local.name && field.is_shorthand { - return Some(local.name); - } - } - _ => {} - } - - None - } - - /// If the given `HirId` corresponds to a block with a trailing expression, return that expression - pub(crate) fn maybe_get_block_expr( - &self, - expr: &hir::Expr<'tcx>, - ) -> Option<&'tcx hir::Expr<'tcx>> { - match expr { - hir::Expr { kind: hir::ExprKind::Block(block, ..), .. } => block.expr, - _ => None, - } - } - - /// Returns whether the given expression is an `else if`. - pub(crate) fn is_else_if_block(&self, expr: &hir::Expr<'_>) -> bool { - if let hir::ExprKind::If(..) = expr.kind { - let parent_id = self.tcx.hir().get_parent_node(expr.hir_id); - if let Some(Node::Expr(hir::Expr { - kind: hir::ExprKind::If(_, _, Some(else_expr)), - .. - })) = self.tcx.hir().find(parent_id) - { - return else_expr.hir_id == expr.hir_id; - } - } - false - } - - /// This function is used to determine potential "simple" improvements or users' errors and - /// provide them useful help. For example: - /// - /// ```compile_fail,E0308 - /// fn some_fn(s: &str) {} - /// - /// let x = "hey!".to_owned(); - /// some_fn(x); // error - /// ``` - /// - /// No need to find every potential function which could make a coercion to transform a - /// `String` into a `&str` since a `&` would do the trick! - /// - /// In addition of this check, it also checks between references mutability state. If the - /// expected is mutable but the provided isn't, maybe we could just say "Hey, try with - /// `&mut`!". - pub fn check_ref( - &self, - expr: &hir::Expr<'tcx>, - checked_ty: Ty<'tcx>, - expected: Ty<'tcx>, - ) -> Option<(Span, String, String, Applicability, bool /* verbose */)> { - let sess = self.sess(); - let sp = expr.span; - - // If the span is from an external macro, there's no suggestion we can make. - if in_external_macro(sess, sp) { - return None; - } - - let sm = sess.source_map(); - - let replace_prefix = |s: &str, old: &str, new: &str| { - s.strip_prefix(old).map(|stripped| new.to_string() + stripped) - }; - - // `ExprKind::DropTemps` is semantically irrelevant for these suggestions. - let expr = expr.peel_drop_temps(); - - match (&expr.kind, expected.kind(), checked_ty.kind()) { - (_, &ty::Ref(_, exp, _), &ty::Ref(_, check, _)) => match (exp.kind(), check.kind()) { - (&ty::Str, &ty::Array(arr, _) | &ty::Slice(arr)) if arr == self.tcx.types.u8 => { - if let hir::ExprKind::Lit(_) = expr.kind - && let Ok(src) = sm.span_to_snippet(sp) - && replace_prefix(&src, "b\"", "\"").is_some() - { - let pos = sp.lo() + BytePos(1); - return Some(( - sp.with_hi(pos), - "consider removing the leading `b`".to_string(), - String::new(), - Applicability::MachineApplicable, - true, - )); - } - } - (&ty::Array(arr, _) | &ty::Slice(arr), &ty::Str) if arr == self.tcx.types.u8 => { - if let hir::ExprKind::Lit(_) = expr.kind - && let Ok(src) = sm.span_to_snippet(sp) - && replace_prefix(&src, "\"", "b\"").is_some() - { - return Some(( - sp.shrink_to_lo(), - "consider adding a leading `b`".to_string(), - "b".to_string(), - Applicability::MachineApplicable, - true, - )); - } - } - _ => {} - }, - (_, &ty::Ref(_, _, mutability), _) => { - // Check if it can work when put into a ref. For example: - // - // ``` - // fn bar(x: &mut i32) {} - // - // let x = 0u32; - // bar(&x); // error, expected &mut - // ``` - let ref_ty = match mutability { - hir::Mutability::Mut => { - self.tcx.mk_mut_ref(self.tcx.mk_region(ty::ReStatic), checked_ty) - } - hir::Mutability::Not => { - self.tcx.mk_imm_ref(self.tcx.mk_region(ty::ReStatic), checked_ty) - } - }; - if self.can_coerce(ref_ty, expected) { - let mut sugg_sp = sp; - if let hir::ExprKind::MethodCall(ref segment, receiver, args, _) = expr.kind { - let clone_trait = - self.tcx.require_lang_item(LangItem::Clone, Some(segment.ident.span)); - if args.is_empty() - && self.typeck_results.borrow().type_dependent_def_id(expr.hir_id).map( - |did| { - let ai = self.tcx.associated_item(did); - ai.trait_container(self.tcx) == Some(clone_trait) - }, - ) == Some(true) - && segment.ident.name == sym::clone - { - // If this expression had a clone call when suggesting borrowing - // we want to suggest removing it because it'd now be unnecessary. - sugg_sp = receiver.span; - } - } - if let Ok(src) = sm.span_to_snippet(sugg_sp) { - let needs_parens = match expr.kind { - // parenthesize if needed (Issue #46756) - hir::ExprKind::Cast(_, _) | hir::ExprKind::Binary(_, _, _) => true, - // parenthesize borrows of range literals (Issue #54505) - _ if is_range_literal(expr) => true, - _ => false, - }; - - if let Some(sugg) = self.can_use_as_ref(expr) { - return Some(( - sugg.0, - sugg.1.to_string(), - sugg.2, - Applicability::MachineApplicable, - false, - )); - } - - let prefix = match self.maybe_get_struct_pattern_shorthand_field(expr) { - Some(ident) => format!("{ident}: "), - None => String::new(), - }; - - if let Some(hir::Node::Expr(hir::Expr { - kind: hir::ExprKind::Assign(..), - .. - })) = self.tcx.hir().find(self.tcx.hir().get_parent_node(expr.hir_id)) - { - if mutability == hir::Mutability::Mut { - // Suppressing this diagnostic, we'll properly print it in `check_expr_assign` - return None; - } - } - - let sugg_expr = if needs_parens { format!("({src})") } else { src }; - return Some(match mutability { - hir::Mutability::Mut => ( - sp, - "consider mutably borrowing here".to_string(), - format!("{prefix}&mut {sugg_expr}"), - Applicability::MachineApplicable, - false, - ), - hir::Mutability::Not => ( - sp, - "consider borrowing here".to_string(), - format!("{prefix}&{sugg_expr}"), - Applicability::MachineApplicable, - false, - ), - }); - } - } - } - ( - hir::ExprKind::AddrOf(hir::BorrowKind::Ref, _, ref expr), - _, - &ty::Ref(_, checked, _), - ) if self.can_sub(self.param_env, checked, expected).is_ok() => { - // We have `&T`, check if what was expected was `T`. If so, - // we may want to suggest removing a `&`. - if sm.is_imported(expr.span) { - // Go through the spans from which this span was expanded, - // and find the one that's pointing inside `sp`. - // - // E.g. for `&format!("")`, where we want the span to the - // `format!()` invocation instead of its expansion. - if let Some(call_span) = - iter::successors(Some(expr.span), |s| s.parent_callsite()) - .find(|&s| sp.contains(s)) - && sm.is_span_accessible(call_span) - { - return Some(( - sp.with_hi(call_span.lo()), - "consider removing the borrow".to_string(), - String::new(), - Applicability::MachineApplicable, - true, - )); - } - return None; - } - if sp.contains(expr.span) - && sm.is_span_accessible(expr.span) - { - return Some(( - sp.with_hi(expr.span.lo()), - "consider removing the borrow".to_string(), - String::new(), - Applicability::MachineApplicable, - true, - )); - } - } - ( - _, - &ty::RawPtr(TypeAndMut { ty: ty_b, mutbl: mutbl_b }), - &ty::Ref(_, ty_a, mutbl_a), - ) => { - if let Some(steps) = self.deref_steps(ty_a, ty_b) - // Only suggest valid if dereferencing needed. - && steps > 0 - // The pointer type implements `Copy` trait so the suggestion is always valid. - && let Ok(src) = sm.span_to_snippet(sp) - { - let derefs = "*".repeat(steps); - if let Some((span, src, applicability)) = match mutbl_b { - hir::Mutability::Mut => { - let new_prefix = "&mut ".to_owned() + &derefs; - match mutbl_a { - hir::Mutability::Mut => { - replace_prefix(&src, "&mut ", &new_prefix).map(|_| { - let pos = sp.lo() + BytePos(5); - let sp = sp.with_lo(pos).with_hi(pos); - (sp, derefs, Applicability::MachineApplicable) - }) - } - hir::Mutability::Not => { - replace_prefix(&src, "&", &new_prefix).map(|_| { - let pos = sp.lo() + BytePos(1); - let sp = sp.with_lo(pos).with_hi(pos); - ( - sp, - format!("mut {derefs}"), - Applicability::Unspecified, - ) - }) - } - } - } - hir::Mutability::Not => { - let new_prefix = "&".to_owned() + &derefs; - match mutbl_a { - hir::Mutability::Mut => { - replace_prefix(&src, "&mut ", &new_prefix).map(|_| { - let lo = sp.lo() + BytePos(1); - let hi = sp.lo() + BytePos(5); - let sp = sp.with_lo(lo).with_hi(hi); - (sp, derefs, Applicability::MachineApplicable) - }) - } - hir::Mutability::Not => { - replace_prefix(&src, "&", &new_prefix).map(|_| { - let pos = sp.lo() + BytePos(1); - let sp = sp.with_lo(pos).with_hi(pos); - (sp, derefs, Applicability::MachineApplicable) - }) - } - } - } - } { - return Some(( - span, - "consider dereferencing".to_string(), - src, - applicability, - true, - )); - } - } - } - _ if sp == expr.span => { - if let Some(mut steps) = self.deref_steps(checked_ty, expected) { - let mut expr = expr.peel_blocks(); - let mut prefix_span = expr.span.shrink_to_lo(); - let mut remove = String::new(); - - // Try peeling off any existing `&` and `&mut` to reach our target type - while steps > 0 { - if let hir::ExprKind::AddrOf(_, mutbl, inner) = expr.kind { - // If the expression has `&`, removing it would fix the error - prefix_span = prefix_span.with_hi(inner.span.lo()); - expr = inner; - remove += match mutbl { - hir::Mutability::Not => "&", - hir::Mutability::Mut => "&mut ", - }; - steps -= 1; - } else { - break; - } - } - // If we've reached our target type with just removing `&`, then just print now. - if steps == 0 { - return Some(( - prefix_span, - format!("consider removing the `{}`", remove.trim()), - String::new(), - // Do not remove `&&` to get to bool, because it might be something like - // { a } && b, which we have a separate fixup suggestion that is more - // likely correct... - if remove.trim() == "&&" && expected == self.tcx.types.bool { - Applicability::MaybeIncorrect - } else { - Applicability::MachineApplicable - }, - true, - )); - } - - // For this suggestion to make sense, the type would need to be `Copy`, - // or we have to be moving out of a `Box<T>` - if self.type_is_copy_modulo_regions(self.param_env, expected, sp) - // FIXME(compiler-errors): We can actually do this if the checked_ty is - // `steps` layers of boxes, not just one, but this is easier and most likely. - || (checked_ty.is_box() && steps == 1) - { - let deref_kind = if checked_ty.is_box() { - "unboxing the value" - } else if checked_ty.is_region_ptr() { - "dereferencing the borrow" - } else { - "dereferencing the type" - }; - - // Suggest removing `&` if we have removed any, otherwise suggest just - // dereferencing the remaining number of steps. - let message = if remove.is_empty() { - format!("consider {deref_kind}") - } else { - format!( - "consider removing the `{}` and {} instead", - remove.trim(), - deref_kind - ) - }; - - let prefix = match self.maybe_get_struct_pattern_shorthand_field(expr) { - Some(ident) => format!("{ident}: "), - None => String::new(), - }; - - let (span, suggestion) = if self.is_else_if_block(expr) { - // Don't suggest nonsense like `else *if` - return None; - } else if let Some(expr) = self.maybe_get_block_expr(expr) { - // prefix should be empty here.. - (expr.span.shrink_to_lo(), "*".to_string()) - } else { - (prefix_span, format!("{}{}", prefix, "*".repeat(steps))) - }; - - return Some(( - span, - message, - suggestion, - Applicability::MachineApplicable, - true, - )); - } - } - } - _ => {} - } - None - } - - pub fn check_for_cast( - &self, - err: &mut Diagnostic, - expr: &hir::Expr<'_>, - checked_ty: Ty<'tcx>, - expected_ty: Ty<'tcx>, - expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>, - ) -> bool { - if self.tcx.sess.source_map().is_imported(expr.span) { - // Ignore if span is from within a macro. - return false; - } - - let Ok(src) = self.tcx.sess.source_map().span_to_snippet(expr.span) else { - return false; - }; - - // If casting this expression to a given numeric type would be appropriate in case of a type - // mismatch. - // - // We want to minimize the amount of casting operations that are suggested, as it can be a - // lossy operation with potentially bad side effects, so we only suggest when encountering - // an expression that indicates that the original type couldn't be directly changed. - // - // For now, don't suggest casting with `as`. - let can_cast = false; - - let mut sugg = vec![]; - - if let Some(hir::Node::ExprField(field)) = - self.tcx.hir().find(self.tcx.hir().get_parent_node(expr.hir_id)) - { - // `expr` is a literal field for a struct, only suggest if appropriate - if field.is_shorthand { - // This is a field literal - sugg.push((field.ident.span.shrink_to_lo(), format!("{}: ", field.ident))); - } else { - // Likely a field was meant, but this field wasn't found. Do not suggest anything. - return false; - } - }; - - if let hir::ExprKind::Call(path, args) = &expr.kind - && let (hir::ExprKind::Path(hir::QPath::TypeRelative(base_ty, path_segment)), 1) = - (&path.kind, args.len()) - // `expr` is a conversion like `u32::from(val)`, do not suggest anything (#63697). - && let (hir::TyKind::Path(hir::QPath::Resolved(None, base_ty_path)), sym::from) = - (&base_ty.kind, path_segment.ident.name) - { - if let Some(ident) = &base_ty_path.segments.iter().map(|s| s.ident).next() { - match ident.name { - sym::i128 - | sym::i64 - | sym::i32 - | sym::i16 - | sym::i8 - | sym::u128 - | sym::u64 - | sym::u32 - | sym::u16 - | sym::u8 - | sym::isize - | sym::usize - if base_ty_path.segments.len() == 1 => - { - return false; - } - _ => {} - } - } - } - - let msg = format!( - "you can convert {} `{}` to {} `{}`", - checked_ty.kind().article(), - checked_ty, - expected_ty.kind().article(), - expected_ty, - ); - let cast_msg = format!( - "you can cast {} `{}` to {} `{}`", - checked_ty.kind().article(), - checked_ty, - expected_ty.kind().article(), - expected_ty, - ); - let lit_msg = format!( - "change the type of the numeric literal from `{checked_ty}` to `{expected_ty}`", - ); - - let close_paren = if expr.precedence().order() < PREC_POSTFIX { - sugg.push((expr.span.shrink_to_lo(), "(".to_string())); - ")" - } else { - "" - }; - - let mut cast_suggestion = sugg.clone(); - cast_suggestion.push((expr.span.shrink_to_hi(), format!("{close_paren} as {expected_ty}"))); - let mut into_suggestion = sugg.clone(); - into_suggestion.push((expr.span.shrink_to_hi(), format!("{close_paren}.into()"))); - let mut suffix_suggestion = sugg.clone(); - suffix_suggestion.push(( - if matches!( - (&expected_ty.kind(), &checked_ty.kind()), - (ty::Int(_) | ty::Uint(_), ty::Float(_)) - ) { - // Remove fractional part from literal, for example `42.0f32` into `42` - let src = src.trim_end_matches(&checked_ty.to_string()); - let len = src.split('.').next().unwrap().len(); - expr.span.with_lo(expr.span.lo() + BytePos(len as u32)) - } else { - let len = src.trim_end_matches(&checked_ty.to_string()).len(); - expr.span.with_lo(expr.span.lo() + BytePos(len as u32)) - }, - if expr.precedence().order() < PREC_POSTFIX { - // Readd `)` - format!("{expected_ty})") - } else { - expected_ty.to_string() - }, - )); - let literal_is_ty_suffixed = |expr: &hir::Expr<'_>| { - if let hir::ExprKind::Lit(lit) = &expr.kind { lit.node.is_suffixed() } else { false } - }; - let is_negative_int = - |expr: &hir::Expr<'_>| matches!(expr.kind, hir::ExprKind::Unary(hir::UnOp::Neg, ..)); - let is_uint = |ty: Ty<'_>| matches!(ty.kind(), ty::Uint(..)); - - let in_const_context = self.tcx.hir().is_inside_const_context(expr.hir_id); - - let suggest_fallible_into_or_lhs_from = - |err: &mut Diagnostic, exp_to_found_is_fallible: bool| { - // If we know the expression the expected type is derived from, we might be able - // to suggest a widening conversion rather than a narrowing one (which may - // panic). For example, given x: u8 and y: u32, if we know the span of "x", - // x > y - // can be given the suggestion "u32::from(x) > y" rather than - // "x > y.try_into().unwrap()". - let lhs_expr_and_src = expected_ty_expr.and_then(|expr| { - self.tcx - .sess - .source_map() - .span_to_snippet(expr.span) - .ok() - .map(|src| (expr, src)) - }); - let (msg, suggestion) = if let (Some((lhs_expr, lhs_src)), false) = - (lhs_expr_and_src, exp_to_found_is_fallible) - { - let msg = format!( - "you can convert `{lhs_src}` from `{expected_ty}` to `{checked_ty}`, matching the type of `{src}`", - ); - let suggestion = vec![ - (lhs_expr.span.shrink_to_lo(), format!("{checked_ty}::from(")), - (lhs_expr.span.shrink_to_hi(), ")".to_string()), - ]; - (msg, suggestion) - } else { - let msg = format!("{msg} and panic if the converted value doesn't fit"); - let mut suggestion = sugg.clone(); - suggestion.push(( - expr.span.shrink_to_hi(), - format!("{close_paren}.try_into().unwrap()"), - )); - (msg, suggestion) - }; - err.multipart_suggestion_verbose( - &msg, - suggestion, - Applicability::MachineApplicable, - ); - }; - - let suggest_to_change_suffix_or_into = - |err: &mut Diagnostic, - found_to_exp_is_fallible: bool, - exp_to_found_is_fallible: bool| { - let exp_is_lhs = - expected_ty_expr.map(|e| self.tcx.hir().is_lhs(e.hir_id)).unwrap_or(false); - - if exp_is_lhs { - return; - } - - let always_fallible = found_to_exp_is_fallible - && (exp_to_found_is_fallible || expected_ty_expr.is_none()); - let msg = if literal_is_ty_suffixed(expr) { - &lit_msg - } else if always_fallible && (is_negative_int(expr) && is_uint(expected_ty)) { - // We now know that converting either the lhs or rhs is fallible. Before we - // suggest a fallible conversion, check if the value can never fit in the - // expected type. - let msg = format!("`{src}` cannot fit into type `{expected_ty}`"); - err.note(&msg); - return; - } else if in_const_context { - // Do not recommend `into` or `try_into` in const contexts. - return; - } else if found_to_exp_is_fallible { - return suggest_fallible_into_or_lhs_from(err, exp_to_found_is_fallible); - } else { - &msg - }; - let suggestion = if literal_is_ty_suffixed(expr) { - suffix_suggestion.clone() - } else { - into_suggestion.clone() - }; - err.multipart_suggestion_verbose(msg, suggestion, Applicability::MachineApplicable); - }; - - match (&expected_ty.kind(), &checked_ty.kind()) { - (&ty::Int(ref exp), &ty::Int(ref found)) => { - let (f2e_is_fallible, e2f_is_fallible) = match (exp.bit_width(), found.bit_width()) - { - (Some(exp), Some(found)) if exp < found => (true, false), - (Some(exp), Some(found)) if exp > found => (false, true), - (None, Some(8 | 16)) => (false, true), - (Some(8 | 16), None) => (true, false), - (None, _) | (_, None) => (true, true), - _ => (false, false), - }; - suggest_to_change_suffix_or_into(err, f2e_is_fallible, e2f_is_fallible); - true - } - (&ty::Uint(ref exp), &ty::Uint(ref found)) => { - let (f2e_is_fallible, e2f_is_fallible) = match (exp.bit_width(), found.bit_width()) - { - (Some(exp), Some(found)) if exp < found => (true, false), - (Some(exp), Some(found)) if exp > found => (false, true), - (None, Some(8 | 16)) => (false, true), - (Some(8 | 16), None) => (true, false), - (None, _) | (_, None) => (true, true), - _ => (false, false), - }; - suggest_to_change_suffix_or_into(err, f2e_is_fallible, e2f_is_fallible); - true - } - (&ty::Int(exp), &ty::Uint(found)) => { - let (f2e_is_fallible, e2f_is_fallible) = match (exp.bit_width(), found.bit_width()) - { - (Some(exp), Some(found)) if found < exp => (false, true), - (None, Some(8)) => (false, true), - _ => (true, true), - }; - suggest_to_change_suffix_or_into(err, f2e_is_fallible, e2f_is_fallible); - true - } - (&ty::Uint(exp), &ty::Int(found)) => { - let (f2e_is_fallible, e2f_is_fallible) = match (exp.bit_width(), found.bit_width()) - { - (Some(exp), Some(found)) if found > exp => (true, false), - (Some(8), None) => (true, false), - _ => (true, true), - }; - suggest_to_change_suffix_or_into(err, f2e_is_fallible, e2f_is_fallible); - true - } - (&ty::Float(ref exp), &ty::Float(ref found)) => { - if found.bit_width() < exp.bit_width() { - suggest_to_change_suffix_or_into(err, false, true); - } else if literal_is_ty_suffixed(expr) { - err.multipart_suggestion_verbose( - &lit_msg, - suffix_suggestion, - Applicability::MachineApplicable, - ); - } else if can_cast { - // Missing try_into implementation for `f64` to `f32` - err.multipart_suggestion_verbose( - &format!("{cast_msg}, producing the closest possible value"), - cast_suggestion, - Applicability::MaybeIncorrect, // lossy conversion - ); - } - true - } - (&ty::Uint(_) | &ty::Int(_), &ty::Float(_)) => { - if literal_is_ty_suffixed(expr) { - err.multipart_suggestion_verbose( - &lit_msg, - suffix_suggestion, - Applicability::MachineApplicable, - ); - } else if can_cast { - // Missing try_into implementation for `{float}` to `{integer}` - err.multipart_suggestion_verbose( - &format!("{msg}, rounding the float towards zero"), - cast_suggestion, - Applicability::MaybeIncorrect, // lossy conversion - ); - } - true - } - (&ty::Float(ref exp), &ty::Uint(ref found)) => { - // if `found` is `None` (meaning found is `usize`), don't suggest `.into()` - if exp.bit_width() > found.bit_width().unwrap_or(256) { - err.multipart_suggestion_verbose( - &format!( - "{msg}, producing the floating point representation of the integer", - ), - into_suggestion, - Applicability::MachineApplicable, - ); - } else if literal_is_ty_suffixed(expr) { - err.multipart_suggestion_verbose( - &lit_msg, - suffix_suggestion, - Applicability::MachineApplicable, - ); - } else { - // Missing try_into implementation for `{integer}` to `{float}` - err.multipart_suggestion_verbose( - &format!( - "{cast_msg}, producing the floating point representation of the integer, \ - rounded if necessary", - ), - cast_suggestion, - Applicability::MaybeIncorrect, // lossy conversion - ); - } - true - } - (&ty::Float(ref exp), &ty::Int(ref found)) => { - // if `found` is `None` (meaning found is `isize`), don't suggest `.into()` - if exp.bit_width() > found.bit_width().unwrap_or(256) { - err.multipart_suggestion_verbose( - &format!( - "{}, producing the floating point representation of the integer", - &msg, - ), - into_suggestion, - Applicability::MachineApplicable, - ); - } else if literal_is_ty_suffixed(expr) { - err.multipart_suggestion_verbose( - &lit_msg, - suffix_suggestion, - Applicability::MachineApplicable, - ); - } else { - // Missing try_into implementation for `{integer}` to `{float}` - err.multipart_suggestion_verbose( - &format!( - "{}, producing the floating point representation of the integer, \ - rounded if necessary", - &msg, - ), - cast_suggestion, - Applicability::MaybeIncorrect, // lossy conversion - ); - } - true - } - ( - &ty::Uint(ty::UintTy::U32 | ty::UintTy::U64 | ty::UintTy::U128) - | &ty::Int(ty::IntTy::I32 | ty::IntTy::I64 | ty::IntTy::I128), - &ty::Char, - ) => { - err.multipart_suggestion_verbose( - &format!("{cast_msg}, since a `char` always occupies 4 bytes"), - cast_suggestion, - Applicability::MachineApplicable, - ); - true - } - _ => false, - } - } -} diff --git a/compiler/rustc_typeck/src/check/diverges.rs b/compiler/rustc_typeck/src/check/diverges.rs deleted file mode 100644 index 963a93a95..000000000 --- a/compiler/rustc_typeck/src/check/diverges.rs +++ /dev/null @@ -1,78 +0,0 @@ -use rustc_span::source_map::DUMMY_SP; -use rustc_span::{self, Span}; -use std::{cmp, ops}; - -/// Tracks whether executing a node may exit normally (versus -/// return/break/panic, which "diverge", leaving dead code in their -/// wake). Tracked semi-automatically (through type variables marked -/// as diverging), with some manual adjustments for control-flow -/// primitives (approximating a CFG). -#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord)] -pub enum Diverges { - /// Potentially unknown, some cases converge, - /// others require a CFG to determine them. - Maybe, - - /// Definitely known to diverge and therefore - /// not reach the next sibling or its parent. - Always { - /// The `Span` points to the expression - /// that caused us to diverge - /// (e.g. `return`, `break`, etc). - span: Span, - /// In some cases (e.g. a `match` expression - /// where all arms diverge), we may be - /// able to provide a more informative - /// message to the user. - /// If this is `None`, a default message - /// will be generated, which is suitable - /// for most cases. - custom_note: Option<&'static str>, - }, - - /// Same as `Always` but with a reachability - /// warning already emitted. - WarnedAlways, -} - -// Convenience impls for combining `Diverges`. - -impl ops::BitAnd for Diverges { - type Output = Self; - fn bitand(self, other: Self) -> Self { - cmp::min(self, other) - } -} - -impl ops::BitOr for Diverges { - type Output = Self; - fn bitor(self, other: Self) -> Self { - cmp::max(self, other) - } -} - -impl ops::BitAndAssign for Diverges { - fn bitand_assign(&mut self, other: Self) { - *self = *self & other; - } -} - -impl ops::BitOrAssign for Diverges { - fn bitor_assign(&mut self, other: Self) { - *self = *self | other; - } -} - -impl Diverges { - /// Creates a `Diverges::Always` with the provided `span` and the default note message. - pub(super) fn always(span: Span) -> Diverges { - Diverges::Always { span, custom_note: None } - } - - pub(super) fn is_always(self) -> bool { - // Enum comparison ignores the - // contents of fields, so we just - // fill them in with garbage here. - self >= Diverges::Always { span: DUMMY_SP, custom_note: None } - } -} diff --git a/compiler/rustc_typeck/src/check/dropck.rs b/compiler/rustc_typeck/src/check/dropck.rs deleted file mode 100644 index ab143c059..000000000 --- a/compiler/rustc_typeck/src/check/dropck.rs +++ /dev/null @@ -1,329 +0,0 @@ -// FIXME(@lcnr): Move this module out of `rustc_typeck`. -// -// We don't do any drop checking during hir typeck. -use crate::hir::def_id::{DefId, LocalDefId}; -use rustc_errors::{struct_span_err, ErrorGuaranteed}; -use rustc_middle::ty::error::TypeError; -use rustc_middle::ty::relate::{Relate, RelateResult, TypeRelation}; -use rustc_middle::ty::subst::SubstsRef; -use rustc_middle::ty::util::IgnoreRegions; -use rustc_middle::ty::{self, Predicate, Ty, TyCtxt}; - -/// This function confirms that the `Drop` implementation identified by -/// `drop_impl_did` is not any more specialized than the type it is -/// attached to (Issue #8142). -/// -/// This means: -/// -/// 1. The self type must be nominal (this is already checked during -/// coherence), -/// -/// 2. The generic region/type parameters of the impl's self type must -/// all be parameters of the Drop impl itself (i.e., no -/// specialization like `impl Drop for Foo<i32>`), and, -/// -/// 3. Any bounds on the generic parameters must be reflected in the -/// struct/enum definition for the nominal type itself (i.e. -/// cannot do `struct S<T>; impl<T:Clone> Drop for S<T> { ... }`). -/// -pub fn check_drop_impl(tcx: TyCtxt<'_>, drop_impl_did: DefId) -> Result<(), ErrorGuaranteed> { - let dtor_self_type = tcx.type_of(drop_impl_did); - let dtor_predicates = tcx.predicates_of(drop_impl_did); - match dtor_self_type.kind() { - ty::Adt(adt_def, self_to_impl_substs) => { - ensure_drop_params_and_item_params_correspond( - tcx, - drop_impl_did.expect_local(), - adt_def.did(), - self_to_impl_substs, - )?; - - ensure_drop_predicates_are_implied_by_item_defn( - tcx, - dtor_predicates, - adt_def.did().expect_local(), - self_to_impl_substs, - ) - } - _ => { - // Destructors only work on nominal types. This was - // already checked by coherence, but compilation may - // not have been terminated. - let span = tcx.def_span(drop_impl_did); - let reported = tcx.sess.delay_span_bug( - span, - &format!("should have been rejected by coherence check: {dtor_self_type}"), - ); - Err(reported) - } - } -} - -fn ensure_drop_params_and_item_params_correspond<'tcx>( - tcx: TyCtxt<'tcx>, - drop_impl_did: LocalDefId, - self_type_did: DefId, - drop_impl_substs: SubstsRef<'tcx>, -) -> Result<(), ErrorGuaranteed> { - let Err(arg) = tcx.uses_unique_generic_params(drop_impl_substs, IgnoreRegions::No) else { - return Ok(()) - }; - - let drop_impl_span = tcx.def_span(drop_impl_did); - 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 = - struct_span_err!(tcx.sess, drop_impl_span, E0366, "`Drop` impls cannot be specialized"); - match arg { - ty::util::NotUniqueParam::DuplicateParam(arg) => { - err.note(&format!("`{arg}` is mentioned multiple times")) - } - ty::util::NotUniqueParam::NotParam(arg) => { - err.note(&format!("`{arg}` is not a generic parameter")) - } - }; - err.span_note( - item_span, - &format!( - "use the same sequence of generic lifetime, type and const parameters \ - as the {self_descr} definition", - ), - ); - Err(err.emit()) -} - -/// Confirms that every predicate imposed by dtor_predicates is -/// implied by assuming the predicates attached to self_type_did. -fn ensure_drop_predicates_are_implied_by_item_defn<'tcx>( - tcx: TyCtxt<'tcx>, - dtor_predicates: ty::GenericPredicates<'tcx>, - self_type_did: LocalDefId, - self_to_impl_substs: SubstsRef<'tcx>, -) -> Result<(), ErrorGuaranteed> { - let mut result = Ok(()); - - // Here is an example, analogous to that from - // `compare_impl_method`. - // - // Consider a struct type: - // - // struct Type<'c, 'b:'c, 'a> { - // x: &'a Contents // (contents are irrelevant; - // y: &'c Cell<&'b Contents>, // only the bounds matter for our purposes.) - // } - // - // and a Drop impl: - // - // impl<'z, 'y:'z, 'x:'y> Drop for P<'z, 'y, 'x> { - // fn drop(&mut self) { self.y.set(self.x); } // (only legal if 'x: 'y) - // } - // - // We start out with self_to_impl_substs, that maps the generic - // parameters of Type to that of the Drop impl. - // - // self_to_impl_substs = {'c => 'z, 'b => 'y, 'a => 'x} - // - // Applying this to the predicates (i.e., assumptions) provided by the item - // definition yields the instantiated assumptions: - // - // ['y : 'z] - // - // We then check all of the predicates of the Drop impl: - // - // ['y:'z, 'x:'y] - // - // and ensure each is in the list of instantiated - // assumptions. Here, `'y:'z` is present, but `'x:'y` is - // absent. So we report an error that the Drop impl injected a - // predicate that is not present on the struct definition. - - // We can assume the predicates attached to struct/enum definition - // hold. - let generic_assumptions = tcx.predicates_of(self_type_did); - - 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 - // via the traits::fulfill machinery. However, it ran into trouble - // since the fulfill machinery merely turns outlives-predicates - // 'a:'b and T:'b into region inference constraints. It is simpler - // just to look for all the predicates directly. - - assert_eq!(dtor_predicates.parent, None); - for &(predicate, predicate_sp) in dtor_predicates.predicates { - // (We do not need to worry about deep analysis of type - // expressions etc because the Drop impls are already forced - // to take on a structure that is roughly an alpha-renaming of - // the generic parameters of the item definition.) - - // This path now just checks *all* predicates via an instantiation of - // the `SimpleEqRelation`, which simply forwards to the `relate` machinery - // after taking care of anonymizing late bound regions. - // - // However, it may be more efficient in the future to batch - // the analysis together via the fulfill (see comment above regarding - // the usage of the fulfill machinery), rather than the - // repeated `.iter().any(..)` calls. - - // This closure is a more robust way to check `Predicate` equality - // than simple `==` checks (which were the previous implementation). - // It relies on `ty::relate` for `TraitPredicate`, `ProjectionPredicate`, - // `ConstEvaluatable` and `TypeOutlives` (which implement the Relate trait), - // while delegating on simple equality for the other `Predicate`. - // This implementation solves (Issue #59497) and (Issue #58311). - // It is unclear to me at the moment whether the approach based on `relate` - // could be extended easily also to the other `Predicate`. - let predicate_matches_closure = |p: Predicate<'tcx>| { - let mut relator: SimpleEqRelation<'tcx> = SimpleEqRelation::new(tcx, self_param_env); - let predicate = predicate.kind(); - 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() - } - (ty::PredicateKind::Projection(a), ty::PredicateKind::Projection(b)) => { - relator.relate(predicate.rebind(a), p.rebind(b)).is_ok() - } - ( - ty::PredicateKind::ConstEvaluatable(a), - ty::PredicateKind::ConstEvaluatable(b), - ) => tcx.try_unify_abstract_consts(self_param_env.and((a, b))), - ( - ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(ty_a, lt_a)), - ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(ty_b, lt_b)), - ) => { - relator.relate(predicate.rebind(ty_a), p.rebind(ty_b)).is_ok() - && relator.relate(predicate.rebind(lt_a), p.rebind(lt_b)).is_ok() - } - (ty::PredicateKind::WellFormed(arg_a), ty::PredicateKind::WellFormed(arg_b)) => { - relator.relate(predicate.rebind(arg_a), p.rebind(arg_b)).is_ok() - } - _ => predicate == p, - } - }; - - if !assumptions_in_impl_context.iter().copied().any(predicate_matches_closure) { - let item_span = tcx.def_span(self_type_did); - let self_descr = tcx.def_kind(self_type_did).descr(self_type_did.to_def_id()); - let reported = struct_span_err!( - tcx.sess, - predicate_sp, - E0367, - "`Drop` impl requires `{predicate}` but the {self_descr} it is implemented for does not", - ) - .span_note(item_span, "the implementor must specify the same requirement") - .emit(); - result = Err(reported); - } - } - - result -} - -// This is an implementation of the TypeRelation trait with the -// aim of simply comparing for equality (without side-effects). -// It is not intended to be used anywhere else other than here. -pub(crate) struct SimpleEqRelation<'tcx> { - tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, -} - -impl<'tcx> SimpleEqRelation<'tcx> { - fn new(tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> SimpleEqRelation<'tcx> { - SimpleEqRelation { tcx, param_env } - } -} - -impl<'tcx> TypeRelation<'tcx> for SimpleEqRelation<'tcx> { - fn tcx(&self) -> TyCtxt<'tcx> { - self.tcx - } - - fn param_env(&self) -> ty::ParamEnv<'tcx> { - self.param_env - } - - fn tag(&self) -> &'static str { - "dropck::SimpleEqRelation" - } - - fn a_is_expected(&self) -> bool { - true - } - - fn relate_with_variance<T: Relate<'tcx>>( - &mut self, - _: ty::Variance, - _info: ty::VarianceDiagInfo<'tcx>, - a: T, - b: T, - ) -> RelateResult<'tcx, T> { - // Here we ignore variance because we require drop impl's types - // to be *exactly* the same as to the ones in the struct definition. - self.relate(a, b) - } - - fn tys(&mut self, a: Ty<'tcx>, b: Ty<'tcx>) -> RelateResult<'tcx, Ty<'tcx>> { - debug!("SimpleEqRelation::tys(a={:?}, b={:?})", a, b); - ty::relate::super_relate_tys(self, a, b) - } - - fn regions( - &mut self, - a: ty::Region<'tcx>, - b: ty::Region<'tcx>, - ) -> RelateResult<'tcx, ty::Region<'tcx>> { - debug!("SimpleEqRelation::regions(a={:?}, b={:?})", a, b); - - // We can just equate the regions because LBRs have been - // already anonymized. - if a == b { - Ok(a) - } else { - // I'm not sure is this `TypeError` is the right one, but - // it should not matter as it won't be checked (the dropck - // will emit its own, more informative and higher-level errors - // in case anything goes wrong). - Err(TypeError::RegionsPlaceholderMismatch) - } - } - - fn consts( - &mut self, - a: ty::Const<'tcx>, - b: ty::Const<'tcx>, - ) -> RelateResult<'tcx, ty::Const<'tcx>> { - debug!("SimpleEqRelation::consts(a={:?}, b={:?})", a, b); - ty::relate::super_relate_consts(self, a, b) - } - - fn binders<T>( - &mut self, - a: ty::Binder<'tcx, T>, - b: ty::Binder<'tcx, T>, - ) -> RelateResult<'tcx, ty::Binder<'tcx, T>> - where - T: Relate<'tcx>, - { - debug!("SimpleEqRelation::binders({:?}: {:?}", a, b); - - // Anonymizing the LBRs is necessary to solve (Issue #59497). - // After we do so, it should be totally fine to skip the binders. - let anon_a = self.tcx.anonymize_bound_vars(a); - let anon_b = self.tcx.anonymize_bound_vars(b); - self.relate(anon_a.skip_binder(), anon_b.skip_binder())?; - - Ok(a) - } -} diff --git a/compiler/rustc_typeck/src/check/expectation.rs b/compiler/rustc_typeck/src/check/expectation.rs deleted file mode 100644 index e9e810344..000000000 --- a/compiler/rustc_typeck/src/check/expectation.rs +++ /dev/null @@ -1,122 +0,0 @@ -use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; -use rustc_middle::ty::{self, Ty}; -use rustc_span::{self, Span}; - -use super::Expectation::*; -use super::FnCtxt; - -/// When type-checking an expression, we propagate downward -/// whatever type hint we are able in the form of an `Expectation`. -#[derive(Copy, Clone, Debug)] -pub enum Expectation<'tcx> { - /// We know nothing about what type this expression should have. - NoExpectation, - - /// This expression should have the type given (or some subtype). - ExpectHasType(Ty<'tcx>), - - /// This expression will be cast to the `Ty`. - ExpectCastableToType(Ty<'tcx>), - - /// This rvalue expression will be wrapped in `&` or `Box` and coerced - /// to `&Ty` or `Box<Ty>`, respectively. `Ty` is `[A]` or `Trait`. - ExpectRvalueLikeUnsized(Ty<'tcx>), - - IsLast(Span), -} - -impl<'a, 'tcx> Expectation<'tcx> { - // Disregard "castable to" expectations because they - // can lead us astray. Consider for example `if cond - // {22} else {c} as u8` -- if we propagate the - // "castable to u8" constraint to 22, it will pick the - // type 22u8, which is overly constrained (c might not - // be a u8). In effect, the problem is that the - // "castable to" expectation is not the tightest thing - // we can say, so we want to drop it in this case. - // The tightest thing we can say is "must unify with - // else branch". Note that in the case of a "has type" - // constraint, this limitation does not hold. - - // If the expected type is just a type variable, then don't use - // an expected type. Otherwise, we might write parts of the type - // when checking the 'then' block which are incompatible with the - // 'else' branch. - pub(super) fn adjust_for_branches(&self, fcx: &FnCtxt<'a, 'tcx>) -> Expectation<'tcx> { - match *self { - ExpectHasType(ety) => { - let ety = fcx.shallow_resolve(ety); - if !ety.is_ty_var() { ExpectHasType(ety) } else { NoExpectation } - } - ExpectRvalueLikeUnsized(ety) => ExpectRvalueLikeUnsized(ety), - _ => NoExpectation, - } - } - - /// Provides an expectation for an rvalue expression given an *optional* - /// hint, which is not required for type safety (the resulting type might - /// be checked higher up, as is the case with `&expr` and `box expr`), but - /// is useful in determining the concrete type. - /// - /// The primary use case is where the expected type is a fat pointer, - /// like `&[isize]`. For example, consider the following statement: - /// - /// let x: &[isize] = &[1, 2, 3]; - /// - /// In this case, the expected type for the `&[1, 2, 3]` expression is - /// `&[isize]`. If however we were to say that `[1, 2, 3]` has the - /// expectation `ExpectHasType([isize])`, that would be too strong -- - /// `[1, 2, 3]` does not have the type `[isize]` but rather `[isize; 3]`. - /// It is only the `&[1, 2, 3]` expression as a whole that can be coerced - /// to the type `&[isize]`. Therefore, we propagate this more limited hint, - /// which still is useful, because it informs integer literals and the like. - /// See the test case `test/ui/coerce-expect-unsized.rs` and #20169 - /// for examples of where this comes up,. - pub(super) fn rvalue_hint(fcx: &FnCtxt<'a, 'tcx>, ty: Ty<'tcx>) -> Expectation<'tcx> { - match fcx.tcx.struct_tail_without_normalization(ty).kind() { - ty::Slice(_) | ty::Str | ty::Dynamic(..) => ExpectRvalueLikeUnsized(ty), - _ => ExpectHasType(ty), - } - } - - // Resolves `expected` by a single level if it is a variable. If - // there is no expected type or resolution is not possible (e.g., - // no constraints yet present), just returns `self`. - fn resolve(self, fcx: &FnCtxt<'a, 'tcx>) -> Expectation<'tcx> { - match self { - NoExpectation => NoExpectation, - ExpectCastableToType(t) => ExpectCastableToType(fcx.resolve_vars_if_possible(t)), - ExpectHasType(t) => ExpectHasType(fcx.resolve_vars_if_possible(t)), - ExpectRvalueLikeUnsized(t) => ExpectRvalueLikeUnsized(fcx.resolve_vars_if_possible(t)), - IsLast(sp) => IsLast(sp), - } - } - - pub(super) fn to_option(self, fcx: &FnCtxt<'a, 'tcx>) -> Option<Ty<'tcx>> { - match self.resolve(fcx) { - NoExpectation | IsLast(_) => None, - ExpectCastableToType(ty) | ExpectHasType(ty) | ExpectRvalueLikeUnsized(ty) => Some(ty), - } - } - - /// It sometimes happens that we want to turn an expectation into - /// a **hard constraint** (i.e., something that must be satisfied - /// for the program to type-check). `only_has_type` will return - /// such a constraint, if it exists. - pub(super) fn only_has_type(self, fcx: &FnCtxt<'a, 'tcx>) -> Option<Ty<'tcx>> { - match self { - ExpectHasType(ty) => Some(fcx.resolve_vars_if_possible(ty)), - NoExpectation | ExpectCastableToType(_) | ExpectRvalueLikeUnsized(_) | IsLast(_) => { - None - } - } - } - - /// Like `only_has_type`, but instead of returning `None` if no - /// hard constraint exists, creates a fresh type variable. - pub(super) fn coercion_target_type(self, fcx: &FnCtxt<'a, 'tcx>, span: Span) -> Ty<'tcx> { - self.only_has_type(fcx).unwrap_or_else(|| { - fcx.next_ty_var(TypeVariableOrigin { kind: TypeVariableOriginKind::MiscVariable, span }) - }) - } -} diff --git a/compiler/rustc_typeck/src/check/expr.rs b/compiler/rustc_typeck/src/check/expr.rs deleted file mode 100644 index 93b008500..000000000 --- a/compiler/rustc_typeck/src/check/expr.rs +++ /dev/null @@ -1,2876 +0,0 @@ -//! Type checking expressions. -//! -//! See `mod.rs` for more context on type checking in general. - -use crate::astconv::AstConv as _; -use crate::check::cast::{self, CastCheckResult}; -use crate::check::coercion::CoerceMany; -use crate::check::fatally_break_rust; -use crate::check::method::SelfSource; -use crate::check::Expectation::{self, ExpectCastableToType, ExpectHasType, NoExpectation}; -use crate::check::{ - report_unexpected_variant_res, BreakableCtxt, Diverges, DynamicCoerceMany, FnCtxt, Needs, - TupleArgumentsFlag::DontTupleArguments, -}; -use crate::errors::{ - FieldMultiplySpecifiedInInitializer, FunctionalRecordUpdateOnNonStruct, - YieldExprOutsideOfGenerator, -}; -use crate::type_error_struct; - -use crate::errors::{AddressOfTemporaryTaken, ReturnStmtOutsideOfFnBody, StructExprNonExhaustive}; -use rustc_ast as ast; -use rustc_data_structures::fx::FxHashMap; -use rustc_data_structures::stack::ensure_sufficient_stack; -use rustc_errors::{ - pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, DiagnosticId, - ErrorGuaranteed, StashKey, -}; -use rustc_hir as hir; -use rustc_hir::def::{CtorKind, DefKind, Res}; -use rustc_hir::def_id::DefId; -use rustc_hir::intravisit::Visitor; -use rustc_hir::lang_items::LangItem; -use rustc_hir::{Closure, ExprKind, HirId, QPath}; -use rustc_infer::infer; -use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; -use rustc_infer::infer::InferOk; -use rustc_infer::traits::ObligationCause; -use rustc_middle::middle::stability; -use rustc_middle::ty::adjustment::{Adjust, Adjustment, AllowTwoPhase}; -use rustc_middle::ty::error::TypeError::FieldMisMatch; -use rustc_middle::ty::subst::SubstsRef; -use rustc_middle::ty::{self, AdtKind, Ty, TypeVisitable}; -use rustc_session::parse::feature_err; -use rustc_span::hygiene::DesugaringKind; -use rustc_span::lev_distance::find_best_match_for_name; -use rustc_span::source_map::{Span, Spanned}; -use rustc_span::symbol::{kw, sym, Ident, Symbol}; -use rustc_target::spec::abi::Abi::RustIntrinsic; -use rustc_trait_selection::infer::InferCtxtExt; -use rustc_trait_selection::traits::{self, ObligationCauseCode}; - -impl<'a, 'tcx> FnCtxt<'a, 'tcx> { - fn check_expr_eq_type(&self, expr: &'tcx hir::Expr<'tcx>, expected: Ty<'tcx>) { - let ty = self.check_expr_with_hint(expr, expected); - self.demand_eqtype(expr.span, expected, ty); - } - - pub fn check_expr_has_type_or_error( - &self, - expr: &'tcx hir::Expr<'tcx>, - expected: Ty<'tcx>, - extend_err: impl FnMut(&mut Diagnostic), - ) -> Ty<'tcx> { - self.check_expr_meets_expectation_or_error(expr, ExpectHasType(expected), extend_err) - } - - fn check_expr_meets_expectation_or_error( - &self, - expr: &'tcx hir::Expr<'tcx>, - expected: Expectation<'tcx>, - mut extend_err: impl FnMut(&mut Diagnostic), - ) -> Ty<'tcx> { - let expected_ty = expected.to_option(&self).unwrap_or(self.tcx.types.bool); - let mut ty = self.check_expr_with_expectation(expr, expected); - - // While we don't allow *arbitrary* coercions here, we *do* allow - // coercions from ! to `expected`. - if ty.is_never() { - if let Some(adjustments) = self.typeck_results.borrow().adjustments().get(expr.hir_id) { - self.tcx().sess.delay_span_bug( - expr.span, - "expression with never type wound up being adjusted", - ); - return if let [Adjustment { kind: Adjust::NeverToAny, target }] = &adjustments[..] { - target.to_owned() - } else { - self.tcx().ty_error() - }; - } - - let adj_ty = self.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::AdjustmentType, - span: expr.span, - }); - self.apply_adjustments( - expr, - vec![Adjustment { kind: Adjust::NeverToAny, target: adj_ty }], - ); - ty = adj_ty; - } - - if let Some(mut err) = self.demand_suptype_diag(expr.span, expected_ty, ty) { - let expr = expr.peel_drop_temps(); - self.suggest_deref_ref_or_into(&mut err, expr, expected_ty, ty, None); - extend_err(&mut err); - err.emit(); - } - ty - } - - pub(super) fn check_expr_coercable_to_type( - &self, - expr: &'tcx hir::Expr<'tcx>, - expected: Ty<'tcx>, - expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>, - ) -> Ty<'tcx> { - let ty = self.check_expr_with_hint(expr, expected); - // checks don't need two phase - self.demand_coerce(expr, ty, expected, expected_ty_expr, AllowTwoPhase::No) - } - - pub(super) fn check_expr_with_hint( - &self, - expr: &'tcx hir::Expr<'tcx>, - expected: Ty<'tcx>, - ) -> Ty<'tcx> { - self.check_expr_with_expectation(expr, ExpectHasType(expected)) - } - - fn check_expr_with_expectation_and_needs( - &self, - expr: &'tcx hir::Expr<'tcx>, - expected: Expectation<'tcx>, - needs: Needs, - ) -> Ty<'tcx> { - let ty = self.check_expr_with_expectation(expr, expected); - - // If the expression is used in a place whether mutable place is required - // e.g. LHS of assignment, perform the conversion. - if let Needs::MutPlace = needs { - self.convert_place_derefs_to_mutable(expr); - } - - ty - } - - pub(super) fn check_expr(&self, expr: &'tcx hir::Expr<'tcx>) -> Ty<'tcx> { - self.check_expr_with_expectation(expr, NoExpectation) - } - - pub(super) fn check_expr_with_needs( - &self, - expr: &'tcx hir::Expr<'tcx>, - needs: Needs, - ) -> Ty<'tcx> { - self.check_expr_with_expectation_and_needs(expr, NoExpectation, needs) - } - - /// Invariant: - /// If an expression has any sub-expressions that result in a type error, - /// inspecting that expression's type with `ty.references_error()` will return - /// true. Likewise, if an expression is known to diverge, inspecting its - /// type with `ty::type_is_bot` will return true (n.b.: since Rust is - /// strict, _|_ can appear in the type of an expression that does not, - /// itself, diverge: for example, fn() -> _|_.) - /// Note that inspecting a type's structure *directly* may expose the fact - /// that there are actually multiple representations for `Error`, so avoid - /// that when err needs to be handled differently. - #[instrument(skip(self, expr), level = "debug")] - pub(super) fn check_expr_with_expectation( - &self, - expr: &'tcx hir::Expr<'tcx>, - expected: Expectation<'tcx>, - ) -> Ty<'tcx> { - self.check_expr_with_expectation_and_args(expr, expected, &[]) - } - - /// Same as `check_expr_with_expectation`, but allows us to pass in the arguments of a - /// `ExprKind::Call` when evaluating its callee when it is an `ExprKind::Path`. - pub(super) fn check_expr_with_expectation_and_args( - &self, - expr: &'tcx hir::Expr<'tcx>, - expected: Expectation<'tcx>, - args: &'tcx [hir::Expr<'tcx>], - ) -> Ty<'tcx> { - if self.tcx().sess.verbose() { - // make this code only run with -Zverbose because it is probably slow - if let Ok(lint_str) = self.tcx.sess.source_map().span_to_snippet(expr.span) { - if !lint_str.contains('\n') { - debug!("expr text: {lint_str}"); - } else { - let mut lines = lint_str.lines(); - if let Some(line0) = lines.next() { - let remaining_lines = lines.count(); - debug!("expr text: {line0}"); - debug!("expr text: ...(and {remaining_lines} more lines)"); - } - } - } - } - - // True if `expr` is a `Try::from_ok(())` that is a result of desugaring a try block - // without the final expr (e.g. `try { return; }`). We don't want to generate an - // unreachable_code lint for it since warnings for autogenerated code are confusing. - let is_try_block_generated_unit_expr = match expr.kind { - ExprKind::Call(_, args) if expr.span.is_desugaring(DesugaringKind::TryBlock) => { - args.len() == 1 && args[0].span.is_desugaring(DesugaringKind::TryBlock) - } - - _ => false, - }; - - // Warn for expressions after diverging siblings. - if !is_try_block_generated_unit_expr { - self.warn_if_unreachable(expr.hir_id, expr.span, "expression"); - } - - // Hide the outer diverging and has_errors flags. - let old_diverges = self.diverges.replace(Diverges::Maybe); - let old_has_errors = self.has_errors.replace(false); - - let ty = ensure_sufficient_stack(|| match &expr.kind { - hir::ExprKind::Path( - qpath @ hir::QPath::Resolved(..) | qpath @ hir::QPath::TypeRelative(..), - ) => self.check_expr_path(qpath, expr, args), - _ => self.check_expr_kind(expr, expected), - }); - - // Warn for non-block expressions with diverging children. - match expr.kind { - ExprKind::Block(..) - | ExprKind::If(..) - | ExprKind::Let(..) - | ExprKind::Loop(..) - | ExprKind::Match(..) => {} - // If `expr` is a result of desugaring the try block and is an ok-wrapped - // diverging expression (e.g. it arose from desugaring of `try { return }`), - // we skip issuing a warning because it is autogenerated code. - ExprKind::Call(..) if expr.span.is_desugaring(DesugaringKind::TryBlock) => {} - ExprKind::Call(callee, _) => self.warn_if_unreachable(expr.hir_id, callee.span, "call"), - ExprKind::MethodCall(segment, ..) => { - self.warn_if_unreachable(expr.hir_id, segment.ident.span, "call") - } - _ => self.warn_if_unreachable(expr.hir_id, expr.span, "expression"), - } - - // Any expression that produces a value of type `!` must have diverged - if ty.is_never() { - self.diverges.set(self.diverges.get() | Diverges::always(expr.span)); - } - - // Record the type, which applies it effects. - // We need to do this after the warning above, so that - // we don't warn for the diverging expression itself. - self.write_ty(expr.hir_id, ty); - - // Combine the diverging and has_error flags. - self.diverges.set(self.diverges.get() | old_diverges); - self.has_errors.set(self.has_errors.get() | old_has_errors); - - debug!("type of {} is...", self.tcx.hir().node_to_string(expr.hir_id)); - debug!("... {:?}, expected is {:?}", ty, expected); - - ty - } - - #[instrument(skip(self, expr), level = "debug")] - fn check_expr_kind( - &self, - expr: &'tcx hir::Expr<'tcx>, - expected: Expectation<'tcx>, - ) -> Ty<'tcx> { - trace!("expr={:#?}", expr); - - let tcx = self.tcx; - match expr.kind { - ExprKind::Box(subexpr) => self.check_expr_box(subexpr, expected), - ExprKind::Lit(ref lit) => self.check_lit(&lit, expected), - ExprKind::Binary(op, lhs, rhs) => self.check_binop(expr, op, lhs, rhs, expected), - ExprKind::Assign(lhs, rhs, span) => { - self.check_expr_assign(expr, expected, lhs, rhs, span) - } - ExprKind::AssignOp(op, lhs, rhs) => { - self.check_binop_assign(expr, op, lhs, rhs, expected) - } - ExprKind::Unary(unop, oprnd) => self.check_expr_unary(unop, oprnd, expected, expr), - ExprKind::AddrOf(kind, mutbl, oprnd) => { - self.check_expr_addr_of(kind, mutbl, oprnd, expected, expr) - } - ExprKind::Path(QPath::LangItem(lang_item, _, hir_id)) => { - self.check_lang_item_path(lang_item, expr, hir_id) - } - ExprKind::Path(ref qpath) => self.check_expr_path(qpath, expr, &[]), - ExprKind::InlineAsm(asm) => { - // We defer some asm checks as we may not have resolved the input and output types yet (they may still be infer vars). - self.deferred_asm_checks.borrow_mut().push((asm, expr.hir_id)); - self.check_expr_asm(asm) - } - ExprKind::Break(destination, ref expr_opt) => { - self.check_expr_break(destination, expr_opt.as_deref(), expr) - } - ExprKind::Continue(destination) => { - if destination.target_id.is_ok() { - tcx.types.never - } else { - // There was an error; make type-check fail. - tcx.ty_error() - } - } - ExprKind::Ret(ref expr_opt) => self.check_expr_return(expr_opt.as_deref(), expr), - ExprKind::Let(let_expr) => self.check_expr_let(let_expr), - ExprKind::Loop(body, _, source, _) => { - self.check_expr_loop(body, source, expected, expr) - } - ExprKind::Match(discrim, arms, match_src) => { - self.check_match(expr, &discrim, arms, expected, match_src) - } - ExprKind::Closure(&Closure { capture_clause, fn_decl, body, movability, .. }) => { - self.check_expr_closure(expr, capture_clause, &fn_decl, body, movability, expected) - } - ExprKind::Block(body, _) => self.check_block_with_expected(&body, expected), - ExprKind::Call(callee, args) => self.check_call(expr, &callee, args, expected), - ExprKind::MethodCall(segment, receiver, args, _) => { - self.check_method_call(expr, segment, receiver, args, expected) - } - ExprKind::Cast(e, t) => self.check_expr_cast(e, t, expr), - ExprKind::Type(e, t) => { - let ty = self.to_ty_saving_user_provided_ty(&t); - self.check_expr_eq_type(&e, ty); - ty - } - ExprKind::If(cond, then_expr, opt_else_expr) => { - self.check_then_else(cond, then_expr, opt_else_expr, expr.span, expected) - } - ExprKind::DropTemps(e) => self.check_expr_with_expectation(e, expected), - ExprKind::Array(args) => self.check_expr_array(args, expected, expr), - ExprKind::ConstBlock(ref anon_const) => { - self.check_expr_const_block(anon_const, expected, expr) - } - ExprKind::Repeat(element, ref count) => { - self.check_expr_repeat(element, count, expected, expr) - } - ExprKind::Tup(elts) => self.check_expr_tuple(elts, expected, expr), - ExprKind::Struct(qpath, fields, ref base_expr) => { - self.check_expr_struct(expr, expected, qpath, fields, base_expr) - } - ExprKind::Field(base, field) => self.check_field(expr, &base, field), - ExprKind::Index(base, idx) => self.check_expr_index(base, idx, expr), - ExprKind::Yield(value, ref src) => self.check_expr_yield(value, expr, src), - hir::ExprKind::Err => tcx.ty_error(), - } - } - - fn check_expr_box(&self, expr: &'tcx hir::Expr<'tcx>, expected: Expectation<'tcx>) -> Ty<'tcx> { - let expected_inner = expected.to_option(self).map_or(NoExpectation, |ty| match ty.kind() { - ty::Adt(def, _) if def.is_box() => Expectation::rvalue_hint(self, ty.boxed_ty()), - _ => NoExpectation, - }); - let referent_ty = self.check_expr_with_expectation(expr, expected_inner); - self.require_type_is_sized(referent_ty, expr.span, traits::SizedBoxType); - self.tcx.mk_box(referent_ty) - } - - fn check_expr_unary( - &self, - unop: hir::UnOp, - oprnd: &'tcx hir::Expr<'tcx>, - expected: Expectation<'tcx>, - expr: &'tcx hir::Expr<'tcx>, - ) -> Ty<'tcx> { - let tcx = self.tcx; - let expected_inner = match unop { - hir::UnOp::Not | hir::UnOp::Neg => expected, - hir::UnOp::Deref => NoExpectation, - }; - let mut oprnd_t = self.check_expr_with_expectation(&oprnd, expected_inner); - - if !oprnd_t.references_error() { - oprnd_t = self.structurally_resolved_type(expr.span, oprnd_t); - match unop { - hir::UnOp::Deref => { - if let Some(ty) = self.lookup_derefing(expr, oprnd, oprnd_t) { - oprnd_t = ty; - } else { - let mut err = type_error_struct!( - tcx.sess, - expr.span, - oprnd_t, - E0614, - "type `{oprnd_t}` cannot be dereferenced", - ); - let sp = tcx.sess.source_map().start_point(expr.span); - if let Some(sp) = - tcx.sess.parse_sess.ambiguous_block_expr_parse.borrow().get(&sp) - { - tcx.sess.parse_sess.expr_parentheses_needed(&mut err, *sp); - } - err.emit(); - oprnd_t = tcx.ty_error(); - } - } - hir::UnOp::Not => { - let result = self.check_user_unop(expr, oprnd_t, unop, expected_inner); - // If it's builtin, we can reuse the type, this helps inference. - if !(oprnd_t.is_integral() || *oprnd_t.kind() == ty::Bool) { - oprnd_t = result; - } - } - hir::UnOp::Neg => { - let result = self.check_user_unop(expr, oprnd_t, unop, expected_inner); - // If it's builtin, we can reuse the type, this helps inference. - if !oprnd_t.is_numeric() { - oprnd_t = result; - } - } - } - } - oprnd_t - } - - fn check_expr_addr_of( - &self, - kind: hir::BorrowKind, - mutbl: hir::Mutability, - oprnd: &'tcx hir::Expr<'tcx>, - expected: Expectation<'tcx>, - expr: &'tcx hir::Expr<'tcx>, - ) -> Ty<'tcx> { - let hint = expected.only_has_type(self).map_or(NoExpectation, |ty| { - match ty.kind() { - ty::Ref(_, ty, _) | ty::RawPtr(ty::TypeAndMut { ty, .. }) => { - if oprnd.is_syntactic_place_expr() { - // Places may legitimately have unsized types. - // For example, dereferences of a fat pointer and - // the last field of a struct can be unsized. - ExpectHasType(*ty) - } else { - Expectation::rvalue_hint(self, *ty) - } - } - _ => NoExpectation, - } - }); - let ty = - self.check_expr_with_expectation_and_needs(&oprnd, hint, Needs::maybe_mut_place(mutbl)); - - let tm = ty::TypeAndMut { ty, mutbl }; - match kind { - _ if tm.ty.references_error() => self.tcx.ty_error(), - hir::BorrowKind::Raw => { - self.check_named_place_expr(oprnd); - self.tcx.mk_ptr(tm) - } - hir::BorrowKind::Ref => { - // Note: at this point, we cannot say what the best lifetime - // is to use for resulting pointer. We want to use the - // shortest lifetime possible so as to avoid spurious borrowck - // errors. Moreover, the longest lifetime will depend on the - // precise details of the value whose address is being taken - // (and how long it is valid), which we don't know yet until - // type inference is complete. - // - // Therefore, here we simply generate a region variable. The - // region inferencer will then select a suitable value. - // Finally, borrowck will infer the value of the region again, - // this time with enough precision to check that the value - // whose address was taken can actually be made to live as long - // as it needs to live. - let region = self.next_region_var(infer::AddrOfRegion(expr.span)); - self.tcx.mk_ref(region, tm) - } - } - } - - /// Does this expression refer to a place that either: - /// * Is based on a local or static. - /// * Contains a dereference - /// Note that the adjustments for the children of `expr` should already - /// have been resolved. - fn check_named_place_expr(&self, oprnd: &'tcx hir::Expr<'tcx>) { - let is_named = oprnd.is_place_expr(|base| { - // Allow raw borrows if there are any deref adjustments. - // - // const VAL: (i32,) = (0,); - // const REF: &(i32,) = &(0,); - // - // &raw const VAL.0; // ERROR - // &raw const REF.0; // OK, same as &raw const (*REF).0; - // - // This is maybe too permissive, since it allows - // `let u = &raw const Box::new((1,)).0`, which creates an - // immediately dangling raw pointer. - self.typeck_results - .borrow() - .adjustments() - .get(base.hir_id) - .map_or(false, |x| x.iter().any(|adj| matches!(adj.kind, Adjust::Deref(_)))) - }); - if !is_named { - self.tcx.sess.emit_err(AddressOfTemporaryTaken { span: oprnd.span }); - } - } - - fn check_lang_item_path( - &self, - lang_item: hir::LangItem, - expr: &'tcx hir::Expr<'tcx>, - hir_id: Option<hir::HirId>, - ) -> Ty<'tcx> { - self.resolve_lang_item_path(lang_item, expr.span, expr.hir_id, hir_id).1 - } - - pub(crate) fn check_expr_path( - &self, - qpath: &'tcx hir::QPath<'tcx>, - expr: &'tcx hir::Expr<'tcx>, - args: &'tcx [hir::Expr<'tcx>], - ) -> Ty<'tcx> { - let tcx = self.tcx; - let (res, opt_ty, segs) = - self.resolve_ty_and_res_fully_qualified_call(qpath, expr.hir_id, expr.span); - let ty = match res { - Res::Err => { - self.set_tainted_by_errors(); - tcx.ty_error() - } - Res::Def(DefKind::Ctor(_, CtorKind::Fictive), _) => { - report_unexpected_variant_res(tcx, res, qpath, expr.span); - tcx.ty_error() - } - _ => self.instantiate_value_path(segs, opt_ty, res, expr.span, expr.hir_id).0, - }; - - if let ty::FnDef(did, ..) = *ty.kind() { - let fn_sig = ty.fn_sig(tcx); - if tcx.fn_sig(did).abi() == RustIntrinsic && tcx.item_name(did) == sym::transmute { - let from = fn_sig.inputs().skip_binder()[0]; - let to = fn_sig.output().skip_binder(); - // We defer the transmute to the end of typeck, once all inference vars have - // been resolved or we errored. This is important as we can only check transmute - // on concrete types, but the output type may not be known yet (it would only - // be known if explicitly specified via turbofish). - self.deferred_transmute_checks.borrow_mut().push((from, to, expr.span)); - } - if !tcx.features().unsized_fn_params { - // We want to remove some Sized bounds from std functions, - // but don't want to expose the removal to stable Rust. - // i.e., we don't want to allow - // - // ```rust - // drop as fn(str); - // ``` - // - // to work in stable even if the Sized bound on `drop` is relaxed. - for i in 0..fn_sig.inputs().skip_binder().len() { - // We just want to check sizedness, so instead of introducing - // placeholder lifetimes with probing, we just replace higher lifetimes - // with fresh vars. - let span = args.get(i).map(|a| a.span).unwrap_or(expr.span); - let input = self.replace_bound_vars_with_fresh_vars( - span, - infer::LateBoundRegionConversionTime::FnCall, - fn_sig.input(i), - ); - self.require_type_is_sized_deferred( - input, - span, - traits::SizedArgumentType(None), - ); - } - } - // Here we want to prevent struct constructors from returning unsized types. - // There were two cases this happened: fn pointer coercion in stable - // and usual function call in presence of unsized_locals. - // Also, as we just want to check sizedness, instead of introducing - // placeholder lifetimes with probing, we just replace higher lifetimes - // with fresh vars. - let output = self.replace_bound_vars_with_fresh_vars( - expr.span, - infer::LateBoundRegionConversionTime::FnCall, - fn_sig.output(), - ); - self.require_type_is_sized_deferred(output, expr.span, traits::SizedReturnType); - } - - // We always require that the type provided as the value for - // a type parameter outlives the moment of instantiation. - let substs = self.typeck_results.borrow().node_substs(expr.hir_id); - self.add_wf_bounds(substs, expr); - - ty - } - - fn check_expr_break( - &self, - destination: hir::Destination, - expr_opt: Option<&'tcx hir::Expr<'tcx>>, - expr: &'tcx hir::Expr<'tcx>, - ) -> Ty<'tcx> { - let tcx = self.tcx; - if let Ok(target_id) = destination.target_id { - let (e_ty, cause); - if let Some(e) = expr_opt { - // If this is a break with a value, we need to type-check - // the expression. Get an expected type from the loop context. - let opt_coerce_to = { - // We should release `enclosing_breakables` before the `check_expr_with_hint` - // below, so can't move this block of code to the enclosing scope and share - // `ctxt` with the second `enclosing_breakables` borrow below. - let mut enclosing_breakables = self.enclosing_breakables.borrow_mut(); - match enclosing_breakables.opt_find_breakable(target_id) { - Some(ctxt) => ctxt.coerce.as_ref().map(|coerce| coerce.expected_ty()), - None => { - // Avoid ICE when `break` is inside a closure (#65383). - return tcx.ty_error_with_message( - expr.span, - "break was outside loop, but no error was emitted", - ); - } - } - }; - - // If the loop context is not a `loop { }`, then break with - // a value is illegal, and `opt_coerce_to` will be `None`. - // Just set expectation to error in that case. - let coerce_to = opt_coerce_to.unwrap_or_else(|| tcx.ty_error()); - - // Recurse without `enclosing_breakables` borrowed. - e_ty = self.check_expr_with_hint(e, coerce_to); - cause = self.misc(e.span); - } else { - // Otherwise, this is a break *without* a value. That's - // always legal, and is equivalent to `break ()`. - e_ty = tcx.mk_unit(); - cause = self.misc(expr.span); - } - - // Now that we have type-checked `expr_opt`, borrow - // the `enclosing_loops` field and let's coerce the - // type of `expr_opt` into what is expected. - let mut enclosing_breakables = self.enclosing_breakables.borrow_mut(); - let Some(ctxt) = enclosing_breakables.opt_find_breakable(target_id) else { - // Avoid ICE when `break` is inside a closure (#65383). - return tcx.ty_error_with_message( - expr.span, - "break was outside loop, but no error was emitted", - ); - }; - - if let Some(ref mut coerce) = ctxt.coerce { - if let Some(ref e) = expr_opt { - coerce.coerce(self, &cause, e, e_ty); - } else { - assert!(e_ty.is_unit()); - let ty = coerce.expected_ty(); - coerce.coerce_forced_unit( - self, - &cause, - &mut |mut err| { - self.suggest_mismatched_types_on_tail( - &mut err, expr, ty, e_ty, target_id, - ); - if let Some(val) = ty_kind_suggestion(ty) { - let label = destination - .label - .map(|l| format!(" {}", l.ident)) - .unwrap_or_else(String::new); - err.span_suggestion( - expr.span, - "give it a value of the expected type", - format!("break{label} {val}"), - Applicability::HasPlaceholders, - ); - } - }, - false, - ); - } - } else { - // If `ctxt.coerce` is `None`, we can just ignore - // the type of the expression. This is because - // either this was a break *without* a value, in - // which case it is always a legal type (`()`), or - // else an error would have been flagged by the - // `loops` pass for using break with an expression - // where you are not supposed to. - assert!(expr_opt.is_none() || self.tcx.sess.has_errors().is_some()); - } - - // If we encountered a `break`, then (no surprise) it may be possible to break from the - // loop... unless the value being returned from the loop diverges itself, e.g. - // `break return 5` or `break loop {}`. - ctxt.may_break |= !self.diverges.get().is_always(); - - // the type of a `break` is always `!`, since it diverges - tcx.types.never - } else { - // Otherwise, we failed to find the enclosing loop; - // this can only happen if the `break` was not - // inside a loop at all, which is caught by the - // loop-checking pass. - let err = self.tcx.ty_error_with_message( - expr.span, - "break was outside loop, but no error was emitted", - ); - - // We still need to assign a type to the inner expression to - // prevent the ICE in #43162. - if let Some(e) = expr_opt { - self.check_expr_with_hint(e, err); - - // ... except when we try to 'break rust;'. - // ICE this expression in particular (see #43162). - if let ExprKind::Path(QPath::Resolved(_, path)) = e.kind { - if path.segments.len() == 1 && path.segments[0].ident.name == sym::rust { - fatally_break_rust(self.tcx.sess); - } - } - } - - // There was an error; make type-check fail. - err - } - } - - fn check_expr_return( - &self, - expr_opt: Option<&'tcx hir::Expr<'tcx>>, - expr: &'tcx hir::Expr<'tcx>, - ) -> Ty<'tcx> { - if self.ret_coercion.is_none() { - let mut err = ReturnStmtOutsideOfFnBody { - span: expr.span, - encl_body_span: None, - encl_fn_span: None, - }; - - let encl_item_id = self.tcx.hir().get_parent_item(expr.hir_id); - - if let Some(hir::Node::Item(hir::Item { - kind: hir::ItemKind::Fn(..), - span: encl_fn_span, - .. - })) - | Some(hir::Node::TraitItem(hir::TraitItem { - kind: hir::TraitItemKind::Fn(_, hir::TraitFn::Provided(_)), - span: encl_fn_span, - .. - })) - | Some(hir::Node::ImplItem(hir::ImplItem { - kind: hir::ImplItemKind::Fn(..), - span: encl_fn_span, - .. - })) = self.tcx.hir().find_by_def_id(encl_item_id) - { - // We are inside a function body, so reporting "return statement - // outside of function body" needs an explanation. - - let encl_body_owner_id = self.tcx.hir().enclosing_body_owner(expr.hir_id); - - // If this didn't hold, we would not have to report an error in - // the first place. - assert_ne!(encl_item_id, encl_body_owner_id); - - let encl_body_id = self.tcx.hir().body_owned_by(encl_body_owner_id); - let encl_body = self.tcx.hir().body(encl_body_id); - - err.encl_body_span = Some(encl_body.value.span); - err.encl_fn_span = Some(*encl_fn_span); - } - - self.tcx.sess.emit_err(err); - - if let Some(e) = expr_opt { - // We still have to type-check `e` (issue #86188), but calling - // `check_return_expr` only works inside fn bodies. - self.check_expr(e); - } - } else if let Some(e) = expr_opt { - if self.ret_coercion_span.get().is_none() { - self.ret_coercion_span.set(Some(e.span)); - } - self.check_return_expr(e, true); - } else { - let mut coercion = self.ret_coercion.as_ref().unwrap().borrow_mut(); - if self.ret_coercion_span.get().is_none() { - self.ret_coercion_span.set(Some(expr.span)); - } - let cause = self.cause(expr.span, ObligationCauseCode::ReturnNoExpression); - if let Some((fn_decl, _)) = self.get_fn_decl(expr.hir_id) { - coercion.coerce_forced_unit( - self, - &cause, - &mut |db| { - let span = fn_decl.output.span(); - if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) { - db.span_label( - span, - format!("expected `{snippet}` because of this return type"), - ); - } - }, - true, - ); - } else { - coercion.coerce_forced_unit(self, &cause, &mut |_| (), true); - } - } - self.tcx.types.never - } - - /// `explicit_return` is `true` if we're checking an explicit `return expr`, - /// and `false` if we're checking a trailing expression. - pub(super) fn check_return_expr( - &self, - return_expr: &'tcx hir::Expr<'tcx>, - explicit_return: bool, - ) { - let ret_coercion = self.ret_coercion.as_ref().unwrap_or_else(|| { - span_bug!(return_expr.span, "check_return_expr called outside fn body") - }); - - let ret_ty = ret_coercion.borrow().expected_ty(); - let return_expr_ty = self.check_expr_with_hint(return_expr, ret_ty); - let mut span = return_expr.span; - // Use the span of the trailing expression for our cause, - // not the span of the entire function - if !explicit_return { - if let ExprKind::Block(body, _) = return_expr.kind && let Some(last_expr) = body.expr { - span = last_expr.span; - } - } - ret_coercion.borrow_mut().coerce( - self, - &self.cause(span, ObligationCauseCode::ReturnValue(return_expr.hir_id)), - return_expr, - return_expr_ty, - ); - - if self.return_type_has_opaque { - // Point any obligations that were registered due to opaque type - // inference at the return expression. - self.select_obligations_where_possible(false, |errors| { - self.point_at_return_for_opaque_ty_error(errors, span, return_expr_ty); - }); - } - } - - fn point_at_return_for_opaque_ty_error( - &self, - errors: &mut Vec<traits::FulfillmentError<'tcx>>, - span: Span, - return_expr_ty: Ty<'tcx>, - ) { - // Don't point at the whole block if it's empty - if span == self.tcx.hir().span(self.body_id) { - return; - } - for err in errors { - let cause = &mut err.obligation.cause; - if let ObligationCauseCode::OpaqueReturnType(None) = cause.code() { - let new_cause = ObligationCause::new( - cause.span, - cause.body_id, - ObligationCauseCode::OpaqueReturnType(Some((return_expr_ty, span))), - ); - *cause = new_cause; - } - } - } - - pub(crate) fn check_lhs_assignable( - &self, - lhs: &'tcx hir::Expr<'tcx>, - err_code: &'static str, - op_span: Span, - adjust_err: impl FnOnce(&mut Diagnostic), - ) { - if lhs.is_syntactic_place_expr() { - return; - } - - // FIXME: Make this use SessionDiagnostic once error codes can be dynamically set. - let mut err = self.tcx.sess.struct_span_err_with_code( - op_span, - "invalid left-hand side of assignment", - DiagnosticId::Error(err_code.into()), - ); - err.span_label(lhs.span, "cannot assign to this expression"); - - self.comes_from_while_condition(lhs.hir_id, |expr| { - err.span_suggestion_verbose( - expr.span.shrink_to_lo(), - "you might have meant to use pattern destructuring", - "let ", - Applicability::MachineApplicable, - ); - }); - - adjust_err(&mut err); - - err.emit(); - } - - // Check if an expression `original_expr_id` comes from the condition of a while loop, - // as opposed from the body of a while loop, which we can naively check by iterating - // parents until we find a loop... - pub(super) fn comes_from_while_condition( - &self, - original_expr_id: HirId, - then: impl FnOnce(&hir::Expr<'_>), - ) { - let mut parent = self.tcx.hir().get_parent_node(original_expr_id); - while let Some(node) = self.tcx.hir().find(parent) { - match node { - hir::Node::Expr(hir::Expr { - kind: - hir::ExprKind::Loop( - hir::Block { - expr: - Some(hir::Expr { - kind: - hir::ExprKind::Match(expr, ..) | hir::ExprKind::If(expr, ..), - .. - }), - .. - }, - _, - hir::LoopSource::While, - _, - ), - .. - }) => { - // Check if our original expression is a child of the condition of a while loop - let expr_is_ancestor = std::iter::successors(Some(original_expr_id), |id| { - self.tcx.hir().find_parent_node(*id) - }) - .take_while(|id| *id != parent) - .any(|id| id == expr.hir_id); - // if it is, then we have a situation like `while Some(0) = value.get(0) {`, - // where `while let` was more likely intended. - if expr_is_ancestor { - then(expr); - } - break; - } - hir::Node::Item(_) - | hir::Node::ImplItem(_) - | hir::Node::TraitItem(_) - | hir::Node::Crate(_) => break, - _ => { - parent = self.tcx.hir().get_parent_node(parent); - } - } - } - } - - // A generic function for checking the 'then' and 'else' clauses in an 'if' - // or 'if-else' expression. - fn check_then_else( - &self, - cond_expr: &'tcx hir::Expr<'tcx>, - then_expr: &'tcx hir::Expr<'tcx>, - opt_else_expr: Option<&'tcx hir::Expr<'tcx>>, - sp: Span, - orig_expected: Expectation<'tcx>, - ) -> Ty<'tcx> { - let cond_ty = self.check_expr_has_type_or_error(cond_expr, self.tcx.types.bool, |_| {}); - - self.warn_if_unreachable( - cond_expr.hir_id, - then_expr.span, - "block in `if` or `while` expression", - ); - - let cond_diverges = self.diverges.get(); - self.diverges.set(Diverges::Maybe); - - let expected = orig_expected.adjust_for_branches(self); - let then_ty = self.check_expr_with_expectation(then_expr, expected); - let then_diverges = self.diverges.get(); - self.diverges.set(Diverges::Maybe); - - // We've already taken the expected type's preferences - // into account when typing the `then` branch. To figure - // out the initial shot at a LUB, we thus only consider - // `expected` if it represents a *hard* constraint - // (`only_has_type`); otherwise, we just go with a - // fresh type variable. - let coerce_to_ty = expected.coercion_target_type(self, sp); - let mut coerce: DynamicCoerceMany<'_> = CoerceMany::new(coerce_to_ty); - - coerce.coerce(self, &self.misc(sp), then_expr, then_ty); - - if let Some(else_expr) = opt_else_expr { - let else_ty = self.check_expr_with_expectation(else_expr, expected); - let else_diverges = self.diverges.get(); - - let opt_suggest_box_span = self.opt_suggest_box_span(then_ty, else_ty, orig_expected); - let if_cause = self.if_cause( - sp, - cond_expr.span, - then_expr, - else_expr, - then_ty, - else_ty, - opt_suggest_box_span, - ); - - coerce.coerce(self, &if_cause, else_expr, else_ty); - - // We won't diverge unless both branches do (or the condition does). - self.diverges.set(cond_diverges | then_diverges & else_diverges); - } else { - self.if_fallback_coercion(sp, then_expr, &mut coerce); - - // If the condition is false we can't diverge. - self.diverges.set(cond_diverges); - } - - let result_ty = coerce.complete(self); - if cond_ty.references_error() { self.tcx.ty_error() } else { result_ty } - } - - /// Type check assignment expression `expr` of form `lhs = rhs`. - /// The expected type is `()` and is passed to the function for the purposes of diagnostics. - fn check_expr_assign( - &self, - expr: &'tcx hir::Expr<'tcx>, - expected: Expectation<'tcx>, - lhs: &'tcx hir::Expr<'tcx>, - rhs: &'tcx hir::Expr<'tcx>, - span: Span, - ) -> Ty<'tcx> { - let expected_ty = expected.coercion_target_type(self, expr.span); - if expected_ty == self.tcx.types.bool { - // The expected type is `bool` but this will result in `()` so we can reasonably - // say that the user intended to write `lhs == rhs` instead of `lhs = rhs`. - // The likely cause of this is `if foo = bar { .. }`. - let actual_ty = self.tcx.mk_unit(); - let mut err = self.demand_suptype_diag(expr.span, expected_ty, actual_ty).unwrap(); - let lhs_ty = self.check_expr(&lhs); - let rhs_ty = self.check_expr(&rhs); - let (applicability, eq) = if self.can_coerce(rhs_ty, lhs_ty) { - (Applicability::MachineApplicable, true) - } else { - (Applicability::MaybeIncorrect, false) - }; - if !lhs.is_syntactic_place_expr() - && lhs.is_approximately_pattern() - && !matches!(lhs.kind, hir::ExprKind::Lit(_)) - { - // Do not suggest `if let x = y` as `==` is way more likely to be the intention. - let hir = self.tcx.hir(); - if let hir::Node::Expr(hir::Expr { kind: ExprKind::If { .. }, .. }) = - hir.get(hir.get_parent_node(hir.get_parent_node(expr.hir_id))) - { - err.span_suggestion_verbose( - expr.span.shrink_to_lo(), - "you might have meant to use pattern matching", - "let ", - applicability, - ); - }; - } - if eq { - err.span_suggestion_verbose( - span, - "you might have meant to compare for equality", - "==", - applicability, - ); - } - - // If the assignment expression itself is ill-formed, don't - // bother emitting another error - if lhs_ty.references_error() || rhs_ty.references_error() { - err.delay_as_bug() - } else { - err.emit(); - } - return self.tcx.ty_error(); - } - - let lhs_ty = self.check_expr_with_needs(&lhs, Needs::MutPlace); - - let suggest_deref_binop = |err: &mut Diagnostic, rhs_ty: Ty<'tcx>| { - if let Some(lhs_deref_ty) = self.deref_once_mutably_for_diagnostic(lhs_ty) { - // Can only assign if the type is sized, so if `DerefMut` yields a type that is - // unsized, do not suggest dereferencing it. - let lhs_deref_ty_is_sized = self - .infcx - .type_implements_trait( - self.tcx.lang_items().sized_trait().unwrap(), - lhs_deref_ty, - ty::List::empty(), - self.param_env, - ) - .may_apply(); - if lhs_deref_ty_is_sized && self.can_coerce(rhs_ty, lhs_deref_ty) { - err.span_suggestion_verbose( - lhs.span.shrink_to_lo(), - "consider dereferencing here to assign to the mutably borrowed value", - "*", - Applicability::MachineApplicable, - ); - } - } - }; - - self.check_lhs_assignable(lhs, "E0070", span, |err| { - let rhs_ty = self.check_expr(&rhs); - suggest_deref_binop(err, rhs_ty); - }); - - // This is (basically) inlined `check_expr_coercable_to_type`, but we want - // to suggest an additional fixup here in `suggest_deref_binop`. - let rhs_ty = self.check_expr_with_hint(&rhs, lhs_ty); - if let (_, Some(mut diag)) = - self.demand_coerce_diag(rhs, rhs_ty, lhs_ty, Some(lhs), AllowTwoPhase::No) - { - suggest_deref_binop(&mut diag, rhs_ty); - diag.emit(); - } - - self.require_type_is_sized(lhs_ty, lhs.span, traits::AssignmentLhsSized); - - if lhs_ty.references_error() || rhs_ty.references_error() { - self.tcx.ty_error() - } else { - self.tcx.mk_unit() - } - } - - pub(super) fn check_expr_let(&self, let_expr: &'tcx hir::Let<'tcx>) -> Ty<'tcx> { - // for let statements, this is done in check_stmt - let init = let_expr.init; - self.warn_if_unreachable(init.hir_id, init.span, "block in `let` expression"); - // otherwise check exactly as a let statement - self.check_decl(let_expr.into()); - // but return a bool, for this is a boolean expression - self.tcx.types.bool - } - - fn check_expr_loop( - &self, - body: &'tcx hir::Block<'tcx>, - source: hir::LoopSource, - expected: Expectation<'tcx>, - expr: &'tcx hir::Expr<'tcx>, - ) -> Ty<'tcx> { - let coerce = match source { - // you can only use break with a value from a normal `loop { }` - hir::LoopSource::Loop => { - let coerce_to = expected.coercion_target_type(self, body.span); - Some(CoerceMany::new(coerce_to)) - } - - hir::LoopSource::While | hir::LoopSource::ForLoop => None, - }; - - let ctxt = BreakableCtxt { - coerce, - may_break: false, // Will get updated if/when we find a `break`. - }; - - let (ctxt, ()) = self.with_breakable_ctxt(expr.hir_id, ctxt, || { - self.check_block_no_value(&body); - }); - - if ctxt.may_break { - // No way to know whether it's diverging because - // of a `break` or an outer `break` or `return`. - self.diverges.set(Diverges::Maybe); - } - - // If we permit break with a value, then result type is - // the LUB of the breaks (possibly ! if none); else, it - // is nil. This makes sense because infinite loops - // (which would have type !) are only possible iff we - // permit break with a value [1]. - if ctxt.coerce.is_none() && !ctxt.may_break { - // [1] - self.tcx.sess.delay_span_bug(body.span, "no coercion, but loop may not break"); - } - ctxt.coerce.map(|c| c.complete(self)).unwrap_or_else(|| self.tcx.mk_unit()) - } - - /// Checks a method call. - fn check_method_call( - &self, - expr: &'tcx hir::Expr<'tcx>, - segment: &hir::PathSegment<'_>, - rcvr: &'tcx hir::Expr<'tcx>, - args: &'tcx [hir::Expr<'tcx>], - expected: Expectation<'tcx>, - ) -> Ty<'tcx> { - let rcvr_t = self.check_expr(&rcvr); - // no need to check for bot/err -- callee does that - let rcvr_t = self.structurally_resolved_type(rcvr.span, rcvr_t); - let span = segment.ident.span; - - let method = match self.lookup_method(rcvr_t, segment, span, expr, rcvr, args) { - Ok(method) => { - // We could add a "consider `foo::<params>`" suggestion here, but I wasn't able to - // trigger this codepath causing `structurally_resolved_type` to emit an error. - - self.write_method_call(expr.hir_id, method); - Ok(method) - } - Err(error) => { - if segment.ident.name != kw::Empty { - if let Some(mut err) = self.report_method_error( - span, - rcvr_t, - segment.ident, - SelfSource::MethodCall(rcvr), - error, - Some((rcvr, args)), - ) { - err.emit(); - } - } - Err(()) - } - }; - - // Call the generic checker. - self.check_method_argument_types(span, expr, method, &args, DontTupleArguments, expected) - } - - fn check_expr_cast( - &self, - e: &'tcx hir::Expr<'tcx>, - t: &'tcx hir::Ty<'tcx>, - expr: &'tcx hir::Expr<'tcx>, - ) -> Ty<'tcx> { - // Find the type of `e`. Supply hints based on the type we are casting to, - // if appropriate. - let t_cast = self.to_ty_saving_user_provided_ty(t); - let t_cast = self.resolve_vars_if_possible(t_cast); - let t_expr = self.check_expr_with_expectation(e, ExpectCastableToType(t_cast)); - let t_expr = self.resolve_vars_if_possible(t_expr); - - // Eagerly check for some obvious errors. - if t_expr.references_error() || t_cast.references_error() { - self.tcx.ty_error() - } else { - // Defer other checks until we're done type checking. - let mut deferred_cast_checks = self.deferred_cast_checks.borrow_mut(); - match cast::check_cast(self, e, t_expr, t_cast, t.span, expr.span) { - CastCheckResult::Ok => t_cast, - CastCheckResult::Deferred(cast_check) => { - debug!( - "check_expr_cast: deferring cast from {:?} to {:?}: {:?}", - t_cast, t_expr, cast_check, - ); - deferred_cast_checks.push(cast_check); - t_cast - } - CastCheckResult::Err(ErrorGuaranteed { .. }) => self.tcx.ty_error(), - } - } - } - - fn check_expr_array( - &self, - args: &'tcx [hir::Expr<'tcx>], - expected: Expectation<'tcx>, - expr: &'tcx hir::Expr<'tcx>, - ) -> Ty<'tcx> { - let element_ty = if !args.is_empty() { - let coerce_to = expected - .to_option(self) - .and_then(|uty| match *uty.kind() { - ty::Array(ty, _) | ty::Slice(ty) => Some(ty), - _ => None, - }) - .unwrap_or_else(|| { - self.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::TypeInference, - span: expr.span, - }) - }); - let mut coerce = CoerceMany::with_coercion_sites(coerce_to, args); - assert_eq!(self.diverges.get(), Diverges::Maybe); - for e in args { - let e_ty = self.check_expr_with_hint(e, coerce_to); - let cause = self.misc(e.span); - coerce.coerce(self, &cause, e, e_ty); - } - coerce.complete(self) - } else { - self.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::TypeInference, - span: expr.span, - }) - }; - let array_len = args.len() as u64; - self.suggest_array_len(expr, array_len); - self.tcx.mk_array(element_ty, array_len) - } - - fn suggest_array_len(&self, expr: &'tcx hir::Expr<'tcx>, array_len: u64) { - let parent_node = self.tcx.hir().parent_iter(expr.hir_id).find(|(_, node)| { - !matches!(node, hir::Node::Expr(hir::Expr { kind: hir::ExprKind::AddrOf(..), .. })) - }); - let Some((_, - hir::Node::Local(hir::Local { ty: Some(ty), .. }) - | hir::Node::Item(hir::Item { kind: hir::ItemKind::Const(ty, _), .. })) - ) = parent_node else { - return - }; - if let hir::TyKind::Array(_, length) = ty.peel_refs().kind - && let hir::ArrayLen::Body(hir::AnonConst { hir_id, .. }) = length - && let Some(span) = self.tcx.hir().opt_span(hir_id) - { - match self.tcx.sess.diagnostic().steal_diagnostic(span, StashKey::UnderscoreForArrayLengths) { - Some(mut err) => { - err.span_suggestion( - span, - "consider specifying the array length", - array_len, - Applicability::MaybeIncorrect, - ); - err.emit(); - } - None => () - } - } - } - - fn check_expr_const_block( - &self, - anon_const: &'tcx hir::AnonConst, - expected: Expectation<'tcx>, - _expr: &'tcx hir::Expr<'tcx>, - ) -> Ty<'tcx> { - let body = self.tcx.hir().body(anon_const.body); - - // Create a new function context. - let fcx = FnCtxt::new(self, self.param_env.with_const(), body.value.hir_id); - crate::check::GatherLocalsVisitor::new(&fcx).visit_body(body); - - let ty = fcx.check_expr_with_expectation(&body.value, expected); - fcx.require_type_is_sized(ty, body.value.span, traits::ConstSized); - fcx.write_ty(anon_const.hir_id, ty); - ty - } - - fn check_expr_repeat( - &self, - element: &'tcx hir::Expr<'tcx>, - count: &'tcx hir::ArrayLen, - expected: Expectation<'tcx>, - expr: &'tcx hir::Expr<'tcx>, - ) -> Ty<'tcx> { - let tcx = self.tcx; - let count = self.array_length_to_const(count); - if let Some(count) = count.try_eval_usize(tcx, self.param_env) { - self.suggest_array_len(expr, count); - } - - let uty = match expected { - ExpectHasType(uty) => match *uty.kind() { - ty::Array(ty, _) | ty::Slice(ty) => Some(ty), - _ => None, - }, - _ => None, - }; - - let (element_ty, t) = match uty { - Some(uty) => { - self.check_expr_coercable_to_type(&element, uty, None); - (uty, uty) - } - None => { - let ty = self.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::MiscVariable, - span: element.span, - }); - let element_ty = self.check_expr_has_type_or_error(&element, ty, |_| {}); - (element_ty, ty) - } - }; - - if element_ty.references_error() { - return tcx.ty_error(); - } - - self.check_repeat_element_needs_copy_bound(element, count, element_ty); - - tcx.mk_ty(ty::Array(t, count)) - } - - fn check_repeat_element_needs_copy_bound( - &self, - element: &hir::Expr<'_>, - count: ty::Const<'tcx>, - element_ty: Ty<'tcx>, - ) { - let tcx = self.tcx; - // Actual constants as the repeat element get inserted repeatedly instead of getting copied via Copy. - match &element.kind { - hir::ExprKind::ConstBlock(..) => return, - hir::ExprKind::Path(qpath) => { - let res = self.typeck_results.borrow().qpath_res(qpath, element.hir_id); - if let Res::Def(DefKind::Const | DefKind::AssocConst | DefKind::AnonConst, _) = res - { - return; - } - } - _ => {} - } - // If someone calls a const fn, they can extract that call out into a separate constant (or a const - // block in the future), so we check that to tell them that in the diagnostic. Does not affect typeck. - let is_const_fn = match element.kind { - hir::ExprKind::Call(func, _args) => match *self.node_ty(func.hir_id).kind() { - ty::FnDef(def_id, _) => tcx.is_const_fn(def_id), - _ => false, - }, - _ => false, - }; - - // If the length is 0, we don't create any elements, so we don't copy any. If the length is 1, we - // don't copy that one element, we move it. Only check for Copy if the length is larger. - if count.try_eval_usize(tcx, self.param_env).map_or(true, |len| len > 1) { - let lang_item = self.tcx.require_lang_item(LangItem::Copy, None); - let code = traits::ObligationCauseCode::RepeatElementCopy { is_const_fn }; - self.require_type_meets(element_ty, element.span, code, lang_item); - } - } - - fn check_expr_tuple( - &self, - elts: &'tcx [hir::Expr<'tcx>], - expected: Expectation<'tcx>, - expr: &'tcx hir::Expr<'tcx>, - ) -> Ty<'tcx> { - let flds = expected.only_has_type(self).and_then(|ty| { - let ty = self.resolve_vars_with_obligations(ty); - match ty.kind() { - ty::Tuple(flds) => Some(&flds[..]), - _ => None, - } - }); - - let elt_ts_iter = elts.iter().enumerate().map(|(i, e)| match flds { - Some(fs) if i < fs.len() => { - let ety = fs[i]; - self.check_expr_coercable_to_type(&e, ety, None); - ety - } - _ => self.check_expr_with_expectation(&e, NoExpectation), - }); - let tuple = self.tcx.mk_tup(elt_ts_iter); - if tuple.references_error() { - self.tcx.ty_error() - } else { - self.require_type_is_sized(tuple, expr.span, traits::TupleInitializerSized); - tuple - } - } - - fn check_expr_struct( - &self, - expr: &hir::Expr<'_>, - expected: Expectation<'tcx>, - qpath: &QPath<'_>, - fields: &'tcx [hir::ExprField<'tcx>], - base_expr: &'tcx Option<&'tcx hir::Expr<'tcx>>, - ) -> Ty<'tcx> { - // Find the relevant variant - let Some((variant, adt_ty)) = self.check_struct_path(qpath, expr.hir_id) else { - self.check_struct_fields_on_error(fields, base_expr); - return self.tcx.ty_error(); - }; - - // Prohibit struct expressions when non-exhaustive flag is set. - let adt = adt_ty.ty_adt_def().expect("`check_struct_path` returned non-ADT type"); - if !adt.did().is_local() && variant.is_field_list_non_exhaustive() { - self.tcx - .sess - .emit_err(StructExprNonExhaustive { span: expr.span, what: adt.variant_descr() }); - } - - self.check_expr_struct_fields( - adt_ty, - expected, - expr.hir_id, - qpath.span(), - variant, - fields, - base_expr, - expr.span, - ); - - self.require_type_is_sized(adt_ty, expr.span, traits::StructInitializerSized); - adt_ty - } - - fn check_expr_struct_fields( - &self, - adt_ty: Ty<'tcx>, - expected: Expectation<'tcx>, - expr_id: hir::HirId, - span: Span, - variant: &'tcx ty::VariantDef, - ast_fields: &'tcx [hir::ExprField<'tcx>], - base_expr: &'tcx Option<&'tcx hir::Expr<'tcx>>, - expr_span: Span, - ) { - let tcx = self.tcx; - - let expected_inputs = - self.expected_inputs_for_expected_output(span, expected, adt_ty, &[adt_ty]); - let adt_ty_hint = if let Some(expected_inputs) = expected_inputs { - expected_inputs.get(0).cloned().unwrap_or(adt_ty) - } else { - adt_ty - }; - // re-link the regions that EIfEO can erase. - self.demand_eqtype(span, adt_ty_hint, adt_ty); - - let ty::Adt(adt, substs) = adt_ty.kind() else { - span_bug!(span, "non-ADT passed to check_expr_struct_fields"); - }; - let adt_kind = adt.adt_kind(); - - let mut remaining_fields = variant - .fields - .iter() - .enumerate() - .map(|(i, field)| (field.ident(tcx).normalize_to_macros_2_0(), (i, field))) - .collect::<FxHashMap<_, _>>(); - - let mut seen_fields = FxHashMap::default(); - - let mut error_happened = false; - - // Type-check each field. - for (idx, field) in ast_fields.iter().enumerate() { - let ident = tcx.adjust_ident(field.ident, variant.def_id); - let field_type = if let Some((i, v_field)) = remaining_fields.remove(&ident) { - seen_fields.insert(ident, field.span); - self.write_field_index(field.hir_id, i); - - // We don't look at stability attributes on - // struct-like enums (yet...), but it's definitely not - // a bug to have constructed one. - if adt_kind != AdtKind::Enum { - tcx.check_stability(v_field.did, Some(expr_id), field.span, None); - } - - self.field_ty(field.span, v_field, substs) - } else { - error_happened = true; - if let Some(prev_span) = seen_fields.get(&ident) { - tcx.sess.emit_err(FieldMultiplySpecifiedInInitializer { - span: field.ident.span, - prev_span: *prev_span, - ident, - }); - } else { - self.report_unknown_field( - adt_ty, - variant, - field, - ast_fields, - adt.variant_descr(), - expr_span, - ); - } - - tcx.ty_error() - }; - - // Make sure to give a type to the field even if there's - // an error, so we can continue type-checking. - let ty = self.check_expr_with_hint(&field.expr, field_type); - let (_, diag) = - self.demand_coerce_diag(&field.expr, ty, field_type, None, AllowTwoPhase::No); - - if let Some(mut diag) = diag { - if idx == ast_fields.len() - 1 && remaining_fields.is_empty() { - self.suggest_fru_from_range(field, variant, substs, &mut diag); - } - diag.emit(); - } - } - - // Make sure the programmer specified correct number of fields. - if adt_kind == AdtKind::Union { - if ast_fields.len() != 1 { - struct_span_err!( - tcx.sess, - span, - E0784, - "union expressions should have exactly one field", - ) - .emit(); - } - } - - // If check_expr_struct_fields hit an error, do not attempt to populate - // the fields with the base_expr. This could cause us to hit errors later - // when certain fields are assumed to exist that in fact do not. - if error_happened { - return; - } - - if let Some(base_expr) = base_expr { - // FIXME: We are currently creating two branches here in order to maintain - // consistency. But they should be merged as much as possible. - let fru_tys = if self.tcx.features().type_changing_struct_update { - if adt.is_struct() { - // Make some fresh substitutions for our ADT type. - let fresh_substs = self.fresh_substs_for_item(base_expr.span, adt.did()); - // We do subtyping on the FRU fields first, so we can - // learn exactly what types we expect the base expr - // needs constrained to be compatible with the struct - // type we expect from the expectation value. - let fru_tys = variant - .fields - .iter() - .map(|f| { - let fru_ty = self.normalize_associated_types_in( - expr_span, - self.field_ty(base_expr.span, f, fresh_substs), - ); - let ident = self.tcx.adjust_ident(f.ident(self.tcx), variant.def_id); - if let Some(_) = remaining_fields.remove(&ident) { - let target_ty = self.field_ty(base_expr.span, f, substs); - let cause = self.misc(base_expr.span); - match self.at(&cause, self.param_env).sup(target_ty, fru_ty) { - Ok(InferOk { obligations, value: () }) => { - self.register_predicates(obligations) - } - Err(_) => { - // This should never happen, since we're just subtyping the - // remaining_fields, but it's fine to emit this, I guess. - self.report_mismatched_types( - &cause, - target_ty, - fru_ty, - FieldMisMatch(variant.name, ident.name), - ) - .emit(); - } - } - } - self.resolve_vars_if_possible(fru_ty) - }) - .collect(); - // The use of fresh substs that we have subtyped against - // our base ADT type's fields allows us to guide inference - // along so that, e.g. - // ``` - // MyStruct<'a, F1, F2, const C: usize> { - // f: F1, - // // Other fields that reference `'a`, `F2`, and `C` - // } - // - // let x = MyStruct { - // f: 1usize, - // ..other_struct - // }; - // ``` - // will have the `other_struct` expression constrained to - // `MyStruct<'a, _, F2, C>`, as opposed to just `_`... - // This is important to allow coercions to happen in - // `other_struct` itself. See `coerce-in-base-expr.rs`. - let fresh_base_ty = self.tcx.mk_adt(*adt, fresh_substs); - self.check_expr_has_type_or_error( - base_expr, - self.resolve_vars_if_possible(fresh_base_ty), - |_| {}, - ); - fru_tys - } else { - // Check the base_expr, regardless of a bad expected adt_ty, so we can get - // type errors on that expression, too. - self.check_expr(base_expr); - self.tcx - .sess - .emit_err(FunctionalRecordUpdateOnNonStruct { span: base_expr.span }); - return; - } - } else { - self.check_expr_has_type_or_error(base_expr, adt_ty, |_| { - let base_ty = self.typeck_results.borrow().expr_ty(*base_expr); - let same_adt = match (adt_ty.kind(), base_ty.kind()) { - (ty::Adt(adt, _), ty::Adt(base_adt, _)) if adt == base_adt => true, - _ => false, - }; - if self.tcx.sess.is_nightly_build() && same_adt { - feature_err( - &self.tcx.sess.parse_sess, - sym::type_changing_struct_update, - base_expr.span, - "type changing struct updating is experimental", - ) - .emit(); - } - }); - match adt_ty.kind() { - ty::Adt(adt, substs) if adt.is_struct() => variant - .fields - .iter() - .map(|f| { - self.normalize_associated_types_in(expr_span, f.ty(self.tcx, substs)) - }) - .collect(), - _ => { - self.tcx - .sess - .emit_err(FunctionalRecordUpdateOnNonStruct { span: base_expr.span }); - return; - } - } - }; - self.typeck_results.borrow_mut().fru_field_types_mut().insert(expr_id, fru_tys); - } else if adt_kind != AdtKind::Union && !remaining_fields.is_empty() { - debug!(?remaining_fields); - let private_fields: Vec<&ty::FieldDef> = variant - .fields - .iter() - .filter(|field| !field.vis.is_accessible_from(tcx.parent_module(expr_id), tcx)) - .collect(); - - if !private_fields.is_empty() { - self.report_private_fields(adt_ty, span, private_fields, ast_fields); - } else { - self.report_missing_fields( - adt_ty, - span, - remaining_fields, - variant, - ast_fields, - substs, - ); - } - } - } - - fn check_struct_fields_on_error( - &self, - fields: &'tcx [hir::ExprField<'tcx>], - base_expr: &'tcx Option<&'tcx hir::Expr<'tcx>>, - ) { - for field in fields { - self.check_expr(&field.expr); - } - if let Some(base) = *base_expr { - self.check_expr(&base); - } - } - - /// Report an error for a struct field expression when there are fields which aren't provided. - /// - /// ```text - /// error: missing field `you_can_use_this_field` in initializer of `foo::Foo` - /// --> src/main.rs:8:5 - /// | - /// 8 | foo::Foo {}; - /// | ^^^^^^^^ missing `you_can_use_this_field` - /// - /// error: aborting due to previous error - /// ``` - fn report_missing_fields( - &self, - adt_ty: Ty<'tcx>, - span: Span, - remaining_fields: FxHashMap<Ident, (usize, &ty::FieldDef)>, - variant: &'tcx ty::VariantDef, - ast_fields: &'tcx [hir::ExprField<'tcx>], - substs: SubstsRef<'tcx>, - ) { - let len = remaining_fields.len(); - - let mut displayable_field_names: Vec<&str> = - remaining_fields.keys().map(|ident| ident.as_str()).collect(); - // sorting &str primitives here, sort_unstable is ok - displayable_field_names.sort_unstable(); - - let mut truncated_fields_error = String::new(); - let remaining_fields_names = match &displayable_field_names[..] { - [field1] => format!("`{}`", field1), - [field1, field2] => format!("`{field1}` and `{field2}`"), - [field1, field2, field3] => format!("`{field1}`, `{field2}` and `{field3}`"), - _ => { - truncated_fields_error = - format!(" and {} other field{}", len - 3, pluralize!(len - 3)); - displayable_field_names - .iter() - .take(3) - .map(|n| format!("`{n}`")) - .collect::<Vec<_>>() - .join(", ") - } - }; - - let mut err = struct_span_err!( - self.tcx.sess, - span, - E0063, - "missing field{} {}{} in initializer of `{}`", - pluralize!(len), - remaining_fields_names, - truncated_fields_error, - adt_ty - ); - err.span_label(span, format!("missing {remaining_fields_names}{truncated_fields_error}")); - - if let Some(last) = ast_fields.last() { - self.suggest_fru_from_range(last, variant, substs, &mut err); - } - - err.emit(); - } - - /// If the last field is a range literal, but it isn't supposed to be, then they probably - /// meant to use functional update syntax. - fn suggest_fru_from_range( - &self, - last_expr_field: &hir::ExprField<'tcx>, - variant: &ty::VariantDef, - substs: SubstsRef<'tcx>, - err: &mut Diagnostic, - ) { - // I don't use 'is_range_literal' because only double-sided, half-open ranges count. - if let ExprKind::Struct( - QPath::LangItem(LangItem::Range, ..), - &[ref range_start, ref range_end], - _, - ) = last_expr_field.expr.kind - && let variant_field = - variant.fields.iter().find(|field| field.ident(self.tcx) == last_expr_field.ident) - && let range_def_id = self.tcx.lang_items().range_struct() - && variant_field - .and_then(|field| field.ty(self.tcx, substs).ty_adt_def()) - .map(|adt| adt.did()) - != range_def_id - { - let instead = self - .tcx - .sess - .source_map() - .span_to_snippet(range_end.expr.span) - .map(|s| format!(" from `{s}`")) - .unwrap_or_default(); - err.span_suggestion( - range_start.span.shrink_to_hi(), - &format!("to set the remaining fields{instead}, separate the last named field with a comma"), - ",", - Applicability::MaybeIncorrect, - ); - } - } - - /// Report an error for a struct field expression when there are invisible fields. - /// - /// ```text - /// error: cannot construct `Foo` with struct literal syntax due to private fields - /// --> src/main.rs:8:5 - /// | - /// 8 | foo::Foo {}; - /// | ^^^^^^^^ - /// - /// error: aborting due to previous error - /// ``` - fn report_private_fields( - &self, - adt_ty: Ty<'tcx>, - span: Span, - private_fields: Vec<&ty::FieldDef>, - used_fields: &'tcx [hir::ExprField<'tcx>], - ) { - let mut err = self.tcx.sess.struct_span_err( - span, - &format!( - "cannot construct `{adt_ty}` with struct literal syntax due to private fields", - ), - ); - let (used_private_fields, remaining_private_fields): ( - Vec<(Symbol, Span, bool)>, - Vec<(Symbol, Span, bool)>, - ) = private_fields - .iter() - .map(|field| { - match used_fields.iter().find(|used_field| field.name == used_field.ident.name) { - Some(used_field) => (field.name, used_field.span, true), - None => (field.name, self.tcx.def_span(field.did), false), - } - }) - .partition(|field| field.2); - err.span_labels(used_private_fields.iter().map(|(_, span, _)| *span), "private field"); - if !remaining_private_fields.is_empty() { - let remaining_private_fields_len = remaining_private_fields.len(); - let names = match &remaining_private_fields - .iter() - .map(|(name, _, _)| name) - .collect::<Vec<_>>()[..] - { - _ if remaining_private_fields_len > 6 => String::new(), - [name] => format!("`{name}` "), - [names @ .., last] => { - let names = names.iter().map(|name| format!("`{name}`")).collect::<Vec<_>>(); - format!("{} and `{last}` ", names.join(", ")) - } - [] => unreachable!(), - }; - err.note(format!( - "... and other private field{s} {names}that {were} not provided", - s = pluralize!(remaining_private_fields_len), - were = pluralize!("was", remaining_private_fields_len), - )); - } - err.emit(); - } - - fn report_unknown_field( - &self, - ty: Ty<'tcx>, - variant: &'tcx ty::VariantDef, - field: &hir::ExprField<'_>, - skip_fields: &[hir::ExprField<'_>], - kind_name: &str, - expr_span: Span, - ) { - if variant.is_recovered() { - self.set_tainted_by_errors(); - return; - } - let mut err = self.type_error_struct_with_diag( - field.ident.span, - |actual| match ty.kind() { - ty::Adt(adt, ..) if adt.is_enum() => struct_span_err!( - self.tcx.sess, - field.ident.span, - E0559, - "{} `{}::{}` has no field named `{}`", - kind_name, - actual, - variant.name, - field.ident - ), - _ => struct_span_err!( - self.tcx.sess, - field.ident.span, - E0560, - "{} `{}` has no field named `{}`", - kind_name, - actual, - field.ident - ), - }, - ty, - ); - - let variant_ident_span = self.tcx.def_ident_span(variant.def_id).unwrap(); - match variant.ctor_kind { - CtorKind::Fn => match ty.kind() { - ty::Adt(adt, ..) if adt.is_enum() => { - err.span_label( - variant_ident_span, - format!( - "`{adt}::{variant}` defined here", - adt = ty, - variant = variant.name, - ), - ); - err.span_label(field.ident.span, "field does not exist"); - err.span_suggestion_verbose( - expr_span, - &format!( - "`{adt}::{variant}` is a tuple {kind_name}, use the appropriate syntax", - adt = ty, - variant = variant.name, - ), - format!( - "{adt}::{variant}(/* fields */)", - adt = ty, - variant = variant.name, - ), - Applicability::HasPlaceholders, - ); - } - _ => { - err.span_label(variant_ident_span, format!("`{adt}` defined here", adt = ty)); - err.span_label(field.ident.span, "field does not exist"); - err.span_suggestion_verbose( - expr_span, - &format!( - "`{adt}` is a tuple {kind_name}, use the appropriate syntax", - adt = ty, - kind_name = kind_name, - ), - format!("{adt}(/* fields */)", adt = ty), - Applicability::HasPlaceholders, - ); - } - }, - _ => { - // prevent all specified fields from being suggested - let skip_fields = skip_fields.iter().map(|x| x.ident.name); - if let Some(field_name) = self.suggest_field_name( - variant, - field.ident.name, - skip_fields.collect(), - expr_span, - ) { - err.span_suggestion( - field.ident.span, - "a field with a similar name exists", - field_name, - Applicability::MaybeIncorrect, - ); - } else { - match ty.kind() { - ty::Adt(adt, ..) => { - if adt.is_enum() { - err.span_label( - field.ident.span, - format!("`{}::{}` does not have this field", ty, variant.name), - ); - } else { - err.span_label( - field.ident.span, - format!("`{ty}` does not have this field"), - ); - } - let available_field_names = - self.available_field_names(variant, expr_span); - if !available_field_names.is_empty() { - err.note(&format!( - "available fields are: {}", - self.name_series_display(available_field_names) - )); - } - } - _ => bug!("non-ADT passed to report_unknown_field"), - } - }; - } - } - err.emit(); - } - - // Return a hint about the closest match in field names - fn suggest_field_name( - &self, - variant: &'tcx ty::VariantDef, - field: Symbol, - skip: Vec<Symbol>, - // The span where stability will be checked - span: Span, - ) -> Option<Symbol> { - let names = variant - .fields - .iter() - .filter_map(|field| { - // ignore already set fields and private fields from non-local crates - // and unstable fields. - if skip.iter().any(|&x| x == field.name) - || (!variant.def_id.is_local() && !field.vis.is_public()) - || matches!( - self.tcx.eval_stability(field.did, None, span, None), - stability::EvalResult::Deny { .. } - ) - { - None - } else { - Some(field.name) - } - }) - .collect::<Vec<Symbol>>(); - - find_best_match_for_name(&names, field, None) - } - - fn available_field_names( - &self, - variant: &'tcx ty::VariantDef, - access_span: Span, - ) -> Vec<Symbol> { - variant - .fields - .iter() - .filter(|field| { - let def_scope = self - .tcx - .adjust_ident_and_get_scope(field.ident(self.tcx), variant.def_id, self.body_id) - .1; - field.vis.is_accessible_from(def_scope, self.tcx) - && !matches!( - self.tcx.eval_stability(field.did, None, access_span, None), - stability::EvalResult::Deny { .. } - ) - }) - .filter(|field| !self.tcx.is_doc_hidden(field.did)) - .map(|field| field.name) - .collect() - } - - fn name_series_display(&self, names: Vec<Symbol>) -> String { - // dynamic limit, to never omit just one field - let limit = if names.len() == 6 { 6 } else { 5 }; - let mut display = - names.iter().take(limit).map(|n| format!("`{}`", n)).collect::<Vec<_>>().join(", "); - if names.len() > limit { - display = format!("{} ... and {} others", display, names.len() - limit); - } - display - } - - // Check field access expressions - fn check_field( - &self, - expr: &'tcx hir::Expr<'tcx>, - base: &'tcx hir::Expr<'tcx>, - field: Ident, - ) -> Ty<'tcx> { - debug!("check_field(expr: {:?}, base: {:?}, field: {:?})", expr, base, field); - let base_ty = self.check_expr(base); - let base_ty = self.structurally_resolved_type(base.span, base_ty); - let mut private_candidate = None; - let mut autoderef = self.autoderef(expr.span, base_ty); - while let Some((deref_base_ty, _)) = autoderef.next() { - debug!("deref_base_ty: {:?}", deref_base_ty); - match deref_base_ty.kind() { - ty::Adt(base_def, substs) if !base_def.is_enum() => { - debug!("struct named {:?}", deref_base_ty); - let (ident, def_scope) = - self.tcx.adjust_ident_and_get_scope(field, base_def.did(), self.body_id); - let fields = &base_def.non_enum_variant().fields; - if let Some(index) = fields - .iter() - .position(|f| f.ident(self.tcx).normalize_to_macros_2_0() == ident) - { - let field = &fields[index]; - let field_ty = self.field_ty(expr.span, field, substs); - // Save the index of all fields regardless of their visibility in case - // of error recovery. - self.write_field_index(expr.hir_id, index); - let adjustments = self.adjust_steps(&autoderef); - if field.vis.is_accessible_from(def_scope, self.tcx) { - self.apply_adjustments(base, adjustments); - self.register_predicates(autoderef.into_obligations()); - - self.tcx.check_stability(field.did, Some(expr.hir_id), expr.span, None); - return field_ty; - } - private_candidate = Some((adjustments, base_def.did(), field_ty)); - } - } - ty::Tuple(tys) => { - let fstr = field.as_str(); - if let Ok(index) = fstr.parse::<usize>() { - if fstr == index.to_string() { - if let Some(&field_ty) = tys.get(index) { - let adjustments = self.adjust_steps(&autoderef); - self.apply_adjustments(base, adjustments); - self.register_predicates(autoderef.into_obligations()); - - self.write_field_index(expr.hir_id, index); - return field_ty; - } - } - } - } - _ => {} - } - } - self.structurally_resolved_type(autoderef.span(), autoderef.final_ty(false)); - - if let Some((adjustments, did, field_ty)) = private_candidate { - // (#90483) apply adjustments to avoid ExprUseVisitor from - // creating erroneous projection. - self.apply_adjustments(base, adjustments); - self.ban_private_field_access(expr, base_ty, field, did); - return field_ty; - } - - if field.name == kw::Empty { - } else if self.method_exists(field, base_ty, expr.hir_id, true) { - self.ban_take_value_of_method(expr, base_ty, field); - } else if !base_ty.is_primitive_ty() { - self.ban_nonexisting_field(field, base, expr, base_ty); - } else { - let field_name = field.to_string(); - let mut err = type_error_struct!( - self.tcx().sess, - field.span, - base_ty, - E0610, - "`{base_ty}` is a primitive type and therefore doesn't have fields", - ); - let is_valid_suffix = |field: &str| { - if field == "f32" || field == "f64" { - return true; - } - let mut chars = field.chars().peekable(); - match chars.peek() { - Some('e') | Some('E') => { - chars.next(); - if let Some(c) = chars.peek() - && !c.is_numeric() && *c != '-' && *c != '+' - { - return false; - } - while let Some(c) = chars.peek() { - if !c.is_numeric() { - break; - } - chars.next(); - } - } - _ => (), - } - let suffix = chars.collect::<String>(); - suffix.is_empty() || suffix == "f32" || suffix == "f64" - }; - let maybe_partial_suffix = |field: &str| -> Option<&str> { - let first_chars = ['f', 'l']; - if field.len() >= 1 - && field.to_lowercase().starts_with(first_chars) - && field[1..].chars().all(|c| c.is_ascii_digit()) - { - if field.to_lowercase().starts_with(['f']) { Some("f32") } else { Some("f64") } - } else { - None - } - }; - if let ty::Infer(ty::IntVar(_)) = base_ty.kind() - && let ExprKind::Lit(Spanned { - node: ast::LitKind::Int(_, ast::LitIntType::Unsuffixed), - .. - }) = base.kind - && !base.span.from_expansion() - { - if is_valid_suffix(&field_name) { - err.span_suggestion_verbose( - field.span.shrink_to_lo(), - "if intended to be a floating point literal, consider adding a `0` after the period", - '0', - Applicability::MaybeIncorrect, - ); - } else if let Some(correct_suffix) = maybe_partial_suffix(&field_name) { - err.span_suggestion_verbose( - field.span, - format!("if intended to be a floating point literal, consider adding a `0` after the period and a `{correct_suffix}` suffix"), - format!("0{correct_suffix}"), - Applicability::MaybeIncorrect, - ); - } - } - err.emit(); - } - - self.tcx().ty_error() - } - - fn suggest_await_on_field_access( - &self, - err: &mut Diagnostic, - field_ident: Ident, - base: &'tcx hir::Expr<'tcx>, - ty: Ty<'tcx>, - ) { - let output_ty = match self.get_impl_future_output_ty(ty) { - Some(output_ty) => self.resolve_vars_if_possible(output_ty), - _ => return, - }; - let mut add_label = true; - if let ty::Adt(def, _) = output_ty.skip_binder().kind() { - // no field access on enum type - if !def.is_enum() { - if def - .non_enum_variant() - .fields - .iter() - .any(|field| field.ident(self.tcx) == field_ident) - { - add_label = false; - err.span_label( - field_ident.span, - "field not available in `impl Future`, but it is available in its `Output`", - ); - err.span_suggestion_verbose( - base.span.shrink_to_hi(), - "consider `await`ing on the `Future` and access the field of its `Output`", - ".await", - Applicability::MaybeIncorrect, - ); - } - } - } - if add_label { - err.span_label(field_ident.span, &format!("field not found in `{ty}`")); - } - } - - fn ban_nonexisting_field( - &self, - ident: Ident, - base: &'tcx hir::Expr<'tcx>, - expr: &'tcx hir::Expr<'tcx>, - base_ty: Ty<'tcx>, - ) { - debug!( - "ban_nonexisting_field: field={:?}, base={:?}, expr={:?}, base_ty={:?}", - ident, base, expr, base_ty - ); - let mut err = self.no_such_field_err(ident, base_ty, base.hir_id); - - match *base_ty.peel_refs().kind() { - ty::Array(_, len) => { - self.maybe_suggest_array_indexing(&mut err, expr, base, ident, len); - } - ty::RawPtr(..) => { - self.suggest_first_deref_field(&mut err, expr, base, ident); - } - ty::Adt(def, _) if !def.is_enum() => { - self.suggest_fields_on_recordish(&mut err, def, ident, expr.span); - } - ty::Param(param_ty) => { - self.point_at_param_definition(&mut err, param_ty); - } - ty::Opaque(_, _) => { - self.suggest_await_on_field_access(&mut err, ident, base, base_ty.peel_refs()); - } - _ => {} - } - - self.suggest_fn_call(&mut err, base, base_ty, |output_ty| { - if let ty::Adt(def, _) = output_ty.kind() && !def.is_enum() { - def.non_enum_variant().fields.iter().any(|field| { - field.ident(self.tcx) == ident - && field.vis.is_accessible_from(expr.hir_id.owner, self.tcx) - }) - } else if let ty::Tuple(tys) = output_ty.kind() - && let Ok(idx) = ident.as_str().parse::<usize>() - { - idx < tys.len() - } else { - false - } - }); - - if ident.name == kw::Await { - // We know by construction that `<expr>.await` is either on Rust 2015 - // or results in `ExprKind::Await`. Suggest switching the edition to 2018. - err.note("to `.await` a `Future`, switch to Rust 2018 or later"); - err.help_use_latest_edition(); - } - - err.emit(); - } - - fn ban_private_field_access( - &self, - expr: &hir::Expr<'_>, - expr_t: Ty<'tcx>, - field: Ident, - base_did: DefId, - ) { - let struct_path = self.tcx().def_path_str(base_did); - let kind_name = self.tcx().def_kind(base_did).descr(base_did); - let mut err = struct_span_err!( - self.tcx().sess, - field.span, - E0616, - "field `{field}` of {kind_name} `{struct_path}` is private", - ); - err.span_label(field.span, "private field"); - // Also check if an accessible method exists, which is often what is meant. - if self.method_exists(field, expr_t, expr.hir_id, false) && !self.expr_in_place(expr.hir_id) - { - self.suggest_method_call( - &mut err, - &format!("a method `{field}` also exists, call it with parentheses"), - field, - expr_t, - expr, - None, - ); - } - err.emit(); - } - - fn ban_take_value_of_method(&self, expr: &hir::Expr<'_>, expr_t: Ty<'tcx>, field: Ident) { - let mut err = type_error_struct!( - self.tcx().sess, - field.span, - expr_t, - E0615, - "attempted to take value of method `{field}` on type `{expr_t}`", - ); - err.span_label(field.span, "method, not a field"); - let expr_is_call = - if let hir::Node::Expr(hir::Expr { kind: ExprKind::Call(callee, _args), .. }) = - self.tcx.hir().get(self.tcx.hir().get_parent_node(expr.hir_id)) - { - expr.hir_id == callee.hir_id - } else { - false - }; - let expr_snippet = - self.tcx.sess.source_map().span_to_snippet(expr.span).unwrap_or_default(); - let is_wrapped = expr_snippet.starts_with('(') && expr_snippet.ends_with(')'); - let after_open = expr.span.lo() + rustc_span::BytePos(1); - let before_close = expr.span.hi() - rustc_span::BytePos(1); - - if expr_is_call && is_wrapped { - err.multipart_suggestion( - "remove wrapping parentheses to call the method", - vec![ - (expr.span.with_hi(after_open), String::new()), - (expr.span.with_lo(before_close), String::new()), - ], - Applicability::MachineApplicable, - ); - } else if !self.expr_in_place(expr.hir_id) { - // Suggest call parentheses inside the wrapping parentheses - let span = if is_wrapped { - expr.span.with_lo(after_open).with_hi(before_close) - } else { - expr.span - }; - self.suggest_method_call( - &mut err, - "use parentheses to call the method", - field, - expr_t, - expr, - Some(span), - ); - } else if let ty::RawPtr(ty_and_mut) = expr_t.kind() - && let ty::Adt(adt_def, _) = ty_and_mut.ty.kind() - && let ExprKind::Field(base_expr, _) = expr.kind - && adt_def.variants().len() == 1 - && adt_def - .variants() - .iter() - .next() - .unwrap() - .fields - .iter() - .any(|f| f.ident(self.tcx) == field) - { - err.multipart_suggestion( - "to access the field, dereference first", - vec![ - (base_expr.span.shrink_to_lo(), "(*".to_string()), - (base_expr.span.shrink_to_hi(), ")".to_string()), - ], - Applicability::MaybeIncorrect, - ); - } else { - err.help("methods are immutable and cannot be assigned to"); - } - - err.emit(); - } - - fn point_at_param_definition(&self, err: &mut Diagnostic, param: ty::ParamTy) { - let generics = self.tcx.generics_of(self.body_id.owner.to_def_id()); - let generic_param = generics.type_param(¶m, self.tcx); - if let ty::GenericParamDefKind::Type { synthetic: true, .. } = generic_param.kind { - return; - } - let param_def_id = generic_param.def_id; - let param_hir_id = match param_def_id.as_local() { - Some(x) => self.tcx.hir().local_def_id_to_hir_id(x), - None => return, - }; - let param_span = self.tcx.hir().span(param_hir_id); - let param_name = self.tcx.hir().ty_param_name(param_def_id.expect_local()); - - err.span_label(param_span, &format!("type parameter '{param_name}' declared here")); - } - - fn suggest_fields_on_recordish( - &self, - err: &mut Diagnostic, - def: ty::AdtDef<'tcx>, - field: Ident, - access_span: Span, - ) { - if let Some(suggested_field_name) = - self.suggest_field_name(def.non_enum_variant(), field.name, vec![], access_span) - { - err.span_suggestion( - field.span, - "a field with a similar name exists", - suggested_field_name, - Applicability::MaybeIncorrect, - ); - } else { - err.span_label(field.span, "unknown field"); - let struct_variant_def = def.non_enum_variant(); - let field_names = self.available_field_names(struct_variant_def, access_span); - if !field_names.is_empty() { - err.note(&format!( - "available fields are: {}", - self.name_series_display(field_names), - )); - } - } - } - - fn maybe_suggest_array_indexing( - &self, - err: &mut Diagnostic, - expr: &hir::Expr<'_>, - base: &hir::Expr<'_>, - field: Ident, - len: ty::Const<'tcx>, - ) { - if let (Some(len), Ok(user_index)) = - (len.try_eval_usize(self.tcx, self.param_env), field.as_str().parse::<u64>()) - && let Ok(base) = self.tcx.sess.source_map().span_to_snippet(base.span) - { - let help = "instead of using tuple indexing, use array indexing"; - let suggestion = format!("{base}[{field}]"); - let applicability = if len < user_index { - Applicability::MachineApplicable - } else { - Applicability::MaybeIncorrect - }; - err.span_suggestion(expr.span, help, suggestion, applicability); - } - } - - fn suggest_first_deref_field( - &self, - err: &mut Diagnostic, - expr: &hir::Expr<'_>, - base: &hir::Expr<'_>, - field: Ident, - ) { - if let Ok(base) = self.tcx.sess.source_map().span_to_snippet(base.span) { - let msg = format!("`{base}` is a raw pointer; try dereferencing it"); - let suggestion = format!("(*{base}).{field}"); - err.span_suggestion(expr.span, &msg, suggestion, Applicability::MaybeIncorrect); - } - } - - fn no_such_field_err( - &self, - field: Ident, - expr_t: Ty<'tcx>, - id: HirId, - ) -> DiagnosticBuilder<'_, ErrorGuaranteed> { - let span = field.span; - debug!("no_such_field_err(span: {:?}, field: {:?}, expr_t: {:?})", span, field, expr_t); - - let mut err = type_error_struct!( - self.tcx().sess, - field.span, - expr_t, - E0609, - "no field `{field}` on type `{expr_t}`", - ); - - // try to add a suggestion in case the field is a nested field of a field of the Adt - let mod_id = self.tcx.parent_module(id).to_def_id(); - if let Some((fields, substs)) = - self.get_field_candidates_considering_privacy(span, expr_t, mod_id) - { - let candidate_fields: Vec<_> = fields - .filter_map(|candidate_field| { - self.check_for_nested_field_satisfying( - span, - &|candidate_field, _| candidate_field.ident(self.tcx()) == field, - candidate_field, - substs, - vec![], - mod_id, - ) - }) - .map(|mut field_path| { - field_path.pop(); - field_path - .iter() - .map(|id| id.name.to_ident_string()) - .collect::<Vec<String>>() - .join(".") - }) - .collect::<Vec<_>>(); - - let len = candidate_fields.len(); - if len > 0 { - err.span_suggestions( - field.span.shrink_to_lo(), - format!( - "{} of the expressions' fields {} a field of the same name", - if len > 1 { "some" } else { "one" }, - if len > 1 { "have" } else { "has" }, - ), - candidate_fields.iter().map(|path| format!("{path}.")), - Applicability::MaybeIncorrect, - ); - } - } - err - } - - pub(crate) fn get_field_candidates_considering_privacy( - &self, - span: Span, - base_ty: Ty<'tcx>, - mod_id: DefId, - ) -> Option<(impl Iterator<Item = &'tcx ty::FieldDef> + 'tcx, SubstsRef<'tcx>)> { - debug!("get_field_candidates(span: {:?}, base_t: {:?}", span, base_ty); - - for (base_t, _) in self.autoderef(span, base_ty) { - match base_t.kind() { - ty::Adt(base_def, substs) if !base_def.is_enum() => { - let tcx = self.tcx; - let fields = &base_def.non_enum_variant().fields; - // Some struct, e.g. some that impl `Deref`, have all private fields - // because you're expected to deref them to access the _real_ fields. - // This, for example, will help us suggest accessing a field through a `Box<T>`. - if fields.iter().all(|field| !field.vis.is_accessible_from(mod_id, tcx)) { - continue; - } - return Some(( - fields - .iter() - .filter(move |field| field.vis.is_accessible_from(mod_id, tcx)) - // For compile-time reasons put a limit on number of fields we search - .take(100), - substs, - )); - } - _ => {} - } - } - None - } - - /// This method is called after we have encountered a missing field error to recursively - /// search for the field - pub(crate) fn check_for_nested_field_satisfying( - &self, - span: Span, - matches: &impl Fn(&ty::FieldDef, Ty<'tcx>) -> bool, - candidate_field: &ty::FieldDef, - subst: SubstsRef<'tcx>, - mut field_path: Vec<Ident>, - mod_id: DefId, - ) -> Option<Vec<Ident>> { - debug!( - "check_for_nested_field_satisfying(span: {:?}, candidate_field: {:?}, field_path: {:?}", - span, candidate_field, field_path - ); - - if field_path.len() > 3 { - // For compile-time reasons and to avoid infinite recursion we only check for fields - // up to a depth of three - None - } else { - field_path.push(candidate_field.ident(self.tcx).normalize_to_macros_2_0()); - let field_ty = candidate_field.ty(self.tcx, subst); - if matches(candidate_field, field_ty) { - return Some(field_path); - } else if let Some((nested_fields, subst)) = - self.get_field_candidates_considering_privacy(span, field_ty, mod_id) - { - // recursively search fields of `candidate_field` if it's a ty::Adt - for field in nested_fields { - if let Some(field_path) = self.check_for_nested_field_satisfying( - span, - matches, - field, - subst, - field_path.clone(), - mod_id, - ) { - return Some(field_path); - } - } - } - None - } - } - - fn check_expr_index( - &self, - base: &'tcx hir::Expr<'tcx>, - idx: &'tcx hir::Expr<'tcx>, - expr: &'tcx hir::Expr<'tcx>, - ) -> Ty<'tcx> { - let base_t = self.check_expr(&base); - let idx_t = self.check_expr(&idx); - - if base_t.references_error() { - base_t - } else if idx_t.references_error() { - idx_t - } else { - let base_t = self.structurally_resolved_type(base.span, base_t); - match self.lookup_indexing(expr, base, base_t, idx, idx_t) { - Some((index_ty, element_ty)) => { - // two-phase not needed because index_ty is never mutable - self.demand_coerce(idx, idx_t, index_ty, None, AllowTwoPhase::No); - self.select_obligations_where_possible(false, |errors| { - self.point_at_index_if_possible(errors, idx.span) - }); - element_ty - } - None => { - let mut err = type_error_struct!( - self.tcx.sess, - expr.span, - base_t, - E0608, - "cannot index into a value of type `{base_t}`", - ); - // Try to give some advice about indexing tuples. - if let ty::Tuple(..) = base_t.kind() { - let mut needs_note = true; - // If the index is an integer, we can show the actual - // fixed expression: - if let ExprKind::Lit(ref lit) = idx.kind { - if let ast::LitKind::Int(i, ast::LitIntType::Unsuffixed) = lit.node { - let snip = self.tcx.sess.source_map().span_to_snippet(base.span); - if let Ok(snip) = snip { - err.span_suggestion( - expr.span, - "to access tuple elements, use", - format!("{snip}.{i}"), - Applicability::MachineApplicable, - ); - needs_note = false; - } - } - } - if needs_note { - err.help( - "to access tuple elements, use tuple indexing \ - syntax (e.g., `tuple.0`)", - ); - } - } - err.emit(); - self.tcx.ty_error() - } - } - } - } - - fn point_at_index_if_possible( - &self, - errors: &mut Vec<traits::FulfillmentError<'tcx>>, - span: Span, - ) { - for error in errors { - match error.obligation.predicate.kind().skip_binder() { - ty::PredicateKind::Trait(predicate) - if self.tcx.is_diagnostic_item(sym::SliceIndex, predicate.trait_ref.def_id) => { - } - _ => continue, - } - error.obligation.cause.span = span; - } - } - - fn check_expr_yield( - &self, - value: &'tcx hir::Expr<'tcx>, - expr: &'tcx hir::Expr<'tcx>, - src: &'tcx hir::YieldSource, - ) -> Ty<'tcx> { - match self.resume_yield_tys { - Some((resume_ty, yield_ty)) => { - self.check_expr_coercable_to_type(&value, yield_ty, None); - - resume_ty - } - // Given that this `yield` expression was generated as a result of lowering a `.await`, - // we know that the yield type must be `()`; however, the context won't contain this - // information. Hence, we check the source of the yield expression here and check its - // value's type against `()` (this check should always hold). - None if src.is_await() => { - self.check_expr_coercable_to_type(&value, self.tcx.mk_unit(), None); - self.tcx.mk_unit() - } - _ => { - self.tcx.sess.emit_err(YieldExprOutsideOfGenerator { span: expr.span }); - // Avoid expressions without types during writeback (#78653). - self.check_expr(value); - self.tcx.mk_unit() - } - } - } - - fn check_expr_asm_operand(&self, expr: &'tcx hir::Expr<'tcx>, is_input: bool) { - let needs = if is_input { Needs::None } else { Needs::MutPlace }; - let ty = self.check_expr_with_needs(expr, needs); - self.require_type_is_sized(ty, expr.span, traits::InlineAsmSized); - - if !is_input && !expr.is_syntactic_place_expr() { - let mut err = self.tcx.sess.struct_span_err(expr.span, "invalid asm output"); - err.span_label(expr.span, "cannot assign to this expression"); - err.emit(); - } - - // If this is an input value, we require its type to be fully resolved - // at this point. This allows us to provide helpful coercions which help - // pass the type candidate list in a later pass. - // - // We don't require output types to be resolved at this point, which - // allows them to be inferred based on how they are used later in the - // function. - if is_input { - let ty = self.structurally_resolved_type(expr.span, ty); - match *ty.kind() { - ty::FnDef(..) => { - let fnptr_ty = self.tcx.mk_fn_ptr(ty.fn_sig(self.tcx)); - self.demand_coerce(expr, ty, fnptr_ty, None, AllowTwoPhase::No); - } - ty::Ref(_, base_ty, mutbl) => { - let ptr_ty = self.tcx.mk_ptr(ty::TypeAndMut { ty: base_ty, mutbl }); - self.demand_coerce(expr, ty, ptr_ty, None, AllowTwoPhase::No); - } - _ => {} - } - } - } - - fn check_expr_asm(&self, asm: &'tcx hir::InlineAsm<'tcx>) -> Ty<'tcx> { - for (op, _op_sp) in asm.operands { - match op { - hir::InlineAsmOperand::In { expr, .. } => { - self.check_expr_asm_operand(expr, true); - } - hir::InlineAsmOperand::Out { expr: Some(expr), .. } - | hir::InlineAsmOperand::InOut { expr, .. } => { - self.check_expr_asm_operand(expr, false); - } - hir::InlineAsmOperand::Out { expr: None, .. } => {} - hir::InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => { - self.check_expr_asm_operand(in_expr, true); - if let Some(out_expr) = out_expr { - self.check_expr_asm_operand(out_expr, false); - } - } - // `AnonConst`s have their own body and is type-checked separately. - // As they don't flow into the type system we don't need them to - // be well-formed. - hir::InlineAsmOperand::Const { .. } | hir::InlineAsmOperand::SymFn { .. } => {} - hir::InlineAsmOperand::SymStatic { .. } => {} - } - } - if asm.options.contains(ast::InlineAsmOptions::NORETURN) { - self.tcx.types.never - } else { - self.tcx.mk_unit() - } - } -} - -pub(super) 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", - }) -} diff --git a/compiler/rustc_typeck/src/check/fallback.rs b/compiler/rustc_typeck/src/check/fallback.rs deleted file mode 100644 index 4059b3403..000000000 --- a/compiler/rustc_typeck/src/check/fallback.rs +++ /dev/null @@ -1,398 +0,0 @@ -use crate::check::FnCtxt; -use rustc_data_structures::{ - fx::{FxHashMap, FxHashSet}, - graph::WithSuccessors, - graph::{iterate::DepthFirstSearch, vec_graph::VecGraph}, -}; -use rustc_middle::ty::{self, Ty}; - -impl<'tcx> FnCtxt<'_, 'tcx> { - /// Performs type inference fallback, returning true if any fallback - /// occurs. - pub(super) fn type_inference_fallback(&self) -> bool { - debug!( - "type-inference-fallback start obligations: {:#?}", - self.fulfillment_cx.borrow_mut().pending_obligations() - ); - - // All type checking constraints were added, try to fallback unsolved variables. - self.select_obligations_where_possible(false, |_| {}); - - debug!( - "type-inference-fallback post selection obligations: {:#?}", - self.fulfillment_cx.borrow_mut().pending_obligations() - ); - - // Check if we have any unsolved variables. If not, no need for fallback. - let unsolved_variables = self.unsolved_variables(); - if unsolved_variables.is_empty() { - return false; - } - - let diverging_fallback = self.calculate_diverging_fallback(&unsolved_variables); - - let mut fallback_has_occurred = false; - // We do fallback in two passes, to try to generate - // better error messages. - // The first time, we do *not* replace opaque types. - for ty in unsolved_variables { - debug!("unsolved_variable = {:?}", ty); - fallback_has_occurred |= self.fallback_if_possible(ty, &diverging_fallback); - } - - // We now see if we can make progress. This might cause us to - // unify inference variables for opaque types, since we may - // have unified some other type variables during the first - // phase of fallback. This means that we only replace - // inference variables with their underlying opaque types as a - // last resort. - // - // In code like this: - // - // ```rust - // type MyType = impl Copy; - // fn produce() -> MyType { true } - // fn bad_produce() -> MyType { panic!() } - // ``` - // - // we want to unify the opaque inference variable in `bad_produce` - // with the diverging fallback for `panic!` (e.g. `()` or `!`). - // This will produce a nice error message about conflicting concrete - // types for `MyType`. - // - // If we had tried to fallback the opaque inference variable to `MyType`, - // we will generate a confusing type-check error that does not explicitly - // refer to opaque types. - self.select_obligations_where_possible(fallback_has_occurred, |_| {}); - - fallback_has_occurred - } - - // Tries to apply a fallback to `ty` if it is an unsolved variable. - // - // - Unconstrained ints are replaced with `i32`. - // - // - Unconstrained floats are replaced with with `f64`. - // - // - Non-numerics may get replaced with `()` or `!`, depending on - // how they were categorized by `calculate_diverging_fallback` - // (and the setting of `#![feature(never_type_fallback)]`). - // - // Fallback becomes very dubious if we have encountered - // type-checking errors. In that case, fallback to Error. - // - // The return value indicates whether fallback has occurred. - fn fallback_if_possible( - &self, - ty: Ty<'tcx>, - diverging_fallback: &FxHashMap<Ty<'tcx>, Ty<'tcx>>, - ) -> bool { - // Careful: we do NOT shallow-resolve `ty`. We know that `ty` - // is an unsolved variable, and we determine its fallback - // based solely on how it was created, not what other type - // variables it may have been unified with since then. - // - // The reason this matters is that other attempts at fallback - // may (in principle) conflict with this fallback, and we wish - // to generate a type error in that case. (However, this - // actually isn't true right now, because we're only using the - // builtin fallback rules. This would be true if we were using - // user-supplied fallbacks. But it's still useful to write the - // code to detect bugs.) - // - // (Note though that if we have a general type variable `?T` - // that is then unified with an integer type variable `?I` - // that ultimately never gets resolved to a special integral - // type, `?T` is not considered unsolved, but `?I` is. The - // same is true for float variables.) - let fallback = match ty.kind() { - _ if self.is_tainted_by_errors() => self.tcx.ty_error(), - ty::Infer(ty::IntVar(_)) => self.tcx.types.i32, - ty::Infer(ty::FloatVar(_)) => self.tcx.types.f64, - _ => match diverging_fallback.get(&ty) { - Some(&fallback_ty) => fallback_ty, - None => return false, - }, - }; - debug!("fallback_if_possible(ty={:?}): defaulting to `{:?}`", ty, fallback); - - let span = self - .infcx - .type_var_origin(ty) - .map(|origin| origin.span) - .unwrap_or(rustc_span::DUMMY_SP); - self.demand_eqtype(span, ty, fallback); - true - } - - /// The "diverging fallback" system is rather complicated. This is - /// a result of our need to balance 'do the right thing' with - /// backwards compatibility. - /// - /// "Diverging" type variables are variables created when we - /// coerce a `!` type into an unbound type variable `?X`. If they - /// never wind up being constrained, the "right and natural" thing - /// is that `?X` should "fallback" to `!`. This means that e.g. an - /// expression like `Some(return)` will ultimately wind up with a - /// type like `Option<!>` (presuming it is not assigned or - /// constrained to have some other type). - /// - /// However, the fallback used to be `()` (before the `!` type was - /// added). Moreover, there are cases where the `!` type 'leaks - /// out' from dead code into type variables that affect live - /// code. The most common case is something like this: - /// - /// ```rust - /// # fn foo() -> i32 { 4 } - /// match foo() { - /// 22 => Default::default(), // call this type `?D` - /// _ => return, // return has type `!` - /// } // call the type of this match `?M` - /// ``` - /// - /// Here, coercing the type `!` into `?M` will create a diverging - /// type variable `?X` where `?X <: ?M`. We also have that `?D <: - /// ?M`. If `?M` winds up unconstrained, then `?X` will - /// fallback. If it falls back to `!`, then all the type variables - /// will wind up equal to `!` -- this includes the type `?D` - /// (since `!` doesn't implement `Default`, we wind up a "trait - /// not implemented" error in code like this). But since the - /// original fallback was `()`, this code used to compile with `?D - /// = ()`. This is somewhat surprising, since `Default::default()` - /// on its own would give an error because the types are - /// insufficiently constrained. - /// - /// Our solution to this dilemma is to modify diverging variables - /// so that they can *either* fallback to `!` (the default) or to - /// `()` (the backwards compatibility case). We decide which - /// fallback to use based on whether there is a coercion pattern - /// like this: - /// - /// ```ignore (not-rust) - /// ?Diverging -> ?V - /// ?NonDiverging -> ?V - /// ?V != ?NonDiverging - /// ``` - /// - /// Here `?Diverging` represents some diverging type variable and - /// `?NonDiverging` represents some non-diverging type - /// variable. `?V` can be any type variable (diverging or not), so - /// long as it is not equal to `?NonDiverging`. - /// - /// Intuitively, what we are looking for is a case where a - /// "non-diverging" type variable (like `?M` in our example above) - /// is coerced *into* some variable `?V` that would otherwise - /// fallback to `!`. In that case, we make `?V` fallback to `!`, - /// along with anything that would flow into `?V`. - /// - /// The algorithm we use: - /// * Identify all variables that are coerced *into* by a - /// diverging variable. Do this by iterating over each - /// diverging, unsolved variable and finding all variables - /// reachable from there. Call that set `D`. - /// * Walk over all unsolved, non-diverging variables, and find - /// any variable that has an edge into `D`. - fn calculate_diverging_fallback( - &self, - unsolved_variables: &[Ty<'tcx>], - ) -> FxHashMap<Ty<'tcx>, Ty<'tcx>> { - debug!("calculate_diverging_fallback({:?})", unsolved_variables); - - let relationships = self.fulfillment_cx.borrow_mut().relationships().clone(); - - // Construct a coercion graph where an edge `A -> B` indicates - // a type variable is that is coerced - let coercion_graph = self.create_coercion_graph(); - - // Extract the unsolved type inference variable vids; note that some - // unsolved variables are integer/float variables and are excluded. - let unsolved_vids = unsolved_variables.iter().filter_map(|ty| ty.ty_vid()); - - // Compute the diverging root vids D -- that is, the root vid of - // those type variables that (a) are the target of a coercion from - // a `!` type and (b) have not yet been solved. - // - // These variables are the ones that are targets for fallback to - // either `!` or `()`. - let diverging_roots: FxHashSet<ty::TyVid> = self - .diverging_type_vars - .borrow() - .iter() - .map(|&ty| self.shallow_resolve(ty)) - .filter_map(|ty| ty.ty_vid()) - .map(|vid| self.root_var(vid)) - .collect(); - debug!( - "calculate_diverging_fallback: diverging_type_vars={:?}", - self.diverging_type_vars.borrow() - ); - debug!("calculate_diverging_fallback: diverging_roots={:?}", diverging_roots); - - // Find all type variables that are reachable from a diverging - // type variable. These will typically default to `!`, unless - // we find later that they are *also* reachable from some - // other type variable outside this set. - let mut roots_reachable_from_diverging = DepthFirstSearch::new(&coercion_graph); - let mut diverging_vids = vec![]; - let mut non_diverging_vids = vec![]; - for unsolved_vid in unsolved_vids { - let root_vid = self.root_var(unsolved_vid); - debug!( - "calculate_diverging_fallback: unsolved_vid={:?} root_vid={:?} diverges={:?}", - unsolved_vid, - root_vid, - diverging_roots.contains(&root_vid), - ); - if diverging_roots.contains(&root_vid) { - diverging_vids.push(unsolved_vid); - roots_reachable_from_diverging.push_start_node(root_vid); - - debug!( - "calculate_diverging_fallback: root_vid={:?} reaches {:?}", - root_vid, - coercion_graph.depth_first_search(root_vid).collect::<Vec<_>>() - ); - - // drain the iterator to visit all nodes reachable from this node - roots_reachable_from_diverging.complete_search(); - } else { - non_diverging_vids.push(unsolved_vid); - } - } - - debug!( - "calculate_diverging_fallback: roots_reachable_from_diverging={:?}", - roots_reachable_from_diverging, - ); - - // Find all type variables N0 that are not reachable from a - // diverging variable, and then compute the set reachable from - // N0, which we call N. These are the *non-diverging* type - // variables. (Note that this set consists of "root variables".) - let mut roots_reachable_from_non_diverging = DepthFirstSearch::new(&coercion_graph); - for &non_diverging_vid in &non_diverging_vids { - let root_vid = self.root_var(non_diverging_vid); - if roots_reachable_from_diverging.visited(root_vid) { - continue; - } - roots_reachable_from_non_diverging.push_start_node(root_vid); - roots_reachable_from_non_diverging.complete_search(); - } - debug!( - "calculate_diverging_fallback: roots_reachable_from_non_diverging={:?}", - roots_reachable_from_non_diverging, - ); - - debug!("inherited: {:#?}", self.inh.fulfillment_cx.borrow_mut().pending_obligations()); - debug!("obligations: {:#?}", self.fulfillment_cx.borrow_mut().pending_obligations()); - debug!("relationships: {:#?}", relationships); - - // For each diverging variable, figure out whether it can - // reach a member of N. If so, it falls back to `()`. Else - // `!`. - let mut diverging_fallback = FxHashMap::default(); - diverging_fallback.reserve(diverging_vids.len()); - for &diverging_vid in &diverging_vids { - let diverging_ty = self.tcx.mk_ty_var(diverging_vid); - let root_vid = self.root_var(diverging_vid); - let can_reach_non_diverging = coercion_graph - .depth_first_search(root_vid) - .any(|n| roots_reachable_from_non_diverging.visited(n)); - - let mut relationship = ty::FoundRelationships { self_in_trait: false, output: false }; - - for (vid, rel) in relationships.iter() { - if self.root_var(*vid) == root_vid { - relationship.self_in_trait |= rel.self_in_trait; - relationship.output |= rel.output; - } - } - - if relationship.self_in_trait && relationship.output { - // This case falls back to () to ensure that the code pattern in - // src/test/ui/never_type/fallback-closure-ret.rs continues to - // compile when never_type_fallback is enabled. - // - // This rule is not readily explainable from first principles, - // but is rather intended as a patchwork fix to ensure code - // which compiles before the stabilization of never type - // fallback continues to work. - // - // Typically this pattern is encountered in a function taking a - // closure as a parameter, where the return type of that closure - // (checked by `relationship.output`) is expected to implement - // some trait (checked by `relationship.self_in_trait`). This - // can come up in non-closure cases too, so we do not limit this - // rule to specifically `FnOnce`. - // - // When the closure's body is something like `panic!()`, the - // return type would normally be inferred to `!`. However, it - // needs to fall back to `()` in order to still compile, as the - // trait is specifically implemented for `()` but not `!`. - // - // For details on the requirements for these relationships to be - // set, see the relationship finding module in - // compiler/rustc_trait_selection/src/traits/relationships.rs. - debug!("fallback to () - found trait and projection: {:?}", diverging_vid); - diverging_fallback.insert(diverging_ty, self.tcx.types.unit); - } else if can_reach_non_diverging { - debug!("fallback to () - reached non-diverging: {:?}", diverging_vid); - diverging_fallback.insert(diverging_ty, self.tcx.types.unit); - } else { - debug!("fallback to ! - all diverging: {:?}", diverging_vid); - diverging_fallback.insert(diverging_ty, self.tcx.mk_diverging_default()); - } - } - - diverging_fallback - } - - /// Returns a graph whose nodes are (unresolved) inference variables and where - /// an edge `?A -> ?B` indicates that the variable `?A` is coerced to `?B`. - fn create_coercion_graph(&self) -> VecGraph<ty::TyVid> { - let pending_obligations = self.fulfillment_cx.borrow_mut().pending_obligations(); - debug!("create_coercion_graph: pending_obligations={:?}", pending_obligations); - let coercion_edges: Vec<(ty::TyVid, ty::TyVid)> = pending_obligations - .into_iter() - .filter_map(|obligation| { - // The predicates we are looking for look like `Coerce(?A -> ?B)`. - // They will have no bound variables. - obligation.predicate.kind().no_bound_vars() - }) - .filter_map(|atom| { - // We consider both subtyping and coercion to imply 'flow' from - // some position in the code `a` to a different position `b`. - // This is then used to determine which variables interact with - // live code, and as such must fall back to `()` to preserve - // soundness. - // - // In practice currently the two ways that this happens is - // coercion and subtyping. - let (a, b) = if let ty::PredicateKind::Coerce(ty::CoercePredicate { a, b }) = atom { - (a, b) - } else if let ty::PredicateKind::Subtype(ty::SubtypePredicate { - a_is_expected: _, - a, - b, - }) = atom - { - (a, b) - } else { - return None; - }; - - let a_vid = self.root_vid(a)?; - let b_vid = self.root_vid(b)?; - Some((a_vid, b_vid)) - }) - .collect(); - debug!("create_coercion_graph: coercion_edges={:?}", coercion_edges); - let num_ty_vars = self.num_ty_vars(); - VecGraph::new(num_ty_vars, coercion_edges) - } - - /// If `ty` is an unresolved type variable, returns its root vid. - fn root_vid(&self, ty: Ty<'tcx>) -> Option<ty::TyVid> { - Some(self.root_var(self.shallow_resolve(ty).ty_vid()?)) - } -} diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs b/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs deleted file mode 100644 index a40478db9..000000000 --- a/compiler/rustc_typeck/src/check/fn_ctxt/_impl.rs +++ /dev/null @@ -1,1512 +0,0 @@ -use crate::astconv::{ - AstConv, CreateSubstsForGenericArgsCtxt, ExplicitLateBound, GenericArgCountMismatch, - GenericArgCountResult, IsMethodCall, PathSeg, -}; -use crate::check::callee::{self, DeferredCallResolution}; -use crate::check::method::{self, MethodCallee, SelfSource}; -use crate::check::rvalue_scopes; -use crate::check::{BreakableCtxt, Diverges, Expectation, FnCtxt, LocalTy}; - -use rustc_data_structures::captures::Captures; -use rustc_data_structures::fx::FxHashSet; -use rustc_errors::{Applicability, Diagnostic, ErrorGuaranteed, MultiSpan}; -use rustc_hir as hir; -use rustc_hir::def::{CtorOf, DefKind, Res}; -use rustc_hir::def_id::DefId; -use rustc_hir::lang_items::LangItem; -use rustc_hir::{ExprKind, GenericArg, Node, QPath}; -use rustc_infer::infer::canonical::{Canonical, OriginalQueryValues, QueryResponse}; -use rustc_infer::infer::error_reporting::TypeAnnotationNeeded::E0282; -use rustc_infer::infer::{InferOk, InferResult}; -use rustc_middle::ty::adjustment::{Adjust, Adjustment, AutoBorrow, AutoBorrowMutability}; -use rustc_middle::ty::fold::TypeFoldable; -use rustc_middle::ty::subst::{ - self, GenericArgKind, InternalSubsts, Subst, SubstsRef, UserSelfTy, UserSubsts, -}; -use rustc_middle::ty::visit::TypeVisitable; -use rustc_middle::ty::{ - self, AdtKind, CanonicalUserType, DefIdTree, EarlyBinder, GenericParamDefKind, ToPolyTraitRef, - ToPredicate, Ty, UserType, -}; -use rustc_session::lint; -use rustc_span::def_id::LocalDefId; -use rustc_span::hygiene::DesugaringKind; -use rustc_span::symbol::{kw, sym, Ident}; -use rustc_span::{Span, DUMMY_SP}; -use rustc_trait_selection::infer::InferCtxtExt as _; -use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _; -use rustc_trait_selection::traits::{ - self, ObligationCause, ObligationCauseCode, TraitEngine, TraitEngineExt, -}; - -use std::collections::hash_map::Entry; -use std::slice; - -impl<'a, 'tcx> FnCtxt<'a, 'tcx> { - /// Produces warning on the given node, if the current point in the - /// function is unreachable, and there hasn't been another warning. - pub(in super::super) fn warn_if_unreachable(&self, id: hir::HirId, span: Span, kind: &str) { - // FIXME: Combine these two 'if' expressions into one once - // let chains are implemented - if let Diverges::Always { span: orig_span, custom_note } = self.diverges.get() { - // If span arose from a desugaring of `if` or `while`, then it is the condition itself, - // which diverges, that we are about to lint on. This gives suboptimal diagnostics. - // Instead, stop here so that the `if`- or `while`-expression's block is linted instead. - if !span.is_desugaring(DesugaringKind::CondTemporary) - && !span.is_desugaring(DesugaringKind::Async) - && !orig_span.is_desugaring(DesugaringKind::Await) - { - self.diverges.set(Diverges::WarnedAlways); - - debug!("warn_if_unreachable: id={:?} span={:?} kind={}", id, span, kind); - - self.tcx().struct_span_lint_hir(lint::builtin::UNREACHABLE_CODE, id, span, |lint| { - let msg = format!("unreachable {}", kind); - lint.build(&msg) - .span_label(span, &msg) - .span_label( - orig_span, - custom_note - .unwrap_or("any code following this expression is unreachable"), - ) - .emit(); - }) - } - } - } - - /// Resolves type and const variables in `ty` if possible. Unlike the infcx - /// version (resolve_vars_if_possible), this version will - /// also select obligations if it seems useful, in an effort - /// to get more type information. - pub(in super::super) fn resolve_vars_with_obligations(&self, ty: Ty<'tcx>) -> Ty<'tcx> { - self.resolve_vars_with_obligations_and_mutate_fulfillment(ty, |_| {}) - } - - #[instrument(skip(self, mutate_fulfillment_errors), level = "debug", ret)] - pub(in super::super) fn resolve_vars_with_obligations_and_mutate_fulfillment( - &self, - mut ty: Ty<'tcx>, - mutate_fulfillment_errors: impl Fn(&mut Vec<traits::FulfillmentError<'tcx>>), - ) -> Ty<'tcx> { - // No Infer()? Nothing needs doing. - if !ty.has_infer_types_or_consts() { - debug!("no inference var, nothing needs doing"); - return ty; - } - - // If `ty` is a type variable, see whether we already know what it is. - ty = self.resolve_vars_if_possible(ty); - if !ty.has_infer_types_or_consts() { - debug!(?ty); - return ty; - } - - // If not, try resolving pending obligations as much as - // possible. This can help substantially when there are - // indirect dependencies that don't seem worth tracking - // precisely. - self.select_obligations_where_possible(false, mutate_fulfillment_errors); - self.resolve_vars_if_possible(ty) - } - - pub(in super::super) fn record_deferred_call_resolution( - &self, - closure_def_id: LocalDefId, - r: DeferredCallResolution<'tcx>, - ) { - let mut deferred_call_resolutions = self.deferred_call_resolutions.borrow_mut(); - deferred_call_resolutions.entry(closure_def_id).or_default().push(r); - } - - pub(in super::super) fn remove_deferred_call_resolutions( - &self, - closure_def_id: LocalDefId, - ) -> Vec<DeferredCallResolution<'tcx>> { - let mut deferred_call_resolutions = self.deferred_call_resolutions.borrow_mut(); - deferred_call_resolutions.remove(&closure_def_id).unwrap_or_default() - } - - pub fn tag(&self) -> String { - format!("{:p}", self) - } - - pub fn local_ty(&self, span: Span, nid: hir::HirId) -> LocalTy<'tcx> { - self.locals.borrow().get(&nid).cloned().unwrap_or_else(|| { - span_bug!(span, "no type for local variable {}", self.tcx.hir().node_to_string(nid)) - }) - } - - #[inline] - pub fn write_ty(&self, id: hir::HirId, ty: Ty<'tcx>) { - debug!("write_ty({:?}, {:?}) in fcx {}", id, self.resolve_vars_if_possible(ty), self.tag()); - self.typeck_results.borrow_mut().node_types_mut().insert(id, ty); - - if ty.references_error() { - self.has_errors.set(true); - self.set_tainted_by_errors(); - } - } - - pub fn write_field_index(&self, hir_id: hir::HirId, index: usize) { - self.typeck_results.borrow_mut().field_indices_mut().insert(hir_id, index); - } - - #[instrument(level = "debug", skip(self))] - pub(in super::super) fn write_resolution( - &self, - hir_id: hir::HirId, - r: Result<(DefKind, DefId), ErrorGuaranteed>, - ) { - self.typeck_results.borrow_mut().type_dependent_defs_mut().insert(hir_id, r); - } - - #[instrument(level = "debug", skip(self))] - pub fn write_method_call(&self, hir_id: hir::HirId, method: MethodCallee<'tcx>) { - self.write_resolution(hir_id, Ok((DefKind::AssocFn, method.def_id))); - self.write_substs(hir_id, method.substs); - - // When the method is confirmed, the `method.substs` includes - // parameters from not just the method, but also the impl of - // the method -- in particular, the `Self` type will be fully - // resolved. However, those are not something that the "user - // specified" -- i.e., those types come from the inferred type - // of the receiver, not something the user wrote. So when we - // create the user-substs, we want to replace those earlier - // types with just the types that the user actually wrote -- - // that is, those that appear on the *method itself*. - // - // As an example, if the user wrote something like - // `foo.bar::<u32>(...)` -- the `Self` type here will be the - // type of `foo` (possibly adjusted), but we don't want to - // include that. We want just the `[_, u32]` part. - if !method.substs.is_empty() { - let method_generics = self.tcx.generics_of(method.def_id); - if !method_generics.params.is_empty() { - let user_type_annotation = self.probe(|_| { - let user_substs = UserSubsts { - substs: InternalSubsts::for_item(self.tcx, method.def_id, |param, _| { - let i = param.index as usize; - if i < method_generics.parent_count { - self.var_for_def(DUMMY_SP, param) - } else { - method.substs[i] - } - }), - user_self_ty: None, // not relevant here - }; - - self.canonicalize_user_type_annotation(UserType::TypeOf( - method.def_id, - user_substs, - )) - }); - - debug!("write_method_call: user_type_annotation={:?}", user_type_annotation); - self.write_user_type_annotation(hir_id, user_type_annotation); - } - } - } - - pub fn write_substs(&self, node_id: hir::HirId, substs: SubstsRef<'tcx>) { - if !substs.is_empty() { - debug!("write_substs({:?}, {:?}) in fcx {}", node_id, substs, self.tag()); - - self.typeck_results.borrow_mut().node_substs_mut().insert(node_id, substs); - } - } - - /// Given the substs that we just converted from the HIR, try to - /// canonicalize them and store them as user-given substitutions - /// (i.e., substitutions that must be respected by the NLL check). - /// - /// This should be invoked **before any unifications have - /// occurred**, so that annotations like `Vec<_>` are preserved - /// properly. - #[instrument(skip(self), level = "debug")] - pub fn write_user_type_annotation_from_substs( - &self, - hir_id: hir::HirId, - def_id: DefId, - substs: SubstsRef<'tcx>, - user_self_ty: Option<UserSelfTy<'tcx>>, - ) { - debug!("fcx {}", self.tag()); - - if Self::can_contain_user_lifetime_bounds((substs, user_self_ty)) { - let canonicalized = self.canonicalize_user_type_annotation(UserType::TypeOf( - def_id, - UserSubsts { substs, user_self_ty }, - )); - debug!(?canonicalized); - self.write_user_type_annotation(hir_id, canonicalized); - } - } - - #[instrument(skip(self), level = "debug")] - pub fn write_user_type_annotation( - &self, - hir_id: hir::HirId, - canonical_user_type_annotation: CanonicalUserType<'tcx>, - ) { - debug!("fcx {}", self.tag()); - - if !canonical_user_type_annotation.is_identity() { - self.typeck_results - .borrow_mut() - .user_provided_types_mut() - .insert(hir_id, canonical_user_type_annotation); - } else { - debug!("skipping identity substs"); - } - } - - #[instrument(skip(self, expr), level = "debug")] - pub fn apply_adjustments(&self, expr: &hir::Expr<'_>, adj: Vec<Adjustment<'tcx>>) { - debug!("expr = {:#?}", expr); - - if adj.is_empty() { - return; - } - - for a in &adj { - if let Adjust::NeverToAny = a.kind { - if a.target.is_ty_var() { - self.diverging_type_vars.borrow_mut().insert(a.target); - debug!("apply_adjustments: adding `{:?}` as diverging type var", a.target); - } - } - } - - let autoborrow_mut = adj.iter().any(|adj| { - matches!( - adj, - &Adjustment { - kind: Adjust::Borrow(AutoBorrow::Ref(_, AutoBorrowMutability::Mut { .. })), - .. - } - ) - }); - - match self.typeck_results.borrow_mut().adjustments_mut().entry(expr.hir_id) { - Entry::Vacant(entry) => { - entry.insert(adj); - } - Entry::Occupied(mut entry) => { - debug!(" - composing on top of {:?}", entry.get()); - match (&entry.get()[..], &adj[..]) { - // Applying any adjustment on top of a NeverToAny - // is a valid NeverToAny adjustment, because it can't - // be reached. - (&[Adjustment { kind: Adjust::NeverToAny, .. }], _) => return, - ( - &[ - Adjustment { kind: Adjust::Deref(_), .. }, - Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(..)), .. }, - ], - &[ - Adjustment { kind: Adjust::Deref(_), .. }, - .., // Any following adjustments are allowed. - ], - ) => { - // A reborrow has no effect before a dereference. - } - // FIXME: currently we never try to compose autoderefs - // and ReifyFnPointer/UnsafeFnPointer, but we could. - _ => { - self.tcx.sess.delay_span_bug( - expr.span, - &format!( - "while adjusting {:?}, can't compose {:?} and {:?}", - expr, - entry.get(), - adj - ), - ); - } - } - *entry.get_mut() = adj; - } - } - - // If there is an mutable auto-borrow, it is equivalent to `&mut <expr>`. - // In this case implicit use of `Deref` and `Index` within `<expr>` should - // instead be `DerefMut` and `IndexMut`, so fix those up. - if autoborrow_mut { - self.convert_place_derefs_to_mutable(expr); - } - } - - /// Basically whenever we are converting from a type scheme into - /// the fn body space, we always want to normalize associated - /// types as well. This function combines the two. - fn instantiate_type_scheme<T>(&self, span: Span, substs: SubstsRef<'tcx>, value: T) -> T - where - T: TypeFoldable<'tcx>, - { - debug!("instantiate_type_scheme(value={:?}, substs={:?})", value, substs); - let value = EarlyBinder(value).subst(self.tcx, substs); - let result = self.normalize_associated_types_in(span, value); - debug!("instantiate_type_scheme = {:?}", result); - result - } - - /// As `instantiate_type_scheme`, but for the bounds found in a - /// generic type scheme. - pub(in super::super) fn instantiate_bounds( - &self, - span: Span, - def_id: DefId, - substs: SubstsRef<'tcx>, - ) -> (ty::InstantiatedPredicates<'tcx>, Vec<Span>) { - let bounds = self.tcx.predicates_of(def_id); - let spans: Vec<Span> = bounds.predicates.iter().map(|(_, span)| *span).collect(); - let result = bounds.instantiate(self.tcx, substs); - let result = self.normalize_associated_types_in(span, result); - debug!( - "instantiate_bounds(bounds={:?}, substs={:?}) = {:?}, {:?}", - bounds, substs, result, spans, - ); - (result, spans) - } - - pub(in super::super) fn normalize_associated_types_in<T>(&self, span: Span, value: T) -> T - where - T: TypeFoldable<'tcx>, - { - self.inh.normalize_associated_types_in(span, self.body_id, self.param_env, value) - } - - pub(in super::super) fn normalize_associated_types_in_as_infer_ok<T>( - &self, - span: Span, - value: T, - ) -> InferOk<'tcx, T> - where - T: TypeFoldable<'tcx>, - { - self.inh.partially_normalize_associated_types_in( - ObligationCause::misc(span, self.body_id), - self.param_env, - value, - ) - } - - pub(in super::super) fn normalize_op_associated_types_in_as_infer_ok<T>( - &self, - span: Span, - value: T, - opt_input_expr: Option<&hir::Expr<'_>>, - ) -> InferOk<'tcx, T> - where - T: TypeFoldable<'tcx>, - { - self.inh.partially_normalize_associated_types_in( - ObligationCause::new( - span, - self.body_id, - traits::BinOp { - rhs_span: opt_input_expr.map(|expr| expr.span), - is_lit: opt_input_expr - .map_or(false, |expr| matches!(expr.kind, ExprKind::Lit(_))), - output_ty: None, - }, - ), - self.param_env, - value, - ) - } - - pub fn require_type_meets( - &self, - ty: Ty<'tcx>, - span: Span, - code: traits::ObligationCauseCode<'tcx>, - def_id: DefId, - ) { - self.register_bound(ty, def_id, traits::ObligationCause::new(span, self.body_id, code)); - } - - pub fn require_type_is_sized( - &self, - ty: Ty<'tcx>, - span: Span, - code: traits::ObligationCauseCode<'tcx>, - ) { - if !ty.references_error() { - let lang_item = self.tcx.require_lang_item(LangItem::Sized, None); - self.require_type_meets(ty, span, code, lang_item); - } - } - - pub fn require_type_is_sized_deferred( - &self, - ty: Ty<'tcx>, - span: Span, - code: traits::ObligationCauseCode<'tcx>, - ) { - if !ty.references_error() { - self.deferred_sized_obligations.borrow_mut().push((ty, span, code)); - } - } - - pub fn register_bound( - &self, - ty: Ty<'tcx>, - def_id: DefId, - cause: traits::ObligationCause<'tcx>, - ) { - if !ty.references_error() { - self.fulfillment_cx.borrow_mut().register_bound( - self, - self.param_env, - ty, - def_id, - cause, - ); - } - } - - pub fn to_ty(&self, ast_t: &hir::Ty<'_>) -> Ty<'tcx> { - let t = <dyn AstConv<'_>>::ast_ty_to_ty(self, ast_t); - self.register_wf_obligation(t.into(), ast_t.span, traits::WellFormed(None)); - t - } - - pub fn to_ty_saving_user_provided_ty(&self, ast_ty: &hir::Ty<'_>) -> Ty<'tcx> { - let ty = self.to_ty(ast_ty); - debug!("to_ty_saving_user_provided_ty: ty={:?}", ty); - - if Self::can_contain_user_lifetime_bounds(ty) { - let c_ty = self.canonicalize_response(UserType::Ty(ty)); - debug!("to_ty_saving_user_provided_ty: c_ty={:?}", c_ty); - self.typeck_results.borrow_mut().user_provided_types_mut().insert(ast_ty.hir_id, c_ty); - } - - ty - } - - pub fn array_length_to_const(&self, length: &hir::ArrayLen) -> ty::Const<'tcx> { - match length { - &hir::ArrayLen::Infer(_, span) => self.ct_infer(self.tcx.types.usize, None, span), - hir::ArrayLen::Body(anon_const) => self.to_const(anon_const), - } - } - - pub fn to_const(&self, ast_c: &hir::AnonConst) -> ty::Const<'tcx> { - let const_def_id = self.tcx.hir().local_def_id(ast_c.hir_id); - let span = self.tcx.hir().span(ast_c.hir_id); - let c = ty::Const::from_anon_const(self.tcx, const_def_id); - self.register_wf_obligation(c.into(), span, ObligationCauseCode::WellFormed(None)); - self.normalize_associated_types_in(span, c) - } - - pub fn const_arg_to_const( - &self, - ast_c: &hir::AnonConst, - param_def_id: DefId, - ) -> ty::Const<'tcx> { - let const_def = ty::WithOptConstParam { - did: self.tcx.hir().local_def_id(ast_c.hir_id), - const_param_did: Some(param_def_id), - }; - let c = ty::Const::from_opt_const_arg_anon_const(self.tcx, const_def); - self.register_wf_obligation( - c.into(), - self.tcx.hir().span(ast_c.hir_id), - ObligationCauseCode::WellFormed(None), - ); - c - } - - // If the type given by the user has free regions, save it for later, since - // NLL would like to enforce those. Also pass in types that involve - // projections, since those can resolve to `'static` bounds (modulo #54940, - // which hopefully will be fixed by the time you see this comment, dear - // reader, although I have my doubts). Also pass in types with inference - // types, because they may be repeated. Other sorts of things are already - // sufficiently enforced with erased regions. =) - fn can_contain_user_lifetime_bounds<T>(t: T) -> bool - where - T: TypeVisitable<'tcx>, - { - t.has_free_regions() || t.has_projections() || t.has_infer_types() - } - - pub fn node_ty(&self, id: hir::HirId) -> Ty<'tcx> { - match self.typeck_results.borrow().node_types().get(id) { - Some(&t) => t, - None if self.is_tainted_by_errors() => self.tcx.ty_error(), - None => { - bug!( - "no type for node {}: {} in fcx {}", - id, - self.tcx.hir().node_to_string(id), - self.tag() - ); - } - } - } - - pub fn node_ty_opt(&self, id: hir::HirId) -> Option<Ty<'tcx>> { - match self.typeck_results.borrow().node_types().get(id) { - Some(&t) => Some(t), - None if self.is_tainted_by_errors() => Some(self.tcx.ty_error()), - None => None, - } - } - - /// Registers an obligation for checking later, during regionck, that `arg` is well-formed. - pub fn register_wf_obligation( - &self, - arg: subst::GenericArg<'tcx>, - span: Span, - code: traits::ObligationCauseCode<'tcx>, - ) { - // WF obligations never themselves fail, so no real need to give a detailed cause: - let cause = traits::ObligationCause::new(span, self.body_id, code); - self.register_predicate(traits::Obligation::new( - cause, - self.param_env, - ty::Binder::dummy(ty::PredicateKind::WellFormed(arg)).to_predicate(self.tcx), - )); - } - - /// Registers obligations that all `substs` are well-formed. - pub fn add_wf_bounds(&self, substs: SubstsRef<'tcx>, expr: &hir::Expr<'_>) { - for arg in substs.iter().filter(|arg| { - matches!(arg.unpack(), GenericArgKind::Type(..) | GenericArgKind::Const(..)) - }) { - self.register_wf_obligation(arg, expr.span, traits::WellFormed(None)); - } - } - - // FIXME(arielb1): use this instead of field.ty everywhere - // Only for fields! Returns <none> for methods> - // Indifferent to privacy flags - pub fn field_ty( - &self, - span: Span, - field: &'tcx ty::FieldDef, - substs: SubstsRef<'tcx>, - ) -> Ty<'tcx> { - self.normalize_associated_types_in(span, field.ty(self.tcx, substs)) - } - - pub(in super::super) fn resolve_rvalue_scopes(&self, def_id: DefId) { - let scope_tree = self.tcx.region_scope_tree(def_id); - let rvalue_scopes = { rvalue_scopes::resolve_rvalue_scopes(self, &scope_tree, def_id) }; - let mut typeck_results = self.inh.typeck_results.borrow_mut(); - typeck_results.rvalue_scopes = rvalue_scopes; - } - - pub(in super::super) fn resolve_generator_interiors(&self, def_id: DefId) { - let mut generators = self.deferred_generator_interiors.borrow_mut(); - for (body_id, interior, kind) in generators.drain(..) { - self.select_obligations_where_possible(false, |_| {}); - crate::check::generator_interior::resolve_interior( - self, def_id, body_id, interior, kind, - ); - } - } - - #[instrument(skip(self), level = "debug")] - pub(in super::super) fn select_all_obligations_or_error(&self) { - let mut errors = self.fulfillment_cx.borrow_mut().select_all_or_error(&self); - - if !errors.is_empty() { - self.adjust_fulfillment_errors_for_expr_obligation(&mut errors); - self.report_fulfillment_errors(&errors, self.inh.body_id, false); - } - } - - /// Select as many obligations as we can at present. - pub(in super::super) fn select_obligations_where_possible( - &self, - fallback_has_occurred: bool, - mutate_fulfillment_errors: impl Fn(&mut Vec<traits::FulfillmentError<'tcx>>), - ) { - let mut result = self.fulfillment_cx.borrow_mut().select_where_possible(self); - if !result.is_empty() { - mutate_fulfillment_errors(&mut result); - self.adjust_fulfillment_errors_for_expr_obligation(&mut result); - self.report_fulfillment_errors(&result, self.inh.body_id, fallback_has_occurred); - } - } - - /// For the overloaded place expressions (`*x`, `x[3]`), the trait - /// returns a type of `&T`, but the actual type we assign to the - /// *expression* is `T`. So this function just peels off the return - /// type by one layer to yield `T`. - pub(in super::super) fn make_overloaded_place_return_type( - &self, - method: MethodCallee<'tcx>, - ) -> ty::TypeAndMut<'tcx> { - // extract method return type, which will be &T; - let ret_ty = method.sig.output(); - - // method returns &T, but the type as visible to user is T, so deref - ret_ty.builtin_deref(true).unwrap() - } - - #[instrument(skip(self), level = "debug")] - fn self_type_matches_expected_vid( - &self, - trait_ref: ty::PolyTraitRef<'tcx>, - expected_vid: ty::TyVid, - ) -> bool { - let self_ty = self.shallow_resolve(trait_ref.skip_binder().self_ty()); - debug!(?self_ty); - - match *self_ty.kind() { - ty::Infer(ty::TyVar(found_vid)) => { - // FIXME: consider using `sub_root_var` here so we - // can see through subtyping. - let found_vid = self.root_var(found_vid); - debug!("self_type_matches_expected_vid - found_vid={:?}", found_vid); - expected_vid == found_vid - } - _ => false, - } - } - - #[instrument(skip(self), level = "debug")] - pub(in super::super) fn obligations_for_self_ty<'b>( - &'b self, - self_ty: ty::TyVid, - ) -> impl Iterator<Item = (ty::PolyTraitRef<'tcx>, traits::PredicateObligation<'tcx>)> - + Captures<'tcx> - + 'b { - // FIXME: consider using `sub_root_var` here so we - // can see through subtyping. - let ty_var_root = self.root_var(self_ty); - trace!("pending_obligations = {:#?}", self.fulfillment_cx.borrow().pending_obligations()); - - self.fulfillment_cx - .borrow() - .pending_obligations() - .into_iter() - .filter_map(move |obligation| { - let bound_predicate = obligation.predicate.kind(); - match bound_predicate.skip_binder() { - ty::PredicateKind::Projection(data) => Some(( - bound_predicate.rebind(data).required_poly_trait_ref(self.tcx), - obligation, - )), - ty::PredicateKind::Trait(data) => { - Some((bound_predicate.rebind(data).to_poly_trait_ref(), obligation)) - } - ty::PredicateKind::Subtype(..) => None, - ty::PredicateKind::Coerce(..) => None, - ty::PredicateKind::RegionOutlives(..) => None, - ty::PredicateKind::TypeOutlives(..) => None, - ty::PredicateKind::WellFormed(..) => None, - ty::PredicateKind::ObjectSafe(..) => None, - ty::PredicateKind::ConstEvaluatable(..) => None, - ty::PredicateKind::ConstEquate(..) => None, - // N.B., this predicate is created by breaking down a - // `ClosureType: FnFoo()` predicate, where - // `ClosureType` represents some `Closure`. It can't - // possibly be referring to the current closure, - // because we haven't produced the `Closure` for - // this closure yet; this is exactly why the other - // code is looking for a self type of an unresolved - // inference variable. - ty::PredicateKind::ClosureKind(..) => None, - ty::PredicateKind::TypeWellFormedFromEnv(..) => None, - } - }) - .filter(move |(tr, _)| self.self_type_matches_expected_vid(*tr, ty_var_root)) - } - - pub(in super::super) fn type_var_is_sized(&self, self_ty: ty::TyVid) -> bool { - self.obligations_for_self_ty(self_ty) - .any(|(tr, _)| Some(tr.def_id()) == self.tcx.lang_items().sized_trait()) - } - - pub(in super::super) fn err_args(&self, len: usize) -> Vec<Ty<'tcx>> { - vec![self.tcx.ty_error(); len] - } - - /// Unifies the output type with the expected type early, for more coercions - /// and forward type information on the input expressions. - #[instrument(skip(self, call_span), level = "debug")] - pub(in super::super) fn expected_inputs_for_expected_output( - &self, - call_span: Span, - expected_ret: Expectation<'tcx>, - formal_ret: Ty<'tcx>, - formal_args: &[Ty<'tcx>], - ) -> Option<Vec<Ty<'tcx>>> { - let formal_ret = self.resolve_vars_with_obligations(formal_ret); - let ret_ty = expected_ret.only_has_type(self)?; - - // HACK(oli-obk): This is a hack to keep RPIT and TAIT in sync wrt their behaviour. - // Without it, the inference - // variable will get instantiated with the opaque type. The inference variable often - // has various helpful obligations registered for it that help closures figure out their - // signature. If we infer the inference var to the opaque type, the closure won't be able - // to find those obligations anymore, and it can't necessarily find them from the opaque - // type itself. We could be more powerful with inference if we *combined* the obligations - // so that we got both the obligations from the opaque type and the ones from the inference - // variable. That will accept more code than we do right now, so we need to carefully consider - // the implications. - // Note: this check is pessimistic, as the inference type could be matched with something other - // than the opaque type, but then we need a new `TypeRelation` just for this specific case and - // can't re-use `sup` below. - // See src/test/ui/impl-trait/hidden-type-is-opaque.rs and - // src/test/ui/impl-trait/hidden-type-is-opaque-2.rs for examples that hit this path. - if formal_ret.has_infer_types() { - for ty in ret_ty.walk() { - if let ty::subst::GenericArgKind::Type(ty) = ty.unpack() - && let ty::Opaque(def_id, _) = *ty.kind() - && let Some(def_id) = def_id.as_local() - && self.opaque_type_origin(def_id, DUMMY_SP).is_some() { - return None; - } - } - } - - let expect_args = self - .fudge_inference_if_ok(|| { - // Attempt to apply a subtyping relationship between the formal - // return type (likely containing type variables if the function - // is polymorphic) and the expected return type. - // No argument expectations are produced if unification fails. - let origin = self.misc(call_span); - let ures = self.at(&origin, self.param_env).sup(ret_ty, formal_ret); - - // FIXME(#27336) can't use ? here, Try::from_error doesn't default - // to identity so the resulting type is not constrained. - match ures { - Ok(ok) => { - // Process any obligations locally as much as - // we can. We don't care if some things turn - // out unconstrained or ambiguous, as we're - // just trying to get hints here. - let errors = self.save_and_restore_in_snapshot_flag(|_| { - let mut fulfill = <dyn TraitEngine<'_>>::new(self.tcx); - for obligation in ok.obligations { - fulfill.register_predicate_obligation(self, obligation); - } - fulfill.select_where_possible(self) - }); - - if !errors.is_empty() { - return Err(()); - } - } - Err(_) => return Err(()), - } - - // Record all the argument types, with the substitutions - // produced from the above subtyping unification. - Ok(Some(formal_args.iter().map(|&ty| self.resolve_vars_if_possible(ty)).collect())) - }) - .unwrap_or_default(); - debug!(?formal_args, ?formal_ret, ?expect_args, ?expected_ret); - expect_args - } - - pub(in super::super) fn resolve_lang_item_path( - &self, - lang_item: hir::LangItem, - span: Span, - hir_id: hir::HirId, - expr_hir_id: Option<hir::HirId>, - ) -> (Res, Ty<'tcx>) { - let def_id = self.tcx.require_lang_item(lang_item, Some(span)); - let def_kind = self.tcx.def_kind(def_id); - - let item_ty = if let DefKind::Variant = def_kind { - self.tcx.bound_type_of(self.tcx.parent(def_id)) - } else { - self.tcx.bound_type_of(def_id) - }; - let substs = self.fresh_substs_for_item(span, def_id); - let ty = item_ty.subst(self.tcx, substs); - - self.write_resolution(hir_id, Ok((def_kind, def_id))); - - let code = match lang_item { - hir::LangItem::IntoFutureIntoFuture => { - Some(ObligationCauseCode::AwaitableExpr(expr_hir_id)) - } - hir::LangItem::IteratorNext | hir::LangItem::IntoIterIntoIter => { - Some(ObligationCauseCode::ForLoopIterator) - } - hir::LangItem::TryTraitFromOutput - | hir::LangItem::TryTraitFromResidual - | hir::LangItem::TryTraitBranch => Some(ObligationCauseCode::QuestionMark), - _ => None, - }; - if let Some(code) = code { - self.add_required_obligations_with_code(span, def_id, substs, move |_, _| code.clone()); - } else { - self.add_required_obligations_for_hir(span, def_id, substs, hir_id); - } - - (Res::Def(def_kind, def_id), ty) - } - - /// Resolves an associated value path into a base type and associated constant, or method - /// resolution. The newly resolved definition is written into `type_dependent_defs`. - pub fn resolve_ty_and_res_fully_qualified_call( - &self, - qpath: &'tcx QPath<'tcx>, - hir_id: hir::HirId, - span: Span, - ) -> (Res, Option<Ty<'tcx>>, &'tcx [hir::PathSegment<'tcx>]) { - debug!( - "resolve_ty_and_res_fully_qualified_call: qpath={:?} hir_id={:?} span={:?}", - qpath, hir_id, span - ); - let (ty, qself, item_segment) = match *qpath { - QPath::Resolved(ref opt_qself, ref path) => { - return ( - path.res, - opt_qself.as_ref().map(|qself| self.to_ty(qself)), - path.segments, - ); - } - QPath::TypeRelative(ref qself, ref segment) => { - // Don't use `self.to_ty`, since this will register a WF obligation. - // If we're trying to call a non-existent method on a trait - // (e.g. `MyTrait::missing_method`), then resolution will - // give us a `QPath::TypeRelative` with a trait object as - // `qself`. In that case, we want to avoid registering a WF obligation - // for `dyn MyTrait`, since we don't actually need the trait - // to be object-safe. - // We manually call `register_wf_obligation` in the success path - // below. - (<dyn AstConv<'_>>::ast_ty_to_ty_in_path(self, qself), qself, segment) - } - QPath::LangItem(..) => { - bug!("`resolve_ty_and_res_fully_qualified_call` called on `LangItem`") - } - }; - if let Some(&cached_result) = self.typeck_results.borrow().type_dependent_defs().get(hir_id) - { - self.register_wf_obligation(ty.into(), qself.span, traits::WellFormed(None)); - // Return directly on cache hit. This is useful to avoid doubly reporting - // errors with default match binding modes. See #44614. - let def = cached_result.map_or(Res::Err, |(kind, def_id)| Res::Def(kind, def_id)); - return (def, Some(ty), slice::from_ref(&**item_segment)); - } - let item_name = item_segment.ident; - let result = self - .resolve_fully_qualified_call(span, item_name, ty, qself.span, hir_id) - .or_else(|error| { - let result = match error { - method::MethodError::PrivateMatch(kind, def_id, _) => Ok((kind, def_id)), - _ => Err(ErrorGuaranteed::unchecked_claim_error_was_emitted()), - }; - - // If we have a path like `MyTrait::missing_method`, then don't register - // a WF obligation for `dyn MyTrait` when method lookup fails. Otherwise, - // register a WF obligation so that we can detect any additional - // errors in the self type. - if !(matches!(error, method::MethodError::NoMatch(_)) && ty.is_trait()) { - self.register_wf_obligation(ty.into(), qself.span, traits::WellFormed(None)); - } - if item_name.name != kw::Empty { - if let Some(mut e) = self.report_method_error( - span, - ty, - item_name, - SelfSource::QPath(qself), - error, - None, - ) { - e.emit(); - } - } - result - }); - - if result.is_ok() { - self.register_wf_obligation(ty.into(), qself.span, traits::WellFormed(None)); - } - - // Write back the new resolution. - self.write_resolution(hir_id, result); - ( - result.map_or(Res::Err, |(kind, def_id)| Res::Def(kind, def_id)), - Some(ty), - slice::from_ref(&**item_segment), - ) - } - - /// Given a function `Node`, return its `FnDecl` if it exists, or `None` otherwise. - pub(in super::super) fn get_node_fn_decl( - &self, - node: Node<'tcx>, - ) -> Option<(&'tcx hir::FnDecl<'tcx>, Ident, bool)> { - match node { - Node::Item(&hir::Item { ident, kind: hir::ItemKind::Fn(ref sig, ..), .. }) => { - // This is less than ideal, it will not suggest a return type span on any - // method called `main`, regardless of whether it is actually the entry point, - // but it will still present it as the reason for the expected type. - Some((&sig.decl, ident, ident.name != sym::main)) - } - Node::TraitItem(&hir::TraitItem { - ident, - kind: hir::TraitItemKind::Fn(ref sig, ..), - .. - }) => Some((&sig.decl, ident, true)), - Node::ImplItem(&hir::ImplItem { - ident, - kind: hir::ImplItemKind::Fn(ref sig, ..), - .. - }) => Some((&sig.decl, ident, false)), - _ => None, - } - } - - /// Given a `HirId`, return the `FnDecl` of the method it is enclosed by and whether a - /// suggestion can be made, `None` otherwise. - pub fn get_fn_decl(&self, blk_id: hir::HirId) -> Option<(&'tcx hir::FnDecl<'tcx>, bool)> { - // Get enclosing Fn, if it is a function or a trait method, unless there's a `loop` or - // `while` before reaching it, as block tail returns are not available in them. - self.tcx.hir().get_return_block(blk_id).and_then(|blk_id| { - let parent = self.tcx.hir().get(blk_id); - self.get_node_fn_decl(parent).map(|(fn_decl, _, is_main)| (fn_decl, is_main)) - }) - } - - pub(in super::super) fn note_internal_mutation_in_method( - &self, - err: &mut Diagnostic, - expr: &hir::Expr<'_>, - expected: Ty<'tcx>, - found: Ty<'tcx>, - ) { - if found != self.tcx.types.unit { - return; - } - if let ExprKind::MethodCall(path_segment, rcvr, ..) = expr.kind { - if self - .typeck_results - .borrow() - .expr_ty_adjusted_opt(rcvr) - .map_or(true, |ty| expected.peel_refs() != ty.peel_refs()) - { - return; - } - let mut sp = MultiSpan::from_span(path_segment.ident.span); - sp.push_span_label( - path_segment.ident.span, - format!( - "this call modifies {} in-place", - match rcvr.kind { - ExprKind::Path(QPath::Resolved( - None, - hir::Path { segments: [segment], .. }, - )) => format!("`{}`", segment.ident), - _ => "its receiver".to_string(), - } - ), - ); - sp.push_span_label( - rcvr.span, - "you probably want to use this value after calling the method...", - ); - err.span_note( - sp, - &format!("method `{}` modifies its receiver in-place", path_segment.ident), - ); - err.note(&format!("...instead of the `()` output of method `{}`", path_segment.ident)); - } - } - - pub(in super::super) fn note_need_for_fn_pointer( - &self, - err: &mut Diagnostic, - expected: Ty<'tcx>, - found: Ty<'tcx>, - ) { - let (sig, did, substs) = match (&expected.kind(), &found.kind()) { - (ty::FnDef(did1, substs1), ty::FnDef(did2, substs2)) => { - let sig1 = self.tcx.bound_fn_sig(*did1).subst(self.tcx, substs1); - let sig2 = self.tcx.bound_fn_sig(*did2).subst(self.tcx, substs2); - if sig1 != sig2 { - return; - } - err.note( - "different `fn` items always have unique types, even if their signatures are \ - the same", - ); - (sig1, *did1, substs1) - } - (ty::FnDef(did, substs), ty::FnPtr(sig2)) => { - let sig1 = self.tcx.bound_fn_sig(*did).subst(self.tcx, substs); - if sig1 != *sig2 { - return; - } - (sig1, *did, substs) - } - _ => return, - }; - err.help(&format!("change the expected type to be function pointer `{}`", sig)); - err.help(&format!( - "if the expected type is due to type inference, cast the expected `fn` to a function \ - pointer: `{} as {}`", - self.tcx.def_path_str_with_substs(did, substs), - sig - )); - } - - // Instantiates the given path, which must refer to an item with the given - // number of type parameters and type. - #[instrument(skip(self, span), level = "debug")] - pub fn instantiate_value_path( - &self, - segments: &[hir::PathSegment<'_>], - self_ty: Option<Ty<'tcx>>, - res: Res, - span: Span, - hir_id: hir::HirId, - ) -> (Ty<'tcx>, Res) { - let tcx = self.tcx; - - let path_segs = match res { - Res::Local(_) | Res::SelfCtor(_) => vec![], - Res::Def(kind, def_id) => <dyn AstConv<'_>>::def_ids_for_value_path_segments( - self, segments, self_ty, kind, def_id, - ), - _ => bug!("instantiate_value_path on {:?}", res), - }; - - let mut user_self_ty = None; - let mut is_alias_variant_ctor = false; - match res { - Res::Def(DefKind::Ctor(CtorOf::Variant, _), _) - if let Some(self_ty) = self_ty => - { - let adt_def = self_ty.ty_adt_def().unwrap(); - user_self_ty = Some(UserSelfTy { impl_def_id: adt_def.did(), self_ty }); - is_alias_variant_ctor = true; - } - Res::Def(DefKind::AssocFn | DefKind::AssocConst, def_id) => { - let assoc_item = tcx.associated_item(def_id); - let container = assoc_item.container; - let container_id = assoc_item.container_id(tcx); - debug!(?def_id, ?container, ?container_id); - match container { - ty::TraitContainer => { - callee::check_legal_trait_for_method_call(tcx, span, None, span, container_id) - } - ty::ImplContainer => { - if segments.len() == 1 { - // `<T>::assoc` will end up here, and so - // can `T::assoc`. It this came from an - // inherent impl, we need to record the - // `T` for posterity (see `UserSelfTy` for - // details). - let self_ty = self_ty.expect("UFCS sugared assoc missing Self"); - user_self_ty = Some(UserSelfTy { impl_def_id: container_id, self_ty }); - } - } - } - } - _ => {} - } - - // Now that we have categorized what space the parameters for each - // segment belong to, let's sort out the parameters that the user - // provided (if any) into their appropriate spaces. We'll also report - // errors if type parameters are provided in an inappropriate place. - - let generic_segs: FxHashSet<_> = path_segs.iter().map(|PathSeg(_, index)| index).collect(); - let generics_has_err = <dyn AstConv<'_>>::prohibit_generics( - self, - segments.iter().enumerate().filter_map(|(index, seg)| { - if !generic_segs.contains(&index) || is_alias_variant_ctor { - Some(seg) - } else { - None - } - }), - |_| {}, - ); - - if let Res::Local(hid) = res { - let ty = self.local_ty(span, hid).decl_ty; - let ty = self.normalize_associated_types_in(span, ty); - self.write_ty(hir_id, ty); - return (ty, res); - } - - if generics_has_err { - // Don't try to infer type parameters when prohibited generic arguments were given. - user_self_ty = None; - } - - // Now we have to compare the types that the user *actually* - // provided against the types that were *expected*. If the user - // did not provide any types, then we want to substitute inference - // variables. If the user provided some types, we may still need - // to add defaults. If the user provided *too many* types, that's - // a problem. - - let mut infer_args_for_err = FxHashSet::default(); - - let mut explicit_late_bound = ExplicitLateBound::No; - for &PathSeg(def_id, index) in &path_segs { - let seg = &segments[index]; - let generics = tcx.generics_of(def_id); - - // Argument-position `impl Trait` is treated as a normal generic - // parameter internally, but we don't allow users to specify the - // parameter's value explicitly, so we have to do some error- - // checking here. - let arg_count = <dyn AstConv<'_>>::check_generic_arg_count_for_call( - tcx, - span, - def_id, - &generics, - seg, - IsMethodCall::No, - ); - - if let ExplicitLateBound::Yes = arg_count.explicit_late_bound { - explicit_late_bound = ExplicitLateBound::Yes; - } - - if let Err(GenericArgCountMismatch { reported: Some(_), .. }) = arg_count.correct { - infer_args_for_err.insert(index); - self.set_tainted_by_errors(); // See issue #53251. - } - } - - let has_self = path_segs - .last() - .map(|PathSeg(def_id, _)| tcx.generics_of(*def_id).has_self) - .unwrap_or(false); - - let (res, self_ctor_substs) = if let Res::SelfCtor(impl_def_id) = res { - let ty = self.normalize_ty(span, tcx.at(span).type_of(impl_def_id)); - match *ty.kind() { - ty::Adt(adt_def, substs) if adt_def.has_ctor() => { - let variant = adt_def.non_enum_variant(); - let ctor_def_id = variant.ctor_def_id.unwrap(); - ( - Res::Def(DefKind::Ctor(CtorOf::Struct, variant.ctor_kind), ctor_def_id), - Some(substs), - ) - } - _ => { - let mut err = tcx.sess.struct_span_err( - span, - "the `Self` constructor can only be used with tuple or unit structs", - ); - if let Some(adt_def) = ty.ty_adt_def() { - match adt_def.adt_kind() { - AdtKind::Enum => { - err.help("did you mean to use one of the enum's variants?"); - } - AdtKind::Struct | AdtKind::Union => { - err.span_suggestion( - span, - "use curly brackets", - "Self { /* fields */ }", - Applicability::HasPlaceholders, - ); - } - } - } - err.emit(); - - return (tcx.ty_error(), res); - } - } - } else { - (res, None) - }; - let def_id = res.def_id(); - - // The things we are substituting into the type should not contain - // escaping late-bound regions, and nor should the base type scheme. - let ty = tcx.type_of(def_id); - - let arg_count = GenericArgCountResult { - explicit_late_bound, - correct: if infer_args_for_err.is_empty() { - Ok(()) - } else { - Err(GenericArgCountMismatch::default()) - }, - }; - - struct CreateCtorSubstsContext<'a, 'tcx> { - fcx: &'a FnCtxt<'a, 'tcx>, - span: Span, - path_segs: &'a [PathSeg], - infer_args_for_err: &'a FxHashSet<usize>, - segments: &'a [hir::PathSegment<'a>], - } - impl<'tcx, 'a> CreateSubstsForGenericArgsCtxt<'a, 'tcx> for CreateCtorSubstsContext<'a, 'tcx> { - fn args_for_def_id( - &mut self, - def_id: DefId, - ) -> (Option<&'a hir::GenericArgs<'a>>, bool) { - if let Some(&PathSeg(_, index)) = - self.path_segs.iter().find(|&PathSeg(did, _)| *did == def_id) - { - // If we've encountered an `impl Trait`-related error, we're just - // going to infer the arguments for better error messages. - if !self.infer_args_for_err.contains(&index) { - // Check whether the user has provided generic arguments. - if let Some(ref data) = self.segments[index].args { - return (Some(data), self.segments[index].infer_args); - } - } - return (None, self.segments[index].infer_args); - } - - (None, true) - } - - fn provided_kind( - &mut self, - param: &ty::GenericParamDef, - arg: &GenericArg<'_>, - ) -> subst::GenericArg<'tcx> { - match (¶m.kind, arg) { - (GenericParamDefKind::Lifetime, GenericArg::Lifetime(lt)) => { - <dyn AstConv<'_>>::ast_region_to_region(self.fcx, lt, Some(param)).into() - } - (GenericParamDefKind::Type { .. }, GenericArg::Type(ty)) => { - self.fcx.to_ty(ty).into() - } - (GenericParamDefKind::Const { .. }, GenericArg::Const(ct)) => { - self.fcx.const_arg_to_const(&ct.value, param.def_id).into() - } - (GenericParamDefKind::Type { .. }, GenericArg::Infer(inf)) => { - self.fcx.ty_infer(Some(param), inf.span).into() - } - (GenericParamDefKind::Const { .. }, GenericArg::Infer(inf)) => { - let tcx = self.fcx.tcx(); - self.fcx.ct_infer(tcx.type_of(param.def_id), Some(param), inf.span).into() - } - _ => unreachable!(), - } - } - - fn inferred_kind( - &mut self, - substs: Option<&[subst::GenericArg<'tcx>]>, - param: &ty::GenericParamDef, - infer_args: bool, - ) -> subst::GenericArg<'tcx> { - let tcx = self.fcx.tcx(); - match param.kind { - GenericParamDefKind::Lifetime => { - self.fcx.re_infer(Some(param), self.span).unwrap().into() - } - GenericParamDefKind::Type { has_default, .. } => { - if !infer_args && has_default { - // If we have a default, then we it doesn't matter that we're not - // inferring the type arguments: we provide the default where any - // is missing. - let default = tcx.bound_type_of(param.def_id); - self.fcx - .normalize_ty(self.span, default.subst(tcx, substs.unwrap())) - .into() - } else { - // If no type arguments were provided, we have to infer them. - // This case also occurs as a result of some malformed input, e.g. - // a lifetime argument being given instead of a type parameter. - // Using inference instead of `Error` gives better error messages. - self.fcx.var_for_def(self.span, param) - } - } - GenericParamDefKind::Const { has_default } => { - if !infer_args && has_default { - tcx.bound_const_param_default(param.def_id) - .subst(tcx, substs.unwrap()) - .into() - } else { - self.fcx.var_for_def(self.span, param) - } - } - } - } - } - - let substs = self_ctor_substs.unwrap_or_else(|| { - <dyn AstConv<'_>>::create_substs_for_generic_args( - tcx, - def_id, - &[], - has_self, - self_ty, - &arg_count, - &mut CreateCtorSubstsContext { - fcx: self, - span, - path_segs: &path_segs, - infer_args_for_err: &infer_args_for_err, - segments, - }, - ) - }); - assert!(!substs.has_escaping_bound_vars()); - assert!(!ty.has_escaping_bound_vars()); - - // First, store the "user substs" for later. - self.write_user_type_annotation_from_substs(hir_id, def_id, substs, user_self_ty); - - self.add_required_obligations_for_hir(span, def_id, &substs, hir_id); - - // Substitute the values for the type parameters into the type of - // the referenced item. - let ty_substituted = self.instantiate_type_scheme(span, &substs, ty); - - if let Some(UserSelfTy { impl_def_id, self_ty }) = user_self_ty { - // In the case of `Foo<T>::method` and `<Foo<T>>::method`, if `method` - // is inherent, there is no `Self` parameter; instead, the impl needs - // type parameters, which we can infer by unifying the provided `Self` - // with the substituted impl type. - // This also occurs for an enum variant on a type alias. - let ty = tcx.type_of(impl_def_id); - - let impl_ty = self.instantiate_type_scheme(span, &substs, ty); - match self.at(&self.misc(span), self.param_env).eq(impl_ty, self_ty) { - Ok(ok) => self.register_infer_ok_obligations(ok), - Err(_) => { - self.tcx.sess.delay_span_bug( - span, - &format!( - "instantiate_value_path: (UFCS) {:?} was a subtype of {:?} but now is not?", - self_ty, - impl_ty, - ), - ); - } - } - } - - debug!("instantiate_value_path: type of {:?} is {:?}", hir_id, ty_substituted); - self.write_substs(hir_id, substs); - - (ty_substituted, res) - } - - /// Add all the obligations that are required, substituting and normalized appropriately. - pub(crate) fn add_required_obligations_for_hir( - &self, - span: Span, - def_id: DefId, - substs: SubstsRef<'tcx>, - hir_id: hir::HirId, - ) { - self.add_required_obligations_with_code(span, def_id, substs, |idx, span| { - if span.is_dummy() { - ObligationCauseCode::ExprItemObligation(def_id, hir_id, idx) - } else { - ObligationCauseCode::ExprBindingObligation(def_id, span, hir_id, idx) - } - }) - } - - #[instrument(level = "debug", skip(self, code, span, def_id, substs))] - fn add_required_obligations_with_code( - &self, - span: Span, - def_id: DefId, - substs: SubstsRef<'tcx>, - code: impl Fn(usize, Span) -> ObligationCauseCode<'tcx>, - ) { - let (bounds, _) = self.instantiate_bounds(span, def_id, &substs); - - for obligation in traits::predicates_for_generics( - |idx, predicate_span| { - traits::ObligationCause::new(span, self.body_id, code(idx, predicate_span)) - }, - self.param_env, - bounds, - ) { - self.register_predicate(obligation); - } - } - - /// Resolves `typ` by a single level if `typ` is a type variable. - /// If no resolution is possible, then an error is reported. - /// Numeric inference variables may be left unresolved. - pub fn structurally_resolved_type(&self, sp: Span, ty: Ty<'tcx>) -> Ty<'tcx> { - let ty = self.resolve_vars_with_obligations(ty); - if !ty.is_ty_var() { - ty - } else { - if !self.is_tainted_by_errors() { - self.emit_inference_failure_err((**self).body_id, sp, ty.into(), E0282, true) - .emit(); - } - let err = self.tcx.ty_error(); - self.demand_suptype(sp, err, ty); - err - } - } - - pub(in super::super) fn with_breakable_ctxt<F: FnOnce() -> R, R>( - &self, - id: hir::HirId, - ctxt: BreakableCtxt<'tcx>, - f: F, - ) -> (BreakableCtxt<'tcx>, R) { - let index; - { - let mut enclosing_breakables = self.enclosing_breakables.borrow_mut(); - index = enclosing_breakables.stack.len(); - enclosing_breakables.by_id.insert(id, index); - enclosing_breakables.stack.push(ctxt); - } - let result = f(); - let ctxt = { - let mut enclosing_breakables = self.enclosing_breakables.borrow_mut(); - debug_assert!(enclosing_breakables.stack.len() == index + 1); - enclosing_breakables.by_id.remove(&id).expect("missing breakable context"); - enclosing_breakables.stack.pop().expect("missing breakable context") - }; - (ctxt, result) - } - - /// Instantiate a QueryResponse in a probe context, without a - /// good ObligationCause. - pub(in super::super) fn probe_instantiate_query_response( - &self, - span: Span, - original_values: &OriginalQueryValues<'tcx>, - query_result: &Canonical<'tcx, QueryResponse<'tcx, Ty<'tcx>>>, - ) -> InferResult<'tcx, Ty<'tcx>> { - self.instantiate_query_response_and_region_obligations( - &traits::ObligationCause::misc(span, self.body_id), - self.param_env, - original_values, - query_result, - ) - } - - /// Returns `true` if an expression is contained inside the LHS of an assignment expression. - pub(in super::super) fn expr_in_place(&self, mut expr_id: hir::HirId) -> bool { - let mut contained_in_place = false; - - while let hir::Node::Expr(parent_expr) = - self.tcx.hir().get(self.tcx.hir().get_parent_node(expr_id)) - { - match &parent_expr.kind { - hir::ExprKind::Assign(lhs, ..) | hir::ExprKind::AssignOp(_, lhs, ..) => { - if lhs.hir_id == expr_id { - contained_in_place = true; - break; - } - } - _ => (), - } - expr_id = parent_expr.hir_id; - } - - contained_in_place - } -} diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/arg_matrix.rs b/compiler/rustc_typeck/src/check/fn_ctxt/arg_matrix.rs deleted file mode 100644 index fc83994ca..000000000 --- a/compiler/rustc_typeck/src/check/fn_ctxt/arg_matrix.rs +++ /dev/null @@ -1,383 +0,0 @@ -use std::cmp; - -use rustc_index::vec::IndexVec; -use rustc_middle::ty::error::TypeError; - -rustc_index::newtype_index! { - pub(crate) struct ExpectedIdx { - DEBUG_FORMAT = "ExpectedIdx({})", - } -} - -rustc_index::newtype_index! { - pub(crate) struct ProvidedIdx { - DEBUG_FORMAT = "ProvidedIdx({})", - } -} - -impl ExpectedIdx { - pub fn to_provided_idx(self) -> ProvidedIdx { - ProvidedIdx::from_usize(self.as_usize()) - } -} - -// An issue that might be found in the compatibility matrix -#[derive(Debug)] -enum Issue { - /// The given argument is the invalid type for the input - Invalid(usize), - /// There is a missing input - Missing(usize), - /// There's a superfluous argument - Extra(usize), - /// Two arguments should be swapped - Swap(usize, usize), - /// Several arguments should be reordered - Permutation(Vec<Option<usize>>), -} - -#[derive(Clone, Debug)] -pub(crate) enum Compatibility<'tcx> { - Compatible, - Incompatible(Option<TypeError<'tcx>>), -} - -/// Similar to `Issue`, but contains some extra information -#[derive(Debug)] -pub(crate) enum Error<'tcx> { - /// The provided argument is the invalid type for the expected input - Invalid(ProvidedIdx, ExpectedIdx, Compatibility<'tcx>), - /// There is a missing input - Missing(ExpectedIdx), - /// There's a superfluous argument - Extra(ProvidedIdx), - /// Two arguments should be swapped - Swap(ProvidedIdx, ProvidedIdx, ExpectedIdx, ExpectedIdx), - /// Several arguments should be reordered - Permutation(Vec<(ExpectedIdx, ProvidedIdx)>), -} - -pub(crate) struct ArgMatrix<'tcx> { - /// Maps the indices in the `compatibility_matrix` rows to the indices of - /// the *user provided* inputs - provided_indices: Vec<ProvidedIdx>, - /// Maps the indices in the `compatibility_matrix` columns to the indices - /// of the *expected* args - expected_indices: Vec<ExpectedIdx>, - /// The first dimension (rows) are the remaining user provided inputs to - /// match and the second dimension (cols) are the remaining expected args - /// to match - compatibility_matrix: Vec<Vec<Compatibility<'tcx>>>, -} - -impl<'tcx> ArgMatrix<'tcx> { - pub(crate) fn new<F: FnMut(ProvidedIdx, ExpectedIdx) -> Compatibility<'tcx>>( - provided_count: usize, - expected_input_count: usize, - mut is_compatible: F, - ) -> Self { - let compatibility_matrix = (0..provided_count) - .map(|i| { - (0..expected_input_count) - .map(|j| is_compatible(ProvidedIdx::from_usize(i), ExpectedIdx::from_usize(j))) - .collect() - }) - .collect(); - ArgMatrix { - provided_indices: (0..provided_count).map(ProvidedIdx::from_usize).collect(), - expected_indices: (0..expected_input_count).map(ExpectedIdx::from_usize).collect(), - compatibility_matrix, - } - } - - /// Remove a given input from consideration - fn eliminate_provided(&mut self, idx: usize) { - self.provided_indices.remove(idx); - self.compatibility_matrix.remove(idx); - } - - /// Remove a given argument from consideration - fn eliminate_expected(&mut self, idx: usize) { - self.expected_indices.remove(idx); - for row in &mut self.compatibility_matrix { - row.remove(idx); - } - } - - /// "satisfy" an input with a given arg, removing both from consideration - fn satisfy_input(&mut self, provided_idx: usize, expected_idx: usize) { - self.eliminate_provided(provided_idx); - self.eliminate_expected(expected_idx); - } - - // Returns a `Vec` of (user input, expected arg) of matched arguments. These - // are inputs on the remaining diagonal that match. - fn eliminate_satisfied(&mut self) -> Vec<(ProvidedIdx, ExpectedIdx)> { - let num_args = cmp::min(self.provided_indices.len(), self.expected_indices.len()); - let mut eliminated = vec![]; - for i in (0..num_args).rev() { - if matches!(self.compatibility_matrix[i][i], Compatibility::Compatible) { - eliminated.push((self.provided_indices[i], self.expected_indices[i])); - self.satisfy_input(i, i); - } - } - eliminated - } - - // Find some issue in the compatibility matrix - fn find_issue(&self) -> Option<Issue> { - let mat = &self.compatibility_matrix; - let ai = &self.expected_indices; - let ii = &self.provided_indices; - - // Issue: 100478, when we end the iteration, - // `next_unmatched_idx` will point to the index of the first unmatched - let mut next_unmatched_idx = 0; - for i in 0..cmp::max(ai.len(), ii.len()) { - // If we eliminate the last row, any left-over arguments are considered missing - if i >= mat.len() { - return Some(Issue::Missing(next_unmatched_idx)); - } - // If we eliminate the last column, any left-over inputs are extra - if mat[i].len() == 0 { - return Some(Issue::Extra(next_unmatched_idx)); - } - - // Make sure we don't pass the bounds of our matrix - let is_arg = i < ai.len(); - let is_input = i < ii.len(); - if is_arg && is_input && matches!(mat[i][i], Compatibility::Compatible) { - // This is a satisfied input, so move along - next_unmatched_idx += 1; - continue; - } - - let mut useless = true; - let mut unsatisfiable = true; - if is_arg { - for j in 0..ii.len() { - // If we find at least one input this argument could satisfy - // this argument isn't unsatisfiable - if matches!(mat[j][i], Compatibility::Compatible) { - unsatisfiable = false; - break; - } - } - } - if is_input { - for j in 0..ai.len() { - // If we find at least one argument that could satisfy this input - // this input isn't useless - if matches!(mat[i][j], Compatibility::Compatible) { - useless = false; - break; - } - } - } - - match (is_input, is_arg, useless, unsatisfiable) { - // If an argument is unsatisfied, and the input in its position is useless - // then the most likely explanation is that we just got the types wrong - (true, true, true, true) => return Some(Issue::Invalid(i)), - // Otherwise, if an input is useless, then indicate that this is an extra argument - (true, _, true, _) => return Some(Issue::Extra(i)), - // Otherwise, if an argument is unsatisfiable, indicate that it's missing - (_, true, _, true) => return Some(Issue::Missing(i)), - (true, true, _, _) => { - // The argument isn't useless, and the input isn't unsatisfied, - // so look for a parameter we might swap it with - // We look for swaps explicitly, instead of just falling back on permutations - // so that cases like (A,B,C,D) given (B,A,D,C) show up as two swaps, - // instead of a large permutation of 4 elements. - for j in 0..cmp::min(ai.len(), ii.len()) { - if i == j || matches!(mat[j][j], Compatibility::Compatible) { - continue; - } - if matches!(mat[i][j], Compatibility::Compatible) - && matches!(mat[j][i], Compatibility::Compatible) - { - return Some(Issue::Swap(i, j)); - } - } - } - _ => { - continue; - } - } - } - - // We didn't find any of the individual issues above, but - // there might be a larger permutation of parameters, so we now check for that - // by checking for cycles - // We use a double option at position i in this vec to represent: - // - None: We haven't computed anything about this argument yet - // - Some(None): This argument definitely doesn't participate in a cycle - // - Some(Some(x)): the i-th argument could permute to the x-th position - let mut permutation: Vec<Option<Option<usize>>> = vec![None; mat.len()]; - let mut permutation_found = false; - for i in 0..mat.len() { - if permutation[i].is_some() { - // We've already decided whether this argument is or is not in a loop - continue; - } - - let mut stack = vec![]; - let mut j = i; - let mut last = i; - let mut is_cycle = true; - loop { - stack.push(j); - // Look for params this one could slot into - let compat: Vec<_> = - mat[j] - .iter() - .enumerate() - .filter_map(|(i, c)| { - if matches!(c, Compatibility::Compatible) { Some(i) } else { None } - }) - .collect(); - if compat.len() < 1 { - // try to find a cycle even when this could go into multiple slots, see #101097 - is_cycle = false; - break; - } - j = compat[0]; - if stack.contains(&j) { - last = j; - break; - } - } - if stack.len() <= 2 { - // If we encounter a cycle of 1 or 2 elements, we'll let the - // "satisfy" and "swap" code above handle those - is_cycle = false; - } - // We've built up some chain, some of which might be a cycle - // ex: [1,2,3,4]; last = 2; j = 2; - // So, we want to mark 4, 3, and 2 as part of a permutation - permutation_found = is_cycle; - while let Some(x) = stack.pop() { - if is_cycle { - permutation[x] = Some(Some(j)); - j = x; - if j == last { - // From here on out, we're a tail leading into a cycle, - // not the cycle itself - is_cycle = false; - } - } else { - // Some(None) ensures we save time by skipping this argument again - permutation[x] = Some(None); - } - } - } - - if permutation_found { - // Map unwrap to remove the first layer of Some - let final_permutation: Vec<Option<usize>> = - permutation.into_iter().map(|x| x.unwrap()).collect(); - return Some(Issue::Permutation(final_permutation)); - } - return None; - } - - // Obviously, detecting exact user intention is impossible, so the goal here is to - // come up with as likely of a story as we can to be helpful. - // - // We'll iteratively removed "satisfied" input/argument pairs, - // then check for the cases above, until we've eliminated the entire grid - // - // We'll want to know which arguments and inputs these rows and columns correspond to - // even after we delete them. - pub(crate) fn find_errors( - mut self, - ) -> (Vec<Error<'tcx>>, IndexVec<ExpectedIdx, Option<ProvidedIdx>>) { - let provided_arg_count = self.provided_indices.len(); - - let mut errors: Vec<Error<'tcx>> = vec![]; - // For each expected argument, the matched *actual* input - let mut matched_inputs: IndexVec<ExpectedIdx, Option<ProvidedIdx>> = - IndexVec::from_elem_n(None, self.expected_indices.len()); - - // Before we start looking for issues, eliminate any arguments that are already satisfied, - // so that an argument which is already spoken for by the input it's in doesn't - // spill over into another similarly typed input - // ex: - // fn some_func(_a: i32, _b: i32) {} - // some_func(1, ""); - // Without this elimination, the first argument causes the second argument - // to show up as both a missing input and extra argument, rather than - // just an invalid type. - for (provided, expected) in self.eliminate_satisfied() { - matched_inputs[expected] = Some(provided); - } - - while !self.provided_indices.is_empty() || !self.expected_indices.is_empty() { - let res = self.find_issue(); - match res { - Some(Issue::Invalid(idx)) => { - let compatibility = self.compatibility_matrix[idx][idx].clone(); - let input_idx = self.provided_indices[idx]; - let arg_idx = self.expected_indices[idx]; - self.satisfy_input(idx, idx); - errors.push(Error::Invalid(input_idx, arg_idx, compatibility)); - } - Some(Issue::Extra(idx)) => { - let input_idx = self.provided_indices[idx]; - self.eliminate_provided(idx); - errors.push(Error::Extra(input_idx)); - } - Some(Issue::Missing(idx)) => { - let arg_idx = self.expected_indices[idx]; - self.eliminate_expected(idx); - errors.push(Error::Missing(arg_idx)); - } - Some(Issue::Swap(idx, other)) => { - let input_idx = self.provided_indices[idx]; - let other_input_idx = self.provided_indices[other]; - let arg_idx = self.expected_indices[idx]; - let other_arg_idx = self.expected_indices[other]; - let (min, max) = (cmp::min(idx, other), cmp::max(idx, other)); - self.satisfy_input(min, max); - // Subtract 1 because we already removed the "min" row - self.satisfy_input(max - 1, min); - errors.push(Error::Swap(input_idx, other_input_idx, arg_idx, other_arg_idx)); - matched_inputs[other_arg_idx] = Some(input_idx); - matched_inputs[arg_idx] = Some(other_input_idx); - } - Some(Issue::Permutation(args)) => { - let mut idxs: Vec<usize> = args.iter().filter_map(|&a| a).collect(); - - let mut real_idxs: IndexVec<ProvidedIdx, Option<(ExpectedIdx, ProvidedIdx)>> = - IndexVec::from_elem_n(None, provided_arg_count); - for (src, dst) in - args.iter().enumerate().filter_map(|(src, dst)| dst.map(|dst| (src, dst))) - { - let src_input_idx = self.provided_indices[src]; - let dst_input_idx = self.provided_indices[dst]; - let dest_arg_idx = self.expected_indices[dst]; - real_idxs[src_input_idx] = Some((dest_arg_idx, dst_input_idx)); - matched_inputs[dest_arg_idx] = Some(src_input_idx); - } - idxs.sort(); - idxs.reverse(); - for i in idxs { - self.satisfy_input(i, i); - } - errors.push(Error::Permutation(real_idxs.into_iter().flatten().collect())); - } - None => { - // We didn't find any issues, so we need to push the algorithm forward - // First, eliminate any arguments that currently satisfy their inputs - let eliminated = self.eliminate_satisfied(); - assert!(!eliminated.is_empty(), "didn't eliminated any indice in this round"); - for (inp, arg) in eliminated { - matched_inputs[arg] = Some(inp); - } - } - }; - } - - return (errors, matched_inputs); - } -} diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs b/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs deleted file mode 100644 index 311fcaada..000000000 --- a/compiler/rustc_typeck/src/check/fn_ctxt/checks.rs +++ /dev/null @@ -1,2222 +0,0 @@ -use crate::astconv::AstConv; -use crate::check::coercion::CoerceMany; -use crate::check::fn_ctxt::arg_matrix::{ - ArgMatrix, Compatibility, Error, ExpectedIdx, ProvidedIdx, -}; -use crate::check::gather_locals::Declaration; -use crate::check::intrinsicck::InlineAsmCtxt; -use crate::check::method::MethodCallee; -use crate::check::Expectation::*; -use crate::check::TupleArgumentsFlag::*; -use crate::check::{ - potentially_plural_count, struct_span_err, BreakableCtxt, Diverges, Expectation, FnCtxt, - LocalTy, Needs, TupleArgumentsFlag, -}; -use crate::structured_errors::StructuredDiagnostic; - -use rustc_ast as ast; -use rustc_data_structures::fx::FxHashSet; -use rustc_errors::{pluralize, Applicability, Diagnostic, DiagnosticId, MultiSpan}; -use rustc_hir as hir; -use rustc_hir::def::{CtorOf, DefKind, Res}; -use rustc_hir::def_id::DefId; -use rustc_hir::{ExprKind, Node, QPath}; -use rustc_index::vec::IndexVec; -use rustc_infer::infer::error_reporting::{FailureCode, ObligationCauseExt}; -use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; -use rustc_infer::infer::InferOk; -use rustc_infer::infer::TypeTrace; -use rustc_middle::ty::adjustment::AllowTwoPhase; -use rustc_middle::ty::visit::TypeVisitable; -use rustc_middle::ty::{self, DefIdTree, IsSuggestable, Ty, TypeSuperVisitable, TypeVisitor}; -use rustc_session::Session; -use rustc_span::symbol::Ident; -use rustc_span::{self, sym, Span}; -use rustc_trait_selection::traits::{self, ObligationCauseCode, SelectionContext}; - -use std::iter; -use std::ops::ControlFlow; -use std::slice; - -impl<'a, 'tcx> FnCtxt<'a, 'tcx> { - pub(in super::super) fn check_casts(&self) { - let mut deferred_cast_checks = self.deferred_cast_checks.borrow_mut(); - debug!("FnCtxt::check_casts: {} deferred checks", deferred_cast_checks.len()); - for cast in deferred_cast_checks.drain(..) { - cast.check(self); - } - } - - pub(in super::super) fn check_transmutes(&self) { - let mut deferred_transmute_checks = self.deferred_transmute_checks.borrow_mut(); - debug!("FnCtxt::check_transmutes: {} deferred checks", deferred_transmute_checks.len()); - for (from, to, span) in deferred_transmute_checks.drain(..) { - self.check_transmute(span, from, to); - } - } - - pub(in super::super) fn check_asms(&self) { - let mut deferred_asm_checks = self.deferred_asm_checks.borrow_mut(); - debug!("FnCtxt::check_asm: {} deferred checks", deferred_asm_checks.len()); - for (asm, hir_id) in deferred_asm_checks.drain(..) { - let enclosing_id = self.tcx.hir().enclosing_body_owner(hir_id); - let get_operand_ty = |expr| { - let ty = self.typeck_results.borrow().expr_ty_adjusted(expr); - let ty = self.resolve_vars_if_possible(ty); - if ty.has_infer_types_or_consts() { - assert!(self.is_tainted_by_errors()); - self.tcx.ty_error() - } else { - self.tcx.erase_regions(ty) - } - }; - InlineAsmCtxt::new_in_fn(self.tcx, self.param_env, get_operand_ty) - .check_asm(asm, self.tcx.hir().local_def_id_to_hir_id(enclosing_id)); - } - } - - pub(in super::super) fn check_method_argument_types( - &self, - sp: Span, - expr: &'tcx hir::Expr<'tcx>, - method: Result<MethodCallee<'tcx>, ()>, - args_no_rcvr: &'tcx [hir::Expr<'tcx>], - tuple_arguments: TupleArgumentsFlag, - expected: Expectation<'tcx>, - ) -> Ty<'tcx> { - let has_error = match method { - Ok(method) => method.substs.references_error() || method.sig.references_error(), - Err(_) => true, - }; - if has_error { - let err_inputs = self.err_args(args_no_rcvr.len()); - - let err_inputs = match tuple_arguments { - DontTupleArguments => err_inputs, - TupleArguments => vec![self.tcx.intern_tup(&err_inputs)], - }; - - self.check_argument_types( - sp, - expr, - &err_inputs, - None, - args_no_rcvr, - false, - tuple_arguments, - method.ok().map(|method| method.def_id), - ); - return self.tcx.ty_error(); - } - - let method = method.unwrap(); - // HACK(eddyb) ignore self in the definition (see above). - let expected_input_tys = self.expected_inputs_for_expected_output( - sp, - expected, - method.sig.output(), - &method.sig.inputs()[1..], - ); - self.check_argument_types( - sp, - expr, - &method.sig.inputs()[1..], - expected_input_tys, - args_no_rcvr, - method.sig.c_variadic, - tuple_arguments, - Some(method.def_id), - ); - method.sig.output() - } - - /// Generic function that factors out common logic from function calls, - /// method calls and overloaded operators. - pub(in super::super) fn check_argument_types( - &self, - // Span enclosing the call site - call_span: Span, - // Expression of the call site - call_expr: &'tcx hir::Expr<'tcx>, - // Types (as defined in the *signature* of the target function) - formal_input_tys: &[Ty<'tcx>], - // More specific expected types, after unifying with caller output types - expected_input_tys: Option<Vec<Ty<'tcx>>>, - // The expressions for each provided argument - provided_args: &'tcx [hir::Expr<'tcx>], - // Whether the function is variadic, for example when imported from C - c_variadic: bool, - // Whether the arguments have been bundled in a tuple (ex: closures) - tuple_arguments: TupleArgumentsFlag, - // The DefId for the function being called, for better error messages - fn_def_id: Option<DefId>, - ) { - let tcx = self.tcx; - - // Conceptually, we've got some number of expected inputs, and some number of provided arguments - // and we can form a grid of whether each argument could satisfy a given input: - // in1 | in2 | in3 | ... - // arg1 ? | | | - // arg2 | ? | | - // arg3 | | ? | - // ... - // Initially, we just check the diagonal, because in the case of correct code - // these are the only checks that matter - // However, in the unhappy path, we'll fill in this whole grid to attempt to provide - // better error messages about invalid method calls. - - // All the input types from the fn signature must outlive the call - // so as to validate implied bounds. - for (&fn_input_ty, arg_expr) in iter::zip(formal_input_tys, provided_args) { - self.register_wf_obligation(fn_input_ty.into(), arg_expr.span, traits::MiscObligation); - } - - let mut err_code = "E0061"; - - // If the arguments should be wrapped in a tuple (ex: closures), unwrap them here - let (formal_input_tys, expected_input_tys) = if tuple_arguments == TupleArguments { - let tuple_type = self.structurally_resolved_type(call_span, formal_input_tys[0]); - match tuple_type.kind() { - // We expected a tuple and got a tuple - ty::Tuple(arg_types) => { - // Argument length differs - if arg_types.len() != provided_args.len() { - err_code = "E0057"; - } - let expected_input_tys = match expected_input_tys { - Some(expected_input_tys) => match expected_input_tys.get(0) { - Some(ty) => match ty.kind() { - ty::Tuple(tys) => Some(tys.iter().collect()), - _ => None, - }, - None => None, - }, - None => None, - }; - (arg_types.iter().collect(), expected_input_tys) - } - _ => { - // Otherwise, there's a mismatch, so clear out what we're expecting, and set - // our input types to err_args so we don't blow up the error messages - struct_span_err!( - tcx.sess, - call_span, - E0059, - "cannot use call notation; the first type parameter \ - for the function trait is neither a tuple nor unit" - ) - .emit(); - (self.err_args(provided_args.len()), None) - } - } - } else { - (formal_input_tys.to_vec(), expected_input_tys) - }; - - // If there are no external expectations at the call site, just use the types from the function defn - let expected_input_tys = if let Some(expected_input_tys) = expected_input_tys { - assert_eq!(expected_input_tys.len(), formal_input_tys.len()); - expected_input_tys - } else { - formal_input_tys.clone() - }; - - let minimum_input_count = expected_input_tys.len(); - let provided_arg_count = provided_args.len(); - - let is_const_eval_select = matches!(fn_def_id, Some(def_id) if - self.tcx.def_kind(def_id) == hir::def::DefKind::Fn - && self.tcx.is_intrinsic(def_id) - && self.tcx.item_name(def_id) == sym::const_eval_select); - - // We introduce a helper function to demand that a given argument satisfy a given input - // This is more complicated than just checking type equality, as arguments could be coerced - // This version writes those types back so further type checking uses the narrowed types - let demand_compatible = |idx| { - let formal_input_ty: Ty<'tcx> = formal_input_tys[idx]; - let expected_input_ty: Ty<'tcx> = expected_input_tys[idx]; - let provided_arg = &provided_args[idx]; - - debug!("checking argument {}: {:?} = {:?}", idx, provided_arg, formal_input_ty); - - // We're on the happy path here, so we'll do a more involved check and write back types - // To check compatibility, we'll do 3 things: - // 1. Unify the provided argument with the expected type - let expectation = Expectation::rvalue_hint(self, expected_input_ty); - - let checked_ty = self.check_expr_with_expectation(provided_arg, expectation); - - // 2. Coerce to the most detailed type that could be coerced - // to, which is `expected_ty` if `rvalue_hint` returns an - // `ExpectHasType(expected_ty)`, or the `formal_ty` otherwise. - let coerced_ty = expectation.only_has_type(self).unwrap_or(formal_input_ty); - - // Cause selection errors caused by resolving a single argument to point at the - // argument and not the call. This lets us customize the span pointed to in the - // fulfillment error to be more accurate. - let coerced_ty = self.resolve_vars_with_obligations(coerced_ty); - - let coerce_error = self - .try_coerce(provided_arg, checked_ty, coerced_ty, AllowTwoPhase::Yes, None) - .err(); - - if coerce_error.is_some() { - return Compatibility::Incompatible(coerce_error); - } - - // Check that second and third argument of `const_eval_select` must be `FnDef`, and additionally that - // the second argument must be `const fn`. The first argument must be a tuple, but this is already expressed - // in the function signature (`F: FnOnce<ARG>`), so I did not bother to add another check here. - // - // This check is here because there is currently no way to express a trait bound for `FnDef` types only. - if is_const_eval_select && (1..=2).contains(&idx) { - if let ty::FnDef(def_id, _) = checked_ty.kind() { - if idx == 1 && !self.tcx.is_const_fn_raw(*def_id) { - self.tcx - .sess - .struct_span_err(provided_arg.span, "this argument must be a `const fn`") - .help("consult the documentation on `const_eval_select` for more information") - .emit(); - } - } else { - self.tcx - .sess - .struct_span_err(provided_arg.span, "this argument must be a function item") - .note(format!("expected a function item, found {checked_ty}")) - .help( - "consult the documentation on `const_eval_select` for more information", - ) - .emit(); - } - } - - // 3. Check if the formal type is a supertype of the checked one - // and register any such obligations for future type checks - let supertype_error = self - .at(&self.misc(provided_arg.span), self.param_env) - .sup(formal_input_ty, coerced_ty); - let subtyping_error = match supertype_error { - Ok(InferOk { obligations, value: () }) => { - self.register_predicates(obligations); - None - } - Err(err) => Some(err), - }; - - // If neither check failed, the types are compatible - match subtyping_error { - None => Compatibility::Compatible, - Some(_) => Compatibility::Incompatible(subtyping_error), - } - }; - - // To start, we only care "along the diagonal", where we expect every - // provided arg to be in the right spot - let mut compatibility_diagonal = - vec![Compatibility::Incompatible(None); provided_args.len()]; - - // Keep track of whether we *could possibly* be satisfied, i.e. whether we're on the happy path - // if the wrong number of arguments were supplied, we CAN'T be satisfied, - // and if we're c_variadic, the supplied arguments must be >= the minimum count from the function - // otherwise, they need to be identical, because rust doesn't currently support variadic functions - let mut call_appears_satisfied = if c_variadic { - provided_arg_count >= minimum_input_count - } else { - provided_arg_count == minimum_input_count - }; - - // Check the arguments. - // We do this in a pretty awful way: first we type-check any arguments - // that are not closures, then we type-check the closures. This is so - // that we have more information about the types of arguments when we - // type-check the functions. This isn't really the right way to do this. - for check_closures in [false, true] { - // More awful hacks: before we check argument types, try to do - // an "opportunistic" trait resolution of any trait bounds on - // the call. This helps coercions. - if check_closures { - self.select_obligations_where_possible(false, |_| {}) - } - - // Check each argument, to satisfy the input it was provided for - // Visually, we're traveling down the diagonal of the compatibility matrix - for (idx, arg) in provided_args.iter().enumerate() { - // Warn only for the first loop (the "no closures" one). - // Closure arguments themselves can't be diverging, but - // a previous argument can, e.g., `foo(panic!(), || {})`. - if !check_closures { - self.warn_if_unreachable(arg.hir_id, arg.span, "expression"); - } - - // For C-variadic functions, we don't have a declared type for all of - // the arguments hence we only do our usual type checking with - // the arguments who's types we do know. However, we *can* check - // for unreachable expressions (see above). - // FIXME: unreachable warning current isn't emitted - if idx >= minimum_input_count { - continue; - } - - let is_closure = matches!(arg.kind, ExprKind::Closure { .. }); - if is_closure != check_closures { - continue; - } - - let compatible = demand_compatible(idx); - let is_compatible = matches!(compatible, Compatibility::Compatible); - compatibility_diagonal[idx] = compatible; - - if !is_compatible { - call_appears_satisfied = false; - } - } - } - - if c_variadic && provided_arg_count < minimum_input_count { - err_code = "E0060"; - } - - for arg in provided_args.iter().skip(minimum_input_count) { - // Make sure we've checked this expr at least once. - let arg_ty = self.check_expr(&arg); - - // If the function is c-style variadic, we skipped a bunch of arguments - // so we need to check those, and write out the types - // Ideally this would be folded into the above, for uniform style - // but c-variadic is already a corner case - if c_variadic { - fn variadic_error<'tcx>( - sess: &'tcx Session, - span: Span, - ty: Ty<'tcx>, - cast_ty: &str, - ) { - use crate::structured_errors::MissingCastForVariadicArg; - - MissingCastForVariadicArg { sess, span, ty, cast_ty }.diagnostic().emit(); - } - - // There are a few types which get autopromoted when passed via varargs - // in C but we just error out instead and require explicit casts. - let arg_ty = self.structurally_resolved_type(arg.span, arg_ty); - match arg_ty.kind() { - ty::Float(ty::FloatTy::F32) => { - variadic_error(tcx.sess, arg.span, arg_ty, "c_double"); - } - ty::Int(ty::IntTy::I8 | ty::IntTy::I16) | ty::Bool => { - variadic_error(tcx.sess, arg.span, arg_ty, "c_int"); - } - ty::Uint(ty::UintTy::U8 | ty::UintTy::U16) => { - variadic_error(tcx.sess, arg.span, arg_ty, "c_uint"); - } - ty::FnDef(..) => { - let ptr_ty = self.tcx.mk_fn_ptr(arg_ty.fn_sig(self.tcx)); - let ptr_ty = self.resolve_vars_if_possible(ptr_ty); - variadic_error(tcx.sess, arg.span, arg_ty, &ptr_ty.to_string()); - } - _ => {} - } - } - } - - if !call_appears_satisfied { - let compatibility_diagonal = IndexVec::from_raw(compatibility_diagonal); - let provided_args = IndexVec::from_iter(provided_args.iter().take(if c_variadic { - minimum_input_count - } else { - provided_arg_count - })); - debug_assert_eq!( - formal_input_tys.len(), - expected_input_tys.len(), - "expected formal_input_tys to be the same size as expected_input_tys" - ); - let formal_and_expected_inputs = IndexVec::from_iter( - formal_input_tys - .iter() - .copied() - .zip(expected_input_tys.iter().copied()) - .map(|vars| self.resolve_vars_if_possible(vars)), - ); - - self.report_arg_errors( - compatibility_diagonal, - formal_and_expected_inputs, - provided_args, - c_variadic, - err_code, - fn_def_id, - call_span, - call_expr, - ); - } - } - - fn report_arg_errors( - &self, - compatibility_diagonal: IndexVec<ProvidedIdx, Compatibility<'tcx>>, - formal_and_expected_inputs: IndexVec<ExpectedIdx, (Ty<'tcx>, Ty<'tcx>)>, - provided_args: IndexVec<ProvidedIdx, &'tcx hir::Expr<'tcx>>, - c_variadic: bool, - err_code: &str, - fn_def_id: Option<DefId>, - call_span: Span, - call_expr: &hir::Expr<'tcx>, - ) { - // Next, let's construct the error - let (error_span, full_call_span, ctor_of, is_method) = match &call_expr.kind { - hir::ExprKind::Call( - hir::Expr { hir_id, span, kind: hir::ExprKind::Path(qpath), .. }, - _, - ) => { - if let Res::Def(DefKind::Ctor(of, _), _) = - self.typeck_results.borrow().qpath_res(qpath, *hir_id) - { - (call_span, *span, Some(of), false) - } else { - (call_span, *span, None, false) - } - } - hir::ExprKind::Call(hir::Expr { span, .. }, _) => (call_span, *span, None, false), - hir::ExprKind::MethodCall(path_segment, _, _, span) => { - let ident_span = path_segment.ident.span; - let ident_span = if let Some(args) = path_segment.args { - ident_span.with_hi(args.span_ext.hi()) - } else { - ident_span - }; - // methods are never ctors - (*span, ident_span, None, true) - } - k => span_bug!(call_span, "checking argument types on a non-call: `{:?}`", k), - }; - let args_span = error_span.trim_start(full_call_span).unwrap_or(error_span); - let call_name = match ctor_of { - Some(CtorOf::Struct) => "struct", - Some(CtorOf::Variant) => "enum variant", - None => "function", - }; - - // Don't print if it has error types or is just plain `_` - fn has_error_or_infer<'tcx>(tys: impl IntoIterator<Item = Ty<'tcx>>) -> bool { - tys.into_iter().any(|ty| ty.references_error() || ty.is_ty_var()) - } - - self.set_tainted_by_errors(); - let tcx = self.tcx; - - // Get the argument span in the context of the call span so that - // suggestions and labels are (more) correct when an arg is a - // macro invocation. - let normalize_span = |span: Span| -> Span { - let normalized_span = span.find_ancestor_inside(error_span).unwrap_or(span); - // Sometimes macros mess up the spans, so do not normalize the - // arg span to equal the error span, because that's less useful - // than pointing out the arg expr in the wrong context. - if normalized_span.source_equal(error_span) { span } else { normalized_span } - }; - - // Precompute the provided types and spans, since that's all we typically need for below - let provided_arg_tys: IndexVec<ProvidedIdx, (Ty<'tcx>, Span)> = provided_args - .iter() - .map(|expr| { - let ty = self - .typeck_results - .borrow() - .expr_ty_adjusted_opt(*expr) - .unwrap_or_else(|| tcx.ty_error()); - (self.resolve_vars_if_possible(ty), normalize_span(expr.span)) - }) - .collect(); - let callee_expr = match &call_expr.peel_blocks().kind { - hir::ExprKind::Call(callee, _) => Some(*callee), - hir::ExprKind::MethodCall(_, receiver, ..) => { - if let Some((DefKind::AssocFn, def_id)) = - self.typeck_results.borrow().type_dependent_def(call_expr.hir_id) - && let Some(assoc) = tcx.opt_associated_item(def_id) - && assoc.fn_has_self_parameter - { - Some(*receiver) - } else { - None - } - } - _ => None, - }; - let callee_ty = callee_expr - .and_then(|callee_expr| self.typeck_results.borrow().expr_ty_adjusted_opt(callee_expr)); - - // A "softer" version of the `demand_compatible`, which checks types without persisting them, - // and treats error types differently - // This will allow us to "probe" for other argument orders that would likely have been correct - let check_compatible = |provided_idx: ProvidedIdx, expected_idx: ExpectedIdx| { - if provided_idx.as_usize() == expected_idx.as_usize() { - return compatibility_diagonal[provided_idx].clone(); - } - - let (formal_input_ty, expected_input_ty) = formal_and_expected_inputs[expected_idx]; - // If either is an error type, we defy the usual convention and consider them to *not* be - // coercible. This prevents our error message heuristic from trying to pass errors into - // every argument. - if (formal_input_ty, expected_input_ty).references_error() { - return Compatibility::Incompatible(None); - } - - let (arg_ty, arg_span) = provided_arg_tys[provided_idx]; - - let expectation = Expectation::rvalue_hint(self, expected_input_ty); - let coerced_ty = expectation.only_has_type(self).unwrap_or(formal_input_ty); - let can_coerce = self.can_coerce(arg_ty, coerced_ty); - if !can_coerce { - return Compatibility::Incompatible(Some(ty::error::TypeError::Sorts( - ty::error::ExpectedFound::new(true, coerced_ty, arg_ty), - ))); - } - - // Using probe here, since we don't want this subtyping to affect inference. - let subtyping_error = self.probe(|_| { - self.at(&self.misc(arg_span), self.param_env).sup(formal_input_ty, coerced_ty).err() - }); - - // Same as above: if either the coerce type or the checked type is an error type, - // consider them *not* compatible. - let references_error = (coerced_ty, arg_ty).references_error(); - match (references_error, subtyping_error) { - (false, None) => Compatibility::Compatible, - (_, subtyping_error) => Compatibility::Incompatible(subtyping_error), - } - }; - - // The algorithm here is inspired by levenshtein distance and longest common subsequence. - // We'll try to detect 4 different types of mistakes: - // - An extra parameter has been provided that doesn't satisfy *any* of the other inputs - // - An input is missing, which isn't satisfied by *any* of the other arguments - // - Some number of arguments have been provided in the wrong order - // - A type is straight up invalid - - // First, let's find the errors - let (mut errors, matched_inputs) = - ArgMatrix::new(provided_args.len(), formal_and_expected_inputs.len(), check_compatible) - .find_errors(); - - // First, check if we just need to wrap some arguments in a tuple. - if let Some((mismatch_idx, terr)) = - compatibility_diagonal.iter().enumerate().find_map(|(i, c)| { - if let Compatibility::Incompatible(Some(terr)) = c { - Some((i, *terr)) - } else { - None - } - }) - { - // Is the first bad expected argument a tuple? - // Do we have as many extra provided arguments as the tuple's length? - // If so, we might have just forgotten to wrap some args in a tuple. - if let Some(ty::Tuple(tys)) = - formal_and_expected_inputs.get(mismatch_idx.into()).map(|tys| tys.1.kind()) - // If the tuple is unit, we're not actually wrapping any arguments. - && !tys.is_empty() - && provided_arg_tys.len() == formal_and_expected_inputs.len() - 1 + tys.len() - { - // Wrap up the N provided arguments starting at this position in a tuple. - let provided_as_tuple = tcx.mk_tup( - provided_arg_tys.iter().map(|(ty, _)| *ty).skip(mismatch_idx).take(tys.len()), - ); - - let mut satisfied = true; - // Check if the newly wrapped tuple + rest of the arguments are compatible. - for ((_, expected_ty), provided_ty) in std::iter::zip( - formal_and_expected_inputs.iter().skip(mismatch_idx), - [provided_as_tuple].into_iter().chain( - provided_arg_tys.iter().map(|(ty, _)| *ty).skip(mismatch_idx + tys.len()), - ), - ) { - if !self.can_coerce(provided_ty, *expected_ty) { - satisfied = false; - break; - } - } - - // If they're compatible, suggest wrapping in an arg, and we're done! - // Take some care with spans, so we don't suggest wrapping a macro's - // innards in parenthesis, for example. - if satisfied - && let Some((_, lo)) = - provided_arg_tys.get(ProvidedIdx::from_usize(mismatch_idx)) - && let Some((_, hi)) = - provided_arg_tys.get(ProvidedIdx::from_usize(mismatch_idx + tys.len() - 1)) - { - let mut err; - if tys.len() == 1 { - // A tuple wrap suggestion actually occurs within, - // so don't do anything special here. - err = self.report_and_explain_type_error( - TypeTrace::types( - &self.misc(*lo), - true, - formal_and_expected_inputs[mismatch_idx.into()].1, - provided_arg_tys[mismatch_idx.into()].0, - ), - terr, - ); - err.span_label( - full_call_span, - format!("arguments to this {} are incorrect", call_name), - ); - } else { - err = tcx.sess.struct_span_err_with_code( - full_call_span, - &format!( - "this {} takes {}{} but {} {} supplied", - call_name, - if c_variadic { "at least " } else { "" }, - potentially_plural_count( - formal_and_expected_inputs.len(), - "argument" - ), - potentially_plural_count(provided_args.len(), "argument"), - pluralize!("was", provided_args.len()) - ), - DiagnosticId::Error(err_code.to_owned()), - ); - err.multipart_suggestion_verbose( - "wrap these arguments in parentheses to construct a tuple", - vec![ - (lo.shrink_to_lo(), "(".to_string()), - (hi.shrink_to_hi(), ")".to_string()), - ], - Applicability::MachineApplicable, - ); - }; - self.label_fn_like( - &mut err, - fn_def_id, - callee_ty, - Some(mismatch_idx), - is_method, - ); - err.emit(); - return; - } - } - } - - // Okay, so here's where it gets complicated in regards to what errors - // we emit and how. - // There are 3 different "types" of errors we might encounter. - // 1) Missing/extra/swapped arguments - // 2) Valid but incorrect arguments - // 3) Invalid arguments - // - Currently I think this only comes up with `CyclicTy` - // - // We first need to go through, remove those from (3) and emit those - // as their own error, particularly since they're error code and - // message is special. From what I can tell, we *must* emit these - // here (vs somewhere prior to this function) since the arguments - // become invalid *because* of how they get used in the function. - // It is what it is. - - if errors.is_empty() { - if cfg!(debug_assertions) { - span_bug!(error_span, "expected errors from argument matrix"); - } else { - tcx.sess - .struct_span_err( - error_span, - "argument type mismatch was detected, \ - but rustc had trouble determining where", - ) - .note( - "we would appreciate a bug report: \ - https://github.com/rust-lang/rust/issues/new", - ) - .emit(); - } - return; - } - - errors.drain_filter(|error| { - let Error::Invalid(provided_idx, expected_idx, Compatibility::Incompatible(Some(e))) = error else { return false }; - let (provided_ty, provided_span) = provided_arg_tys[*provided_idx]; - let (expected_ty, _) = formal_and_expected_inputs[*expected_idx]; - let cause = &self.misc(provided_span); - let trace = TypeTrace::types(cause, true, expected_ty, provided_ty); - if !matches!(trace.cause.as_failure_code(*e), FailureCode::Error0308(_)) { - self.report_and_explain_type_error(trace, *e).emit(); - return true; - } - false - }); - - // We're done if we found errors, but we already emitted them. - if errors.is_empty() { - return; - } - - // Okay, now that we've emitted the special errors separately, we - // are only left missing/extra/swapped and mismatched arguments, both - // can be collated pretty easily if needed. - - // Next special case: if there is only one "Incompatible" error, just emit that - if let [ - Error::Invalid(provided_idx, expected_idx, Compatibility::Incompatible(Some(err))), - ] = &errors[..] - { - let (formal_ty, expected_ty) = formal_and_expected_inputs[*expected_idx]; - let (provided_ty, provided_arg_span) = provided_arg_tys[*provided_idx]; - let cause = &self.misc(provided_arg_span); - let trace = TypeTrace::types(cause, true, expected_ty, provided_ty); - let mut err = self.report_and_explain_type_error(trace, *err); - self.emit_coerce_suggestions( - &mut err, - &provided_args[*provided_idx], - provided_ty, - Expectation::rvalue_hint(self, expected_ty) - .only_has_type(self) - .unwrap_or(formal_ty), - None, - None, - ); - err.span_label( - full_call_span, - format!("arguments to this {} are incorrect", call_name), - ); - // Call out where the function is defined - self.label_fn_like( - &mut err, - fn_def_id, - callee_ty, - Some(expected_idx.as_usize()), - is_method, - ); - err.emit(); - return; - } - - let mut err = if formal_and_expected_inputs.len() == provided_args.len() { - struct_span_err!( - tcx.sess, - full_call_span, - E0308, - "arguments to this {} are incorrect", - call_name, - ) - } else { - tcx.sess.struct_span_err_with_code( - full_call_span, - &format!( - "this {} takes {}{} but {} {} supplied", - call_name, - if c_variadic { "at least " } else { "" }, - potentially_plural_count(formal_and_expected_inputs.len(), "argument"), - potentially_plural_count(provided_args.len(), "argument"), - pluralize!("was", provided_args.len()) - ), - DiagnosticId::Error(err_code.to_owned()), - ) - }; - - // As we encounter issues, keep track of what we want to provide for the suggestion - let mut labels = vec![]; - // If there is a single error, we give a specific suggestion; otherwise, we change to - // "did you mean" with the suggested function call - enum SuggestionText { - None, - Provide(bool), - Remove(bool), - Swap, - Reorder, - DidYouMean, - } - let mut suggestion_text = SuggestionText::None; - - let mut errors = errors.into_iter().peekable(); - while let Some(error) = errors.next() { - match error { - Error::Invalid(provided_idx, expected_idx, compatibility) => { - let (formal_ty, expected_ty) = formal_and_expected_inputs[expected_idx]; - let (provided_ty, provided_span) = provided_arg_tys[provided_idx]; - if let Compatibility::Incompatible(error) = compatibility { - let cause = &self.misc(provided_span); - let trace = TypeTrace::types(cause, true, expected_ty, provided_ty); - if let Some(e) = error { - self.note_type_err( - &mut err, - &trace.cause, - None, - Some(trace.values), - e, - false, - true, - ); - } - } - - self.emit_coerce_suggestions( - &mut err, - &provided_args[provided_idx], - provided_ty, - Expectation::rvalue_hint(self, expected_ty) - .only_has_type(self) - .unwrap_or(formal_ty), - None, - None, - ); - } - Error::Extra(arg_idx) => { - let (provided_ty, provided_span) = provided_arg_tys[arg_idx]; - let provided_ty_name = if !has_error_or_infer([provided_ty]) { - // FIXME: not suggestable, use something else - format!(" of type `{}`", provided_ty) - } else { - "".to_string() - }; - labels - .push((provided_span, format!("argument{} unexpected", provided_ty_name))); - suggestion_text = match suggestion_text { - SuggestionText::None => SuggestionText::Remove(false), - SuggestionText::Remove(_) => SuggestionText::Remove(true), - _ => SuggestionText::DidYouMean, - }; - } - Error::Missing(expected_idx) => { - // If there are multiple missing arguments adjacent to each other, - // then we can provide a single error. - - let mut missing_idxs = vec![expected_idx]; - while let Some(e) = errors.next_if(|e| { - matches!(e, Error::Missing(next_expected_idx) - if *next_expected_idx == *missing_idxs.last().unwrap() + 1) - }) { - match e { - Error::Missing(expected_idx) => missing_idxs.push(expected_idx), - _ => unreachable!(), - } - } - - // NOTE: Because we might be re-arranging arguments, might have extra - // arguments, etc. it's hard to *really* know where we should provide - // this error label, so as a heuristic, we point to the provided arg, or - // to the call if the missing inputs pass the provided args. - match &missing_idxs[..] { - &[expected_idx] => { - let (_, input_ty) = formal_and_expected_inputs[expected_idx]; - let span = if let Some((_, arg_span)) = - provided_arg_tys.get(expected_idx.to_provided_idx()) - { - *arg_span - } else { - args_span - }; - let rendered = if !has_error_or_infer([input_ty]) { - format!(" of type `{}`", input_ty) - } else { - "".to_string() - }; - labels.push((span, format!("an argument{} is missing", rendered))); - suggestion_text = match suggestion_text { - SuggestionText::None => SuggestionText::Provide(false), - SuggestionText::Provide(_) => SuggestionText::Provide(true), - _ => SuggestionText::DidYouMean, - }; - } - &[first_idx, second_idx] => { - let (_, first_expected_ty) = formal_and_expected_inputs[first_idx]; - let (_, second_expected_ty) = formal_and_expected_inputs[second_idx]; - let span = if let (Some((_, first_span)), Some((_, second_span))) = ( - provided_arg_tys.get(first_idx.to_provided_idx()), - provided_arg_tys.get(second_idx.to_provided_idx()), - ) { - first_span.to(*second_span) - } else { - args_span - }; - let rendered = - if !has_error_or_infer([first_expected_ty, second_expected_ty]) { - format!( - " of type `{}` and `{}`", - first_expected_ty, second_expected_ty - ) - } else { - "".to_string() - }; - labels.push((span, format!("two arguments{} are missing", rendered))); - suggestion_text = match suggestion_text { - SuggestionText::None | SuggestionText::Provide(_) => { - SuggestionText::Provide(true) - } - _ => SuggestionText::DidYouMean, - }; - } - &[first_idx, second_idx, third_idx] => { - let (_, first_expected_ty) = formal_and_expected_inputs[first_idx]; - let (_, second_expected_ty) = formal_and_expected_inputs[second_idx]; - let (_, third_expected_ty) = formal_and_expected_inputs[third_idx]; - let span = if let (Some((_, first_span)), Some((_, third_span))) = ( - provided_arg_tys.get(first_idx.to_provided_idx()), - provided_arg_tys.get(third_idx.to_provided_idx()), - ) { - first_span.to(*third_span) - } else { - args_span - }; - let rendered = if !has_error_or_infer([ - first_expected_ty, - second_expected_ty, - third_expected_ty, - ]) { - format!( - " of type `{}`, `{}`, and `{}`", - first_expected_ty, second_expected_ty, third_expected_ty - ) - } else { - "".to_string() - }; - labels.push((span, format!("three arguments{} are missing", rendered))); - suggestion_text = match suggestion_text { - SuggestionText::None | SuggestionText::Provide(_) => { - SuggestionText::Provide(true) - } - _ => SuggestionText::DidYouMean, - }; - } - missing_idxs => { - let first_idx = *missing_idxs.first().unwrap(); - let last_idx = *missing_idxs.last().unwrap(); - // NOTE: Because we might be re-arranging arguments, might have extra arguments, etc. - // It's hard to *really* know where we should provide this error label, so this is a - // decent heuristic - let span = if let (Some((_, first_span)), Some((_, last_span))) = ( - provided_arg_tys.get(first_idx.to_provided_idx()), - provided_arg_tys.get(last_idx.to_provided_idx()), - ) { - first_span.to(*last_span) - } else { - args_span - }; - labels.push((span, format!("multiple arguments are missing"))); - suggestion_text = match suggestion_text { - SuggestionText::None | SuggestionText::Provide(_) => { - SuggestionText::Provide(true) - } - _ => SuggestionText::DidYouMean, - }; - } - } - } - Error::Swap( - first_provided_idx, - second_provided_idx, - first_expected_idx, - second_expected_idx, - ) => { - let (first_provided_ty, first_span) = provided_arg_tys[first_provided_idx]; - let (_, first_expected_ty) = formal_and_expected_inputs[first_expected_idx]; - let first_provided_ty_name = if !has_error_or_infer([first_provided_ty]) { - format!(", found `{}`", first_provided_ty) - } else { - String::new() - }; - labels.push(( - first_span, - format!("expected `{}`{}", first_expected_ty, first_provided_ty_name), - )); - - let (second_provided_ty, second_span) = provided_arg_tys[second_provided_idx]; - let (_, second_expected_ty) = formal_and_expected_inputs[second_expected_idx]; - let second_provided_ty_name = if !has_error_or_infer([second_provided_ty]) { - format!(", found `{}`", second_provided_ty) - } else { - String::new() - }; - labels.push(( - second_span, - format!("expected `{}`{}", second_expected_ty, second_provided_ty_name), - )); - - suggestion_text = match suggestion_text { - SuggestionText::None => SuggestionText::Swap, - _ => SuggestionText::DidYouMean, - }; - } - Error::Permutation(args) => { - for (dst_arg, dest_input) in args { - let (_, expected_ty) = formal_and_expected_inputs[dst_arg]; - let (provided_ty, provided_span) = provided_arg_tys[dest_input]; - let provided_ty_name = if !has_error_or_infer([provided_ty]) { - format!(", found `{}`", provided_ty) - } else { - String::new() - }; - labels.push(( - provided_span, - format!("expected `{}`{}", expected_ty, provided_ty_name), - )); - } - - suggestion_text = match suggestion_text { - SuggestionText::None => SuggestionText::Reorder, - _ => SuggestionText::DidYouMean, - }; - } - } - } - - // If we have less than 5 things to say, it would be useful to call out exactly what's wrong - if labels.len() <= 5 { - for (span, label) in labels { - err.span_label(span, label); - } - } - - // Call out where the function is defined - self.label_fn_like(&mut err, fn_def_id, callee_ty, None, is_method); - - // And add a suggestion block for all of the parameters - let suggestion_text = match suggestion_text { - SuggestionText::None => None, - SuggestionText::Provide(plural) => { - Some(format!("provide the argument{}", if plural { "s" } else { "" })) - } - SuggestionText::Remove(plural) => { - Some(format!("remove the extra argument{}", if plural { "s" } else { "" })) - } - SuggestionText::Swap => Some("swap these arguments".to_string()), - SuggestionText::Reorder => Some("reorder these arguments".to_string()), - SuggestionText::DidYouMean => Some("did you mean".to_string()), - }; - if let Some(suggestion_text) = suggestion_text { - let source_map = self.sess().source_map(); - let (mut suggestion, suggestion_span) = - if let Some(call_span) = full_call_span.find_ancestor_inside(error_span) { - ("(".to_string(), call_span.shrink_to_hi().to(error_span.shrink_to_hi())) - } else { - ( - format!( - "{}(", - source_map.span_to_snippet(full_call_span).unwrap_or_else(|_| { - fn_def_id.map_or("".to_string(), |fn_def_id| { - tcx.item_name(fn_def_id).to_string() - }) - }) - ), - error_span, - ) - }; - let mut needs_comma = false; - for (expected_idx, provided_idx) in matched_inputs.iter_enumerated() { - if needs_comma { - suggestion += ", "; - } else { - needs_comma = true; - } - let suggestion_text = if let Some(provided_idx) = provided_idx - && let (_, provided_span) = provided_arg_tys[*provided_idx] - && let Ok(arg_text) = source_map.span_to_snippet(provided_span) - { - arg_text - } else { - // Propose a placeholder of the correct type - let (_, expected_ty) = formal_and_expected_inputs[expected_idx]; - if expected_ty.is_unit() { - "()".to_string() - } else if expected_ty.is_suggestable(tcx, false) { - format!("/* {} */", expected_ty) - } else { - "/* value */".to_string() - } - }; - suggestion += &suggestion_text; - } - suggestion += ")"; - err.span_suggestion_verbose( - suggestion_span, - &suggestion_text, - suggestion, - Applicability::HasPlaceholders, - ); - } - - err.emit(); - } - - // AST fragment checking - pub(in super::super) fn check_lit( - &self, - lit: &hir::Lit, - expected: Expectation<'tcx>, - ) -> Ty<'tcx> { - let tcx = self.tcx; - - match lit.node { - ast::LitKind::Str(..) => tcx.mk_static_str(), - ast::LitKind::ByteStr(ref v) => { - tcx.mk_imm_ref(tcx.lifetimes.re_static, tcx.mk_array(tcx.types.u8, v.len() as u64)) - } - ast::LitKind::Byte(_) => tcx.types.u8, - ast::LitKind::Char(_) => tcx.types.char, - ast::LitKind::Int(_, ast::LitIntType::Signed(t)) => tcx.mk_mach_int(ty::int_ty(t)), - ast::LitKind::Int(_, ast::LitIntType::Unsigned(t)) => tcx.mk_mach_uint(ty::uint_ty(t)), - ast::LitKind::Int(_, ast::LitIntType::Unsuffixed) => { - let opt_ty = expected.to_option(self).and_then(|ty| match ty.kind() { - ty::Int(_) | ty::Uint(_) => Some(ty), - ty::Char => Some(tcx.types.u8), - ty::RawPtr(..) => Some(tcx.types.usize), - ty::FnDef(..) | ty::FnPtr(_) => Some(tcx.types.usize), - _ => None, - }); - opt_ty.unwrap_or_else(|| self.next_int_var()) - } - ast::LitKind::Float(_, ast::LitFloatType::Suffixed(t)) => { - tcx.mk_mach_float(ty::float_ty(t)) - } - ast::LitKind::Float(_, ast::LitFloatType::Unsuffixed) => { - let opt_ty = expected.to_option(self).and_then(|ty| match ty.kind() { - ty::Float(_) => Some(ty), - _ => None, - }); - opt_ty.unwrap_or_else(|| self.next_float_var()) - } - ast::LitKind::Bool(_) => tcx.types.bool, - ast::LitKind::Err => tcx.ty_error(), - } - } - - pub fn check_struct_path( - &self, - qpath: &QPath<'_>, - hir_id: hir::HirId, - ) -> Option<(&'tcx ty::VariantDef, Ty<'tcx>)> { - let path_span = qpath.span(); - let (def, ty) = self.finish_resolving_struct_path(qpath, path_span, hir_id); - let variant = match def { - Res::Err => { - self.set_tainted_by_errors(); - return None; - } - Res::Def(DefKind::Variant, _) => match ty.kind() { - ty::Adt(adt, substs) => Some((adt.variant_of_res(def), adt.did(), substs)), - _ => bug!("unexpected type: {:?}", ty), - }, - Res::Def(DefKind::Struct | DefKind::Union | DefKind::TyAlias | DefKind::AssocTy, _) - | Res::SelfTy { .. } => match ty.kind() { - ty::Adt(adt, substs) if !adt.is_enum() => { - Some((adt.non_enum_variant(), adt.did(), substs)) - } - _ => None, - }, - _ => bug!("unexpected definition: {:?}", def), - }; - - if let Some((variant, did, substs)) = variant { - debug!("check_struct_path: did={:?} substs={:?}", did, substs); - self.write_user_type_annotation_from_substs(hir_id, did, substs, None); - - // Check bounds on type arguments used in the path. - self.add_required_obligations_for_hir(path_span, did, substs, hir_id); - - Some((variant, ty)) - } else { - match ty.kind() { - ty::Error(_) => { - // E0071 might be caused by a spelling error, which will have - // already caused an error message and probably a suggestion - // elsewhere. Refrain from emitting more unhelpful errors here - // (issue #88844). - } - _ => { - struct_span_err!( - self.tcx.sess, - path_span, - E0071, - "expected struct, variant or union type, found {}", - ty.sort_string(self.tcx) - ) - .span_label(path_span, "not a struct") - .emit(); - } - } - None - } - } - - pub fn check_decl_initializer( - &self, - hir_id: hir::HirId, - pat: &'tcx hir::Pat<'tcx>, - init: &'tcx hir::Expr<'tcx>, - ) -> Ty<'tcx> { - // FIXME(tschottdorf): `contains_explicit_ref_binding()` must be removed - // for #42640 (default match binding modes). - // - // See #44848. - let ref_bindings = pat.contains_explicit_ref_binding(); - - let local_ty = self.local_ty(init.span, hir_id).revealed_ty; - if let Some(m) = ref_bindings { - // Somewhat subtle: if we have a `ref` binding in the pattern, - // we want to avoid introducing coercions for the RHS. This is - // both because it helps preserve sanity and, in the case of - // ref mut, for soundness (issue #23116). In particular, in - // the latter case, we need to be clear that the type of the - // referent for the reference that results is *equal to* the - // type of the place it is referencing, and not some - // supertype thereof. - let init_ty = self.check_expr_with_needs(init, Needs::maybe_mut_place(m)); - self.demand_eqtype(init.span, local_ty, init_ty); - init_ty - } else { - self.check_expr_coercable_to_type(init, local_ty, None) - } - } - - pub(in super::super) fn check_decl(&self, decl: Declaration<'tcx>) { - // Determine and write the type which we'll check the pattern against. - let decl_ty = self.local_ty(decl.span, decl.hir_id).decl_ty; - self.write_ty(decl.hir_id, decl_ty); - - // Type check the initializer. - if let Some(ref init) = decl.init { - let init_ty = self.check_decl_initializer(decl.hir_id, decl.pat, &init); - self.overwrite_local_ty_if_err(decl.hir_id, decl.pat, decl_ty, init_ty); - } - - // Does the expected pattern type originate from an expression and what is the span? - let (origin_expr, ty_span) = match (decl.ty, decl.init) { - (Some(ty), _) => (false, Some(ty.span)), // Bias towards the explicit user type. - (_, Some(init)) => { - (true, Some(init.span.find_ancestor_inside(decl.span).unwrap_or(init.span))) - } // No explicit type; so use the scrutinee. - _ => (false, None), // We have `let $pat;`, so the expected type is unconstrained. - }; - - // Type check the pattern. Override if necessary to avoid knock-on errors. - self.check_pat_top(&decl.pat, decl_ty, ty_span, origin_expr); - let pat_ty = self.node_ty(decl.pat.hir_id); - self.overwrite_local_ty_if_err(decl.hir_id, decl.pat, decl_ty, pat_ty); - - if let Some(blk) = decl.els { - let previous_diverges = self.diverges.get(); - let else_ty = self.check_block_with_expected(blk, NoExpectation); - let cause = self.cause(blk.span, ObligationCauseCode::LetElse); - if let Some(mut err) = - self.demand_eqtype_with_origin(&cause, self.tcx.types.never, else_ty) - { - err.emit(); - } - self.diverges.set(previous_diverges); - } - } - - /// Type check a `let` statement. - pub fn check_decl_local(&self, local: &'tcx hir::Local<'tcx>) { - self.check_decl(local.into()); - } - - pub fn check_stmt(&self, stmt: &'tcx hir::Stmt<'tcx>, is_last: bool) { - // Don't do all the complex logic below for `DeclItem`. - match stmt.kind { - hir::StmtKind::Item(..) => return, - hir::StmtKind::Local(..) | hir::StmtKind::Expr(..) | hir::StmtKind::Semi(..) => {} - } - - self.warn_if_unreachable(stmt.hir_id, stmt.span, "statement"); - - // Hide the outer diverging and `has_errors` flags. - let old_diverges = self.diverges.replace(Diverges::Maybe); - let old_has_errors = self.has_errors.replace(false); - - match stmt.kind { - hir::StmtKind::Local(l) => { - self.check_decl_local(l); - } - // Ignore for now. - hir::StmtKind::Item(_) => {} - hir::StmtKind::Expr(ref expr) => { - // Check with expected type of `()`. - self.check_expr_has_type_or_error(&expr, self.tcx.mk_unit(), |err| { - if expr.can_have_side_effects() { - self.suggest_semicolon_at_end(expr.span, err); - } - }); - } - hir::StmtKind::Semi(ref expr) => { - // All of this is equivalent to calling `check_expr`, but it is inlined out here - // in order to capture the fact that this `match` is the last statement in its - // function. This is done for better suggestions to remove the `;`. - let expectation = match expr.kind { - hir::ExprKind::Match(..) if is_last => IsLast(stmt.span), - _ => NoExpectation, - }; - self.check_expr_with_expectation(expr, expectation); - } - } - - // Combine the diverging and `has_error` flags. - self.diverges.set(self.diverges.get() | old_diverges); - self.has_errors.set(self.has_errors.get() | old_has_errors); - } - - pub fn check_block_no_value(&self, blk: &'tcx hir::Block<'tcx>) { - let unit = self.tcx.mk_unit(); - let ty = self.check_block_with_expected(blk, ExpectHasType(unit)); - - // if the block produces a `!` value, that can always be - // (effectively) coerced to unit. - if !ty.is_never() { - self.demand_suptype(blk.span, unit, ty); - } - } - - pub(in super::super) fn check_block_with_expected( - &self, - blk: &'tcx hir::Block<'tcx>, - expected: Expectation<'tcx>, - ) -> Ty<'tcx> { - let prev = self.ps.replace(self.ps.get().recurse(blk)); - - // In some cases, blocks have just one exit, but other blocks - // can be targeted by multiple breaks. This can happen both - // with labeled blocks as well as when we desugar - // a `try { ... }` expression. - // - // Example 1: - // - // 'a: { if true { break 'a Err(()); } Ok(()) } - // - // Here we would wind up with two coercions, one from - // `Err(())` and the other from the tail expression - // `Ok(())`. If the tail expression is omitted, that's a - // "forced unit" -- unless the block diverges, in which - // case we can ignore the tail expression (e.g., `'a: { - // break 'a 22; }` would not force the type of the block - // to be `()`). - let tail_expr = blk.expr.as_ref(); - let coerce_to_ty = expected.coercion_target_type(self, blk.span); - let coerce = if blk.targeted_by_break { - CoerceMany::new(coerce_to_ty) - } else { - let tail_expr: &[&hir::Expr<'_>] = match tail_expr { - Some(e) => slice::from_ref(e), - None => &[], - }; - CoerceMany::with_coercion_sites(coerce_to_ty, tail_expr) - }; - - let prev_diverges = self.diverges.get(); - let ctxt = BreakableCtxt { coerce: Some(coerce), may_break: false }; - - let (ctxt, ()) = self.with_breakable_ctxt(blk.hir_id, ctxt, || { - for (pos, s) in blk.stmts.iter().enumerate() { - self.check_stmt(s, blk.stmts.len() - 1 == pos); - } - - // check the tail expression **without** holding the - // `enclosing_breakables` lock below. - let tail_expr_ty = tail_expr.map(|t| self.check_expr_with_expectation(t, expected)); - - let mut enclosing_breakables = self.enclosing_breakables.borrow_mut(); - let ctxt = enclosing_breakables.find_breakable(blk.hir_id); - let coerce = ctxt.coerce.as_mut().unwrap(); - if let Some(tail_expr_ty) = tail_expr_ty { - let tail_expr = tail_expr.unwrap(); - let span = self.get_expr_coercion_span(tail_expr); - let cause = self.cause(span, ObligationCauseCode::BlockTailExpression(blk.hir_id)); - let ty_for_diagnostic = coerce.merged_ty(); - // We use coerce_inner here because we want to augment the error - // suggesting to wrap the block in square brackets if it might've - // been mistaken array syntax - coerce.coerce_inner( - self, - &cause, - Some(tail_expr), - tail_expr_ty, - Some(&mut |diag: &mut Diagnostic| { - self.suggest_block_to_brackets(diag, blk, tail_expr_ty, ty_for_diagnostic); - }), - false, - ); - } else { - // Subtle: if there is no explicit tail expression, - // that is typically equivalent to a tail expression - // of `()` -- except if the block diverges. In that - // case, there is no value supplied from the tail - // expression (assuming there are no other breaks, - // this implies that the type of the block will be - // `!`). - // - // #41425 -- label the implicit `()` as being the - // "found type" here, rather than the "expected type". - if !self.diverges.get().is_always() { - // #50009 -- Do not point at the entire fn block span, point at the return type - // span, as it is the cause of the requirement, and - // `consider_hint_about_removing_semicolon` will point at the last expression - // if it were a relevant part of the error. This improves usability in editors - // that highlight errors inline. - let mut sp = blk.span; - let mut fn_span = None; - if let Some((decl, ident)) = self.get_parent_fn_decl(blk.hir_id) { - let ret_sp = decl.output.span(); - if let Some(block_sp) = self.parent_item_span(blk.hir_id) { - // HACK: on some cases (`ui/liveness/liveness-issue-2163.rs`) the - // output would otherwise be incorrect and even misleading. Make sure - // the span we're aiming at correspond to a `fn` body. - if block_sp == blk.span { - sp = ret_sp; - fn_span = Some(ident.span); - } - } - } - coerce.coerce_forced_unit( - self, - &self.misc(sp), - &mut |err| { - if let Some(expected_ty) = expected.only_has_type(self) { - if !self.consider_removing_semicolon(blk, expected_ty, err) { - self.consider_returning_binding(blk, expected_ty, err); - } - if expected_ty == self.tcx.types.bool { - // If this is caused by a missing `let` in a `while let`, - // silence this redundant error, as we already emit E0070. - - // Our block must be a `assign desugar local; assignment` - if let Some(hir::Node::Block(hir::Block { - stmts: - [ - hir::Stmt { - kind: - hir::StmtKind::Local(hir::Local { - source: - hir::LocalSource::AssignDesugar(_), - .. - }), - .. - }, - hir::Stmt { - kind: - hir::StmtKind::Expr(hir::Expr { - kind: hir::ExprKind::Assign(..), - .. - }), - .. - }, - ], - .. - })) = self.tcx.hir().find(blk.hir_id) - { - self.comes_from_while_condition(blk.hir_id, |_| { - err.downgrade_to_delayed_bug(); - }) - } - } - } - if let Some(fn_span) = fn_span { - err.span_label( - fn_span, - "implicitly returns `()` as its body has no tail or `return` \ - expression", - ); - } - }, - false, - ); - } - } - }); - - if ctxt.may_break { - // If we can break from the block, then the block's exit is always reachable - // (... as long as the entry is reachable) - regardless of the tail of the block. - self.diverges.set(prev_diverges); - } - - let mut ty = ctxt.coerce.unwrap().complete(self); - - if self.has_errors.get() || ty.references_error() { - ty = self.tcx.ty_error() - } - - self.write_ty(blk.hir_id, ty); - - self.ps.set(prev); - ty - } - - fn parent_item_span(&self, id: hir::HirId) -> Option<Span> { - let node = self.tcx.hir().get_by_def_id(self.tcx.hir().get_parent_item(id)); - match node { - Node::Item(&hir::Item { kind: hir::ItemKind::Fn(_, _, body_id), .. }) - | Node::ImplItem(&hir::ImplItem { kind: hir::ImplItemKind::Fn(_, body_id), .. }) => { - let body = self.tcx.hir().body(body_id); - if let ExprKind::Block(block, _) = &body.value.kind { - return Some(block.span); - } - } - _ => {} - } - None - } - - /// Given a function block's `HirId`, returns its `FnDecl` if it exists, or `None` otherwise. - fn get_parent_fn_decl(&self, blk_id: hir::HirId) -> Option<(&'tcx hir::FnDecl<'tcx>, Ident)> { - let parent = self.tcx.hir().get_by_def_id(self.tcx.hir().get_parent_item(blk_id)); - self.get_node_fn_decl(parent).map(|(fn_decl, ident, _)| (fn_decl, ident)) - } - - /// If `expr` is a `match` expression that has only one non-`!` arm, use that arm's tail - /// expression's `Span`, otherwise return `expr.span`. This is done to give better errors - /// when given code like the following: - /// ```text - /// if false { return 0i32; } else { 1u32 } - /// // ^^^^ point at this instead of the whole `if` expression - /// ``` - fn get_expr_coercion_span(&self, expr: &hir::Expr<'_>) -> rustc_span::Span { - let check_in_progress = |elem: &hir::Expr<'_>| { - self.typeck_results.borrow().node_type_opt(elem.hir_id).filter(|ty| !ty.is_never()).map( - |_| match elem.kind { - // Point at the tail expression when possible. - hir::ExprKind::Block(block, _) => block.expr.map_or(block.span, |e| e.span), - _ => elem.span, - }, - ) - }; - - if let hir::ExprKind::If(_, _, Some(el)) = expr.kind { - if let Some(rslt) = check_in_progress(el) { - return rslt; - } - } - - if let hir::ExprKind::Match(_, arms, _) = expr.kind { - let mut iter = arms.iter().filter_map(|arm| check_in_progress(arm.body)); - if let Some(span) = iter.next() { - if iter.next().is_none() { - return span; - } - } - } - - expr.span - } - - fn overwrite_local_ty_if_err( - &self, - hir_id: hir::HirId, - pat: &'tcx hir::Pat<'tcx>, - decl_ty: Ty<'tcx>, - ty: Ty<'tcx>, - ) { - if ty.references_error() { - // Override the types everywhere with `err()` to avoid knock on errors. - self.write_ty(hir_id, ty); - self.write_ty(pat.hir_id, ty); - let local_ty = LocalTy { decl_ty, revealed_ty: ty }; - self.locals.borrow_mut().insert(hir_id, local_ty); - self.locals.borrow_mut().insert(pat.hir_id, local_ty); - } - } - - // Finish resolving a path in a struct expression or pattern `S::A { .. }` if necessary. - // The newly resolved definition is written into `type_dependent_defs`. - fn finish_resolving_struct_path( - &self, - qpath: &QPath<'_>, - path_span: Span, - hir_id: hir::HirId, - ) -> (Res, Ty<'tcx>) { - match *qpath { - QPath::Resolved(ref maybe_qself, ref path) => { - let self_ty = maybe_qself.as_ref().map(|qself| self.to_ty(qself)); - let ty = <dyn AstConv<'_>>::res_to_ty(self, self_ty, path, true); - (path.res, ty) - } - QPath::TypeRelative(ref qself, ref segment) => { - let ty = self.to_ty(qself); - - let result = <dyn AstConv<'_>>::associated_path_to_ty( - self, hir_id, path_span, ty, qself, segment, true, - ); - let ty = result.map(|(ty, _, _)| ty).unwrap_or_else(|_| self.tcx().ty_error()); - let result = result.map(|(_, kind, def_id)| (kind, def_id)); - - // Write back the new resolution. - self.write_resolution(hir_id, result); - - (result.map_or(Res::Err, |(kind, def_id)| Res::Def(kind, def_id)), ty) - } - QPath::LangItem(lang_item, span, id) => { - self.resolve_lang_item_path(lang_item, span, hir_id, id) - } - } - } - - /// Given a vector of fulfillment errors, try to adjust the spans of the - /// errors to more accurately point at the cause of the failure. - /// - /// This applies to calls, methods, and struct expressions. This will also - /// try to deduplicate errors that are due to the same cause but might - /// have been created with different [`ObligationCause`][traits::ObligationCause]s. - pub(super) fn adjust_fulfillment_errors_for_expr_obligation( - &self, - errors: &mut Vec<traits::FulfillmentError<'tcx>>, - ) { - // Store a mapping from `(Span, Predicate) -> ObligationCause`, so that - // other errors that have the same span and predicate can also get fixed, - // even if their `ObligationCauseCode` isn't an `Expr*Obligation` kind. - // This is important since if we adjust one span but not the other, then - // we will have "duplicated" the error on the UI side. - let mut remap_cause = FxHashSet::default(); - let mut not_adjusted = vec![]; - - for error in errors { - let before_span = error.obligation.cause.span; - if self.adjust_fulfillment_error_for_expr_obligation(error) - || before_span != error.obligation.cause.span - { - // Store both the predicate and the predicate *without constness* - // since sometimes we instantiate and check both of these in a - // method call, for example. - remap_cause.insert(( - before_span, - error.obligation.predicate, - error.obligation.cause.clone(), - )); - remap_cause.insert(( - before_span, - error.obligation.predicate.without_const(self.tcx), - error.obligation.cause.clone(), - )); - } else { - // If it failed to be adjusted once around, it may be adjusted - // via the "remap cause" mapping the second time... - not_adjusted.push(error); - } - } - - for error in not_adjusted { - for (span, predicate, cause) in &remap_cause { - if *predicate == error.obligation.predicate - && span.contains(error.obligation.cause.span) - { - error.obligation.cause = cause.clone(); - continue; - } - } - } - } - - fn adjust_fulfillment_error_for_expr_obligation( - &self, - error: &mut traits::FulfillmentError<'tcx>, - ) -> bool { - let (traits::ExprItemObligation(def_id, hir_id, idx) | traits::ExprBindingObligation(def_id, _, hir_id, idx)) - = *error.obligation.cause.code().peel_derives() else { return false; }; - let hir = self.tcx.hir(); - let hir::Node::Expr(expr) = hir.get(hir_id) else { return false; }; - - // Skip over mentioning async lang item - if Some(def_id) == self.tcx.lang_items().from_generator_fn() - && error.obligation.cause.span.desugaring_kind() - == Some(rustc_span::DesugaringKind::Async) - { - return false; - } - - let Some(unsubstituted_pred) = - self.tcx.predicates_of(def_id).instantiate_identity(self.tcx).predicates.into_iter().nth(idx) - else { return false; }; - - let generics = self.tcx.generics_of(def_id); - let predicate_substs = match unsubstituted_pred.kind().skip_binder() { - ty::PredicateKind::Trait(pred) => pred.trait_ref.substs, - ty::PredicateKind::Projection(pred) => pred.projection_ty.substs, - _ => ty::List::empty(), - }; - - let find_param_matching = |matches: &dyn Fn(&ty::ParamTy) -> bool| { - predicate_substs.types().find_map(|ty| { - ty.walk().find_map(|arg| { - if let ty::GenericArgKind::Type(ty) = arg.unpack() - && let ty::Param(param_ty) = ty.kind() - && matches(param_ty) - { - Some(arg) - } else { - None - } - }) - }) - }; - - // Prefer generics that are local to the fn item, since these are likely - // to be the cause of the unsatisfied predicate. - let mut param_to_point_at = find_param_matching(&|param_ty| { - self.tcx.parent(generics.type_param(param_ty, self.tcx).def_id) == def_id - }); - // Fall back to generic that isn't local to the fn item. This will come - // from a trait or impl, for example. - let mut fallback_param_to_point_at = find_param_matching(&|param_ty| { - self.tcx.parent(generics.type_param(param_ty, self.tcx).def_id) != def_id - && param_ty.name != rustc_span::symbol::kw::SelfUpper - }); - // Finally, the `Self` parameter is possibly the reason that the predicate - // is unsatisfied. This is less likely to be true for methods, because - // method probe means that we already kinda check that the predicates due - // to the `Self` type are true. - let mut self_param_to_point_at = - find_param_matching(&|param_ty| param_ty.name == rustc_span::symbol::kw::SelfUpper); - - // Finally, for ambiguity-related errors, we actually want to look - // for a parameter that is the source of the inference type left - // over in this predicate. - if let traits::FulfillmentErrorCode::CodeAmbiguity = error.code { - fallback_param_to_point_at = None; - self_param_to_point_at = None; - param_to_point_at = - self.find_ambiguous_parameter_in(def_id, error.root_obligation.predicate); - } - - if self.closure_span_overlaps_error(error, expr.span) { - return false; - } - - match &expr.kind { - hir::ExprKind::Path(qpath) => { - if let hir::Node::Expr(hir::Expr { - kind: hir::ExprKind::Call(callee, args), - hir_id: call_hir_id, - span: call_span, - .. - }) = hir.get(hir.get_parent_node(expr.hir_id)) - && callee.hir_id == expr.hir_id - { - if self.closure_span_overlaps_error(error, *call_span) { - return false; - } - - for param in - [param_to_point_at, fallback_param_to_point_at, self_param_to_point_at] - .into_iter() - .flatten() - { - if self.point_at_arg_if_possible( - error, - def_id, - param, - *call_hir_id, - callee.span, - None, - args, - ) - { - return true; - } - } - } - // Notably, we only point to params that are local to the - // item we're checking, since those are the ones we are able - // to look in the final `hir::PathSegment` for. Everything else - // would require a deeper search into the `qpath` than I think - // is worthwhile. - if let Some(param_to_point_at) = param_to_point_at - && self.point_at_path_if_possible(error, def_id, param_to_point_at, qpath) - { - return true; - } - } - hir::ExprKind::MethodCall(segment, receiver, args, ..) => { - for param in [param_to_point_at, fallback_param_to_point_at, self_param_to_point_at] - .into_iter() - .flatten() - { - if self.point_at_arg_if_possible( - error, - def_id, - param, - hir_id, - segment.ident.span, - Some(receiver), - args, - ) { - return true; - } - } - if let Some(param_to_point_at) = param_to_point_at - && self.point_at_generic_if_possible(error, def_id, param_to_point_at, segment) - { - return true; - } - } - hir::ExprKind::Struct(qpath, fields, ..) => { - if let Res::Def(DefKind::Struct | DefKind::Variant, variant_def_id) = - self.typeck_results.borrow().qpath_res(qpath, hir_id) - { - for param in - [param_to_point_at, fallback_param_to_point_at, self_param_to_point_at] - { - if let Some(param) = param - && self.point_at_field_if_possible( - error, - def_id, - param, - variant_def_id, - fields, - ) - { - return true; - } - } - } - if let Some(param_to_point_at) = param_to_point_at - && self.point_at_path_if_possible(error, def_id, param_to_point_at, qpath) - { - return true; - } - } - _ => {} - } - - false - } - - fn closure_span_overlaps_error( - &self, - error: &traits::FulfillmentError<'tcx>, - span: Span, - ) -> bool { - if let traits::FulfillmentErrorCode::CodeSelectionError( - traits::SelectionError::OutputTypeParameterMismatch(_, expected, _), - ) = error.code - && let ty::Closure(def_id, _) | ty::Generator(def_id, ..) = expected.skip_binder().self_ty().kind() - && span.overlaps(self.tcx.def_span(*def_id)) - { - true - } else { - false - } - } - - fn point_at_arg_if_possible( - &self, - error: &mut traits::FulfillmentError<'tcx>, - def_id: DefId, - param_to_point_at: ty::GenericArg<'tcx>, - call_hir_id: hir::HirId, - callee_span: Span, - receiver: Option<&'tcx hir::Expr<'tcx>>, - args: &'tcx [hir::Expr<'tcx>], - ) -> bool { - let sig = self.tcx.fn_sig(def_id).skip_binder(); - let args_referencing_param: Vec<_> = sig - .inputs() - .iter() - .enumerate() - .filter(|(_, ty)| find_param_in_ty(**ty, param_to_point_at)) - .collect(); - // If there's one field that references the given generic, great! - if let [(idx, _)] = args_referencing_param.as_slice() - && let Some(arg) = receiver - .map_or(args.get(*idx), |rcvr| if *idx == 0 { Some(rcvr) } else { args.get(*idx - 1) }) { - error.obligation.cause.span = arg.span.find_ancestor_in_same_ctxt(error.obligation.cause.span).unwrap_or(arg.span); - error.obligation.cause.map_code(|parent_code| { - ObligationCauseCode::FunctionArgumentObligation { - arg_hir_id: arg.hir_id, - call_hir_id, - parent_code, - } - }); - return true; - } else if args_referencing_param.len() > 0 { - // If more than one argument applies, then point to the callee span at least... - // We have chance to fix this up further in `point_at_generics_if_possible` - error.obligation.cause.span = callee_span; - } - - false - } - - fn point_at_field_if_possible( - &self, - error: &mut traits::FulfillmentError<'tcx>, - def_id: DefId, - param_to_point_at: ty::GenericArg<'tcx>, - variant_def_id: DefId, - expr_fields: &[hir::ExprField<'tcx>], - ) -> bool { - let def = self.tcx.adt_def(def_id); - - let identity_substs = ty::InternalSubsts::identity_for_item(self.tcx, def_id); - let fields_referencing_param: Vec<_> = def - .variant_with_id(variant_def_id) - .fields - .iter() - .filter(|field| { - let field_ty = field.ty(self.tcx, identity_substs); - find_param_in_ty(field_ty, param_to_point_at) - }) - .collect(); - - if let [field] = fields_referencing_param.as_slice() { - for expr_field in expr_fields { - // Look for the ExprField that matches the field, using the - // same rules that check_expr_struct uses for macro hygiene. - if self.tcx.adjust_ident(expr_field.ident, variant_def_id) == field.ident(self.tcx) - { - error.obligation.cause.span = expr_field - .expr - .span - .find_ancestor_in_same_ctxt(error.obligation.cause.span) - .unwrap_or(expr_field.span); - return true; - } - } - } - - false - } - - fn point_at_path_if_possible( - &self, - error: &mut traits::FulfillmentError<'tcx>, - def_id: DefId, - param: ty::GenericArg<'tcx>, - qpath: &QPath<'tcx>, - ) -> bool { - match qpath { - hir::QPath::Resolved(_, path) => { - if let Some(segment) = path.segments.last() - && self.point_at_generic_if_possible(error, def_id, param, segment) - { - return true; - } - } - hir::QPath::TypeRelative(_, segment) => { - if self.point_at_generic_if_possible(error, def_id, param, segment) { - return true; - } - } - _ => {} - } - - false - } - - fn point_at_generic_if_possible( - &self, - error: &mut traits::FulfillmentError<'tcx>, - def_id: DefId, - param_to_point_at: ty::GenericArg<'tcx>, - segment: &hir::PathSegment<'tcx>, - ) -> bool { - let own_substs = self - .tcx - .generics_of(def_id) - .own_substs(ty::InternalSubsts::identity_for_item(self.tcx, def_id)); - let Some((index, _)) = own_substs - .iter() - .filter(|arg| matches!(arg.unpack(), ty::GenericArgKind::Type(_))) - .enumerate() - .find(|(_, arg)| **arg == param_to_point_at) else { return false }; - let Some(arg) = segment - .args() - .args - .iter() - .filter(|arg| matches!(arg, hir::GenericArg::Type(_))) - .nth(index) else { return false; }; - error.obligation.cause.span = arg - .span() - .find_ancestor_in_same_ctxt(error.obligation.cause.span) - .unwrap_or(arg.span()); - true - } - - fn find_ambiguous_parameter_in<T: TypeVisitable<'tcx>>( - &self, - item_def_id: DefId, - t: T, - ) -> Option<ty::GenericArg<'tcx>> { - struct FindAmbiguousParameter<'a, 'tcx>(&'a FnCtxt<'a, 'tcx>, DefId); - impl<'tcx> TypeVisitor<'tcx> for FindAmbiguousParameter<'_, 'tcx> { - type BreakTy = ty::GenericArg<'tcx>; - fn visit_ty(&mut self, ty: Ty<'tcx>) -> std::ops::ControlFlow<Self::BreakTy> { - if let Some(origin) = self.0.type_var_origin(ty) - && let TypeVariableOriginKind::TypeParameterDefinition(_, Some(def_id)) = - origin.kind - && let generics = self.0.tcx.generics_of(self.1) - && let Some(index) = generics.param_def_id_to_index(self.0.tcx, def_id) - && let Some(subst) = ty::InternalSubsts::identity_for_item(self.0.tcx, self.1) - .get(index as usize) - { - ControlFlow::Break(*subst) - } else { - ty.super_visit_with(self) - } - } - } - t.visit_with(&mut FindAmbiguousParameter(self, item_def_id)).break_value() - } - - fn label_fn_like( - &self, - err: &mut Diagnostic, - callable_def_id: Option<DefId>, - callee_ty: Option<Ty<'tcx>>, - // A specific argument should be labeled, instead of all of them - expected_idx: Option<usize>, - is_method: bool, - ) { - let Some(mut def_id) = callable_def_id else { - return; - }; - - if let Some(assoc_item) = self.tcx.opt_associated_item(def_id) - // Possibly points at either impl or trait item, so try to get it - // to point to trait item, then get the parent. - // This parent might be an impl in the case of an inherent function, - // but the next check will fail. - && let maybe_trait_item_def_id = assoc_item.trait_item_def_id.unwrap_or(def_id) - && let maybe_trait_def_id = self.tcx.parent(maybe_trait_item_def_id) - // Just an easy way to check "trait_def_id == Fn/FnMut/FnOnce" - && let Some(call_kind) = ty::ClosureKind::from_def_id(self.tcx, maybe_trait_def_id) - && let Some(callee_ty) = callee_ty - { - let callee_ty = callee_ty.peel_refs(); - match *callee_ty.kind() { - ty::Param(param) => { - let param = - self.tcx.generics_of(self.body_id.owner).type_param(¶m, self.tcx); - if param.kind.is_synthetic() { - // if it's `impl Fn() -> ..` then just fall down to the def-id based logic - def_id = param.def_id; - } else { - // Otherwise, find the predicate that makes this generic callable, - // and point at that. - let instantiated = self - .tcx - .explicit_predicates_of(self.body_id.owner) - .instantiate_identity(self.tcx); - // FIXME(compiler-errors): This could be problematic if something has two - // fn-like predicates with different args, but callable types really never - // do that, so it's OK. - for (predicate, span) in - std::iter::zip(instantiated.predicates, instantiated.spans) - { - if let ty::PredicateKind::Trait(pred) = predicate.kind().skip_binder() - && pred.self_ty().peel_refs() == callee_ty - && ty::ClosureKind::from_def_id(self.tcx, pred.def_id()).is_some() - { - err.span_note(span, "callable defined here"); - return; - } - } - } - } - ty::Opaque(new_def_id, _) - | ty::Closure(new_def_id, _) - | ty::FnDef(new_def_id, _) => { - def_id = new_def_id; - } - _ => { - // Look for a user-provided impl of a `Fn` trait, and point to it. - let new_def_id = self.probe(|_| { - let trait_ref = ty::TraitRef::new( - call_kind.to_def_id(self.tcx), - self.tcx.mk_substs( - [ - ty::GenericArg::from(callee_ty), - self.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::MiscVariable, - span: rustc_span::DUMMY_SP, - }) - .into(), - ] - .into_iter(), - ), - ); - let obligation = traits::Obligation::new( - traits::ObligationCause::dummy(), - self.param_env, - ty::Binder::dummy(ty::TraitPredicate { - trait_ref, - constness: ty::BoundConstness::NotConst, - polarity: ty::ImplPolarity::Positive, - }), - ); - match SelectionContext::new(&self).select(&obligation) { - Ok(Some(traits::ImplSource::UserDefined(impl_source))) => { - Some(impl_source.impl_def_id) - } - _ => None, - } - }); - if let Some(new_def_id) = new_def_id { - def_id = new_def_id; - } else { - return; - } - } - } - } - - if let Some(def_span) = self.tcx.def_ident_span(def_id) && !def_span.is_dummy() { - let mut spans: MultiSpan = def_span.into(); - - let params = self - .tcx - .hir() - .get_if_local(def_id) - .and_then(|node| node.body_id()) - .into_iter() - .flat_map(|id| self.tcx.hir().body(id).params) - .skip(if is_method { 1 } else { 0 }); - - for (_, param) in params - .into_iter() - .enumerate() - .filter(|(idx, _)| expected_idx.map_or(true, |expected_idx| expected_idx == *idx)) - { - spans.push_span_label(param.span, ""); - } - - let def_kind = self.tcx.def_kind(def_id); - err.span_note(spans, &format!("{} defined here", def_kind.descr(def_id))); - } else if let Some(hir::Node::Expr(e)) = self.tcx.hir().get_if_local(def_id) - && let hir::ExprKind::Closure(hir::Closure { body, .. }) = &e.kind - { - let param = expected_idx - .and_then(|expected_idx| self.tcx.hir().body(*body).params.get(expected_idx)); - let (kind, span) = if let Some(param) = param { - ("closure parameter", param.span) - } else { - ("closure", self.tcx.def_span(def_id)) - }; - err.span_note(span, &format!("{} defined here", kind)); - } else { - let def_kind = self.tcx.def_kind(def_id); - err.span_note( - self.tcx.def_span(def_id), - &format!("{} defined here", def_kind.descr(def_id)), - ); - } - } -} - -fn find_param_in_ty<'tcx>(ty: Ty<'tcx>, param_to_point_at: ty::GenericArg<'tcx>) -> bool { - let mut walk = ty.walk(); - while let Some(arg) = walk.next() { - if arg == param_to_point_at { - return true; - } else if let ty::GenericArgKind::Type(ty) = arg.unpack() - && let ty::Projection(..) = ty.kind() - { - // This logic may seem a bit strange, but typically when - // we have a projection type in a function signature, the - // argument that's being passed into that signature is - // not actually constraining that projection's substs in - // a meaningful way. So we skip it, and see improvements - // in some UI tests. - walk.skip_current_subtree(); - } - } - false -} diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/mod.rs b/compiler/rustc_typeck/src/check/fn_ctxt/mod.rs deleted file mode 100644 index 0e22971d3..000000000 --- a/compiler/rustc_typeck/src/check/fn_ctxt/mod.rs +++ /dev/null @@ -1,304 +0,0 @@ -mod _impl; -mod arg_matrix; -mod checks; -mod suggestions; - -pub use _impl::*; -pub use suggestions::*; - -use crate::astconv::AstConv; -use crate::check::coercion::DynamicCoerceMany; -use crate::check::{Diverges, EnclosingBreakables, Inherited, UnsafetyState}; - -use rustc_hir as hir; -use rustc_hir::def_id::DefId; -use rustc_infer::infer; -use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; -use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind}; -use rustc_middle::ty::subst::GenericArgKind; -use rustc_middle::ty::visit::TypeVisitable; -use rustc_middle::ty::{self, Const, Ty, TyCtxt}; -use rustc_session::Session; -use rustc_span::symbol::Ident; -use rustc_span::{self, Span}; -use rustc_trait_selection::traits::{ObligationCause, ObligationCauseCode}; - -use std::cell::{Cell, RefCell}; -use std::ops::Deref; - -/// The `FnCtxt` stores type-checking context needed to type-check bodies of -/// functions, closures, and `const`s, including performing type inference -/// with [`InferCtxt`]. -/// -/// This is in contrast to [`ItemCtxt`], which is used to type-check item *signatures* -/// and thus does not perform type inference. -/// -/// See [`ItemCtxt`]'s docs for more. -/// -/// [`ItemCtxt`]: crate::collect::ItemCtxt -/// [`InferCtxt`]: infer::InferCtxt -pub struct FnCtxt<'a, 'tcx> { - pub(super) body_id: hir::HirId, - - /// The parameter environment used for proving trait obligations - /// in this function. This can change when we descend into - /// closures (as they bring new things into scope), hence it is - /// not part of `Inherited` (as of the time of this writing, - /// closures do not yet change the environment, but they will - /// eventually). - pub(super) param_env: ty::ParamEnv<'tcx>, - - /// Number of errors that had been reported when we started - /// checking this function. On exit, if we find that *more* errors - /// have been reported, we will skip regionck and other work that - /// expects the types within the function to be consistent. - // FIXME(matthewjasper) This should not exist, and it's not correct - // if type checking is run in parallel. - err_count_on_creation: usize, - - /// If `Some`, this stores coercion information for returned - /// expressions. If `None`, this is in a context where return is - /// inappropriate, such as a const expression. - /// - /// This is a `RefCell<DynamicCoerceMany>`, which means that we - /// can track all the return expressions and then use them to - /// compute a useful coercion from the set, similar to a match - /// expression or other branching context. You can use methods - /// like `expected_ty` to access the declared return type (if - /// any). - pub(super) ret_coercion: Option<RefCell<DynamicCoerceMany<'tcx>>>, - - /// Used exclusively to reduce cost of advanced evaluation used for - /// more helpful diagnostics. - pub(super) in_tail_expr: bool, - - /// First span of a return site that we find. Used in error messages. - pub(super) ret_coercion_span: Cell<Option<Span>>, - - pub(super) resume_yield_tys: Option<(Ty<'tcx>, Ty<'tcx>)>, - - pub(super) ps: Cell<UnsafetyState>, - - /// Whether the last checked node generates a divergence (e.g., - /// `return` will set this to `Always`). In general, when entering - /// an expression or other node in the tree, the initial value - /// indicates whether prior parts of the containing expression may - /// have diverged. It is then typically set to `Maybe` (and the - /// old value remembered) for processing the subparts of the - /// current expression. As each subpart is processed, they may set - /// the flag to `Always`, etc. Finally, at the end, we take the - /// result and "union" it with the original value, so that when we - /// return the flag indicates if any subpart of the parent - /// expression (up to and including this part) has diverged. So, - /// if you read it after evaluating a subexpression `X`, the value - /// you get indicates whether any subexpression that was - /// evaluating up to and including `X` diverged. - /// - /// We currently use this flag only for diagnostic purposes: - /// - /// - To warn about unreachable code: if, after processing a - /// sub-expression but before we have applied the effects of the - /// current node, we see that the flag is set to `Always`, we - /// can issue a warning. This corresponds to something like - /// `foo(return)`; we warn on the `foo()` expression. (We then - /// update the flag to `WarnedAlways` to suppress duplicate - /// reports.) Similarly, if we traverse to a fresh statement (or - /// tail expression) from an `Always` setting, we will issue a - /// warning. This corresponds to something like `{return; - /// foo();}` or `{return; 22}`, where we would warn on the - /// `foo()` or `22`. - /// - /// An expression represents dead code if, after checking it, - /// the diverges flag is set to something other than `Maybe`. - pub(super) diverges: Cell<Diverges>, - - /// Whether any child nodes have any type errors. - pub(super) has_errors: Cell<bool>, - - pub(super) enclosing_breakables: RefCell<EnclosingBreakables<'tcx>>, - - pub(super) inh: &'a Inherited<'a, 'tcx>, - - /// True if the function or closure's return type is known before - /// entering the function/closure, i.e. if the return type is - /// either given explicitly or inferred from, say, an `Fn*` trait - /// bound. Used for diagnostic purposes only. - pub(super) return_type_pre_known: bool, - - /// True if the return type has an Opaque type - pub(super) return_type_has_opaque: bool, -} - -impl<'a, 'tcx> FnCtxt<'a, 'tcx> { - pub fn new( - inh: &'a Inherited<'a, 'tcx>, - param_env: ty::ParamEnv<'tcx>, - body_id: hir::HirId, - ) -> FnCtxt<'a, 'tcx> { - FnCtxt { - body_id, - param_env, - err_count_on_creation: inh.tcx.sess.err_count(), - ret_coercion: None, - in_tail_expr: false, - ret_coercion_span: Cell::new(None), - resume_yield_tys: None, - ps: Cell::new(UnsafetyState::function(hir::Unsafety::Normal, hir::CRATE_HIR_ID)), - diverges: Cell::new(Diverges::Maybe), - has_errors: Cell::new(false), - enclosing_breakables: RefCell::new(EnclosingBreakables { - stack: Vec::new(), - by_id: Default::default(), - }), - inh, - return_type_pre_known: true, - return_type_has_opaque: false, - } - } - - pub fn cause(&self, span: Span, code: ObligationCauseCode<'tcx>) -> ObligationCause<'tcx> { - ObligationCause::new(span, self.body_id, code) - } - - pub fn misc(&self, span: Span) -> ObligationCause<'tcx> { - self.cause(span, ObligationCauseCode::MiscObligation) - } - - pub fn sess(&self) -> &Session { - &self.tcx.sess - } - - pub fn errors_reported_since_creation(&self) -> bool { - self.tcx.sess.err_count() > self.err_count_on_creation - } -} - -impl<'a, 'tcx> Deref for FnCtxt<'a, 'tcx> { - type Target = Inherited<'a, 'tcx>; - fn deref(&self) -> &Self::Target { - &self.inh - } -} - -impl<'a, 'tcx> AstConv<'tcx> for FnCtxt<'a, 'tcx> { - fn tcx<'b>(&'b self) -> TyCtxt<'tcx> { - self.tcx - } - - fn item_def_id(&self) -> Option<DefId> { - None - } - - fn get_type_parameter_bounds( - &self, - _: Span, - def_id: DefId, - _: Ident, - ) -> ty::GenericPredicates<'tcx> { - let tcx = self.tcx; - 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]; - ty::GenericPredicates { - parent: None, - predicates: tcx.arena.alloc_from_iter( - self.param_env.caller_bounds().iter().filter_map(|predicate| { - match predicate.kind().skip_binder() { - ty::PredicateKind::Trait(data) if data.self_ty().is_param(index) => { - // HACK(eddyb) should get the original `Span`. - let span = tcx.def_span(def_id); - Some((predicate, span)) - } - _ => None, - } - }), - ), - } - } - - fn re_infer(&self, def: Option<&ty::GenericParamDef>, span: Span) -> Option<ty::Region<'tcx>> { - let v = match def { - Some(def) => infer::EarlyBoundRegion(span, def.name), - None => infer::MiscVariable(span), - }; - Some(self.next_region_var(v)) - } - - fn allow_ty_infer(&self) -> bool { - true - } - - fn ty_infer(&self, param: Option<&ty::GenericParamDef>, span: Span) -> Ty<'tcx> { - if let Some(param) = param { - if let GenericArgKind::Type(ty) = self.var_for_def(span, param).unpack() { - return ty; - } - unreachable!() - } else { - self.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::TypeInference, - span, - }) - } - } - - fn ct_infer( - &self, - ty: Ty<'tcx>, - param: Option<&ty::GenericParamDef>, - span: Span, - ) -> Const<'tcx> { - if let Some(param) = param { - if let GenericArgKind::Const(ct) = self.var_for_def(span, param).unpack() { - return ct; - } - unreachable!() - } else { - self.next_const_var( - ty, - ConstVariableOrigin { kind: ConstVariableOriginKind::ConstInference, span }, - ) - } - } - - 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> { - let trait_ref = self.replace_bound_vars_with_fresh_vars( - span, - infer::LateBoundRegionConversionTime::AssocTypeProjection(item_def_id), - poly_trait_ref, - ); - - let item_substs = <dyn AstConv<'tcx>>::create_substs_for_associated_item( - self, - self.tcx, - span, - item_def_id, - item_segment, - trait_ref.substs, - ); - - self.tcx().mk_projection(item_def_id, item_substs) - } - - fn normalize_ty(&self, span: Span, ty: Ty<'tcx>) -> Ty<'tcx> { - if ty.has_escaping_bound_vars() { - ty // FIXME: normalization and escaping regions - } else { - self.normalize_associated_types_in(span, ty) - } - } - - fn set_tainted_by_errors(&self) { - self.infcx.set_tainted_by_errors() - } - - fn record_ty(&self, hir_id: hir::HirId, ty: Ty<'tcx>, _span: Span) { - self.write_ty(hir_id, ty) - } -} diff --git a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs b/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs deleted file mode 100644 index ee0ad7b5d..000000000 --- a/compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs +++ /dev/null @@ -1,1139 +0,0 @@ -use super::FnCtxt; -use crate::astconv::AstConv; -use crate::errors::{AddReturnTypeSuggestion, ExpectedReturnTypeLabel}; - -use hir::def_id::DefId; -use rustc_ast::util::parser::ExprPrecedence; -use rustc_errors::{Applicability, Diagnostic, MultiSpan}; -use rustc_hir as hir; -use rustc_hir::def::{CtorOf, DefKind}; -use rustc_hir::lang_items::LangItem; -use rustc_hir::{ - Expr, ExprKind, GenericBound, Node, Path, QPath, Stmt, StmtKind, TyKind, WherePredicate, -}; -use rustc_infer::infer::{self, TyCtxtInferExt}; -use rustc_infer::traits::{self, StatementAsExpression}; -use rustc_middle::lint::in_external_macro; -use rustc_middle::ty::{self, Binder, IsSuggestable, Subst, ToPredicate, Ty}; -use rustc_span::symbol::sym; -use rustc_span::Span; -use rustc_trait_selection::infer::InferCtxtExt; -use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; - -impl<'a, 'tcx> FnCtxt<'a, 'tcx> { - pub(in super::super) fn suggest_semicolon_at_end(&self, span: Span, err: &mut Diagnostic) { - err.span_suggestion_short( - span.shrink_to_hi(), - "consider using a semicolon here", - ";", - Applicability::MachineApplicable, - ); - } - - /// On implicit return expressions with mismatched types, provides the following suggestions: - /// - /// - Points out the method's return type as the reason for the expected type. - /// - Possible missing semicolon. - /// - Possible missing return type if the return type is the default, and not `fn main()`. - pub fn suggest_mismatched_types_on_tail( - &self, - err: &mut Diagnostic, - expr: &'tcx hir::Expr<'tcx>, - expected: Ty<'tcx>, - found: Ty<'tcx>, - blk_id: hir::HirId, - ) -> bool { - let expr = expr.peel_drop_temps(); - self.suggest_missing_semicolon(err, expr, expected, false); - let mut pointing_at_return_type = false; - if let Some((fn_decl, can_suggest)) = self.get_fn_decl(blk_id) { - let fn_id = self.tcx.hir().get_return_block(blk_id).unwrap(); - pointing_at_return_type = self.suggest_missing_return_type( - err, - &fn_decl, - expected, - found, - can_suggest, - fn_id, - ); - self.suggest_missing_break_or_return_expr( - err, expr, &fn_decl, expected, found, blk_id, fn_id, - ); - } - pointing_at_return_type - } - - /// When encountering an fn-like type, try accessing the output of the type - /// // and suggesting calling it if it satisfies a predicate (i.e. if the - /// output has a method or a field): - /// ```compile_fail,E0308 - /// fn foo(x: usize) -> usize { x } - /// let x: usize = foo; // suggest calling the `foo` function: `foo(42)` - /// ``` - pub(crate) fn suggest_fn_call( - &self, - err: &mut Diagnostic, - expr: &hir::Expr<'_>, - found: Ty<'tcx>, - can_satisfy: impl FnOnce(Ty<'tcx>) -> bool, - ) -> bool { - let Some((def_id_or_name, output, inputs)) = self.extract_callable_info(expr, found) - else { return false; }; - if can_satisfy(output) { - let (sugg_call, mut applicability) = match inputs.len() { - 0 => ("".to_string(), Applicability::MachineApplicable), - 1..=4 => ( - inputs - .iter() - .map(|ty| { - if ty.is_suggestable(self.tcx, false) { - format!("/* {ty} */") - } else { - "".to_string() - } - }) - .collect::<Vec<_>>() - .join(", "), - Applicability::HasPlaceholders, - ), - _ => ("/* ... */".to_string(), Applicability::HasPlaceholders), - }; - - let msg = match def_id_or_name { - DefIdOrName::DefId(def_id) => match self.tcx.def_kind(def_id) { - DefKind::Ctor(CtorOf::Struct, _) => "instantiate this tuple struct".to_string(), - DefKind::Ctor(CtorOf::Variant, _) => { - "instantiate this tuple variant".to_string() - } - kind => format!("call this {}", kind.descr(def_id)), - }, - DefIdOrName::Name(name) => format!("call this {name}"), - }; - - let sugg = match expr.kind { - hir::ExprKind::Call(..) - | hir::ExprKind::Path(..) - | hir::ExprKind::Index(..) - | hir::ExprKind::Lit(..) => { - vec![(expr.span.shrink_to_hi(), format!("({sugg_call})"))] - } - hir::ExprKind::Closure { .. } => { - // Might be `{ expr } || { bool }` - applicability = Applicability::MaybeIncorrect; - vec![ - (expr.span.shrink_to_lo(), "(".to_string()), - (expr.span.shrink_to_hi(), format!(")({sugg_call})")), - ] - } - _ => { - vec![ - (expr.span.shrink_to_lo(), "(".to_string()), - (expr.span.shrink_to_hi(), format!(")({sugg_call})")), - ] - } - }; - - err.multipart_suggestion_verbose( - format!("use parentheses to {msg}"), - sugg, - applicability, - ); - - return true; - } - false - } - - /// Extracts information about a callable type for diagnostics. This is a - /// heuristic -- it doesn't necessarily mean that a type is always callable, - /// because the callable type must also be well-formed to be called. - pub(in super::super) fn extract_callable_info( - &self, - expr: &Expr<'_>, - found: Ty<'tcx>, - ) -> Option<(DefIdOrName, Ty<'tcx>, Vec<Ty<'tcx>>)> { - // Autoderef is useful here because sometimes we box callables, etc. - let Some((def_id_or_name, output, inputs)) = self.autoderef(expr.span, found).silence_errors().find_map(|(found, _)| { - match *found.kind() { - ty::FnPtr(fn_sig) => - Some((DefIdOrName::Name("function pointer"), fn_sig.output(), fn_sig.inputs())), - ty::FnDef(def_id, _) => { - let fn_sig = found.fn_sig(self.tcx); - Some((DefIdOrName::DefId(def_id), fn_sig.output(), fn_sig.inputs())) - } - ty::Closure(def_id, substs) => { - let fn_sig = substs.as_closure().sig(); - Some((DefIdOrName::DefId(def_id), fn_sig.output(), fn_sig.inputs().map_bound(|inputs| &inputs[1..]))) - } - ty::Opaque(def_id, substs) => { - self.tcx.bound_item_bounds(def_id).subst(self.tcx, substs).iter().find_map(|pred| { - if let ty::PredicateKind::Projection(proj) = pred.kind().skip_binder() - && Some(proj.projection_ty.item_def_id) == self.tcx.lang_items().fn_once_output() - // args tuple will always be substs[1] - && let ty::Tuple(args) = proj.projection_ty.substs.type_at(1).kind() - { - Some(( - DefIdOrName::DefId(def_id), - pred.kind().rebind(proj.term.ty().unwrap()), - pred.kind().rebind(args.as_slice()), - )) - } else { - None - } - }) - } - ty::Dynamic(data, _, ty::Dyn) => { - data.iter().find_map(|pred| { - if let ty::ExistentialPredicate::Projection(proj) = pred.skip_binder() - && Some(proj.item_def_id) == self.tcx.lang_items().fn_once_output() - // for existential projection, substs are shifted over by 1 - && let ty::Tuple(args) = proj.substs.type_at(0).kind() - { - Some(( - DefIdOrName::Name("trait object"), - pred.rebind(proj.term.ty().unwrap()), - pred.rebind(args.as_slice()), - )) - } else { - None - } - }) - } - ty::Param(param) => { - let def_id = self.tcx.generics_of(self.body_id.owner).type_param(¶m, self.tcx).def_id; - self.tcx.predicates_of(self.body_id.owner).predicates.iter().find_map(|(pred, _)| { - if let ty::PredicateKind::Projection(proj) = pred.kind().skip_binder() - && Some(proj.projection_ty.item_def_id) == self.tcx.lang_items().fn_once_output() - && proj.projection_ty.self_ty() == found - // args tuple will always be substs[1] - && let ty::Tuple(args) = proj.projection_ty.substs.type_at(1).kind() - { - Some(( - DefIdOrName::DefId(def_id), - pred.kind().rebind(proj.term.ty().unwrap()), - pred.kind().rebind(args.as_slice()), - )) - } else { - None - } - }) - } - _ => None, - } - }) else { return None; }; - - let output = self.replace_bound_vars_with_fresh_vars(expr.span, infer::FnCall, output); - let inputs = inputs - .skip_binder() - .iter() - .map(|ty| { - self.replace_bound_vars_with_fresh_vars( - expr.span, - infer::FnCall, - inputs.rebind(*ty), - ) - }) - .collect(); - - // We don't want to register any extra obligations, which should be - // implied by wf, but also because that would possibly result in - // erroneous errors later on. - let infer::InferOk { value: output, obligations: _ } = - self.normalize_associated_types_in_as_infer_ok(expr.span, output); - - if output.is_ty_var() { None } else { Some((def_id_or_name, output, inputs)) } - } - - pub fn suggest_two_fn_call( - &self, - err: &mut Diagnostic, - lhs_expr: &'tcx hir::Expr<'tcx>, - lhs_ty: Ty<'tcx>, - rhs_expr: &'tcx hir::Expr<'tcx>, - rhs_ty: Ty<'tcx>, - can_satisfy: impl FnOnce(Ty<'tcx>, Ty<'tcx>) -> bool, - ) -> bool { - let Some((_, lhs_output_ty, lhs_inputs)) = self.extract_callable_info(lhs_expr, lhs_ty) - else { return false; }; - let Some((_, rhs_output_ty, rhs_inputs)) = self.extract_callable_info(rhs_expr, rhs_ty) - else { return false; }; - - if can_satisfy(lhs_output_ty, rhs_output_ty) { - let mut sugg = vec![]; - let mut applicability = Applicability::MachineApplicable; - - for (expr, inputs) in [(lhs_expr, lhs_inputs), (rhs_expr, rhs_inputs)] { - let (sugg_call, this_applicability) = match inputs.len() { - 0 => ("".to_string(), Applicability::MachineApplicable), - 1..=4 => ( - inputs - .iter() - .map(|ty| { - if ty.is_suggestable(self.tcx, false) { - format!("/* {ty} */") - } else { - "/* value */".to_string() - } - }) - .collect::<Vec<_>>() - .join(", "), - Applicability::HasPlaceholders, - ), - _ => ("/* ... */".to_string(), Applicability::HasPlaceholders), - }; - - applicability = applicability.max(this_applicability); - - match expr.kind { - hir::ExprKind::Call(..) - | hir::ExprKind::Path(..) - | hir::ExprKind::Index(..) - | hir::ExprKind::Lit(..) => { - sugg.extend([(expr.span.shrink_to_hi(), format!("({sugg_call})"))]); - } - hir::ExprKind::Closure { .. } => { - // Might be `{ expr } || { bool }` - applicability = Applicability::MaybeIncorrect; - sugg.extend([ - (expr.span.shrink_to_lo(), "(".to_string()), - (expr.span.shrink_to_hi(), format!(")({sugg_call})")), - ]); - } - _ => { - sugg.extend([ - (expr.span.shrink_to_lo(), "(".to_string()), - (expr.span.shrink_to_hi(), format!(")({sugg_call})")), - ]); - } - } - } - - err.multipart_suggestion_verbose( - format!("use parentheses to call these"), - sugg, - applicability, - ); - - true - } else { - false - } - } - - pub fn suggest_deref_ref_or_into( - &self, - err: &mut Diagnostic, - expr: &hir::Expr<'tcx>, - expected: Ty<'tcx>, - found: Ty<'tcx>, - expected_ty_expr: Option<&'tcx hir::Expr<'tcx>>, - ) { - let expr = expr.peel_blocks(); - if let Some((sp, msg, suggestion, applicability, verbose)) = - self.check_ref(expr, found, expected) - { - if verbose { - err.span_suggestion_verbose(sp, &msg, suggestion, applicability); - } else { - err.span_suggestion(sp, &msg, suggestion, applicability); - } - } else if self.suggest_fn_call(err, expr, found, |output| self.can_coerce(output, expected)) - && let ty::FnDef(def_id, ..) = &found.kind() - && let Some(sp) = self.tcx.hir().span_if_local(*def_id) - { - err.span_label(sp, format!("{found} defined here")); - } else if !self.check_for_cast(err, expr, found, expected, expected_ty_expr) { - let methods = self.get_conversion_methods(expr.span, expected, found, expr.hir_id); - if !methods.is_empty() { - let mut suggestions = methods.iter() - .filter_map(|conversion_method| { - let receiver_method_ident = expr.method_ident(); - if let Some(method_ident) = receiver_method_ident - && method_ident.name == conversion_method.name - { - return None // do not suggest code that is already there (#53348) - } - - let method_call_list = [sym::to_vec, sym::to_string]; - let mut sugg = if let ExprKind::MethodCall(receiver_method, ..) = expr.kind - && receiver_method.ident.name == sym::clone - && method_call_list.contains(&conversion_method.name) - // If receiver is `.clone()` and found type has one of those methods, - // we guess that the user wants to convert from a slice type (`&[]` or `&str`) - // to an owned type (`Vec` or `String`). These conversions clone internally, - // so we remove the user's `clone` call. - { - vec![( - receiver_method.ident.span, - conversion_method.name.to_string() - )] - } else if expr.precedence().order() - < ExprPrecedence::MethodCall.order() - { - vec![ - (expr.span.shrink_to_lo(), "(".to_string()), - (expr.span.shrink_to_hi(), format!(").{}()", conversion_method.name)), - ] - } else { - vec![(expr.span.shrink_to_hi(), format!(".{}()", conversion_method.name))] - }; - let struct_pat_shorthand_field = self.maybe_get_struct_pattern_shorthand_field(expr); - if let Some(name) = struct_pat_shorthand_field { - sugg.insert( - 0, - (expr.span.shrink_to_lo(), format!("{}: ", name)), - ); - } - Some(sugg) - }) - .peekable(); - if suggestions.peek().is_some() { - err.multipart_suggestions( - "try using a conversion method", - suggestions, - Applicability::MaybeIncorrect, - ); - } - } else if let ty::Adt(found_adt, found_substs) = found.kind() - && self.tcx.is_diagnostic_item(sym::Option, found_adt.did()) - && let ty::Adt(expected_adt, expected_substs) = expected.kind() - && self.tcx.is_diagnostic_item(sym::Option, expected_adt.did()) - && let ty::Ref(_, inner_ty, _) = expected_substs.type_at(0).kind() - && inner_ty.is_str() - { - let ty = found_substs.type_at(0); - let mut peeled = ty; - let mut ref_cnt = 0; - while let ty::Ref(_, inner, _) = peeled.kind() { - peeled = *inner; - ref_cnt += 1; - } - if let ty::Adt(adt, _) = peeled.kind() - && self.tcx.is_diagnostic_item(sym::String, adt.did()) - { - err.span_suggestion_verbose( - expr.span.shrink_to_hi(), - "try converting the passed type into a `&str`", - format!(".map(|x| &*{}x)", "*".repeat(ref_cnt)), - Applicability::MaybeIncorrect, - ); - } - } - } - } - - /// When encountering the expected boxed value allocated in the stack, suggest allocating it - /// in the heap by calling `Box::new()`. - pub(in super::super) fn suggest_boxing_when_appropriate( - &self, - err: &mut Diagnostic, - expr: &hir::Expr<'_>, - expected: Ty<'tcx>, - found: Ty<'tcx>, - ) { - if self.tcx.hir().is_inside_const_context(expr.hir_id) { - // Do not suggest `Box::new` in const context. - return; - } - if !expected.is_box() || found.is_box() { - return; - } - let boxed_found = self.tcx.mk_box(found); - if self.can_coerce(boxed_found, expected) { - err.multipart_suggestion( - "store this in the heap by calling `Box::new`", - vec![ - (expr.span.shrink_to_lo(), "Box::new(".to_string()), - (expr.span.shrink_to_hi(), ")".to_string()), - ], - Applicability::MachineApplicable, - ); - err.note( - "for more on the distinction between the stack and the heap, read \ - https://doc.rust-lang.org/book/ch15-01-box.html, \ - https://doc.rust-lang.org/rust-by-example/std/box.html, and \ - https://doc.rust-lang.org/std/boxed/index.html", - ); - } - } - - /// When encountering a closure that captures variables, where a FnPtr is expected, - /// suggest a non-capturing closure - pub(in super::super) fn suggest_no_capture_closure( - &self, - err: &mut Diagnostic, - expected: Ty<'tcx>, - found: Ty<'tcx>, - ) { - if let (ty::FnPtr(_), ty::Closure(def_id, _)) = (expected.kind(), found.kind()) { - if let Some(upvars) = self.tcx.upvars_mentioned(*def_id) { - // Report upto four upvars being captured to reduce the amount error messages - // reported back to the user. - let spans_and_labels = upvars - .iter() - .take(4) - .map(|(var_hir_id, upvar)| { - let var_name = self.tcx.hir().name(*var_hir_id).to_string(); - let msg = format!("`{}` captured here", var_name); - (upvar.span, msg) - }) - .collect::<Vec<_>>(); - - let mut multi_span: MultiSpan = - spans_and_labels.iter().map(|(sp, _)| *sp).collect::<Vec<_>>().into(); - for (sp, label) in spans_and_labels { - multi_span.push_span_label(sp, label); - } - err.span_note( - multi_span, - "closures can only be coerced to `fn` types if they do not capture any variables" - ); - } - } - } - - /// When encountering an `impl Future` where `BoxFuture` is expected, suggest `Box::pin`. - #[instrument(skip(self, err))] - pub(in super::super) fn suggest_calling_boxed_future_when_appropriate( - &self, - err: &mut Diagnostic, - expr: &hir::Expr<'_>, - expected: Ty<'tcx>, - found: Ty<'tcx>, - ) -> bool { - // Handle #68197. - - if self.tcx.hir().is_inside_const_context(expr.hir_id) { - // Do not suggest `Box::new` in const context. - return false; - } - let pin_did = self.tcx.lang_items().pin_type(); - // This guards the `unwrap` and `mk_box` below. - if pin_did.is_none() || self.tcx.lang_items().owned_box().is_none() { - return false; - } - let box_found = self.tcx.mk_box(found); - let pin_box_found = self.tcx.mk_lang_item(box_found, LangItem::Pin).unwrap(); - let pin_found = self.tcx.mk_lang_item(found, LangItem::Pin).unwrap(); - match expected.kind() { - ty::Adt(def, _) if Some(def.did()) == pin_did => { - if self.can_coerce(pin_box_found, expected) { - debug!("can coerce {:?} to {:?}, suggesting Box::pin", pin_box_found, expected); - match found.kind() { - ty::Adt(def, _) if def.is_box() => { - err.help("use `Box::pin`"); - } - _ => { - err.multipart_suggestion( - "you need to pin and box this expression", - vec![ - (expr.span.shrink_to_lo(), "Box::pin(".to_string()), - (expr.span.shrink_to_hi(), ")".to_string()), - ], - Applicability::MaybeIncorrect, - ); - } - } - true - } else if self.can_coerce(pin_found, expected) { - match found.kind() { - ty::Adt(def, _) if def.is_box() => { - err.help("use `Box::pin`"); - true - } - _ => false, - } - } else { - false - } - } - ty::Adt(def, _) if def.is_box() && self.can_coerce(box_found, expected) => { - // Check if the parent expression is a call to Pin::new. If it - // is and we were expecting a Box, ergo Pin<Box<expected>>, we - // can suggest Box::pin. - let parent = self.tcx.hir().get_parent_node(expr.hir_id); - let Some(Node::Expr(Expr { kind: ExprKind::Call(fn_name, _), .. })) = self.tcx.hir().find(parent) else { - return false; - }; - match fn_name.kind { - ExprKind::Path(QPath::TypeRelative( - hir::Ty { - kind: TyKind::Path(QPath::Resolved(_, Path { res: recv_ty, .. })), - .. - }, - method, - )) if recv_ty.opt_def_id() == pin_did && method.ident.name == sym::new => { - err.span_suggestion( - fn_name.span, - "use `Box::pin` to pin and box this expression", - "Box::pin", - Applicability::MachineApplicable, - ); - true - } - _ => false, - } - } - _ => false, - } - } - - /// A common error is to forget to add a semicolon at the end of a block, e.g., - /// - /// ```compile_fail,E0308 - /// # fn bar_that_returns_u32() -> u32 { 4 } - /// fn foo() { - /// bar_that_returns_u32() - /// } - /// ``` - /// - /// This routine checks if the return expression in a block would make sense on its own as a - /// statement and the return type has been left as default or has been specified as `()`. If so, - /// it suggests adding a semicolon. - /// - /// If the expression is the expression of a closure without block (`|| expr`), a - /// block is needed to be added too (`|| { expr; }`). This is denoted by `needs_block`. - pub fn suggest_missing_semicolon( - &self, - err: &mut Diagnostic, - expression: &'tcx hir::Expr<'tcx>, - expected: Ty<'tcx>, - needs_block: bool, - ) { - if expected.is_unit() { - // `BlockTailExpression` only relevant if the tail expr would be - // useful on its own. - match expression.kind { - ExprKind::Call(..) - | ExprKind::MethodCall(..) - | ExprKind::Loop(..) - | ExprKind::If(..) - | ExprKind::Match(..) - | ExprKind::Block(..) - if expression.can_have_side_effects() - // If the expression is from an external macro, then do not suggest - // adding a semicolon, because there's nowhere to put it. - // See issue #81943. - && !in_external_macro(self.tcx.sess, expression.span) => - { - if needs_block { - err.multipart_suggestion( - "consider using a semicolon here", - vec![ - (expression.span.shrink_to_lo(), "{ ".to_owned()), - (expression.span.shrink_to_hi(), "; }".to_owned()), - ], - Applicability::MachineApplicable, - ); - } else { - err.span_suggestion( - expression.span.shrink_to_hi(), - "consider using a semicolon here", - ";", - Applicability::MachineApplicable, - ); - } - } - _ => (), - } - } - } - - /// A possible error is to forget to add a return type that is needed: - /// - /// ```compile_fail,E0308 - /// # fn bar_that_returns_u32() -> u32 { 4 } - /// fn foo() { - /// bar_that_returns_u32() - /// } - /// ``` - /// - /// This routine checks if the return type is left as default, the method is not part of an - /// `impl` block and that it isn't the `main` method. If so, it suggests setting the return - /// type. - pub(in super::super) fn suggest_missing_return_type( - &self, - err: &mut Diagnostic, - fn_decl: &hir::FnDecl<'_>, - expected: Ty<'tcx>, - found: Ty<'tcx>, - can_suggest: bool, - fn_id: hir::HirId, - ) -> bool { - let found = - self.resolve_numeric_literals_with_default(self.resolve_vars_if_possible(found)); - // Only suggest changing the return type for methods that - // haven't set a return type at all (and aren't `fn main()` or an impl). - match &fn_decl.output { - &hir::FnRetTy::DefaultReturn(span) if expected.is_unit() && !can_suggest => { - // `fn main()` must return `()`, do not suggest changing return type - err.subdiagnostic(ExpectedReturnTypeLabel::Unit { span }); - return true; - } - &hir::FnRetTy::DefaultReturn(span) if expected.is_unit() => { - if found.is_suggestable(self.tcx, false) { - err.subdiagnostic(AddReturnTypeSuggestion::Add { span, found: found.to_string() }); - return true; - } else if let ty::Closure(_, substs) = found.kind() - // FIXME(compiler-errors): Get better at printing binders... - && let closure = substs.as_closure() - && closure.sig().is_suggestable(self.tcx, false) - { - err.subdiagnostic(AddReturnTypeSuggestion::Add { span, found: closure.print_as_impl_trait().to_string() }); - return true; - } else { - // FIXME: if `found` could be `impl Iterator` we should suggest that. - err.subdiagnostic(AddReturnTypeSuggestion::MissingHere { span }); - return true - } - } - &hir::FnRetTy::Return(ref ty) => { - // Only point to return type if the expected type is the return type, as if they - // are not, the expectation must have been caused by something else. - debug!("suggest_missing_return_type: return type {:?} node {:?}", ty, ty.kind); - let span = ty.span; - let ty = <dyn AstConv<'_>>::ast_ty_to_ty(self, ty); - debug!("suggest_missing_return_type: return type {:?}", ty); - debug!("suggest_missing_return_type: expected type {:?}", ty); - let bound_vars = self.tcx.late_bound_vars(fn_id); - let ty = Binder::bind_with_vars(ty, bound_vars); - let ty = self.normalize_associated_types_in(span, ty); - let ty = self.tcx.erase_late_bound_regions(ty); - if self.can_coerce(expected, ty) { - err.subdiagnostic(ExpectedReturnTypeLabel::Other { span, expected }); - self.try_suggest_return_impl_trait(err, expected, ty, fn_id); - return true; - } - } - _ => {} - } - false - } - - /// check whether the return type is a generic type with a trait bound - /// only suggest this if the generic param is not present in the arguments - /// if this is true, hint them towards changing the return type to `impl Trait` - /// ```compile_fail,E0308 - /// fn cant_name_it<T: Fn() -> u32>() -> T { - /// || 3 - /// } - /// ``` - fn try_suggest_return_impl_trait( - &self, - err: &mut Diagnostic, - expected: Ty<'tcx>, - found: Ty<'tcx>, - fn_id: hir::HirId, - ) { - // Only apply the suggestion if: - // - the return type is a generic parameter - // - the generic param is not used as a fn param - // - the generic param has at least one bound - // - the generic param doesn't appear in any other bounds where it's not the Self type - // Suggest: - // - Changing the return type to be `impl <all bounds>` - - debug!("try_suggest_return_impl_trait, expected = {:?}, found = {:?}", expected, found); - - let ty::Param(expected_ty_as_param) = expected.kind() else { return }; - - let fn_node = self.tcx.hir().find(fn_id); - - let Some(hir::Node::Item(hir::Item { - kind: - hir::ItemKind::Fn( - hir::FnSig { decl: hir::FnDecl { inputs: fn_parameters, output: fn_return, .. }, .. }, - hir::Generics { params, predicates, .. }, - _body_id, - ), - .. - })) = fn_node else { return }; - - if params.get(expected_ty_as_param.index as usize).is_none() { - return; - }; - - // get all where BoundPredicates here, because they are used in to cases below - let where_predicates = predicates - .iter() - .filter_map(|p| match p { - WherePredicate::BoundPredicate(hir::WhereBoundPredicate { - bounds, - bounded_ty, - .. - }) => { - // FIXME: Maybe these calls to `ast_ty_to_ty` can be removed (and the ones below) - let ty = <dyn AstConv<'_>>::ast_ty_to_ty(self, bounded_ty); - Some((ty, bounds)) - } - _ => None, - }) - .map(|(ty, bounds)| match ty.kind() { - ty::Param(param_ty) if param_ty == expected_ty_as_param => Ok(Some(bounds)), - // check whether there is any predicate that contains our `T`, like `Option<T>: Send` - _ => match ty.contains(expected) { - true => Err(()), - false => Ok(None), - }, - }) - .collect::<Result<Vec<_>, _>>(); - - let Ok(where_predicates) = where_predicates else { return }; - - // now get all predicates in the same types as the where bounds, so we can chain them - let predicates_from_where = - where_predicates.iter().flatten().flat_map(|bounds| bounds.iter()); - - // extract all bounds from the source code using their spans - let all_matching_bounds_strs = predicates_from_where - .filter_map(|bound| match bound { - GenericBound::Trait(_, _) => { - self.tcx.sess.source_map().span_to_snippet(bound.span()).ok() - } - _ => None, - }) - .collect::<Vec<String>>(); - - if all_matching_bounds_strs.len() == 0 { - return; - } - - let all_bounds_str = all_matching_bounds_strs.join(" + "); - - let ty_param_used_in_fn_params = fn_parameters.iter().any(|param| { - let ty = <dyn AstConv<'_>>::ast_ty_to_ty(self, param); - matches!(ty.kind(), ty::Param(fn_param_ty_param) if expected_ty_as_param == fn_param_ty_param) - }); - - if ty_param_used_in_fn_params { - return; - } - - err.span_suggestion( - fn_return.span(), - "consider using an impl return type", - format!("impl {}", all_bounds_str), - Applicability::MaybeIncorrect, - ); - } - - pub(in super::super) fn suggest_missing_break_or_return_expr( - &self, - err: &mut Diagnostic, - expr: &'tcx hir::Expr<'tcx>, - fn_decl: &hir::FnDecl<'_>, - expected: Ty<'tcx>, - found: Ty<'tcx>, - id: hir::HirId, - fn_id: hir::HirId, - ) { - if !expected.is_unit() { - return; - } - let found = self.resolve_vars_with_obligations(found); - - let in_loop = self.is_loop(id) - || self.tcx.hir().parent_iter(id).any(|(parent_id, _)| self.is_loop(parent_id)); - - let in_local_statement = self.is_local_statement(id) - || self - .tcx - .hir() - .parent_iter(id) - .any(|(parent_id, _)| self.is_local_statement(parent_id)); - - if in_loop && in_local_statement { - err.multipart_suggestion( - "you might have meant to break the loop with this value", - vec![ - (expr.span.shrink_to_lo(), "break ".to_string()), - (expr.span.shrink_to_hi(), ";".to_string()), - ], - Applicability::MaybeIncorrect, - ); - return; - } - - if let hir::FnRetTy::Return(ty) = fn_decl.output { - let ty = <dyn AstConv<'_>>::ast_ty_to_ty(self, ty); - let bound_vars = self.tcx.late_bound_vars(fn_id); - let ty = self.tcx.erase_late_bound_regions(Binder::bind_with_vars(ty, bound_vars)); - let ty = self.normalize_associated_types_in(expr.span, ty); - let ty = match self.tcx.asyncness(fn_id.owner) { - hir::IsAsync::Async => self - .tcx - .infer_ctxt() - .enter(|infcx| { - infcx.get_impl_future_output_ty(ty).unwrap_or_else(|| { - span_bug!( - fn_decl.output.span(), - "failed to get output type of async function" - ) - }) - }) - .skip_binder(), - hir::IsAsync::NotAsync => ty, - }; - if self.can_coerce(found, ty) { - err.multipart_suggestion( - "you might have meant to return this value", - vec![ - (expr.span.shrink_to_lo(), "return ".to_string()), - (expr.span.shrink_to_hi(), ";".to_string()), - ], - Applicability::MaybeIncorrect, - ); - } - } - } - - pub(in super::super) fn suggest_missing_parentheses( - &self, - err: &mut Diagnostic, - expr: &hir::Expr<'_>, - ) { - let sp = self.tcx.sess.source_map().start_point(expr.span); - if let Some(sp) = self.tcx.sess.parse_sess.ambiguous_block_expr_parse.borrow().get(&sp) { - // `{ 42 } &&x` (#61475) or `{ 42 } && if x { 1 } else { 0 }` - self.tcx.sess.parse_sess.expr_parentheses_needed(err, *sp); - } - } - - /// Given an expression type mismatch, peel any `&` expressions until we get to - /// a block expression, and then suggest replacing the braces with square braces - /// if it was possibly mistaken array syntax. - pub(crate) fn suggest_block_to_brackets_peeling_refs( - &self, - diag: &mut Diagnostic, - mut expr: &hir::Expr<'_>, - mut expr_ty: Ty<'tcx>, - mut expected_ty: Ty<'tcx>, - ) { - loop { - match (&expr.kind, expr_ty.kind(), expected_ty.kind()) { - ( - hir::ExprKind::AddrOf(_, _, inner_expr), - ty::Ref(_, inner_expr_ty, _), - ty::Ref(_, inner_expected_ty, _), - ) => { - expr = *inner_expr; - expr_ty = *inner_expr_ty; - expected_ty = *inner_expected_ty; - } - (hir::ExprKind::Block(blk, _), _, _) => { - self.suggest_block_to_brackets(diag, *blk, expr_ty, expected_ty); - break; - } - _ => break, - } - } - } - - pub(crate) fn suggest_copied_or_cloned( - &self, - diag: &mut Diagnostic, - expr: &hir::Expr<'_>, - expr_ty: Ty<'tcx>, - expected_ty: Ty<'tcx>, - ) { - let ty::Adt(adt_def, substs) = expr_ty.kind() else { return; }; - let ty::Adt(expected_adt_def, expected_substs) = expected_ty.kind() else { return; }; - if adt_def != expected_adt_def { - return; - } - - let mut suggest_copied_or_cloned = || { - let expr_inner_ty = substs.type_at(0); - let expected_inner_ty = expected_substs.type_at(0); - if let ty::Ref(_, ty, hir::Mutability::Not) = expr_inner_ty.kind() - && self.can_eq(self.param_env, *ty, expected_inner_ty).is_ok() - { - let def_path = self.tcx.def_path_str(adt_def.did()); - if self.type_is_copy_modulo_regions(self.param_env, *ty, expr.span) { - diag.span_suggestion_verbose( - expr.span.shrink_to_hi(), - format!( - "use `{def_path}::copied` to copy the value inside the `{def_path}`" - ), - ".copied()", - Applicability::MachineApplicable, - ); - } else if let Some(clone_did) = self.tcx.lang_items().clone_trait() - && rustc_trait_selection::traits::type_known_to_meet_bound_modulo_regions( - self, - self.param_env, - *ty, - clone_did, - expr.span - ) - { - diag.span_suggestion_verbose( - expr.span.shrink_to_hi(), - format!( - "use `{def_path}::cloned` to clone the value inside the `{def_path}`" - ), - ".cloned()", - Applicability::MachineApplicable, - ); - } - } - }; - - if let Some(result_did) = self.tcx.get_diagnostic_item(sym::Result) - && adt_def.did() == result_did - // Check that the error types are equal - && self.can_eq(self.param_env, substs.type_at(1), expected_substs.type_at(1)).is_ok() - { - suggest_copied_or_cloned(); - } else if let Some(option_did) = self.tcx.get_diagnostic_item(sym::Option) - && adt_def.did() == option_did - { - suggest_copied_or_cloned(); - } - } - - /// Suggest wrapping the block in square brackets instead of curly braces - /// in case the block was mistaken array syntax, e.g. `{ 1 }` -> `[ 1 ]`. - pub(crate) fn suggest_block_to_brackets( - &self, - diag: &mut Diagnostic, - blk: &hir::Block<'_>, - blk_ty: Ty<'tcx>, - expected_ty: Ty<'tcx>, - ) { - if let ty::Slice(elem_ty) | ty::Array(elem_ty, _) = expected_ty.kind() { - if self.can_coerce(blk_ty, *elem_ty) - && blk.stmts.is_empty() - && blk.rules == hir::BlockCheckMode::DefaultBlock - { - let source_map = self.tcx.sess.source_map(); - if let Ok(snippet) = source_map.span_to_snippet(blk.span) { - if snippet.starts_with('{') && snippet.ends_with('}') { - diag.multipart_suggestion_verbose( - "to create an array, use square brackets instead of curly braces", - vec![ - ( - blk.span - .shrink_to_lo() - .with_hi(rustc_span::BytePos(blk.span.lo().0 + 1)), - "[".to_string(), - ), - ( - blk.span - .shrink_to_hi() - .with_lo(rustc_span::BytePos(blk.span.hi().0 - 1)), - "]".to_string(), - ), - ], - Applicability::MachineApplicable, - ); - } - } - } - } - } - - fn is_loop(&self, id: hir::HirId) -> bool { - let node = self.tcx.hir().get(id); - matches!(node, Node::Expr(Expr { kind: ExprKind::Loop(..), .. })) - } - - fn is_local_statement(&self, id: hir::HirId) -> bool { - let node = self.tcx.hir().get(id); - matches!(node, Node::Stmt(Stmt { kind: StmtKind::Local(..), .. })) - } - - /// Suggest that `&T` was cloned instead of `T` because `T` does not implement `Clone`, - /// which is a side-effect of autoref. - pub(crate) fn note_type_is_not_clone( - &self, - diag: &mut Diagnostic, - expected_ty: Ty<'tcx>, - found_ty: Ty<'tcx>, - expr: &hir::Expr<'_>, - ) { - let hir::ExprKind::MethodCall(segment, callee_expr, &[], _) = expr.kind else { return; }; - let Some(clone_trait_did) = self.tcx.lang_items().clone_trait() else { return; }; - let ty::Ref(_, pointee_ty, _) = found_ty.kind() else { return }; - let results = self.typeck_results.borrow(); - // First, look for a `Clone::clone` call - if segment.ident.name == sym::clone - && results.type_dependent_def_id(expr.hir_id).map_or( - false, - |did| { - let assoc_item = self.tcx.associated_item(did); - assoc_item.container == ty::AssocItemContainer::TraitContainer - && assoc_item.container_id(self.tcx) == clone_trait_did - }, - ) - // If that clone call hasn't already dereferenced the self type (i.e. don't give this - // diagnostic in cases where we have `(&&T).clone()` and we expect `T`). - && !results.expr_adjustments(callee_expr).iter().any(|adj| matches!(adj.kind, ty::adjustment::Adjust::Deref(..))) - // Check that we're in fact trying to clone into the expected type - && self.can_coerce(*pointee_ty, expected_ty) - // And the expected type doesn't implement `Clone` - && !self.predicate_must_hold_considering_regions(&traits::Obligation { - cause: traits::ObligationCause::dummy(), - param_env: self.param_env, - recursion_depth: 0, - predicate: ty::Binder::dummy(ty::TraitRef { - def_id: clone_trait_did, - substs: self.tcx.mk_substs([expected_ty.into()].iter()), - }) - .without_const() - .to_predicate(self.tcx), - }) - { - diag.span_note( - callee_expr.span, - &format!( - "`{expected_ty}` does not implement `Clone`, so `{found_ty}` was cloned instead" - ), - ); - } - } - - /// A common error is to add an extra semicolon: - /// - /// ```compile_fail,E0308 - /// fn foo() -> usize { - /// 22; - /// } - /// ``` - /// - /// This routine checks if the final statement in a block is an - /// expression with an explicit semicolon whose type is compatible - /// with `expected_ty`. If so, it suggests removing the semicolon. - pub(crate) fn consider_removing_semicolon( - &self, - blk: &'tcx hir::Block<'tcx>, - expected_ty: Ty<'tcx>, - err: &mut Diagnostic, - ) -> bool { - if let Some((span_semi, boxed)) = self.could_remove_semicolon(blk, expected_ty) { - if let StatementAsExpression::NeedsBoxing = boxed { - err.span_suggestion_verbose( - span_semi, - "consider removing this semicolon and boxing the expression", - "", - Applicability::HasPlaceholders, - ); - } else { - err.span_suggestion_short( - span_semi, - "remove this semicolon", - "", - Applicability::MachineApplicable, - ); - } - true - } else { - false - } - } -} - -pub enum DefIdOrName { - DefId(DefId), - Name(&'static str), -} diff --git a/compiler/rustc_typeck/src/check/gather_locals.rs b/compiler/rustc_typeck/src/check/gather_locals.rs deleted file mode 100644 index 8f34a970f..000000000 --- a/compiler/rustc_typeck/src/check/gather_locals.rs +++ /dev/null @@ -1,160 +0,0 @@ -use crate::check::{FnCtxt, LocalTy, UserType}; -use rustc_hir as hir; -use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::PatKind; -use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; -use rustc_middle::ty::Ty; -use rustc_span::Span; -use rustc_trait_selection::traits; - -/// A declaration is an abstraction of [hir::Local] and [hir::Let]. -/// -/// It must have a hir_id, as this is how we connect gather_locals to the check functions. -pub(super) struct Declaration<'a> { - pub hir_id: hir::HirId, - pub pat: &'a hir::Pat<'a>, - pub ty: Option<&'a hir::Ty<'a>>, - pub span: Span, - pub init: Option<&'a hir::Expr<'a>>, - pub els: Option<&'a hir::Block<'a>>, -} - -impl<'a> From<&'a hir::Local<'a>> for Declaration<'a> { - fn from(local: &'a hir::Local<'a>) -> Self { - let hir::Local { hir_id, pat, ty, span, init, els, source: _ } = *local; - Declaration { hir_id, pat, ty, span, init, els } - } -} - -impl<'a> From<&'a hir::Let<'a>> for Declaration<'a> { - fn from(let_expr: &'a hir::Let<'a>) -> Self { - let hir::Let { hir_id, pat, ty, span, init } = *let_expr; - Declaration { hir_id, pat, ty, span, init: Some(init), els: None } - } -} - -pub(super) struct GatherLocalsVisitor<'a, 'tcx> { - fcx: &'a FnCtxt<'a, 'tcx>, - // parameters are special cases of patterns, but we want to handle them as - // *distinct* cases. so track when we are hitting a pattern *within* an fn - // parameter. - outermost_fn_param_pat: Option<Span>, -} - -impl<'a, 'tcx> GatherLocalsVisitor<'a, 'tcx> { - pub(super) fn new(fcx: &'a FnCtxt<'a, 'tcx>) -> Self { - Self { fcx, outermost_fn_param_pat: None } - } - - fn assign(&mut self, span: Span, nid: hir::HirId, ty_opt: Option<LocalTy<'tcx>>) -> Ty<'tcx> { - match ty_opt { - None => { - // Infer the variable's type. - let var_ty = self.fcx.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::TypeInference, - span, - }); - self.fcx - .locals - .borrow_mut() - .insert(nid, LocalTy { decl_ty: var_ty, revealed_ty: var_ty }); - var_ty - } - Some(typ) => { - // Take type that the user specified. - self.fcx.locals.borrow_mut().insert(nid, typ); - typ.revealed_ty - } - } - } - - /// Allocates a [LocalTy] for a declaration, which may have a type annotation. If it does have - /// a type annotation, then the LocalTy stored will be the resolved type. This may be found - /// again during type checking by querying [FnCtxt::local_ty] for the same hir_id. - fn declare(&mut self, decl: Declaration<'tcx>) { - let local_ty = match decl.ty { - Some(ref ty) => { - let o_ty = self.fcx.to_ty(&ty); - - let c_ty = self.fcx.inh.infcx.canonicalize_user_type_annotation(UserType::Ty(o_ty)); - debug!("visit_local: ty.hir_id={:?} o_ty={:?} c_ty={:?}", ty.hir_id, o_ty, c_ty); - self.fcx - .typeck_results - .borrow_mut() - .user_provided_types_mut() - .insert(ty.hir_id, c_ty); - - Some(LocalTy { decl_ty: o_ty, revealed_ty: o_ty }) - } - None => None, - }; - self.assign(decl.span, decl.hir_id, local_ty); - - debug!( - "local variable {:?} is assigned type {}", - decl.pat, - self.fcx.ty_to_string(self.fcx.locals.borrow().get(&decl.hir_id).unwrap().decl_ty) - ); - } -} - -impl<'a, 'tcx> Visitor<'tcx> for GatherLocalsVisitor<'a, 'tcx> { - // Add explicitly-declared locals. - fn visit_local(&mut self, local: &'tcx hir::Local<'tcx>) { - self.declare(local.into()); - intravisit::walk_local(self, local) - } - - fn visit_let_expr(&mut self, let_expr: &'tcx hir::Let<'tcx>) { - self.declare(let_expr.into()); - intravisit::walk_let_expr(self, let_expr); - } - - fn visit_param(&mut self, param: &'tcx hir::Param<'tcx>) { - let old_outermost_fn_param_pat = self.outermost_fn_param_pat.replace(param.ty_span); - intravisit::walk_param(self, param); - self.outermost_fn_param_pat = old_outermost_fn_param_pat; - } - - // Add pattern bindings. - fn visit_pat(&mut self, p: &'tcx hir::Pat<'tcx>) { - if let PatKind::Binding(_, _, ident, _) = p.kind { - let var_ty = self.assign(p.span, p.hir_id, None); - - if let Some(ty_span) = self.outermost_fn_param_pat { - if !self.fcx.tcx.features().unsized_fn_params { - self.fcx.require_type_is_sized( - var_ty, - p.span, - traits::SizedArgumentType(Some(ty_span)), - ); - } - } else { - if !self.fcx.tcx.features().unsized_locals { - self.fcx.require_type_is_sized(var_ty, p.span, traits::VariableType(p.hir_id)); - } - } - - debug!( - "pattern binding {} is assigned to {} with type {:?}", - ident, - self.fcx.ty_to_string(self.fcx.locals.borrow().get(&p.hir_id).unwrap().decl_ty), - var_ty - ); - } - let old_outermost_fn_param_pat = self.outermost_fn_param_pat.take(); - intravisit::walk_pat(self, p); - self.outermost_fn_param_pat = old_outermost_fn_param_pat; - } - - // Don't descend into the bodies of nested closures. - fn visit_fn( - &mut self, - _: intravisit::FnKind<'tcx>, - _: &'tcx hir::FnDecl<'tcx>, - _: hir::BodyId, - _: Span, - _: hir::HirId, - ) { - } -} diff --git a/compiler/rustc_typeck/src/check/generator_interior.rs b/compiler/rustc_typeck/src/check/generator_interior.rs deleted file mode 100644 index 7ab6d9e2b..000000000 --- a/compiler/rustc_typeck/src/check/generator_interior.rs +++ /dev/null @@ -1,646 +0,0 @@ -//! This calculates the types which has storage which lives across a suspension point in a -//! generator from the perspective of typeck. The actual types used at runtime -//! is calculated in `rustc_mir_transform::generator` and may be a subset of the -//! types computed here. - -use self::drop_ranges::DropRanges; -use super::FnCtxt; -use rustc_data_structures::fx::{FxHashSet, FxIndexSet}; -use rustc_errors::pluralize; -use rustc_hir as hir; -use rustc_hir::def::{CtorKind, DefKind, Res}; -use rustc_hir::def_id::DefId; -use rustc_hir::hir_id::HirIdSet; -use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::{Arm, Expr, ExprKind, Guard, HirId, Pat, PatKind}; -use rustc_middle::middle::region::{self, Scope, ScopeData, YieldData}; -use rustc_middle::ty::{self, RvalueScopes, Ty, TyCtxt, TypeVisitable}; -use rustc_span::symbol::sym; -use rustc_span::Span; - -mod drop_ranges; - -struct InteriorVisitor<'a, 'tcx> { - fcx: &'a FnCtxt<'a, 'tcx>, - region_scope_tree: &'a region::ScopeTree, - types: FxIndexSet<ty::GeneratorInteriorTypeCause<'tcx>>, - rvalue_scopes: &'a RvalueScopes, - expr_count: usize, - kind: hir::GeneratorKind, - prev_unresolved_span: Option<Span>, - linted_values: HirIdSet, - drop_ranges: DropRanges, -} - -impl<'a, 'tcx> InteriorVisitor<'a, 'tcx> { - fn record( - &mut self, - ty: Ty<'tcx>, - hir_id: HirId, - scope: Option<region::Scope>, - expr: Option<&'tcx Expr<'tcx>>, - source_span: Span, - ) { - use rustc_span::DUMMY_SP; - - let ty = self.fcx.resolve_vars_if_possible(ty); - - debug!( - "attempting to record type ty={:?}; hir_id={:?}; scope={:?}; expr={:?}; source_span={:?}; expr_count={:?}", - ty, hir_id, scope, expr, source_span, self.expr_count, - ); - - let live_across_yield = scope - .map(|s| { - self.region_scope_tree.yield_in_scope(s).and_then(|yield_data| { - // If we are recording an expression that is the last yield - // in the scope, or that has a postorder CFG index larger - // than the one of all of the yields, then its value can't - // be storage-live (and therefore live) at any of the yields. - // - // See the mega-comment at `yield_in_scope` for a proof. - - yield_data - .iter() - .find(|yield_data| { - debug!( - "comparing counts yield: {} self: {}, source_span = {:?}", - yield_data.expr_and_pat_count, self.expr_count, source_span - ); - - if self.fcx.sess().opts.unstable_opts.drop_tracking - && self - .drop_ranges - .is_dropped_at(hir_id, yield_data.expr_and_pat_count) - { - debug!("value is dropped at yield point; not recording"); - return false; - } - - // If it is a borrowing happening in the guard, - // it needs to be recorded regardless because they - // do live across this yield point. - yield_data.expr_and_pat_count >= self.expr_count - }) - .cloned() - }) - }) - .unwrap_or_else(|| { - Some(YieldData { span: DUMMY_SP, expr_and_pat_count: 0, source: self.kind.into() }) - }); - - if let Some(yield_data) = live_across_yield { - debug!( - "type in expr = {:?}, scope = {:?}, type = {:?}, count = {}, yield_span = {:?}", - expr, scope, ty, self.expr_count, yield_data.span - ); - - if let Some((unresolved_type, unresolved_type_span)) = - self.fcx.unresolved_type_vars(&ty) - { - // If unresolved type isn't a ty_var then unresolved_type_span is None - let span = self - .prev_unresolved_span - .unwrap_or_else(|| unresolved_type_span.unwrap_or(source_span)); - - // If we encounter an int/float variable, then inference fallback didn't - // finish due to some other error. Don't emit spurious additional errors. - if let ty::Infer(ty::InferTy::IntVar(_) | ty::InferTy::FloatVar(_)) = - unresolved_type.kind() - { - self.fcx - .tcx - .sess - .delay_span_bug(span, &format!("Encountered var {:?}", unresolved_type)); - } else { - let note = format!( - "the type is part of the {} because of this {}", - self.kind, yield_data.source - ); - - self.fcx - .need_type_info_err_in_generator(self.kind, span, unresolved_type) - .span_note(yield_data.span, &*note) - .emit(); - } - } else { - // Insert the type into the ordered set. - let scope_span = scope.map(|s| s.span(self.fcx.tcx, self.region_scope_tree)); - - if !self.linted_values.contains(&hir_id) { - check_must_not_suspend_ty( - self.fcx, - ty, - hir_id, - SuspendCheckData { - expr, - source_span, - yield_span: yield_data.span, - plural_len: 1, - ..Default::default() - }, - ); - self.linted_values.insert(hir_id); - } - - self.types.insert(ty::GeneratorInteriorTypeCause { - span: source_span, - ty, - scope_span, - yield_span: yield_data.span, - expr: expr.map(|e| e.hir_id), - }); - } - } else { - debug!( - "no type in expr = {:?}, count = {:?}, span = {:?}", - expr, - self.expr_count, - expr.map(|e| e.span) - ); - if let Some((unresolved_type, unresolved_type_span)) = - self.fcx.unresolved_type_vars(&ty) - { - debug!( - "remained unresolved_type = {:?}, unresolved_type_span: {:?}", - unresolved_type, unresolved_type_span - ); - self.prev_unresolved_span = unresolved_type_span; - } - } - } -} - -pub fn resolve_interior<'a, 'tcx>( - fcx: &'a FnCtxt<'a, 'tcx>, - def_id: DefId, - body_id: hir::BodyId, - interior: Ty<'tcx>, - kind: hir::GeneratorKind, -) { - let body = fcx.tcx.hir().body(body_id); - let typeck_results = fcx.inh.typeck_results.borrow(); - let mut visitor = InteriorVisitor { - fcx, - types: FxIndexSet::default(), - region_scope_tree: fcx.tcx.region_scope_tree(def_id), - rvalue_scopes: &typeck_results.rvalue_scopes, - expr_count: 0, - kind, - prev_unresolved_span: None, - linted_values: <_>::default(), - drop_ranges: drop_ranges::compute_drop_ranges(fcx, def_id, body), - }; - intravisit::walk_body(&mut visitor, body); - - // Check that we visited the same amount of expressions as the RegionResolutionVisitor - let region_expr_count = fcx.tcx.region_scope_tree(def_id).body_expr_count(body_id).unwrap(); - assert_eq!(region_expr_count, visitor.expr_count); - - // The types are already kept in insertion order. - let types = visitor.types; - - // The types in the generator interior contain lifetimes local to the generator itself, - // which should not be exposed outside of the generator. Therefore, we replace these - // lifetimes with existentially-bound lifetimes, which reflect the exact value of the - // lifetimes not being known by users. - // - // These lifetimes are used in auto trait impl checking (for example, - // if a Sync generator contains an &'α T, we need to check whether &'α T: Sync), - // so knowledge of the exact relationships between them isn't particularly important. - - debug!("types in generator {:?}, span = {:?}", types, body.value.span); - - let mut counter = 0; - let mut captured_tys = FxHashSet::default(); - let type_causes: Vec<_> = types - .into_iter() - .filter_map(|mut cause| { - // Erase regions and canonicalize late-bound regions to deduplicate as many types as we - // can. - let erased = fcx.tcx.erase_regions(cause.ty); - if captured_tys.insert(erased) { - // Replace all regions inside the generator interior with late bound regions. - // Note that each region slot in the types gets a new fresh late bound region, - // which means that none of the regions inside relate to any other, even if - // typeck had previously found constraints that would cause them to be related. - let folded = fcx.tcx.fold_regions(erased, |_, current_depth| { - let br = ty::BoundRegion { - var: ty::BoundVar::from_u32(counter), - kind: ty::BrAnon(counter), - }; - let r = fcx.tcx.mk_region(ty::ReLateBound(current_depth, br)); - counter += 1; - r - }); - - cause.ty = folded; - Some(cause) - } else { - None - } - }) - .collect(); - - // Extract type components to build the witness type. - let type_list = fcx.tcx.mk_type_list(type_causes.iter().map(|cause| cause.ty)); - let bound_vars = fcx.tcx.mk_bound_variable_kinds( - (0..counter).map(|i| ty::BoundVariableKind::Region(ty::BrAnon(i))), - ); - let witness = - fcx.tcx.mk_generator_witness(ty::Binder::bind_with_vars(type_list, bound_vars.clone())); - - drop(typeck_results); - // Store the generator types and spans into the typeck results for this generator. - fcx.inh.typeck_results.borrow_mut().generator_interior_types = - ty::Binder::bind_with_vars(type_causes, bound_vars); - - debug!( - "types in generator after region replacement {:?}, span = {:?}", - witness, body.value.span - ); - - // Unify the type variable inside the generator with the new witness - match fcx.at(&fcx.misc(body.value.span), fcx.param_env).eq(interior, witness) { - Ok(ok) => fcx.register_infer_ok_obligations(ok), - _ => bug!(), - } -} - -// This visitor has to have the same visit_expr calls as RegionResolutionVisitor in -// librustc_middle/middle/region.rs since `expr_count` is compared against the results -// there. -impl<'a, 'tcx> Visitor<'tcx> for InteriorVisitor<'a, 'tcx> { - fn visit_arm(&mut self, arm: &'tcx Arm<'tcx>) { - let Arm { guard, pat, body, .. } = arm; - self.visit_pat(pat); - if let Some(ref g) = guard { - { - // If there is a guard, we need to count all variables bound in the pattern as - // borrowed for the entire guard body, regardless of whether they are accessed. - // We do this by walking the pattern bindings and recording `&T` for any `x: T` - // that is bound. - - struct ArmPatCollector<'a, 'b, 'tcx> { - interior_visitor: &'a mut InteriorVisitor<'b, 'tcx>, - scope: Scope, - } - - impl<'a, 'b, 'tcx> Visitor<'tcx> for ArmPatCollector<'a, 'b, 'tcx> { - fn visit_pat(&mut self, pat: &'tcx Pat<'tcx>) { - intravisit::walk_pat(self, pat); - if let PatKind::Binding(_, id, ident, ..) = pat.kind { - let ty = - self.interior_visitor.fcx.typeck_results.borrow().node_type(id); - let tcx = self.interior_visitor.fcx.tcx; - let ty = tcx.mk_ref( - // Use `ReErased` as `resolve_interior` is going to replace all the - // regions anyway. - tcx.mk_region(ty::ReErased), - ty::TypeAndMut { ty, mutbl: hir::Mutability::Not }, - ); - self.interior_visitor.record( - ty, - id, - Some(self.scope), - None, - ident.span, - ); - } - } - } - - ArmPatCollector { - interior_visitor: self, - scope: Scope { id: g.body().hir_id.local_id, data: ScopeData::Node }, - } - .visit_pat(pat); - } - - match g { - Guard::If(ref e) => { - self.visit_expr(e); - } - Guard::IfLet(ref l) => { - self.visit_let_expr(l); - } - } - } - self.visit_expr(body); - } - - fn visit_pat(&mut self, pat: &'tcx Pat<'tcx>) { - intravisit::walk_pat(self, pat); - - self.expr_count += 1; - - if let PatKind::Binding(..) = pat.kind { - let scope = self.region_scope_tree.var_scope(pat.hir_id.local_id).unwrap(); - let ty = self.fcx.typeck_results.borrow().pat_ty(pat); - self.record(ty, pat.hir_id, Some(scope), None, pat.span); - } - } - - fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { - match &expr.kind { - ExprKind::Call(callee, args) => match &callee.kind { - ExprKind::Path(qpath) => { - let res = self.fcx.typeck_results.borrow().qpath_res(qpath, callee.hir_id); - match res { - // Direct calls never need to keep the callee `ty::FnDef` - // ZST in a temporary, so skip its type, just in case it - // can significantly complicate the generator type. - Res::Def( - DefKind::Fn | DefKind::AssocFn | DefKind::Ctor(_, CtorKind::Fn), - _, - ) => { - // NOTE(eddyb) this assumes a path expression has - // no nested expressions to keep track of. - self.expr_count += 1; - - // Record the rest of the call expression normally. - for arg in *args { - self.visit_expr(arg); - } - } - _ => intravisit::walk_expr(self, expr), - } - } - _ => intravisit::walk_expr(self, expr), - }, - _ => intravisit::walk_expr(self, expr), - } - - self.expr_count += 1; - - debug!("is_borrowed_temporary: {:?}", self.drop_ranges.is_borrowed_temporary(expr)); - - let ty = self.fcx.typeck_results.borrow().expr_ty_adjusted_opt(expr); - let may_need_drop = |ty: Ty<'tcx>| { - // Avoid ICEs in needs_drop. - let ty = self.fcx.resolve_vars_if_possible(ty); - let ty = self.fcx.tcx.erase_regions(ty); - if ty.needs_infer() { - return true; - } - ty.needs_drop(self.fcx.tcx, self.fcx.param_env) - }; - - // Typically, the value produced by an expression is consumed by its parent in some way, - // so we only have to check if the parent contains a yield (note that the parent may, for - // example, store the value into a local variable, but then we already consider local - // variables to be live across their scope). - // - // However, in the case of temporary values, we are going to store the value into a - // temporary on the stack that is live for the current temporary scope and then return a - // reference to it. That value may be live across the entire temporary scope. - // - // There's another subtlety: if the type has an observable drop, it must be dropped after - // the yield, even if it's not borrowed or referenced after the yield. Ideally this would - // *only* happen for types with observable drop, not all types which wrap them, but that - // doesn't match the behavior of MIR borrowck and causes ICEs. See the FIXME comment in - // src/test/ui/generator/drop-tracking-parent-expression.rs. - let scope = if self.drop_ranges.is_borrowed_temporary(expr) - || ty.map_or(true, |ty| { - let needs_drop = may_need_drop(ty); - debug!(?needs_drop, ?ty); - needs_drop - }) { - self.rvalue_scopes.temporary_scope(self.region_scope_tree, expr.hir_id.local_id) - } else { - let parent_expr = self - .fcx - .tcx - .hir() - .parent_iter(expr.hir_id) - .find(|(_, node)| matches!(node, hir::Node::Expr(_))) - .map(|(id, _)| id); - debug!("parent_expr: {:?}", parent_expr); - match parent_expr { - Some(parent) => Some(Scope { id: parent.local_id, data: ScopeData::Node }), - None => { - self.rvalue_scopes.temporary_scope(self.region_scope_tree, expr.hir_id.local_id) - } - } - }; - - // If there are adjustments, then record the final type -- - // this is the actual value that is being produced. - if let Some(adjusted_ty) = ty { - self.record(adjusted_ty, expr.hir_id, scope, Some(expr), expr.span); - } - - // Also record the unadjusted type (which is the only type if - // there are no adjustments). The reason for this is that the - // unadjusted value is sometimes a "temporary" that would wind - // up in a MIR temporary. - // - // As an example, consider an expression like `vec![].push(x)`. - // Here, the `vec![]` would wind up MIR stored into a - // temporary variable `t` which we can borrow to invoke - // `<Vec<_>>::push(&mut t, x)`. - // - // Note that an expression can have many adjustments, and we - // are just ignoring those intermediate types. This is because - // those intermediate values are always linearly "consumed" by - // the other adjustments, and hence would never be directly - // captured in the MIR. - // - // (Note that this partly relies on the fact that the `Deref` - // traits always return references, which means their content - // can be reborrowed without needing to spill to a temporary. - // If this were not the case, then we could conceivably have - // to create intermediate temporaries.) - // - // The type table might not have information for this expression - // if it is in a malformed scope. (#66387) - if let Some(ty) = self.fcx.typeck_results.borrow().expr_ty_opt(expr) { - self.record(ty, expr.hir_id, scope, Some(expr), expr.span); - } else { - self.fcx.tcx.sess.delay_span_bug(expr.span, "no type for node"); - } - } -} - -#[derive(Default)] -struct SuspendCheckData<'a, 'tcx> { - expr: Option<&'tcx Expr<'tcx>>, - source_span: Span, - yield_span: Span, - descr_pre: &'a str, - descr_post: &'a str, - plural_len: usize, -} - -// Returns whether it emitted a diagnostic or not -// Note that this fn and the proceeding one are based on the code -// for creating must_use diagnostics -// -// Note that this technique was chosen over things like a `Suspend` marker trait -// as it is simpler and has precedent in the compiler -fn check_must_not_suspend_ty<'tcx>( - fcx: &FnCtxt<'_, 'tcx>, - ty: Ty<'tcx>, - hir_id: HirId, - data: SuspendCheckData<'_, 'tcx>, -) -> bool { - if ty.is_unit() - // FIXME: should this check `is_ty_uninhabited_from`. This query is not available in this stage - // of typeck (before ReVar and RePlaceholder are removed), but may remove noise, like in - // `must_use` - // || fcx.tcx.is_ty_uninhabited_from(fcx.tcx.parent_module(hir_id).to_def_id(), ty, fcx.param_env) - { - return false; - } - - let plural_suffix = pluralize!(data.plural_len); - - debug!("Checking must_not_suspend for {}", ty); - - match *ty.kind() { - ty::Adt(..) if ty.is_box() => { - let boxed_ty = ty.boxed_ty(); - let descr_pre = &format!("{}boxed ", data.descr_pre); - check_must_not_suspend_ty(fcx, boxed_ty, hir_id, SuspendCheckData { descr_pre, ..data }) - } - ty::Adt(def, _) => check_must_not_suspend_def(fcx.tcx, def.did(), hir_id, data), - // FIXME: support adding the attribute to TAITs - ty::Opaque(def, _) => { - let mut has_emitted = false; - for &(predicate, _) in fcx.tcx.explicit_item_bounds(def) { - // We only look at the `DefId`, so it is safe to skip the binder here. - if let ty::PredicateKind::Trait(ref poly_trait_predicate) = - predicate.kind().skip_binder() - { - let def_id = poly_trait_predicate.trait_ref.def_id; - let descr_pre = &format!("{}implementer{} of ", data.descr_pre, plural_suffix); - if check_must_not_suspend_def( - fcx.tcx, - def_id, - hir_id, - SuspendCheckData { descr_pre, ..data }, - ) { - has_emitted = true; - break; - } - } - } - has_emitted - } - ty::Dynamic(binder, _, _) => { - let mut has_emitted = false; - for predicate in binder.iter() { - if let ty::ExistentialPredicate::Trait(ref trait_ref) = predicate.skip_binder() { - let def_id = trait_ref.def_id; - let descr_post = &format!(" trait object{}{}", plural_suffix, data.descr_post); - if check_must_not_suspend_def( - fcx.tcx, - def_id, - hir_id, - SuspendCheckData { descr_post, ..data }, - ) { - has_emitted = true; - break; - } - } - } - has_emitted - } - ty::Tuple(fields) => { - let mut has_emitted = false; - let comps = match data.expr.map(|e| &e.kind) { - Some(hir::ExprKind::Tup(comps)) => { - debug_assert_eq!(comps.len(), fields.len()); - Some(comps) - } - _ => None, - }; - for (i, ty) in fields.iter().enumerate() { - let descr_post = &format!(" in tuple element {i}"); - let span = comps.and_then(|c| c.get(i)).map(|e| e.span).unwrap_or(data.source_span); - if check_must_not_suspend_ty( - fcx, - ty, - hir_id, - SuspendCheckData { - descr_post, - expr: comps.and_then(|comps| comps.get(i)), - source_span: span, - ..data - }, - ) { - has_emitted = true; - } - } - has_emitted - } - ty::Array(ty, len) => { - let descr_pre = &format!("{}array{} of ", data.descr_pre, plural_suffix); - check_must_not_suspend_ty( - fcx, - ty, - hir_id, - SuspendCheckData { - descr_pre, - plural_len: len.try_eval_usize(fcx.tcx, fcx.param_env).unwrap_or(0) as usize - + 1, - ..data - }, - ) - } - // If drop tracking is enabled, we want to look through references, since the referrent - // may not be considered live across the await point. - ty::Ref(_region, ty, _mutability) if fcx.sess().opts.unstable_opts.drop_tracking => { - let descr_pre = &format!("{}reference{} to ", data.descr_pre, plural_suffix); - check_must_not_suspend_ty(fcx, ty, hir_id, SuspendCheckData { descr_pre, ..data }) - } - _ => false, - } -} - -fn check_must_not_suspend_def( - tcx: TyCtxt<'_>, - def_id: DefId, - hir_id: HirId, - data: SuspendCheckData<'_, '_>, -) -> bool { - if let Some(attr) = tcx.get_attr(def_id, sym::must_not_suspend) { - tcx.struct_span_lint_hir( - rustc_session::lint::builtin::MUST_NOT_SUSPEND, - hir_id, - data.source_span, - |lint| { - let msg = format!( - "{}`{}`{} held across a suspend point, but should not be", - data.descr_pre, - tcx.def_path_str(def_id), - data.descr_post, - ); - let mut err = lint.build(&msg); - - // add span pointing to the offending yield/await - err.span_label(data.yield_span, "the value is held across this suspend point"); - - // Add optional reason note - if let Some(note) = attr.value_str() { - // FIXME(guswynn): consider formatting this better - err.span_note(data.source_span, note.as_str()); - } - - // Add some quick suggestions on what to do - // FIXME: can `drop` work as a suggestion here as well? - err.span_help( - data.source_span, - "consider using a block (`{ ... }`) \ - to shrink the value's scope, ending before the suspend point", - ); - - err.emit(); - }, - ); - - true - } else { - false - } -} diff --git a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges.rs b/compiler/rustc_typeck/src/check/generator_interior/drop_ranges.rs deleted file mode 100644 index 518cd7342..000000000 --- a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges.rs +++ /dev/null @@ -1,309 +0,0 @@ -//! Drop range analysis finds the portions of the tree where a value is guaranteed to be dropped -//! (i.e. moved, uninitialized, etc.). This is used to exclude the types of those values from the -//! generator type. See `InteriorVisitor::record` for where the results of this analysis are used. -//! -//! There are three phases to this analysis: -//! 1. Use `ExprUseVisitor` to identify the interesting values that are consumed and borrowed. -//! 2. Use `DropRangeVisitor` to find where the interesting values are dropped or reinitialized, -//! and also build a control flow graph. -//! 3. Use `DropRanges::propagate_to_fixpoint` to flow the dropped/reinitialized information through -//! the CFG and find the exact points where we know a value is definitely dropped. -//! -//! The end result is a data structure that maps the post-order index of each node in the HIR tree -//! to a set of values that are known to be dropped at that location. - -use self::cfg_build::build_control_flow_graph; -use self::record_consumed_borrow::find_consumed_and_borrowed; -use crate::check::FnCtxt; -use hir::def_id::DefId; -use hir::{Body, HirId, HirIdMap, Node}; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_hir as hir; -use rustc_index::bit_set::BitSet; -use rustc_index::vec::IndexVec; -use rustc_middle::hir::map::Map; -use rustc_middle::hir::place::{PlaceBase, PlaceWithHirId}; -use rustc_middle::ty; -use std::collections::BTreeMap; -use std::fmt::Debug; - -mod cfg_build; -mod cfg_propagate; -mod cfg_visualize; -mod record_consumed_borrow; - -pub fn compute_drop_ranges<'a, 'tcx>( - fcx: &'a FnCtxt<'a, 'tcx>, - def_id: DefId, - body: &'tcx Body<'tcx>, -) -> DropRanges { - if fcx.sess().opts.unstable_opts.drop_tracking { - let consumed_borrowed_places = find_consumed_and_borrowed(fcx, def_id, body); - - let typeck_results = &fcx.typeck_results.borrow(); - let num_exprs = fcx.tcx.region_scope_tree(def_id).body_expr_count(body.id()).unwrap_or(0); - let (mut drop_ranges, borrowed_temporaries) = build_control_flow_graph( - fcx.tcx.hir(), - fcx.tcx, - typeck_results, - consumed_borrowed_places, - body, - num_exprs, - ); - - drop_ranges.propagate_to_fixpoint(); - - debug!("borrowed_temporaries = {borrowed_temporaries:?}"); - DropRanges { - tracked_value_map: drop_ranges.tracked_value_map, - nodes: drop_ranges.nodes, - borrowed_temporaries: Some(borrowed_temporaries), - } - } else { - // If drop range tracking is not enabled, skip all the analysis and produce an - // empty set of DropRanges. - DropRanges { - tracked_value_map: FxHashMap::default(), - nodes: IndexVec::new(), - borrowed_temporaries: None, - } - } -} - -/// Applies `f` to consumable node in the HIR subtree pointed to by `place`. -/// -/// This includes the place itself, and if the place is a reference to a local -/// variable then `f` is also called on the HIR node for that variable as well. -/// -/// For example, if `place` points to `foo()`, then `f` is called once for the -/// result of `foo`. On the other hand, if `place` points to `x` then `f` will -/// be called both on the `ExprKind::Path` node that represents the expression -/// as well as the HirId of the local `x` itself. -fn for_each_consumable<'tcx>(hir: Map<'tcx>, place: TrackedValue, mut f: impl FnMut(TrackedValue)) { - f(place); - let node = hir.find(place.hir_id()); - if let Some(Node::Expr(expr)) = node { - match expr.kind { - hir::ExprKind::Path(hir::QPath::Resolved( - _, - hir::Path { res: hir::def::Res::Local(hir_id), .. }, - )) => { - f(TrackedValue::Variable(*hir_id)); - } - _ => (), - } - } -} - -rustc_index::newtype_index! { - pub struct PostOrderId { - DEBUG_FORMAT = "id({})", - } -} - -rustc_index::newtype_index! { - pub struct TrackedValueIndex { - DEBUG_FORMAT = "hidx({})", - } -} - -/// Identifies a value whose drop state we need to track. -#[derive(PartialEq, Eq, Hash, Clone, Copy)] -enum TrackedValue { - /// Represents a named variable, such as a let binding, parameter, or upvar. - /// - /// The HirId points to the variable's definition site. - Variable(HirId), - /// A value produced as a result of an expression. - /// - /// The HirId points to the expression that returns this value. - Temporary(HirId), -} - -impl Debug for TrackedValue { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - ty::tls::with_opt(|opt_tcx| { - if let Some(tcx) = opt_tcx { - write!(f, "{}", tcx.hir().node_to_string(self.hir_id())) - } else { - match self { - Self::Variable(hir_id) => write!(f, "Variable({:?})", hir_id), - Self::Temporary(hir_id) => write!(f, "Temporary({:?})", hir_id), - } - } - }) - } -} - -impl TrackedValue { - fn hir_id(&self) -> HirId { - match self { - TrackedValue::Variable(hir_id) | TrackedValue::Temporary(hir_id) => *hir_id, - } - } - - fn from_place_with_projections_allowed(place_with_id: &PlaceWithHirId<'_>) -> Self { - match place_with_id.place.base { - PlaceBase::Rvalue | PlaceBase::StaticItem => { - TrackedValue::Temporary(place_with_id.hir_id) - } - PlaceBase::Local(hir_id) - | PlaceBase::Upvar(ty::UpvarId { var_path: ty::UpvarPath { hir_id }, .. }) => { - TrackedValue::Variable(hir_id) - } - } - } -} - -/// Represents a reason why we might not be able to convert a HirId or Place -/// into a tracked value. -#[derive(Debug)] -enum TrackedValueConversionError { - /// Place projects are not currently supported. - /// - /// The reasoning around these is kind of subtle, so we choose to be more - /// conservative around these for now. There is no reason in theory we - /// cannot support these, we just have not implemented it yet. - PlaceProjectionsNotSupported, -} - -impl TryFrom<&PlaceWithHirId<'_>> for TrackedValue { - type Error = TrackedValueConversionError; - - fn try_from(place_with_id: &PlaceWithHirId<'_>) -> Result<Self, Self::Error> { - if !place_with_id.place.projections.is_empty() { - debug!( - "TrackedValue from PlaceWithHirId: {:?} has projections, which are not supported.", - place_with_id - ); - return Err(TrackedValueConversionError::PlaceProjectionsNotSupported); - } - - Ok(TrackedValue::from_place_with_projections_allowed(place_with_id)) - } -} - -pub struct DropRanges { - tracked_value_map: FxHashMap<TrackedValue, TrackedValueIndex>, - nodes: IndexVec<PostOrderId, NodeInfo>, - borrowed_temporaries: Option<FxHashSet<HirId>>, -} - -impl DropRanges { - pub fn is_dropped_at(&self, hir_id: HirId, location: usize) -> bool { - self.tracked_value_map - .get(&TrackedValue::Temporary(hir_id)) - .or(self.tracked_value_map.get(&TrackedValue::Variable(hir_id))) - .cloned() - .map_or(false, |tracked_value_id| { - self.expect_node(location.into()).drop_state.contains(tracked_value_id) - }) - } - - pub fn is_borrowed_temporary(&self, expr: &hir::Expr<'_>) -> bool { - if let Some(b) = &self.borrowed_temporaries { b.contains(&expr.hir_id) } else { true } - } - - /// Returns a reference to the NodeInfo for a node, panicking if it does not exist - fn expect_node(&self, id: PostOrderId) -> &NodeInfo { - &self.nodes[id] - } -} - -/// Tracks information needed to compute drop ranges. -struct DropRangesBuilder { - /// The core of DropRangesBuilder is a set of nodes, which each represent - /// one expression. We primarily refer to them by their index in a - /// post-order traversal of the HIR tree, since this is what - /// generator_interior uses to talk about yield positions. - /// - /// This IndexVec keeps the relevant details for each node. See the - /// NodeInfo struct for more details, but this information includes things - /// such as the set of control-flow successors, which variables are dropped - /// or reinitialized, and whether each variable has been inferred to be - /// known-dropped or potentially reinitialized at each point. - nodes: IndexVec<PostOrderId, NodeInfo>, - /// We refer to values whose drop state we are tracking by the HirId of - /// where they are defined. Within a NodeInfo, however, we store the - /// drop-state in a bit vector indexed by a HirIdIndex - /// (see NodeInfo::drop_state). The hir_id_map field stores the mapping - /// from HirIds to the HirIdIndex that is used to represent that value in - /// bitvector. - tracked_value_map: FxHashMap<TrackedValue, TrackedValueIndex>, - - /// When building the control flow graph, we don't always know the - /// post-order index of the target node at the point we encounter it. - /// For example, this happens with break and continue. In those cases, - /// we store a pair of the PostOrderId of the source and the HirId - /// of the target. Once we have gathered all of these edges, we make a - /// pass over the set of deferred edges (see process_deferred_edges in - /// cfg_build.rs), look up the PostOrderId for the target (since now the - /// post-order index for all nodes is known), and add missing control flow - /// edges. - deferred_edges: Vec<(PostOrderId, HirId)>, - /// This maps HirIds of expressions to their post-order index. It is - /// used in process_deferred_edges to correctly add back-edges. - post_order_map: HirIdMap<PostOrderId>, -} - -impl Debug for DropRangesBuilder { - fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - f.debug_struct("DropRanges") - .field("hir_id_map", &self.tracked_value_map) - .field("post_order_maps", &self.post_order_map) - .field("nodes", &self.nodes.iter_enumerated().collect::<BTreeMap<_, _>>()) - .finish() - } -} - -/// DropRanges keeps track of what values are definitely dropped at each point in the code. -/// -/// Values of interest are defined by the hir_id of their place. Locations in code are identified -/// by their index in the post-order traversal. At its core, DropRanges maps -/// (hir_id, post_order_id) -> bool, where a true value indicates that the value is definitely -/// dropped at the point of the node identified by post_order_id. -impl DropRangesBuilder { - /// Returns the number of values (hir_ids) that are tracked - fn num_values(&self) -> usize { - self.tracked_value_map.len() - } - - fn node_mut(&mut self, id: PostOrderId) -> &mut NodeInfo { - let size = self.num_values(); - self.nodes.ensure_contains_elem(id, || NodeInfo::new(size)); - &mut self.nodes[id] - } - - fn add_control_edge(&mut self, from: PostOrderId, to: PostOrderId) { - trace!("adding control edge from {:?} to {:?}", from, to); - self.node_mut(from).successors.push(to); - } -} - -#[derive(Debug)] -struct NodeInfo { - /// IDs of nodes that can follow this one in the control flow - /// - /// If the vec is empty, then control proceeds to the next node. - successors: Vec<PostOrderId>, - - /// List of hir_ids that are dropped by this node. - drops: Vec<TrackedValueIndex>, - - /// List of hir_ids that are reinitialized by this node. - reinits: Vec<TrackedValueIndex>, - - /// Set of values that are definitely dropped at this point. - drop_state: BitSet<TrackedValueIndex>, -} - -impl NodeInfo { - fn new(num_values: usize) -> Self { - Self { - successors: vec![], - drops: vec![], - reinits: vec![], - drop_state: BitSet::new_filled(num_values), - } - } -} diff --git a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_build.rs b/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_build.rs deleted file mode 100644 index 016f4056b..000000000 --- a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_build.rs +++ /dev/null @@ -1,563 +0,0 @@ -use super::{ - for_each_consumable, record_consumed_borrow::ConsumedAndBorrowedPlaces, DropRangesBuilder, - NodeInfo, PostOrderId, TrackedValue, TrackedValueIndex, -}; -use hir::{ - intravisit::{self, Visitor}, - Body, Expr, ExprKind, Guard, HirId, LoopIdError, -}; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_hir as hir; -use rustc_index::vec::IndexVec; -use rustc_middle::{ - hir::map::Map, - ty::{TyCtxt, TypeckResults}, -}; -use std::mem::swap; - -/// Traverses the body to find the control flow graph and locations for the -/// relevant places are dropped or reinitialized. -/// -/// The resulting structure still needs to be iterated to a fixed point, which -/// can be done with propagate_to_fixpoint in cfg_propagate. -pub(super) fn build_control_flow_graph<'tcx>( - hir: Map<'tcx>, - tcx: TyCtxt<'tcx>, - typeck_results: &TypeckResults<'tcx>, - consumed_borrowed_places: ConsumedAndBorrowedPlaces, - body: &'tcx Body<'tcx>, - num_exprs: usize, -) -> (DropRangesBuilder, FxHashSet<HirId>) { - let mut drop_range_visitor = - DropRangeVisitor::new(hir, tcx, typeck_results, consumed_borrowed_places, num_exprs); - intravisit::walk_body(&mut drop_range_visitor, body); - - drop_range_visitor.drop_ranges.process_deferred_edges(); - if let Some(filename) = &tcx.sess.opts.unstable_opts.dump_drop_tracking_cfg { - super::cfg_visualize::write_graph_to_file(&drop_range_visitor.drop_ranges, filename, tcx); - } - - (drop_range_visitor.drop_ranges, drop_range_visitor.places.borrowed_temporaries) -} - -/// This struct is used to gather the information for `DropRanges` to determine the regions of the -/// HIR tree for which a value is dropped. -/// -/// We are interested in points where a variables is dropped or initialized, and the control flow -/// of the code. We identify locations in code by their post-order traversal index, so it is -/// important for this traversal to match that in `RegionResolutionVisitor` and `InteriorVisitor`. -/// -/// We make several simplifying assumptions, with the goal of being more conservative than -/// necessary rather than less conservative (since being less conservative is unsound, but more -/// conservative is still safe). These assumptions are: -/// -/// 1. Moving a variable `a` counts as a move of the whole variable. -/// 2. Moving a partial path like `a.b.c` is ignored. -/// 3. Reinitializing through a field (e.g. `a.b.c = 5`) counts as a reinitialization of all of -/// `a`. -/// -/// Some examples: -/// -/// Rule 1: -/// ```rust -/// let mut a = (vec![0], vec![0]); -/// drop(a); -/// // `a` is not considered initialized. -/// ``` -/// -/// Rule 2: -/// ```rust -/// let mut a = (vec![0], vec![0]); -/// drop(a.0); -/// drop(a.1); -/// // `a` is still considered initialized. -/// ``` -/// -/// Rule 3: -/// ```compile_fail,E0382 -/// let mut a = (vec![0], vec![0]); -/// drop(a); -/// a.1 = vec![1]; -/// // all of `a` is considered initialized -/// ``` - -struct DropRangeVisitor<'a, 'tcx> { - hir: Map<'tcx>, - places: ConsumedAndBorrowedPlaces, - drop_ranges: DropRangesBuilder, - expr_index: PostOrderId, - tcx: TyCtxt<'tcx>, - typeck_results: &'a TypeckResults<'tcx>, - label_stack: Vec<(Option<rustc_ast::Label>, PostOrderId)>, -} - -impl<'a, 'tcx> DropRangeVisitor<'a, 'tcx> { - fn new( - hir: Map<'tcx>, - tcx: TyCtxt<'tcx>, - typeck_results: &'a TypeckResults<'tcx>, - places: ConsumedAndBorrowedPlaces, - num_exprs: usize, - ) -> Self { - debug!("consumed_places: {:?}", places.consumed); - let drop_ranges = DropRangesBuilder::new( - places.consumed.iter().flat_map(|(_, places)| places.iter().cloned()), - hir, - num_exprs, - ); - Self { - hir, - places, - drop_ranges, - expr_index: PostOrderId::from_u32(0), - typeck_results, - tcx, - label_stack: vec![], - } - } - - fn record_drop(&mut self, value: TrackedValue) { - if self.places.borrowed.contains(&value) { - debug!("not marking {:?} as dropped because it is borrowed at some point", value); - } else { - debug!("marking {:?} as dropped at {:?}", value, self.expr_index); - let count = self.expr_index; - self.drop_ranges.drop_at(value, count); - } - } - - /// ExprUseVisitor's consume callback doesn't go deep enough for our purposes in all - /// expressions. This method consumes a little deeper into the expression when needed. - fn consume_expr(&mut self, expr: &hir::Expr<'_>) { - debug!("consuming expr {:?}, count={:?}", expr.kind, self.expr_index); - let places = self - .places - .consumed - .get(&expr.hir_id) - .map_or(vec![], |places| places.iter().cloned().collect()); - for place in places { - trace!(?place, "consuming place"); - for_each_consumable(self.hir, place, |value| self.record_drop(value)); - } - } - - /// Marks an expression as being reinitialized. - /// - /// Note that we always approximated on the side of things being more - /// initialized than they actually are, as opposed to less. In cases such - /// as `x.y = ...`, we would consider all of `x` as being initialized - /// instead of just the `y` field. - /// - /// This is because it is always safe to consider something initialized - /// even when it is not, but the other way around will cause problems. - /// - /// In the future, we will hopefully tighten up these rules to be more - /// precise. - fn reinit_expr(&mut self, expr: &hir::Expr<'_>) { - // Walk the expression to find the base. For example, in an expression - // like `*a[i].x`, we want to find the `a` and mark that as - // reinitialized. - match expr.kind { - ExprKind::Path(hir::QPath::Resolved( - _, - hir::Path { res: hir::def::Res::Local(hir_id), .. }, - )) => { - // This is the base case, where we have found an actual named variable. - - let location = self.expr_index; - debug!("reinitializing {:?} at {:?}", hir_id, location); - self.drop_ranges.reinit_at(TrackedValue::Variable(*hir_id), location); - } - - ExprKind::Field(base, _) => self.reinit_expr(base), - - // Most expressions do not refer to something where we need to track - // reinitializations. - // - // Some of these may be interesting in the future - ExprKind::Path(..) - | ExprKind::Box(..) - | ExprKind::ConstBlock(..) - | ExprKind::Array(..) - | ExprKind::Call(..) - | ExprKind::MethodCall(..) - | ExprKind::Tup(..) - | ExprKind::Binary(..) - | ExprKind::Unary(..) - | ExprKind::Lit(..) - | ExprKind::Cast(..) - | ExprKind::Type(..) - | ExprKind::DropTemps(..) - | ExprKind::Let(..) - | ExprKind::If(..) - | ExprKind::Loop(..) - | ExprKind::Match(..) - | ExprKind::Closure { .. } - | ExprKind::Block(..) - | ExprKind::Assign(..) - | ExprKind::AssignOp(..) - | ExprKind::Index(..) - | ExprKind::AddrOf(..) - | ExprKind::Break(..) - | ExprKind::Continue(..) - | ExprKind::Ret(..) - | ExprKind::InlineAsm(..) - | ExprKind::Struct(..) - | ExprKind::Repeat(..) - | ExprKind::Yield(..) - | ExprKind::Err => (), - } - } - - /// For an expression with an uninhabited return type (e.g. a function that returns !), - /// this adds a self edge to to the CFG to model the fact that the function does not - /// return. - fn handle_uninhabited_return(&mut self, expr: &Expr<'tcx>) { - let ty = self.typeck_results.expr_ty(expr); - let ty = self.tcx.erase_regions(ty); - let m = self.tcx.parent_module(expr.hir_id).to_def_id(); - let param_env = self.tcx.param_env(m.expect_local()); - if self.tcx.is_ty_uninhabited_from(m, ty, param_env) { - // This function will not return. We model this fact as an infinite loop. - self.drop_ranges.add_control_edge(self.expr_index + 1, self.expr_index + 1); - } - } - - /// Map a Destination to an equivalent expression node - /// - /// The destination field of a Break or Continue expression can target either an - /// expression or a block. The drop range analysis, however, only deals in - /// expression nodes, so blocks that might be the destination of a Break or Continue - /// will not have a PostOrderId. - /// - /// If the destination is an expression, this function will simply return that expression's - /// hir_id. If the destination is a block, this function will return the hir_id of last - /// expression in the block. - fn find_target_expression_from_destination( - &self, - destination: hir::Destination, - ) -> Result<HirId, LoopIdError> { - destination.target_id.map(|target| { - let node = self.hir.get(target); - match node { - hir::Node::Expr(_) => target, - hir::Node::Block(b) => find_last_block_expression(b), - hir::Node::Param(..) - | hir::Node::Item(..) - | hir::Node::ForeignItem(..) - | hir::Node::TraitItem(..) - | hir::Node::ImplItem(..) - | hir::Node::Variant(..) - | hir::Node::Field(..) - | hir::Node::AnonConst(..) - | hir::Node::Stmt(..) - | hir::Node::PathSegment(..) - | hir::Node::Ty(..) - | hir::Node::TypeBinding(..) - | hir::Node::TraitRef(..) - | hir::Node::Pat(..) - | hir::Node::PatField(..) - | hir::Node::ExprField(..) - | hir::Node::Arm(..) - | hir::Node::Local(..) - | hir::Node::Ctor(..) - | hir::Node::Lifetime(..) - | hir::Node::GenericParam(..) - | hir::Node::Crate(..) - | hir::Node::Infer(..) => bug!("Unsupported branch target: {:?}", node), - } - }) - } -} - -fn find_last_block_expression(block: &hir::Block<'_>) -> HirId { - block.expr.map_or_else( - // If there is no tail expression, there will be at least one statement in the - // block because the block contains a break or continue statement. - || block.stmts.last().unwrap().hir_id, - |expr| expr.hir_id, - ) -} - -impl<'a, 'tcx> Visitor<'tcx> for DropRangeVisitor<'a, 'tcx> { - fn visit_expr(&mut self, expr: &'tcx Expr<'tcx>) { - let mut reinit = None; - match expr.kind { - ExprKind::Assign(lhs, rhs, _) => { - self.visit_expr(lhs); - self.visit_expr(rhs); - - reinit = Some(lhs); - } - - ExprKind::If(test, if_true, if_false) => { - self.visit_expr(test); - - let fork = self.expr_index; - - self.drop_ranges.add_control_edge(fork, self.expr_index + 1); - self.visit_expr(if_true); - let true_end = self.expr_index; - - self.drop_ranges.add_control_edge(fork, self.expr_index + 1); - if let Some(if_false) = if_false { - self.visit_expr(if_false); - } - - self.drop_ranges.add_control_edge(true_end, self.expr_index + 1); - } - ExprKind::Match(scrutinee, arms, ..) => { - // We walk through the match expression almost like a chain of if expressions. - // Here's a diagram to follow along with: - // - // ┌─┐ - // match │A│ { - // ┌───┴─┘ - // │ - // ┌▼┌───►┌─┐ ┌─┐ - // │B│ if │C│ =>│D│, - // └─┘ ├─┴──►└─┴──────┐ - // ┌──┘ │ - // ┌──┘ │ - // │ │ - // ┌▼┌───►┌─┐ ┌─┐ │ - // │E│ if │F│ =>│G│, │ - // └─┘ ├─┴──►└─┴┐ │ - // │ │ │ - // } ▼ ▼ │ - // ┌─┐◄───────────────────┘ - // │H│ - // └─┘ - // - // The order we want is that the scrutinee (A) flows into the first pattern (B), - // which flows into the guard (C). Then the guard either flows into the arm body - // (D) or into the start of the next arm (E). Finally, the body flows to the end - // of the match block (H). - // - // The subsequent arms follow the same ordering. First we go to the pattern, then - // the guard (if present, otherwise it flows straight into the body), then into - // the body and then to the end of the match expression. - // - // The comments below show which edge is being added. - self.visit_expr(scrutinee); - - let (guard_exit, arm_end_ids) = arms.iter().fold( - (self.expr_index, vec![]), - |(incoming_edge, mut arm_end_ids), hir::Arm { pat, body, guard, .. }| { - // A -> B, or C -> E - self.drop_ranges.add_control_edge(incoming_edge, self.expr_index + 1); - self.visit_pat(pat); - // B -> C and E -> F are added implicitly due to the traversal order. - match guard { - Some(Guard::If(expr)) => self.visit_expr(expr), - Some(Guard::IfLet(let_expr)) => { - self.visit_let_expr(let_expr); - } - None => (), - } - // Likewise, C -> D and F -> G are added implicitly. - - // Save C, F, so we can add the other outgoing edge. - let to_next_arm = self.expr_index; - - // The default edge does not get added since we also have an explicit edge, - // so we also need to add an edge to the next node as well. - // - // This adds C -> D, F -> G - self.drop_ranges.add_control_edge(self.expr_index, self.expr_index + 1); - self.visit_expr(body); - - // Save the end of the body so we can add the exit edge once we know where - // the exit is. - arm_end_ids.push(self.expr_index); - - // Pass C to the next iteration, as well as vec![D] - // - // On the last round through, we pass F and vec![D, G] so that we can - // add all the exit edges. - (to_next_arm, arm_end_ids) - }, - ); - // F -> H - self.drop_ranges.add_control_edge(guard_exit, self.expr_index + 1); - - arm_end_ids.into_iter().for_each(|arm_end| { - // D -> H, G -> H - self.drop_ranges.add_control_edge(arm_end, self.expr_index + 1) - }); - } - - ExprKind::Loop(body, label, ..) => { - let loop_begin = self.expr_index + 1; - self.label_stack.push((label, loop_begin)); - if body.stmts.is_empty() && body.expr.is_none() { - // For empty loops we won't have updated self.expr_index after visiting the - // body, meaning we'd get an edge from expr_index to expr_index + 1, but - // instead we want an edge from expr_index + 1 to expr_index + 1. - self.drop_ranges.add_control_edge(loop_begin, loop_begin); - } else { - self.visit_block(body); - self.drop_ranges.add_control_edge(self.expr_index, loop_begin); - } - self.label_stack.pop(); - } - // Find the loop entry by searching through the label stack for either the last entry - // (if label is none), or the first entry where the label matches this one. The Loop - // case maintains this stack mapping labels to the PostOrderId for the loop entry. - ExprKind::Continue(hir::Destination { label, .. }, ..) => self - .label_stack - .iter() - .rev() - .find(|(loop_label, _)| label.is_none() || *loop_label == label) - .map_or((), |(_, target)| { - self.drop_ranges.add_control_edge(self.expr_index, *target) - }), - - ExprKind::Break(destination, ..) => { - // destination either points to an expression or to a block. We use - // find_target_expression_from_destination to use the last expression of the block - // if destination points to a block. - // - // We add an edge to the hir_id of the expression/block we are breaking out of, and - // then in process_deferred_edges we will map this hir_id to its PostOrderId, which - // will refer to the end of the block due to the post order traversal. - self.find_target_expression_from_destination(destination).map_or((), |target| { - self.drop_ranges.add_control_edge_hir_id(self.expr_index, target) - }) - } - - ExprKind::Call(f, args) => { - self.visit_expr(f); - for arg in args { - self.visit_expr(arg); - } - - self.handle_uninhabited_return(expr); - } - ExprKind::MethodCall(_, receiver, exprs, _) => { - self.visit_expr(receiver); - for expr in exprs { - self.visit_expr(expr); - } - - self.handle_uninhabited_return(expr); - } - - ExprKind::AddrOf(..) - | ExprKind::Array(..) - | ExprKind::AssignOp(..) - | ExprKind::Binary(..) - | ExprKind::Block(..) - | ExprKind::Box(..) - | ExprKind::Cast(..) - | ExprKind::Closure { .. } - | ExprKind::ConstBlock(..) - | ExprKind::DropTemps(..) - | ExprKind::Err - | ExprKind::Field(..) - | ExprKind::Index(..) - | ExprKind::InlineAsm(..) - | ExprKind::Let(..) - | ExprKind::Lit(..) - | ExprKind::Path(..) - | ExprKind::Repeat(..) - | ExprKind::Ret(..) - | ExprKind::Struct(..) - | ExprKind::Tup(..) - | ExprKind::Type(..) - | ExprKind::Unary(..) - | ExprKind::Yield(..) => intravisit::walk_expr(self, expr), - } - - self.expr_index = self.expr_index + 1; - self.drop_ranges.add_node_mapping(expr.hir_id, self.expr_index); - self.consume_expr(expr); - if let Some(expr) = reinit { - self.reinit_expr(expr); - } - } - - fn visit_pat(&mut self, pat: &'tcx hir::Pat<'tcx>) { - intravisit::walk_pat(self, pat); - - // Increment expr_count here to match what InteriorVisitor expects. - self.expr_index = self.expr_index + 1; - } -} - -impl DropRangesBuilder { - fn new( - tracked_values: impl Iterator<Item = TrackedValue>, - hir: Map<'_>, - num_exprs: usize, - ) -> Self { - let mut tracked_value_map = FxHashMap::<_, TrackedValueIndex>::default(); - let mut next = <_>::from(0u32); - for value in tracked_values { - for_each_consumable(hir, value, |value| { - if !tracked_value_map.contains_key(&value) { - tracked_value_map.insert(value, next); - next = next + 1; - } - }); - } - debug!("hir_id_map: {:?}", tracked_value_map); - let num_values = tracked_value_map.len(); - Self { - tracked_value_map, - nodes: IndexVec::from_fn_n(|_| NodeInfo::new(num_values), num_exprs + 1), - deferred_edges: <_>::default(), - post_order_map: <_>::default(), - } - } - - fn tracked_value_index(&self, tracked_value: TrackedValue) -> TrackedValueIndex { - *self.tracked_value_map.get(&tracked_value).unwrap() - } - - /// Adds an entry in the mapping from HirIds to PostOrderIds - /// - /// Needed so that `add_control_edge_hir_id` can work. - fn add_node_mapping(&mut self, node_hir_id: HirId, post_order_id: PostOrderId) { - self.post_order_map.insert(node_hir_id, post_order_id); - } - - /// Like add_control_edge, but uses a hir_id as the target. - /// - /// This can be used for branches where we do not know the PostOrderId of the target yet, - /// such as when handling `break` or `continue`. - fn add_control_edge_hir_id(&mut self, from: PostOrderId, to: HirId) { - self.deferred_edges.push((from, to)); - } - - fn drop_at(&mut self, value: TrackedValue, location: PostOrderId) { - let value = self.tracked_value_index(value); - self.node_mut(location).drops.push(value); - } - - fn reinit_at(&mut self, value: TrackedValue, location: PostOrderId) { - let value = match self.tracked_value_map.get(&value) { - Some(value) => *value, - // If there's no value, this is never consumed and therefore is never dropped. We can - // ignore this. - None => return, - }; - self.node_mut(location).reinits.push(value); - } - - /// Looks up PostOrderId for any control edges added by HirId and adds a proper edge for them. - /// - /// Should be called after visiting the HIR but before solving the control flow, otherwise some - /// edges will be missed. - fn process_deferred_edges(&mut self) { - trace!("processing deferred edges. post_order_map={:#?}", self.post_order_map); - let mut edges = vec![]; - swap(&mut edges, &mut self.deferred_edges); - edges.into_iter().for_each(|(from, to)| { - trace!("Adding deferred edge from {:?} to {:?}", from, to); - let to = *self.post_order_map.get(&to).expect("Expression ID not found"); - trace!("target edge PostOrderId={:?}", to); - self.add_control_edge(from, to) - }); - } -} diff --git a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_propagate.rs b/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_propagate.rs deleted file mode 100644 index 139d17d2e..000000000 --- a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_propagate.rs +++ /dev/null @@ -1,92 +0,0 @@ -use super::{DropRangesBuilder, PostOrderId}; -use rustc_index::{bit_set::BitSet, vec::IndexVec}; -use std::collections::BTreeMap; - -impl DropRangesBuilder { - pub fn propagate_to_fixpoint(&mut self) { - trace!("before fixpoint: {:#?}", self); - let preds = self.compute_predecessors(); - - trace!("predecessors: {:#?}", preds.iter_enumerated().collect::<BTreeMap<_, _>>()); - - let mut new_state = BitSet::new_empty(self.num_values()); - let mut changed_nodes = BitSet::new_empty(self.nodes.len()); - let mut unchanged_mask = BitSet::new_filled(self.nodes.len()); - changed_nodes.insert(0u32.into()); - - let mut propagate = || { - let mut changed = false; - unchanged_mask.insert_all(); - for id in self.nodes.indices() { - trace!("processing {:?}, changed_nodes: {:?}", id, changed_nodes); - // Check if any predecessor has changed, and if not then short-circuit. - // - // We handle the start node specially, since it doesn't have any predecessors, - // but we need to start somewhere. - if match id.index() { - 0 => !changed_nodes.contains(id), - _ => !preds[id].iter().any(|pred| changed_nodes.contains(*pred)), - } { - trace!("short-circuiting because none of {:?} have changed", preds[id]); - unchanged_mask.remove(id); - continue; - } - - if id.index() == 0 { - new_state.clear(); - } else { - // If we are not the start node and we have no predecessors, treat - // everything as dropped because there's no way to get here anyway. - new_state.insert_all(); - }; - - for pred in &preds[id] { - new_state.intersect(&self.nodes[*pred].drop_state); - } - - for drop in &self.nodes[id].drops { - new_state.insert(*drop); - } - - for reinit in &self.nodes[id].reinits { - new_state.remove(*reinit); - } - - if self.nodes[id].drop_state.intersect(&new_state) { - changed_nodes.insert(id); - changed = true; - } else { - unchanged_mask.remove(id); - } - } - - changed_nodes.intersect(&unchanged_mask); - changed - }; - - while propagate() { - trace!("drop_state changed, re-running propagation"); - } - - trace!("after fixpoint: {:#?}", self); - } - - fn compute_predecessors(&self) -> IndexVec<PostOrderId, Vec<PostOrderId>> { - let mut preds = IndexVec::from_fn_n(|_| vec![], self.nodes.len()); - for (id, node) in self.nodes.iter_enumerated() { - // If the node has no explicit successors, we assume that control - // will from this node into the next one. - // - // If there are successors listed, then we assume that all - // possible successors are given and we do not include the default. - if node.successors.len() == 0 && id.index() != self.nodes.len() - 1 { - preds[id + 1].push(id); - } else { - for succ in &node.successors { - preds[*succ].push(id); - } - } - } - preds - } -} diff --git a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_visualize.rs b/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_visualize.rs deleted file mode 100644 index c0a0bfe8e..000000000 --- a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/cfg_visualize.rs +++ /dev/null @@ -1,91 +0,0 @@ -//! Implementation of GraphWalk for DropRanges so we can visualize the control -//! flow graph when needed for debugging. - -use rustc_graphviz as dot; -use rustc_middle::ty::TyCtxt; - -use super::{DropRangesBuilder, PostOrderId}; - -/// Writes the CFG for DropRangesBuilder to a .dot file for visualization. -/// -/// It is not normally called, but is kept around to easily add debugging -/// code when needed. -pub(super) fn write_graph_to_file( - drop_ranges: &DropRangesBuilder, - filename: &str, - tcx: TyCtxt<'_>, -) { - dot::render( - &DropRangesGraph { drop_ranges, tcx }, - &mut std::fs::File::create(filename).unwrap(), - ) - .unwrap(); -} - -struct DropRangesGraph<'a, 'tcx> { - drop_ranges: &'a DropRangesBuilder, - tcx: TyCtxt<'tcx>, -} - -impl<'a> dot::GraphWalk<'a> for DropRangesGraph<'_, '_> { - type Node = PostOrderId; - - type Edge = (PostOrderId, PostOrderId); - - fn nodes(&'a self) -> dot::Nodes<'a, Self::Node> { - self.drop_ranges.nodes.iter_enumerated().map(|(i, _)| i).collect() - } - - fn edges(&'a self) -> dot::Edges<'a, Self::Edge> { - self.drop_ranges - .nodes - .iter_enumerated() - .flat_map(|(i, node)| { - if node.successors.len() == 0 { - vec![(i, i + 1)] - } else { - node.successors.iter().map(move |&s| (i, s)).collect() - } - }) - .collect() - } - - fn source(&'a self, edge: &Self::Edge) -> Self::Node { - edge.0 - } - - fn target(&'a self, edge: &Self::Edge) -> Self::Node { - edge.1 - } -} - -impl<'a> dot::Labeller<'a> for DropRangesGraph<'_, '_> { - type Node = PostOrderId; - - type Edge = (PostOrderId, PostOrderId); - - fn graph_id(&'a self) -> dot::Id<'a> { - dot::Id::new("drop_ranges").unwrap() - } - - fn node_id(&'a self, n: &Self::Node) -> dot::Id<'a> { - dot::Id::new(format!("id{}", n.index())).unwrap() - } - - fn node_label(&'a self, n: &Self::Node) -> dot::LabelText<'a> { - dot::LabelText::LabelStr( - format!( - "{n:?}: {}", - self.drop_ranges - .post_order_map - .iter() - .find(|(_hir_id, &post_order_id)| post_order_id == *n) - .map_or("<unknown>".into(), |(hir_id, _)| self - .tcx - .hir() - .node_to_string(*hir_id)) - ) - .into(), - ) - } -} diff --git a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/record_consumed_borrow.rs b/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/record_consumed_borrow.rs deleted file mode 100644 index e22675e9d..000000000 --- a/compiler/rustc_typeck/src/check/generator_interior/drop_ranges/record_consumed_borrow.rs +++ /dev/null @@ -1,232 +0,0 @@ -use super::TrackedValue; -use crate::{ - check::FnCtxt, - expr_use_visitor::{self, ExprUseVisitor}, -}; -use hir::{def_id::DefId, Body, HirId, HirIdMap}; -use rustc_data_structures::fx::FxHashSet; -use rustc_hir as hir; -use rustc_middle::hir::place::{PlaceBase, Projection, ProjectionKind}; -use rustc_middle::ty::{ParamEnv, TyCtxt}; - -pub(super) fn find_consumed_and_borrowed<'a, 'tcx>( - fcx: &'a FnCtxt<'a, 'tcx>, - def_id: DefId, - body: &'tcx Body<'tcx>, -) -> ConsumedAndBorrowedPlaces { - let mut expr_use_visitor = ExprUseDelegate::new(fcx.tcx, fcx.param_env); - expr_use_visitor.consume_body(fcx, def_id, body); - expr_use_visitor.places -} - -pub(super) struct ConsumedAndBorrowedPlaces { - /// Records the variables/expressions that are dropped by a given expression. - /// - /// The key is the hir-id of the expression, and the value is a set or hir-ids for variables - /// or values that are consumed by that expression. - /// - /// Note that this set excludes "partial drops" -- for example, a statement like `drop(x.y)` is - /// not considered a drop of `x`, although it would be a drop of `x.y`. - pub(super) consumed: HirIdMap<FxHashSet<TrackedValue>>, - - /// A set of hir-ids of values or variables that are borrowed at some point within the body. - pub(super) borrowed: FxHashSet<TrackedValue>, - - /// A set of hir-ids of values or variables that are borrowed at some point within the body. - pub(super) borrowed_temporaries: FxHashSet<HirId>, -} - -/// Works with ExprUseVisitor to find interesting values for the drop range analysis. -/// -/// Interesting values are those that are either dropped or borrowed. For dropped values, we also -/// record the parent expression, which is the point where the drop actually takes place. -struct ExprUseDelegate<'tcx> { - tcx: TyCtxt<'tcx>, - param_env: ParamEnv<'tcx>, - places: ConsumedAndBorrowedPlaces, -} - -impl<'tcx> ExprUseDelegate<'tcx> { - fn new(tcx: TyCtxt<'tcx>, param_env: ParamEnv<'tcx>) -> Self { - Self { - tcx, - param_env, - places: ConsumedAndBorrowedPlaces { - consumed: <_>::default(), - borrowed: <_>::default(), - borrowed_temporaries: <_>::default(), - }, - } - } - - fn consume_body(&mut self, fcx: &'_ FnCtxt<'_, 'tcx>, def_id: DefId, body: &'tcx Body<'tcx>) { - // Run ExprUseVisitor to find where values are consumed. - ExprUseVisitor::new( - self, - &fcx.infcx, - def_id.expect_local(), - fcx.param_env, - &fcx.typeck_results.borrow(), - ) - .consume_body(body); - } - - fn mark_consumed(&mut self, consumer: HirId, target: TrackedValue) { - self.places.consumed.entry(consumer).or_insert_with(|| <_>::default()); - - debug!(?consumer, ?target, "mark_consumed"); - self.places.consumed.get_mut(&consumer).map(|places| places.insert(target)); - } - - fn borrow_place(&mut self, place_with_id: &expr_use_visitor::PlaceWithHirId<'tcx>) { - self.places - .borrowed - .insert(TrackedValue::from_place_with_projections_allowed(place_with_id)); - - // Ordinarily a value is consumed by it's parent, but in the special case of a - // borrowed RValue, we create a reference that lives as long as the temporary scope - // for that expression (typically, the innermost statement, but sometimes the enclosing - // block). We record this fact here so that later in generator_interior - // we can use the correct scope. - // - // We special case borrows through a dereference (`&*x`, `&mut *x` where `x` is - // some rvalue expression), since these are essentially a copy of a pointer. - // In other words, this borrow does not refer to the - // temporary (`*x`), but to the referent (whatever `x` is a borrow of). - // - // We were considering that we might encounter problems down the line if somehow, - // some part of the compiler were to look at this result and try to use it to - // drive a borrowck-like analysis (this does not currently happen, as of this writing). - // But even this should be fine, because the lifetime of the dereferenced reference - // found in the rvalue is only significant as an intermediate 'link' to the value we - // are producing, and we separately track whether that value is live over a yield. - // Example: - // - // ```notrust - // fn identity<T>(x: &mut T) -> &mut T { x } - // let a: A = ...; - // let y: &'y mut A = &mut *identity(&'a mut a); - // ^^^^^^^^^^^^^^^^^^^^^^^^^ the borrow we are talking about - // ``` - // - // The expression `*identity(...)` is a deref of an rvalue, - // where the `identity(...)` (the rvalue) produces a return type - // of `&'rv mut A`, where `'a: 'rv`. We then assign this result to - // `'y`, resulting in (transitively) `'a: 'y` (i.e., while `y` is in use, - // `a` will be considered borrowed). Other parts of the code will ensure - // that if `y` is live over a yield, `&'y mut A` appears in the generator - // state. If `'y` is live, then any sound region analysis must conclude - // that `'a` is also live. So if this causes a bug, blame some other - // part of the code! - let is_deref = place_with_id - .place - .projections - .iter() - .any(|Projection { kind, .. }| *kind == ProjectionKind::Deref); - - if let (false, PlaceBase::Rvalue) = (is_deref, place_with_id.place.base) { - self.places.borrowed_temporaries.insert(place_with_id.hir_id); - } - } -} - -impl<'tcx> expr_use_visitor::Delegate<'tcx> for ExprUseDelegate<'tcx> { - fn consume( - &mut self, - place_with_id: &expr_use_visitor::PlaceWithHirId<'tcx>, - diag_expr_id: HirId, - ) { - let hir = self.tcx.hir(); - let parent = match hir.find_parent_node(place_with_id.hir_id) { - Some(parent) => parent, - None => place_with_id.hir_id, - }; - debug!( - "consume {:?}; diag_expr_id={}, using parent {}", - place_with_id, - hir.node_to_string(diag_expr_id), - hir.node_to_string(parent) - ); - place_with_id - .try_into() - .map_or((), |tracked_value| self.mark_consumed(parent, tracked_value)); - } - - fn borrow( - &mut self, - place_with_id: &expr_use_visitor::PlaceWithHirId<'tcx>, - diag_expr_id: HirId, - bk: rustc_middle::ty::BorrowKind, - ) { - debug!( - "borrow: place_with_id = {place_with_id:#?}, diag_expr_id={diag_expr_id:#?}, \ - borrow_kind={bk:#?}" - ); - - self.borrow_place(place_with_id); - } - - fn copy( - &mut self, - place_with_id: &expr_use_visitor::PlaceWithHirId<'tcx>, - _diag_expr_id: HirId, - ) { - debug!("copy: place_with_id = {place_with_id:?}"); - - self.places - .borrowed - .insert(TrackedValue::from_place_with_projections_allowed(place_with_id)); - - // For copied we treat this mostly like a borrow except that we don't add the place - // to borrowed_temporaries because the copy is consumed. - } - - fn mutate( - &mut self, - assignee_place: &expr_use_visitor::PlaceWithHirId<'tcx>, - diag_expr_id: HirId, - ) { - debug!("mutate {assignee_place:?}; diag_expr_id={diag_expr_id:?}"); - - if assignee_place.place.base == PlaceBase::Rvalue - && assignee_place.place.projections.is_empty() - { - // Assigning to an Rvalue is illegal unless done through a dereference. We would have - // already gotten a type error, so we will just return here. - return; - } - - // If the type being assigned needs dropped, then the mutation counts as a borrow - // since it is essentially doing `Drop::drop(&mut x); x = new_value;`. - if assignee_place.place.base_ty.needs_drop(self.tcx, self.param_env) { - self.places - .borrowed - .insert(TrackedValue::from_place_with_projections_allowed(assignee_place)); - } - } - - fn bind( - &mut self, - binding_place: &expr_use_visitor::PlaceWithHirId<'tcx>, - diag_expr_id: HirId, - ) { - debug!("bind {binding_place:?}; diag_expr_id={diag_expr_id:?}"); - } - - fn fake_read( - &mut self, - place_with_id: &expr_use_visitor::PlaceWithHirId<'tcx>, - cause: rustc_middle::mir::FakeReadCause, - diag_expr_id: HirId, - ) { - debug!( - "fake_read place_with_id={place_with_id:?}; cause={cause:?}; diag_expr_id={diag_expr_id:?}" - ); - - // fake reads happen in places like the scrutinee of a match expression. - // we treat those as a borrow, much like a copy: the idea is that we are - // transiently creating a `&T` ref that we can read from to observe the current - // value (this `&T` is immediately dropped afterwards). - self.borrow_place(place_with_id); - } -} diff --git a/compiler/rustc_typeck/src/check/inherited.rs b/compiler/rustc_typeck/src/check/inherited.rs deleted file mode 100644 index 37c830d4e..000000000 --- a/compiler/rustc_typeck/src/check/inherited.rs +++ /dev/null @@ -1,209 +0,0 @@ -use super::callee::DeferredCallResolution; - -use rustc_data_structures::fx::FxHashSet; -use rustc_data_structures::sync::Lrc; -use rustc_hir as hir; -use rustc_hir::def_id::LocalDefId; -use rustc_hir::HirIdMap; -use rustc_infer::infer; -use rustc_infer::infer::{InferCtxt, InferOk, TyCtxtInferExt}; -use rustc_middle::ty::fold::TypeFoldable; -use rustc_middle::ty::visit::TypeVisitable; -use rustc_middle::ty::{self, Ty, TyCtxt}; -use rustc_span::def_id::LocalDefIdMap; -use rustc_span::{self, Span}; -use rustc_trait_selection::infer::InferCtxtExt as _; -use rustc_trait_selection::traits::{ - self, ObligationCause, ObligationCtxt, TraitEngine, TraitEngineExt as _, -}; - -use std::cell::RefCell; -use std::ops::Deref; - -/// Closures defined within the function. For example: -/// ```ignore (illustrative) -/// fn foo() { -/// bar(move|| { ... }) -/// } -/// ``` -/// Here, the function `foo()` and the closure passed to -/// `bar()` will each have their own `FnCtxt`, but they will -/// share the inherited fields. -pub struct Inherited<'a, 'tcx> { - pub(super) infcx: InferCtxt<'a, 'tcx>, - - pub(super) typeck_results: &'a RefCell<ty::TypeckResults<'tcx>>, - - pub(super) locals: RefCell<HirIdMap<super::LocalTy<'tcx>>>, - - pub(super) fulfillment_cx: RefCell<Box<dyn TraitEngine<'tcx>>>, - - // Some additional `Sized` obligations badly affect type inference. - // These obligations are added in a later stage of typeck. - // Removing these may also cause additional complications, see #101066. - pub(super) deferred_sized_obligations: - RefCell<Vec<(Ty<'tcx>, Span, traits::ObligationCauseCode<'tcx>)>>, - - // When we process a call like `c()` where `c` is a closure type, - // we may not have decided yet whether `c` is a `Fn`, `FnMut`, or - // `FnOnce` closure. In that case, we defer full resolution of the - // call until upvar inference can kick in and make the - // decision. We keep these deferred resolutions grouped by the - // def-id of the closure, so that once we decide, we can easily go - // back and process them. - pub(super) deferred_call_resolutions: RefCell<LocalDefIdMap<Vec<DeferredCallResolution<'tcx>>>>, - - pub(super) deferred_cast_checks: RefCell<Vec<super::cast::CastCheck<'tcx>>>, - - pub(super) deferred_transmute_checks: RefCell<Vec<(Ty<'tcx>, Ty<'tcx>, Span)>>, - - pub(super) deferred_asm_checks: RefCell<Vec<(&'tcx hir::InlineAsm<'tcx>, hir::HirId)>>, - - pub(super) deferred_generator_interiors: - RefCell<Vec<(hir::BodyId, Ty<'tcx>, hir::GeneratorKind)>>, - - pub(super) body_id: Option<hir::BodyId>, - - /// Whenever we introduce an adjustment from `!` into a type variable, - /// we record that type variable here. This is later used to inform - /// fallback. See the `fallback` module for details. - pub(super) diverging_type_vars: RefCell<FxHashSet<Ty<'tcx>>>, -} - -impl<'a, 'tcx> Deref for Inherited<'a, 'tcx> { - type Target = InferCtxt<'a, 'tcx>; - fn deref(&self) -> &Self::Target { - &self.infcx - } -} - -/// A temporary returned by `Inherited::build(...)`. This is necessary -/// for multiple `InferCtxt` to share the same `in_progress_typeck_results` -/// without using `Rc` or something similar. -pub struct InheritedBuilder<'tcx> { - infcx: infer::InferCtxtBuilder<'tcx>, - def_id: LocalDefId, -} - -impl<'tcx> Inherited<'_, 'tcx> { - pub fn build(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> InheritedBuilder<'tcx> { - let hir_owner = tcx.hir().local_def_id_to_hir_id(def_id).owner; - - InheritedBuilder { - infcx: tcx - .infer_ctxt() - .ignoring_regions() - .with_fresh_in_progress_typeck_results(hir_owner) - .with_normalize_fn_sig_for_diagnostic(Lrc::new(move |infcx, fn_sig| { - if fn_sig.has_escaping_bound_vars() { - return fn_sig; - } - infcx.probe(|_| { - let ocx = ObligationCtxt::new_in_snapshot(infcx); - let normalized_fn_sig = ocx.normalize( - ObligationCause::dummy(), - // FIXME(compiler-errors): This is probably not the right param-env... - infcx.tcx.param_env(def_id), - fn_sig, - ); - if ocx.select_all_or_error().is_empty() { - let normalized_fn_sig = - infcx.resolve_vars_if_possible(normalized_fn_sig); - if !normalized_fn_sig.needs_infer() { - return normalized_fn_sig; - } - } - fn_sig - }) - })), - def_id, - } - } -} - -impl<'tcx> InheritedBuilder<'tcx> { - pub fn enter<F, R>(&mut self, f: F) -> R - where - F: for<'a> FnOnce(Inherited<'a, 'tcx>) -> R, - { - let def_id = self.def_id; - self.infcx.enter(|infcx| f(Inherited::new(infcx, def_id))) - } -} - -impl<'a, 'tcx> Inherited<'a, 'tcx> { - fn new(infcx: InferCtxt<'a, 'tcx>, def_id: LocalDefId) -> Self { - let tcx = infcx.tcx; - let body_id = tcx.hir().maybe_body_owned_by(def_id); - let typeck_results = - infcx.in_progress_typeck_results.expect("building `FnCtxt` without typeck results"); - - Inherited { - typeck_results, - infcx, - fulfillment_cx: RefCell::new(<dyn TraitEngine<'_>>::new(tcx)), - locals: RefCell::new(Default::default()), - deferred_sized_obligations: RefCell::new(Vec::new()), - deferred_call_resolutions: RefCell::new(Default::default()), - deferred_cast_checks: RefCell::new(Vec::new()), - deferred_transmute_checks: RefCell::new(Vec::new()), - deferred_asm_checks: RefCell::new(Vec::new()), - deferred_generator_interiors: RefCell::new(Vec::new()), - diverging_type_vars: RefCell::new(Default::default()), - body_id, - } - } - - #[instrument(level = "debug", skip(self))] - pub(super) fn register_predicate(&self, obligation: traits::PredicateObligation<'tcx>) { - if obligation.has_escaping_bound_vars() { - span_bug!(obligation.cause.span, "escaping bound vars in predicate {:?}", obligation); - } - self.fulfillment_cx.borrow_mut().register_predicate_obligation(self, obligation); - } - - pub(super) fn register_predicates<I>(&self, obligations: I) - where - I: IntoIterator<Item = traits::PredicateObligation<'tcx>>, - { - for obligation in obligations { - self.register_predicate(obligation); - } - } - - pub(super) fn register_infer_ok_obligations<T>(&self, infer_ok: InferOk<'tcx, T>) -> T { - self.register_predicates(infer_ok.obligations); - infer_ok.value - } - - pub(super) fn normalize_associated_types_in<T>( - &self, - span: Span, - body_id: hir::HirId, - param_env: ty::ParamEnv<'tcx>, - value: T, - ) -> T - where - T: TypeFoldable<'tcx>, - { - self.normalize_associated_types_in_with_cause( - ObligationCause::misc(span, body_id), - param_env, - value, - ) - } - - pub(super) fn normalize_associated_types_in_with_cause<T>( - &self, - cause: ObligationCause<'tcx>, - param_env: ty::ParamEnv<'tcx>, - value: T, - ) -> T - where - T: TypeFoldable<'tcx>, - { - let ok = self.partially_normalize_associated_types_in(cause, param_env, value); - debug!(?ok); - self.register_infer_ok_obligations(ok) - } -} diff --git a/compiler/rustc_typeck/src/check/intrinsic.rs b/compiler/rustc_typeck/src/check/intrinsic.rs deleted file mode 100644 index c7425ff78..000000000 --- a/compiler/rustc_typeck/src/check/intrinsic.rs +++ /dev/null @@ -1,533 +0,0 @@ -//! Type-checking for the rust-intrinsic and platform-intrinsic -//! intrinsics that the compiler exposes. - -use crate::errors::{ - UnrecognizedAtomicOperation, UnrecognizedIntrinsicFunction, - WrongNumberOfGenericArgumentsToIntrinsic, -}; -use crate::require_same_types; - -use rustc_errors::struct_span_err; -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; - -use std::iter; - -fn equate_intrinsic_type<'tcx>( - tcx: TyCtxt<'tcx>, - it: &hir::ForeignItem<'_>, - n_tps: usize, - n_lts: usize, - sig: ty::PolyFnSig<'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(); - (own_counts, generics.span) - } - _ => { - struct_span_err!(tcx.sess, it.span, E0622, "intrinsic must be a function") - .span_label(it.span, "expected a function") - .emit(); - return; - } - }; - - let gen_count_ok = |found: usize, expected: usize, descr: &str| -> bool { - if found != expected { - tcx.sess.emit_err(WrongNumberOfGenericArgumentsToIntrinsic { - span, - found, - expected, - descr, - }); - false - } else { - true - } - }; - - if gen_count_ok(own_counts.lifetimes, n_lts, "lifetime") - && gen_count_ok(own_counts.types, n_tps, "type") - && gen_count_ok(own_counts.consts, 0, "const") - { - 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); - } -} - -/// Returns the unsafety of the given intrinsic. -pub fn intrinsic_operation_unsafety(intrinsic: Symbol) -> hir::Unsafety { - match intrinsic { - // 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 - | sym::caller_location - | sym::add_with_overflow - | sym::sub_with_overflow - | sym::mul_with_overflow - | sym::wrapping_add - | sym::wrapping_sub - | sym::wrapping_mul - | sym::saturating_add - | sym::saturating_sub - | sym::rotate_left - | sym::rotate_right - | sym::ctpop - | sym::ctlz - | sym::cttz - | sym::bswap - | sym::bitreverse - | sym::discriminant_value - | sym::type_id - | sym::likely - | sym::unlikely - | sym::ptr_guaranteed_cmp - | sym::minnumf32 - | sym::minnumf64 - | sym::maxnumf32 - | sym::rustc_peek - | sym::maxnumf64 - | sym::type_name - | sym::forget - | sym::black_box - | sym::variant_count - | sym::ptr_mask => hir::Unsafety::Normal, - _ => hir::Unsafety::Unsafe, - } -} - -/// 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 name_str = intrinsic_name.as_str(); - - let bound_vars = tcx.mk_bound_variable_kinds( - [ty::BoundVariableKind::Region(ty::BrAnon(0)), ty::BoundVariableKind::Region(ty::BrEnv)] - .iter() - .copied(), - ); - let mk_va_list_ty = |mutbl| { - tcx.lang_items().va_list().map(|did| { - let region = tcx.mk_region(ty::ReLateBound( - ty::INNERMOST, - ty::BoundRegion { var: ty::BoundVar::from_u32(0), kind: ty::BrAnon(0) }, - )); - let env_region = tcx.mk_region(ty::ReLateBound( - ty::INNERMOST, - ty::BoundRegion { var: ty::BoundVar::from_u32(1), kind: ty::BrEnv }, - )); - let va_list_ty = tcx.bound_type_of(did).subst(tcx, &[region.into()]); - (tcx.mk_ref(env_region, ty::TypeAndMut { ty: va_list_ty, mutbl }), va_list_ty) - }) - }; - - let (n_tps, n_lts, inputs, output, unsafety) = if name_str.starts_with("atomic_") { - let split: Vec<&str> = name_str.split('_').collect(); - assert!(split.len() >= 2, "Atomic intrinsic in an incorrect format"); - - //We only care about the operation here - let (n_tps, inputs, output) = match split[1] { - "cxchg" | "cxchgweak" => ( - 1, - vec![tcx.mk_mut_ptr(param(0)), param(0), param(0)], - tcx.intern_tup(&[param(0), tcx.types.bool]), - ), - "load" => (1, vec![tcx.mk_imm_ptr(param(0))], param(0)), - "store" => (1, vec![tcx.mk_mut_ptr(param(0)), param(0)], tcx.mk_unit()), - - "xchg" | "xadd" | "xsub" | "and" | "nand" | "or" | "xor" | "max" | "min" | "umax" - | "umin" => (1, vec![tcx.mk_mut_ptr(param(0)), param(0)], param(0)), - "fence" | "singlethreadfence" => (0, Vec::new(), tcx.mk_unit()), - op => { - tcx.sess.emit_err(UnrecognizedAtomicOperation { span: it.span, op }); - return; - } - }; - (n_tps, 0, inputs, output, hir::Unsafety::Unsafe) - } else { - let unsafety = intrinsic_operation_unsafety(intrinsic_name); - let (n_tps, inputs, output) = match intrinsic_name { - sym::abort => (0, Vec::new(), tcx.types.never), - sym::unreachable => (0, Vec::new(), tcx.types.never), - sym::breakpoint => (0, Vec::new(), tcx.mk_unit()), - sym::size_of | sym::pref_align_of | sym::min_align_of | sym::variant_count => { - (1, Vec::new(), tcx.types.usize) - } - sym::size_of_val | sym::min_align_of_val => { - (1, vec![tcx.mk_imm_ptr(param(0))], tcx.types.usize) - } - sym::rustc_peek => (1, vec![param(0)], param(0)), - sym::caller_location => (0, vec![], tcx.caller_location_ty()), - sym::assert_inhabited | sym::assert_zero_valid | sym::assert_uninit_valid => { - (1, Vec::new(), tcx.mk_unit()) - } - sym::forget => (1, vec![param(0)], tcx.mk_unit()), - sym::transmute => (2, vec![param(0)], param(1)), - sym::prefetch_read_data - | sym::prefetch_write_data - | sym::prefetch_read_instruction - | sym::prefetch_write_instruction => ( - 1, - vec![ - tcx.mk_ptr(ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Not }), - tcx.types.i32, - ], - tcx.mk_unit(), - ), - sym::drop_in_place => (1, vec![tcx.mk_mut_ptr(param(0))], tcx.mk_unit()), - sym::needs_drop => (1, Vec::new(), tcx.types.bool), - - sym::type_name => (1, Vec::new(), tcx.mk_static_str()), - sym::type_id => (1, Vec::new(), tcx.types.u64), - sym::offset | sym::arith_offset => ( - 1, - vec![ - tcx.mk_ptr(ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Not }), - tcx.types.isize, - ], - 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![ - tcx.mk_ptr(ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Not }), - tcx.mk_ptr(ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Mut }), - tcx.types.usize, - ], - tcx.mk_unit(), - ), - sym::volatile_copy_memory | sym::volatile_copy_nonoverlapping_memory => ( - 1, - vec![ - tcx.mk_ptr(ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Mut }), - tcx.mk_ptr(ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Not }), - tcx.types.usize, - ], - tcx.mk_unit(), - ), - sym::write_bytes | sym::volatile_set_memory => ( - 1, - vec![ - tcx.mk_ptr(ty::TypeAndMut { ty: param(0), mutbl: hir::Mutability::Mut }), - tcx.types.u8, - tcx.types.usize, - ], - tcx.mk_unit(), - ), - sym::sqrtf32 => (0, vec![tcx.types.f32], tcx.types.f32), - sym::sqrtf64 => (0, vec![tcx.types.f64], tcx.types.f64), - sym::powif32 => (0, vec![tcx.types.f32, tcx.types.i32], tcx.types.f32), - sym::powif64 => (0, vec![tcx.types.f64, tcx.types.i32], tcx.types.f64), - sym::sinf32 => (0, vec![tcx.types.f32], tcx.types.f32), - sym::sinf64 => (0, vec![tcx.types.f64], tcx.types.f64), - sym::cosf32 => (0, vec![tcx.types.f32], tcx.types.f32), - sym::cosf64 => (0, vec![tcx.types.f64], tcx.types.f64), - sym::powf32 => (0, vec![tcx.types.f32, tcx.types.f32], tcx.types.f32), - sym::powf64 => (0, vec![tcx.types.f64, tcx.types.f64], tcx.types.f64), - sym::expf32 => (0, vec![tcx.types.f32], tcx.types.f32), - sym::expf64 => (0, vec![tcx.types.f64], tcx.types.f64), - sym::exp2f32 => (0, vec![tcx.types.f32], tcx.types.f32), - sym::exp2f64 => (0, vec![tcx.types.f64], tcx.types.f64), - sym::logf32 => (0, vec![tcx.types.f32], tcx.types.f32), - sym::logf64 => (0, vec![tcx.types.f64], tcx.types.f64), - sym::log10f32 => (0, vec![tcx.types.f32], tcx.types.f32), - sym::log10f64 => (0, vec![tcx.types.f64], tcx.types.f64), - sym::log2f32 => (0, vec![tcx.types.f32], tcx.types.f32), - sym::log2f64 => (0, vec![tcx.types.f64], tcx.types.f64), - sym::fmaf32 => (0, vec![tcx.types.f32, tcx.types.f32, tcx.types.f32], tcx.types.f32), - sym::fmaf64 => (0, vec![tcx.types.f64, tcx.types.f64, tcx.types.f64], tcx.types.f64), - sym::fabsf32 => (0, vec![tcx.types.f32], tcx.types.f32), - sym::fabsf64 => (0, vec![tcx.types.f64], tcx.types.f64), - sym::minnumf32 => (0, vec![tcx.types.f32, tcx.types.f32], tcx.types.f32), - sym::minnumf64 => (0, vec![tcx.types.f64, tcx.types.f64], tcx.types.f64), - sym::maxnumf32 => (0, vec![tcx.types.f32, tcx.types.f32], tcx.types.f32), - sym::maxnumf64 => (0, vec![tcx.types.f64, tcx.types.f64], tcx.types.f64), - sym::copysignf32 => (0, vec![tcx.types.f32, tcx.types.f32], tcx.types.f32), - sym::copysignf64 => (0, vec![tcx.types.f64, tcx.types.f64], tcx.types.f64), - sym::floorf32 => (0, vec![tcx.types.f32], tcx.types.f32), - sym::floorf64 => (0, vec![tcx.types.f64], tcx.types.f64), - sym::ceilf32 => (0, vec![tcx.types.f32], tcx.types.f32), - sym::ceilf64 => (0, vec![tcx.types.f64], tcx.types.f64), - sym::truncf32 => (0, vec![tcx.types.f32], tcx.types.f32), - sym::truncf64 => (0, vec![tcx.types.f64], tcx.types.f64), - sym::rintf32 => (0, vec![tcx.types.f32], tcx.types.f32), - sym::rintf64 => (0, vec![tcx.types.f64], tcx.types.f64), - sym::nearbyintf32 => (0, vec![tcx.types.f32], tcx.types.f32), - sym::nearbyintf64 => (0, vec![tcx.types.f64], tcx.types.f64), - sym::roundf32 => (0, vec![tcx.types.f32], tcx.types.f32), - sym::roundf64 => (0, vec![tcx.types.f64], tcx.types.f64), - - sym::volatile_load | sym::unaligned_volatile_load => { - (1, vec![tcx.mk_imm_ptr(param(0))], param(0)) - } - sym::volatile_store | sym::unaligned_volatile_store => { - (1, vec![tcx.mk_mut_ptr(param(0)), param(0)], tcx.mk_unit()) - } - - sym::ctpop - | sym::ctlz - | sym::ctlz_nonzero - | sym::cttz - | sym::cttz_nonzero - | sym::bswap - | sym::bitreverse => (1, vec![param(0)], param(0)), - - sym::add_with_overflow | sym::sub_with_overflow | sym::mul_with_overflow => { - (1, vec![param(0), param(0)], tcx.intern_tup(&[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 => { - (0, vec![tcx.types.usize, tcx.types.usize], tcx.mk_mut_ptr(tcx.types.u8)) - } - sym::const_deallocate => ( - 0, - vec![tcx.mk_mut_ptr(tcx.types.u8), tcx.types.usize, tcx.types.usize], - tcx.mk_unit(), - ), - - sym::ptr_offset_from => { - (1, vec![tcx.mk_imm_ptr(param(0)), tcx.mk_imm_ptr(param(0))], tcx.types.isize) - } - sym::ptr_offset_from_unsigned => { - (1, vec![tcx.mk_imm_ptr(param(0)), tcx.mk_imm_ptr(param(0))], tcx.types.usize) - } - sym::unchecked_div | sym::unchecked_rem | sym::exact_div => { - (1, vec![param(0), param(0)], param(0)) - } - sym::unchecked_shl | sym::unchecked_shr | sym::rotate_left | sym::rotate_right => { - (1, vec![param(0), param(0)], param(0)) - } - sym::unchecked_add | sym::unchecked_sub | sym::unchecked_mul => { - (1, vec![param(0), param(0)], param(0)) - } - sym::wrapping_add | sym::wrapping_sub | sym::wrapping_mul => { - (1, vec![param(0), param(0)], param(0)) - } - sym::saturating_add | sym::saturating_sub => (1, vec![param(0), param(0)], param(0)), - sym::fadd_fast | sym::fsub_fast | sym::fmul_fast | sym::fdiv_fast | sym::frem_fast => { - (1, vec![param(0), param(0)], param(0)) - } - sym::float_to_int_unchecked => (2, vec![param(0)], param(1)), - - sym::assume => (0, vec![tcx.types.bool], tcx.mk_unit()), - sym::likely => (0, vec![tcx.types.bool], tcx.types.bool), - sym::unlikely => (0, vec![tcx.types.bool], tcx.types.bool), - - sym::discriminant_value => { - let assoc_items = tcx.associated_item_def_ids( - tcx.require_lang_item(hir::LangItem::DiscriminantKind, None), - ); - let discriminant_def_id = assoc_items[0]; - - let br = ty::BoundRegion { var: ty::BoundVar::from_u32(0), kind: ty::BrAnon(0) }; - ( - 1, - vec![ - tcx.mk_imm_ref(tcx.mk_region(ty::ReLateBound(ty::INNERMOST, br)), param(0)), - ], - tcx.mk_projection(discriminant_def_id, tcx.mk_substs([param(0).into()].iter())), - ) - } - - kw::Try => { - let mut_u8 = tcx.mk_mut_ptr(tcx.types.u8); - let try_fn_ty = ty::Binder::dummy(tcx.mk_fn_sig( - iter::once(mut_u8), - tcx.mk_unit(), - false, - hir::Unsafety::Normal, - Abi::Rust, - )); - let catch_fn_ty = ty::Binder::dummy(tcx.mk_fn_sig( - [mut_u8, mut_u8].iter().cloned(), - tcx.mk_unit(), - false, - hir::Unsafety::Normal, - Abi::Rust, - )); - ( - 0, - vec![tcx.mk_fn_ptr(try_fn_ty), mut_u8, tcx.mk_fn_ptr(catch_fn_ty)], - tcx.types.i32, - ) - } - - sym::va_start | sym::va_end => match mk_va_list_ty(hir::Mutability::Mut) { - Some((va_list_ref_ty, _)) => (0, vec![va_list_ref_ty], tcx.mk_unit()), - None => bug!("`va_list` language item needed for C-variadic intrinsics"), - }, - - sym::va_copy => match mk_va_list_ty(hir::Mutability::Not) { - Some((va_list_ref_ty, va_list_ty)) => { - let va_list_ptr_ty = tcx.mk_mut_ptr(va_list_ty); - (0, vec![va_list_ptr_ty, va_list_ref_ty], tcx.mk_unit()) - } - None => bug!("`va_list` language item needed for C-variadic intrinsics"), - }, - - sym::va_arg => match mk_va_list_ty(hir::Mutability::Mut) { - Some((va_list_ref_ty, _)) => (1, vec![va_list_ref_ty], param(0)), - None => bug!("`va_list` language item needed for C-variadic intrinsics"), - }, - - sym::nontemporal_store => (1, vec![tcx.mk_mut_ptr(param(0)), param(0)], tcx.mk_unit()), - - sym::raw_eq => { - let br = ty::BoundRegion { var: ty::BoundVar::from_u32(0), kind: ty::BrAnon(0) }; - let param_ty = - tcx.mk_imm_ref(tcx.mk_region(ty::ReLateBound(ty::INNERMOST, br)), param(0)); - (1, vec![param_ty; 2], tcx.types.bool) - } - - sym::black_box => (1, vec![param(0)], param(0)), - - sym::const_eval_select => (4, vec![param(0), param(1), param(2)], param(3)), - - sym::vtable_size | sym::vtable_align => { - (0, vec![tcx.mk_imm_ptr(tcx.mk_unit())], tcx.types.usize) - } - - other => { - tcx.sess.emit_err(UnrecognizedIntrinsicFunction { span: it.span, name: other }); - return; - } - }; - (n_tps, 0, inputs, output, unsafety) - }; - let sig = tcx.mk_fn_sig(inputs.into_iter(), output, false, unsafety, Abi::RustIntrinsic); - let sig = ty::Binder::bind_with_vars(sig, bound_vars); - equate_intrinsic_type(tcx, it, n_tps, n_lts, sig) -} - -/// Type-check `extern "platform-intrinsic" { ... }` functions. -pub fn check_platform_intrinsic_type(tcx: TyCtxt<'_>, it: &hir::ForeignItem<'_>) { - let param = |n| { - let name = Symbol::intern(&format!("P{}", n)); - tcx.mk_ty_param(n, name) - }; - - let name = it.ident.name; - - let (n_tps, inputs, output) = match name { - sym::simd_eq | sym::simd_ne | sym::simd_lt | sym::simd_le | sym::simd_gt | sym::simd_ge => { - (2, vec![param(0), param(0)], param(1)) - } - sym::simd_add - | sym::simd_sub - | sym::simd_mul - | sym::simd_rem - | sym::simd_div - | sym::simd_shl - | sym::simd_shr - | sym::simd_and - | sym::simd_or - | sym::simd_xor - | sym::simd_fmin - | sym::simd_fmax - | sym::simd_fpow - | sym::simd_saturating_add - | sym::simd_saturating_sub => (1, vec![param(0), param(0)], param(0)), - sym::simd_arith_offset => (2, vec![param(0), param(1)], param(0)), - sym::simd_neg - | sym::simd_fsqrt - | sym::simd_fsin - | sym::simd_fcos - | sym::simd_fexp - | sym::simd_fexp2 - | sym::simd_flog2 - | sym::simd_flog10 - | sym::simd_flog - | sym::simd_fabs - | sym::simd_ceil - | sym::simd_floor - | sym::simd_round - | sym::simd_trunc => (1, vec![param(0)], param(0)), - sym::simd_fpowi => (1, vec![param(0), tcx.types.i32], param(0)), - sym::simd_fma => (1, vec![param(0), param(0), param(0)], param(0)), - sym::simd_gather => (3, vec![param(0), param(1), param(2)], param(0)), - 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 - | 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)) - } - sym::simd_reduce_all | sym::simd_reduce_any => (1, vec![param(0)], tcx.types.bool), - sym::simd_reduce_add_ordered | sym::simd_reduce_mul_ordered => { - (2, vec![param(0), param(1)], param(1)) - } - sym::simd_reduce_add_unordered - | sym::simd_reduce_mul_unordered - | sym::simd_reduce_and - | sym::simd_reduce_or - | sym::simd_reduce_xor - | sym::simd_reduce_min - | sym::simd_reduce_max - | sym::simd_reduce_min_nanless - | sym::simd_reduce_max_nanless => (2, vec![param(0)], param(1)), - sym::simd_shuffle => (3, vec![param(0), param(0), param(1)], param(2)), - name if name.as_str().starts_with("simd_shuffle") => { - match name.as_str()["simd_shuffle".len()..].parse() { - Ok(n) => { - let params = vec![param(0), param(0), tcx.mk_array(tcx.types.u32, n)]; - (2, params, param(1)) - } - Err(_) => { - let msg = - format!("unrecognized platform-specific intrinsic function: `{name}`"); - tcx.sess.struct_span_err(it.span, &msg).emit(); - return; - } - } - } - _ => { - let msg = format!("unrecognized platform-specific intrinsic function: `{name}`"); - tcx.sess.struct_span_err(it.span, &msg).emit(); - return; - } - }; - - let sig = tcx.mk_fn_sig( - inputs.into_iter(), - output, - false, - hir::Unsafety::Unsafe, - Abi::PlatformIntrinsic, - ); - let sig = ty::Binder::dummy(sig); - equate_intrinsic_type(tcx, it, n_tps, 0, sig) -} diff --git a/compiler/rustc_typeck/src/check/intrinsicck.rs b/compiler/rustc_typeck/src/check/intrinsicck.rs deleted file mode 100644 index d8fe63dbf..000000000 --- a/compiler/rustc_typeck/src/check/intrinsicck.rs +++ /dev/null @@ -1,531 +0,0 @@ -use rustc_ast::InlineAsmTemplatePiece; -use rustc_data_structures::fx::FxHashSet; -use rustc_errors::struct_span_err; -use rustc_hir as hir; -use rustc_index::vec::Idx; -use rustc_middle::ty::layout::{LayoutError, SizeSkeleton}; -use rustc_middle::ty::{self, Article, FloatTy, IntTy, Ty, TyCtxt, TypeVisitable, UintTy}; -use rustc_session::lint; -use rustc_span::{Span, Symbol, DUMMY_SP}; -use rustc_target::abi::{Pointer, VariantIdx}; -use rustc_target::asm::{InlineAsmReg, InlineAsmRegClass, InlineAsmRegOrRegClass, InlineAsmType}; - -use super::FnCtxt; - -/// If the type is `Option<T>`, it will return `T`, otherwise -/// the type itself. Works on most `Option`-like types. -fn unpack_option_like<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> Ty<'tcx> { - let ty::Adt(def, substs) = *ty.kind() else { return ty }; - - if def.variants().len() == 2 && !def.repr().c() && def.repr().int.is_none() { - let data_idx; - - let one = VariantIdx::new(1); - let zero = VariantIdx::new(0); - - if def.variant(zero).fields.is_empty() { - data_idx = one; - } else if def.variant(one).fields.is_empty() { - data_idx = zero; - } else { - return ty; - } - - if def.variant(data_idx).fields.len() == 1 { - return def.variant(data_idx).fields[0].ty(tcx, substs); - } - } - - ty -} - -impl<'a, 'tcx> FnCtxt<'a, 'tcx> { - pub fn check_transmute(&self, span: Span, from: Ty<'tcx>, to: Ty<'tcx>) { - let convert = |ty: Ty<'tcx>| { - let ty = self.resolve_vars_if_possible(ty); - let ty = self.tcx.normalize_erasing_regions(self.param_env, ty); - (SizeSkeleton::compute(ty, self.tcx, self.param_env), ty) - }; - let (sk_from, from) = convert(from); - let (sk_to, to) = convert(to); - - // Check for same size using the skeletons. - if let (Ok(sk_from), Ok(sk_to)) = (sk_from, sk_to) { - if sk_from.same_size(sk_to) { - return; - } - - // Special-case transmuting from `typeof(function)` and - // `Option<typeof(function)>` to present a clearer error. - let from = unpack_option_like(self.tcx, from); - if let (&ty::FnDef(..), SizeSkeleton::Known(size_to)) = (from.kind(), sk_to) && size_to == Pointer.size(&self.tcx) { - struct_span_err!(self.tcx.sess, span, E0591, "can't transmute zero-sized type") - .note(&format!("source type: {from}")) - .note(&format!("target type: {to}")) - .help("cast with `as` to a pointer instead") - .emit(); - return; - } - } - - // Try to display a sensible error with as much information as possible. - let skeleton_string = |ty: Ty<'tcx>, sk| match sk { - Ok(SizeSkeleton::Known(size)) => format!("{} bits", size.bits()), - Ok(SizeSkeleton::Pointer { tail, .. }) => format!("pointer to `{tail}`"), - Err(LayoutError::Unknown(bad)) => { - if bad == ty { - "this type does not have a fixed size".to_owned() - } else { - format!("size can vary because of {bad}") - } - } - Err(err) => err.to_string(), - }; - - let mut err = struct_span_err!( - self.tcx.sess, - span, - E0512, - "cannot transmute between types of different sizes, \ - or dependently-sized types" - ); - if from == to { - err.note(&format!("`{from}` does not have a fixed size")); - } else { - err.note(&format!("source type: `{}` ({})", from, skeleton_string(from, sk_from))) - .note(&format!("target type: `{}` ({})", to, skeleton_string(to, sk_to))); - } - err.emit(); - } -} - -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.at(DUMMY_SP), 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_infer_types_or_consts() { - 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.at(expr.span), 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, - |lint| { - let msg = "formatting may not be suitable for sub-register argument"; - let mut err = lint.build(msg); - err.span_label(expr.span, "for this argument"); - err.help(&format!( - "use `{{{idx}:{suggested_modifier}}}` to have the register formatted as `{suggested_result}`", - )); - err.help(&format!( - "or use `{{{idx}:{default_modifier}}}` to keep the default formatting of `{default_result}`", - )); - err.emit(); - }, - ); - } - } - - 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_typeck/src/check/method/confirm.rs b/compiler/rustc_typeck/src/check/method/confirm.rs deleted file mode 100644 index 59fd5c315..000000000 --- a/compiler/rustc_typeck/src/check/method/confirm.rs +++ /dev/null @@ -1,594 +0,0 @@ -use super::{probe, MethodCallee}; - -use crate::astconv::{AstConv, CreateSubstsForGenericArgsCtxt, IsMethodCall}; -use crate::check::{callee, FnCtxt}; -use rustc_hir as hir; -use rustc_hir::def_id::DefId; -use rustc_hir::GenericArg; -use rustc_infer::infer::{self, InferOk}; -use rustc_middle::traits::{ObligationCauseCode, UnifyReceiverContext}; -use rustc_middle::ty::adjustment::{Adjust, Adjustment, PointerCast}; -use rustc_middle::ty::adjustment::{AllowTwoPhase, AutoBorrow, AutoBorrowMutability}; -use rustc_middle::ty::fold::TypeFoldable; -use rustc_middle::ty::subst::{self, Subst, SubstsRef}; -use rustc_middle::ty::{self, GenericParamDefKind, Ty}; -use rustc_span::Span; -use rustc_trait_selection::traits; - -use std::iter; -use std::ops::Deref; - -struct ConfirmContext<'a, 'tcx> { - fcx: &'a FnCtxt<'a, 'tcx>, - span: Span, - self_expr: &'tcx hir::Expr<'tcx>, - call_expr: &'tcx hir::Expr<'tcx>, -} - -impl<'a, 'tcx> Deref for ConfirmContext<'a, 'tcx> { - type Target = FnCtxt<'a, 'tcx>; - fn deref(&self) -> &Self::Target { - self.fcx - } -} - -#[derive(Debug)] -pub struct ConfirmResult<'tcx> { - pub callee: MethodCallee<'tcx>, - pub illegal_sized_bound: Option<Span>, -} - -impl<'a, 'tcx> FnCtxt<'a, 'tcx> { - pub fn confirm_method( - &self, - span: Span, - self_expr: &'tcx hir::Expr<'tcx>, - call_expr: &'tcx hir::Expr<'tcx>, - unadjusted_self_ty: Ty<'tcx>, - pick: probe::Pick<'tcx>, - segment: &hir::PathSegment<'_>, - ) -> ConfirmResult<'tcx> { - debug!( - "confirm(unadjusted_self_ty={:?}, pick={:?}, generic_args={:?})", - unadjusted_self_ty, pick, segment.args, - ); - - let mut confirm_cx = ConfirmContext::new(self, span, self_expr, call_expr); - confirm_cx.confirm(unadjusted_self_ty, pick, segment) - } -} - -impl<'a, 'tcx> ConfirmContext<'a, 'tcx> { - fn new( - fcx: &'a FnCtxt<'a, 'tcx>, - span: Span, - self_expr: &'tcx hir::Expr<'tcx>, - call_expr: &'tcx hir::Expr<'tcx>, - ) -> ConfirmContext<'a, 'tcx> { - ConfirmContext { fcx, span, self_expr, call_expr } - } - - fn confirm( - &mut self, - unadjusted_self_ty: Ty<'tcx>, - pick: probe::Pick<'tcx>, - segment: &hir::PathSegment<'_>, - ) -> ConfirmResult<'tcx> { - // Adjust the self expression the user provided and obtain the adjusted type. - let self_ty = self.adjust_self_ty(unadjusted_self_ty, &pick); - - // Create substitutions for the method's type parameters. - let rcvr_substs = self.fresh_receiver_substs(self_ty, &pick); - let all_substs = self.instantiate_method_substs(&pick, segment, rcvr_substs); - - debug!("rcvr_substs={rcvr_substs:?}, all_substs={all_substs:?}"); - - // Create the final signature for the method, replacing late-bound regions. - let (method_sig, method_predicates) = self.instantiate_method_sig(&pick, all_substs); - - // If there is a `Self: Sized` bound and `Self` is a trait object, it is possible that - // something which derefs to `Self` actually implements the trait and the caller - // wanted to make a static dispatch on it but forgot to import the trait. - // See test `src/test/ui/issue-35976.rs`. - // - // In that case, we'll error anyway, but we'll also re-run the search with all traits - // in scope, and if we find another method which can be used, we'll output an - // appropriate hint suggesting to import the trait. - let filler_substs = rcvr_substs - .extend_to(self.tcx, pick.item.def_id, |def, _| self.tcx.mk_param_from_def(def)); - let illegal_sized_bound = self.predicates_require_illegal_sized_bound( - &self.tcx.predicates_of(pick.item.def_id).instantiate(self.tcx, filler_substs), - ); - - // Unify the (adjusted) self type with what the method expects. - // - // SUBTLE: if we want good error messages, because of "guessing" while matching - // traits, no trait system method can be called before this point because they - // could alter our Self-type, except for normalizing the receiver from the - // signature (which is also done during probing). - let method_sig_rcvr = self.normalize_associated_types_in(self.span, method_sig.inputs()[0]); - debug!( - "confirm: self_ty={:?} method_sig_rcvr={:?} method_sig={:?} method_predicates={:?}", - self_ty, method_sig_rcvr, method_sig, method_predicates - ); - self.unify_receivers(self_ty, method_sig_rcvr, &pick, all_substs); - - let (method_sig, method_predicates) = - self.normalize_associated_types_in(self.span, (method_sig, method_predicates)); - let method_sig = ty::Binder::dummy(method_sig); - - // Make sure nobody calls `drop()` explicitly. - self.enforce_illegal_method_limitations(&pick); - - // Add any trait/regions obligations specified on the method's type parameters. - // We won't add these if we encountered an illegal sized bound, so that we can use - // a custom error in that case. - if illegal_sized_bound.is_none() { - self.add_obligations( - self.tcx.mk_fn_ptr(method_sig), - all_substs, - method_predicates, - pick.item.def_id, - ); - } - - // Create the final `MethodCallee`. - let callee = MethodCallee { - def_id: pick.item.def_id, - substs: all_substs, - sig: method_sig.skip_binder(), - }; - ConfirmResult { callee, illegal_sized_bound } - } - - /////////////////////////////////////////////////////////////////////////// - // ADJUSTMENTS - - fn adjust_self_ty( - &mut self, - unadjusted_self_ty: Ty<'tcx>, - pick: &probe::Pick<'tcx>, - ) -> Ty<'tcx> { - // Commit the autoderefs by calling `autoderef` again, but this - // time writing the results into the various typeck results. - let mut autoderef = - self.autoderef_overloaded_span(self.span, unadjusted_self_ty, self.call_expr.span); - let Some((ty, n)) = autoderef.nth(pick.autoderefs) else { - return self.tcx.ty_error_with_message( - rustc_span::DUMMY_SP, - &format!("failed autoderef {}", pick.autoderefs), - ); - }; - assert_eq!(n, pick.autoderefs); - - let mut adjustments = self.adjust_steps(&autoderef); - let mut target = self.structurally_resolved_type(autoderef.span(), ty); - - match pick.autoref_or_ptr_adjustment { - Some(probe::AutorefOrPtrAdjustment::Autoref { mutbl, unsize }) => { - let region = self.next_region_var(infer::Autoref(self.span)); - // Type we're wrapping in a reference, used later for unsizing - let base_ty = target; - - target = self.tcx.mk_ref(region, ty::TypeAndMut { mutbl, ty: target }); - let mutbl = match mutbl { - hir::Mutability::Not => AutoBorrowMutability::Not, - hir::Mutability::Mut => AutoBorrowMutability::Mut { - // Method call receivers are the primary use case - // for two-phase borrows. - allow_two_phase_borrow: AllowTwoPhase::Yes, - }, - }; - adjustments.push(Adjustment { - kind: Adjust::Borrow(AutoBorrow::Ref(region, mutbl)), - target, - }); - - if unsize { - let unsized_ty = if let ty::Array(elem_ty, _) = base_ty.kind() { - self.tcx.mk_slice(*elem_ty) - } else { - bug!( - "AutorefOrPtrAdjustment's unsize flag should only be set for array ty, found {}", - base_ty - ) - }; - target = self - .tcx - .mk_ref(region, ty::TypeAndMut { mutbl: mutbl.into(), ty: unsized_ty }); - adjustments - .push(Adjustment { kind: Adjust::Pointer(PointerCast::Unsize), target }); - } - } - Some(probe::AutorefOrPtrAdjustment::ToConstPtr) => { - target = match target.kind() { - &ty::RawPtr(ty::TypeAndMut { ty, mutbl }) => { - assert_eq!(mutbl, hir::Mutability::Mut); - self.tcx.mk_ptr(ty::TypeAndMut { mutbl: hir::Mutability::Not, ty }) - } - other => panic!("Cannot adjust receiver type {:?} to const ptr", other), - }; - - adjustments.push(Adjustment { - kind: Adjust::Pointer(PointerCast::MutToConstPointer), - target, - }); - } - None => {} - } - - self.register_predicates(autoderef.into_obligations()); - - // Write out the final adjustments. - self.apply_adjustments(self.self_expr, adjustments); - - target - } - - /// Returns a set of substitutions for the method *receiver* where all type and region - /// parameters are instantiated with fresh variables. This substitution does not include any - /// parameters declared on the method itself. - /// - /// Note that this substitution may include late-bound regions from the impl level. If so, - /// these are instantiated later in the `instantiate_method_sig` routine. - fn fresh_receiver_substs( - &mut self, - self_ty: Ty<'tcx>, - pick: &probe::Pick<'tcx>, - ) -> SubstsRef<'tcx> { - match pick.kind { - probe::InherentImplPick => { - let impl_def_id = pick.item.container_id(self.tcx); - assert!( - self.tcx.impl_trait_ref(impl_def_id).is_none(), - "impl {:?} is not an inherent impl", - impl_def_id - ); - self.fresh_substs_for_item(self.span, impl_def_id) - } - - probe::ObjectPick => { - let trait_def_id = pick.item.container_id(self.tcx); - self.extract_existential_trait_ref(self_ty, |this, object_ty, principal| { - // The object data has no entry for the Self - // Type. For the purposes of this method call, we - // substitute the object type itself. This - // wouldn't be a sound substitution in all cases, - // since each instance of the object type is a - // different existential and hence could match - // distinct types (e.g., if `Self` appeared as an - // argument type), but those cases have already - // been ruled out when we deemed the trait to be - // "object safe". - let original_poly_trait_ref = principal.with_self_ty(this.tcx, object_ty); - let upcast_poly_trait_ref = this.upcast(original_poly_trait_ref, trait_def_id); - let upcast_trait_ref = - this.replace_bound_vars_with_fresh_vars(upcast_poly_trait_ref); - debug!( - "original_poly_trait_ref={:?} upcast_trait_ref={:?} target_trait={:?}", - original_poly_trait_ref, upcast_trait_ref, trait_def_id - ); - upcast_trait_ref.substs - }) - } - - probe::TraitPick => { - let trait_def_id = pick.item.container_id(self.tcx); - - // Make a trait reference `$0 : Trait<$1...$n>` - // consisting entirely of type variables. Later on in - // the process we will unify the transformed-self-type - // of the method with the actual type in order to - // unify some of these variables. - self.fresh_substs_for_item(self.span, trait_def_id) - } - - probe::WhereClausePick(poly_trait_ref) => { - // Where clauses can have bound regions in them. We need to instantiate - // those to convert from a poly-trait-ref to a trait-ref. - self.replace_bound_vars_with_fresh_vars(poly_trait_ref).substs - } - } - } - - fn extract_existential_trait_ref<R, F>(&mut self, self_ty: Ty<'tcx>, mut closure: F) -> R - where - F: FnMut(&mut ConfirmContext<'a, 'tcx>, Ty<'tcx>, ty::PolyExistentialTraitRef<'tcx>) -> R, - { - // If we specified that this is an object method, then the - // self-type ought to be something that can be dereferenced to - // yield an object-type (e.g., `&Object` or `Box<Object>` - // etc). - - // FIXME: this feels, like, super dubious - self.fcx - .autoderef(self.span, self_ty) - .include_raw_pointers() - .find_map(|(ty, _)| match ty.kind() { - ty::Dynamic(data, ..) => Some(closure( - self, - ty, - data.principal().unwrap_or_else(|| { - span_bug!(self.span, "calling trait method on empty object?") - }), - )), - _ => None, - }) - .unwrap_or_else(|| { - span_bug!( - self.span, - "self-type `{}` for ObjectPick never dereferenced to an object", - self_ty - ) - }) - } - - fn instantiate_method_substs( - &mut self, - pick: &probe::Pick<'tcx>, - seg: &hir::PathSegment<'_>, - parent_substs: SubstsRef<'tcx>, - ) -> SubstsRef<'tcx> { - // Determine the values for the generic parameters of the method. - // If they were not explicitly supplied, just construct fresh - // variables. - let generics = self.tcx.generics_of(pick.item.def_id); - - let arg_count_correct = <dyn AstConv<'_>>::check_generic_arg_count_for_call( - self.tcx, - self.span, - pick.item.def_id, - generics, - seg, - IsMethodCall::Yes, - ); - - // Create subst for early-bound lifetime parameters, combining - // parameters from the type and those from the method. - assert_eq!(generics.parent_count, parent_substs.len()); - - struct MethodSubstsCtxt<'a, 'tcx> { - cfcx: &'a ConfirmContext<'a, 'tcx>, - pick: &'a probe::Pick<'tcx>, - seg: &'a hir::PathSegment<'a>, - } - impl<'a, 'tcx> CreateSubstsForGenericArgsCtxt<'a, 'tcx> for MethodSubstsCtxt<'a, 'tcx> { - fn args_for_def_id( - &mut self, - def_id: DefId, - ) -> (Option<&'a hir::GenericArgs<'a>>, bool) { - if def_id == self.pick.item.def_id { - if let Some(data) = self.seg.args { - return (Some(data), false); - } - } - (None, false) - } - - fn provided_kind( - &mut self, - param: &ty::GenericParamDef, - arg: &GenericArg<'_>, - ) -> subst::GenericArg<'tcx> { - match (¶m.kind, arg) { - (GenericParamDefKind::Lifetime, GenericArg::Lifetime(lt)) => { - <dyn AstConv<'_>>::ast_region_to_region(self.cfcx.fcx, lt, Some(param)) - .into() - } - (GenericParamDefKind::Type { .. }, GenericArg::Type(ty)) => { - self.cfcx.to_ty(ty).into() - } - (GenericParamDefKind::Const { .. }, GenericArg::Const(ct)) => { - self.cfcx.const_arg_to_const(&ct.value, param.def_id).into() - } - (GenericParamDefKind::Type { .. }, GenericArg::Infer(inf)) => { - self.cfcx.ty_infer(Some(param), inf.span).into() - } - (GenericParamDefKind::Const { .. }, GenericArg::Infer(inf)) => { - let tcx = self.cfcx.tcx(); - self.cfcx.ct_infer(tcx.type_of(param.def_id), Some(param), inf.span).into() - } - _ => unreachable!(), - } - } - - fn inferred_kind( - &mut self, - _substs: Option<&[subst::GenericArg<'tcx>]>, - param: &ty::GenericParamDef, - _infer_args: bool, - ) -> subst::GenericArg<'tcx> { - self.cfcx.var_for_def(self.cfcx.span, param) - } - } - <dyn AstConv<'_>>::create_substs_for_generic_args( - self.tcx, - pick.item.def_id, - parent_substs, - false, - None, - &arg_count_correct, - &mut MethodSubstsCtxt { cfcx: self, pick, seg }, - ) - } - - fn unify_receivers( - &mut self, - self_ty: Ty<'tcx>, - method_self_ty: Ty<'tcx>, - pick: &probe::Pick<'tcx>, - substs: SubstsRef<'tcx>, - ) { - debug!( - "unify_receivers: self_ty={:?} method_self_ty={:?} span={:?} pick={:?}", - self_ty, method_self_ty, self.span, pick - ); - let cause = self.cause( - self.span, - ObligationCauseCode::UnifyReceiver(Box::new(UnifyReceiverContext { - assoc_item: pick.item, - param_env: self.param_env, - substs, - })), - ); - match self.at(&cause, self.param_env).sup(method_self_ty, self_ty) { - Ok(InferOk { obligations, value: () }) => { - self.register_predicates(obligations); - } - Err(_) => { - span_bug!( - self.span, - "{} was a subtype of {} but now is not?", - self_ty, - method_self_ty - ); - } - } - } - - // NOTE: this returns the *unnormalized* predicates and method sig. Because of - // inference guessing, the predicates and method signature can't be normalized - // until we unify the `Self` type. - fn instantiate_method_sig( - &mut self, - pick: &probe::Pick<'tcx>, - all_substs: SubstsRef<'tcx>, - ) -> (ty::FnSig<'tcx>, ty::InstantiatedPredicates<'tcx>) { - debug!("instantiate_method_sig(pick={:?}, all_substs={:?})", pick, all_substs); - - // Instantiate the bounds on the method with the - // type/early-bound-regions substitutions performed. There can - // be no late-bound regions appearing here. - let def_id = pick.item.def_id; - let method_predicates = self.tcx.predicates_of(def_id).instantiate(self.tcx, all_substs); - - debug!("method_predicates after subst = {:?}", method_predicates); - - let sig = self.tcx.bound_fn_sig(def_id); - - let sig = sig.subst(self.tcx, all_substs); - debug!("type scheme substituted, sig={:?}", sig); - - let sig = self.replace_bound_vars_with_fresh_vars(sig); - debug!("late-bound lifetimes from method instantiated, sig={:?}", sig); - - (sig, method_predicates) - } - - fn add_obligations( - &mut self, - fty: Ty<'tcx>, - all_substs: SubstsRef<'tcx>, - method_predicates: ty::InstantiatedPredicates<'tcx>, - def_id: DefId, - ) { - debug!( - "add_obligations: fty={:?} all_substs={:?} method_predicates={:?} def_id={:?}", - fty, all_substs, method_predicates, def_id - ); - - // FIXME: could replace with the following, but we already calculated `method_predicates`, - // so we just call `predicates_for_generics` directly to avoid redoing work. - // `self.add_required_obligations(self.span, def_id, &all_substs);` - for obligation in traits::predicates_for_generics( - |idx, span| { - let code = if span.is_dummy() { - ObligationCauseCode::ExprItemObligation(def_id, self.call_expr.hir_id, idx) - } else { - ObligationCauseCode::ExprBindingObligation( - def_id, - span, - self.call_expr.hir_id, - idx, - ) - }; - traits::ObligationCause::new(self.span, self.body_id, code) - }, - self.param_env, - method_predicates, - ) { - self.register_predicate(obligation); - } - - // this is a projection from a trait reference, so we have to - // make sure that the trait reference inputs are well-formed. - self.add_wf_bounds(all_substs, self.call_expr); - - // the function type must also be well-formed (this is not - // implied by the substs being well-formed because of inherent - // impls and late-bound regions - see issue #28609). - self.register_wf_obligation(fty.into(), self.span, traits::WellFormed(None)); - } - - /////////////////////////////////////////////////////////////////////////// - // MISCELLANY - - fn predicates_require_illegal_sized_bound( - &self, - predicates: &ty::InstantiatedPredicates<'tcx>, - ) -> Option<Span> { - let sized_def_id = self.tcx.lang_items().sized_trait()?; - - traits::elaborate_predicates(self.tcx, predicates.predicates.iter().copied()) - // We don't care about regions here. - .filter_map(|obligation| match obligation.predicate.kind().skip_binder() { - ty::PredicateKind::Trait(trait_pred) if trait_pred.def_id() == sized_def_id => { - let span = iter::zip(&predicates.predicates, &predicates.spans) - .find_map( - |(p, span)| { - if *p == obligation.predicate { Some(*span) } else { None } - }, - ) - .unwrap_or(rustc_span::DUMMY_SP); - Some((trait_pred, span)) - } - _ => None, - }) - .find_map(|(trait_pred, span)| match trait_pred.self_ty().kind() { - ty::Dynamic(..) => Some(span), - _ => None, - }) - } - - fn enforce_illegal_method_limitations(&self, pick: &probe::Pick<'_>) { - // Disallow calls to the method `drop` defined in the `Drop` trait. - if let Some(trait_def_id) = pick.item.trait_container(self.tcx) { - callee::check_legal_trait_for_method_call( - self.tcx, - self.span, - Some(self.self_expr.span), - self.call_expr.span, - trait_def_id, - ) - } - } - - fn upcast( - &mut self, - source_trait_ref: ty::PolyTraitRef<'tcx>, - target_trait_def_id: DefId, - ) -> ty::PolyTraitRef<'tcx> { - let upcast_trait_refs = - traits::upcast_choices(self.tcx, source_trait_ref, target_trait_def_id); - - // must be exactly one trait ref or we'd get an ambig error etc - if upcast_trait_refs.len() != 1 { - span_bug!( - self.span, - "cannot uniquely upcast `{:?}` to `{:?}`: `{:?}`", - source_trait_ref, - target_trait_def_id, - upcast_trait_refs - ); - } - - upcast_trait_refs.into_iter().next().unwrap() - } - - fn replace_bound_vars_with_fresh_vars<T>(&self, value: ty::Binder<'tcx, T>) -> T - where - T: TypeFoldable<'tcx> + Copy, - { - self.fcx.replace_bound_vars_with_fresh_vars(self.span, infer::FnCall, value) - } -} diff --git a/compiler/rustc_typeck/src/check/method/mod.rs b/compiler/rustc_typeck/src/check/method/mod.rs deleted file mode 100644 index 249e9c66b..000000000 --- a/compiler/rustc_typeck/src/check/method/mod.rs +++ /dev/null @@ -1,626 +0,0 @@ -//! Method lookup: the secret sauce of Rust. See the [rustc dev guide] for more information. -//! -//! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/method-lookup.html - -mod confirm; -mod prelude2021; -pub mod probe; -mod suggest; - -pub use self::suggest::SelfSource; -pub use self::MethodError::*; - -use crate::check::{Expectation, FnCtxt}; -use crate::ObligationCause; -use rustc_data_structures::sync::Lrc; -use rustc_errors::{Applicability, Diagnostic}; -use rustc_hir as hir; -use rustc_hir::def::{CtorOf, DefKind, Namespace}; -use rustc_hir::def_id::DefId; -use rustc_infer::infer::{self, InferOk}; -use rustc_middle::ty::subst::Subst; -use rustc_middle::ty::subst::{InternalSubsts, SubstsRef}; -use rustc_middle::ty::{self, DefIdTree, GenericParamDefKind, ToPredicate, Ty, TypeVisitable}; -use rustc_span::symbol::Ident; -use rustc_span::Span; -use rustc_trait_selection::traits; -use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; - -use self::probe::{IsSuggestion, ProbeScope}; - -pub fn provide(providers: &mut ty::query::Providers) { - probe::provide(providers); -} - -#[derive(Clone, Copy, Debug)] -pub struct MethodCallee<'tcx> { - /// Impl method ID, for inherent methods, or trait method ID, otherwise. - pub def_id: DefId, - pub substs: SubstsRef<'tcx>, - - /// Instantiated method signature, i.e., it has been - /// substituted, normalized, and has had late-bound - /// lifetimes replaced with inference variables. - pub sig: ty::FnSig<'tcx>, -} - -#[derive(Debug)] -pub enum MethodError<'tcx> { - // Did not find an applicable method, but we did find various near-misses that may work. - NoMatch(NoMatchData<'tcx>), - - // Multiple methods might apply. - Ambiguity(Vec<CandidateSource>), - - // Found an applicable method, but it is not visible. The third argument contains a list of - // not-in-scope traits which may work. - PrivateMatch(DefKind, DefId, Vec<DefId>), - - // Found a `Self: Sized` bound where `Self` is a trait object, also the caller may have - // forgotten to import a trait. - IllegalSizedBound(Vec<DefId>, bool, Span), - - // Found a match, but the return type is wrong - BadReturnType, -} - -// Contains a list of static methods that may apply, a list of unsatisfied trait predicates which -// could lead to matches if satisfied, and a list of not-in-scope traits which may work. -#[derive(Debug)] -pub struct NoMatchData<'tcx> { - pub static_candidates: Vec<CandidateSource>, - pub unsatisfied_predicates: - Vec<(ty::Predicate<'tcx>, Option<ty::Predicate<'tcx>>, Option<ObligationCause<'tcx>>)>, - pub out_of_scope_traits: Vec<DefId>, - pub lev_candidate: Option<ty::AssocItem>, - pub mode: probe::Mode, -} - -// A pared down enum describing just the places from which a method -// candidate can arise. Used for error reporting only. -#[derive(Copy, Clone, Debug, Eq, Ord, PartialEq, PartialOrd)] -pub enum CandidateSource { - Impl(DefId), - Trait(DefId /* trait id */), -} - -impl<'a, 'tcx> FnCtxt<'a, 'tcx> { - /// Determines whether the type `self_ty` supports a method name `method_name` or not. - #[instrument(level = "debug", skip(self))] - pub fn method_exists( - &self, - method_name: Ident, - self_ty: Ty<'tcx>, - call_expr_id: hir::HirId, - allow_private: bool, - ) -> bool { - let mode = probe::Mode::MethodCall; - match self.probe_for_name( - method_name.span, - mode, - method_name, - IsSuggestion(false), - self_ty, - call_expr_id, - ProbeScope::TraitsInScope, - ) { - Ok(..) => true, - Err(NoMatch(..)) => false, - Err(Ambiguity(..)) => true, - Err(PrivateMatch(..)) => allow_private, - Err(IllegalSizedBound(..)) => true, - Err(BadReturnType) => bug!("no return type expectations but got BadReturnType"), - } - } - - /// Adds a suggestion to call the given method to the provided diagnostic. - #[instrument(level = "debug", skip(self, err, call_expr))] - pub(crate) fn suggest_method_call( - &self, - err: &mut Diagnostic, - msg: &str, - method_name: Ident, - self_ty: Ty<'tcx>, - call_expr: &hir::Expr<'_>, - span: Option<Span>, - ) { - let params = self - .probe_for_name( - method_name.span, - probe::Mode::MethodCall, - method_name, - IsSuggestion(false), - self_ty, - call_expr.hir_id, - ProbeScope::TraitsInScope, - ) - .map(|pick| { - let sig = self.tcx.fn_sig(pick.item.def_id); - sig.inputs().skip_binder().len().saturating_sub(1) - }) - .unwrap_or(0); - - // Account for `foo.bar<T>`; - let sugg_span = span.unwrap_or(call_expr.span).shrink_to_hi(); - let (suggestion, applicability) = ( - format!("({})", (0..params).map(|_| "_").collect::<Vec<_>>().join(", ")), - if params > 0 { Applicability::HasPlaceholders } else { Applicability::MaybeIncorrect }, - ); - - err.span_suggestion_verbose(sugg_span, msg, suggestion, applicability); - } - - /// Performs method lookup. If lookup is successful, it will return the callee - /// and store an appropriate adjustment for the self-expr. In some cases it may - /// report an error (e.g., invoking the `drop` method). - /// - /// # Arguments - /// - /// Given a method call like `foo.bar::<T1,...Tn>(a, b + 1, ...)`: - /// - /// * `self`: the surrounding `FnCtxt` (!) - /// * `self_ty`: the (unadjusted) type of the self expression (`foo`) - /// * `segment`: the name and generic arguments of the method (`bar::<T1, ...Tn>`) - /// * `span`: the span for the method call - /// * `call_expr`: the complete method call: (`foo.bar::<T1,...Tn>(...)`) - /// * `self_expr`: the self expression (`foo`) - /// * `args`: the expressions of the arguments (`a, b + 1, ...`) - #[instrument(level = "debug", skip(self))] - pub fn lookup_method( - &self, - self_ty: Ty<'tcx>, - segment: &hir::PathSegment<'_>, - span: Span, - call_expr: &'tcx hir::Expr<'tcx>, - self_expr: &'tcx hir::Expr<'tcx>, - args: &'tcx [hir::Expr<'tcx>], - ) -> Result<MethodCallee<'tcx>, MethodError<'tcx>> { - let pick = - self.lookup_probe(span, segment.ident, self_ty, call_expr, ProbeScope::TraitsInScope)?; - - self.lint_dot_call_from_2018(self_ty, segment, span, call_expr, self_expr, &pick, args); - - for import_id in &pick.import_ids { - debug!("used_trait_import: {:?}", import_id); - Lrc::get_mut(&mut self.typeck_results.borrow_mut().used_trait_imports) - .unwrap() - .insert(*import_id); - } - - self.tcx.check_stability(pick.item.def_id, Some(call_expr.hir_id), span, None); - - let result = - self.confirm_method(span, self_expr, call_expr, self_ty, pick.clone(), segment); - debug!("result = {:?}", result); - - if let Some(span) = result.illegal_sized_bound { - let mut needs_mut = false; - if let ty::Ref(region, t_type, mutability) = self_ty.kind() { - let trait_type = self - .tcx - .mk_ref(*region, ty::TypeAndMut { ty: *t_type, mutbl: mutability.invert() }); - // We probe again to see if there might be a borrow mutability discrepancy. - match self.lookup_probe( - span, - segment.ident, - trait_type, - call_expr, - ProbeScope::TraitsInScope, - ) { - Ok(ref new_pick) if *new_pick != pick => { - needs_mut = true; - } - _ => {} - } - } - - // We probe again, taking all traits into account (not only those in scope). - let mut candidates = match self.lookup_probe( - span, - segment.ident, - self_ty, - call_expr, - ProbeScope::AllTraits, - ) { - // If we find a different result the caller probably forgot to import a trait. - Ok(ref new_pick) if *new_pick != pick => vec![new_pick.item.container_id(self.tcx)], - Err(Ambiguity(ref sources)) => sources - .iter() - .filter_map(|source| { - match *source { - // Note: this cannot come from an inherent impl, - // because the first probing succeeded. - CandidateSource::Impl(def) => self.tcx.trait_id_of_impl(def), - CandidateSource::Trait(_) => None, - } - }) - .collect(), - _ => Vec::new(), - }; - candidates.retain(|candidate| *candidate != self.tcx.parent(result.callee.def_id)); - - return Err(IllegalSizedBound(candidates, needs_mut, span)); - } - - Ok(result.callee) - } - - #[instrument(level = "debug", skip(self, call_expr))] - pub fn lookup_probe( - &self, - span: Span, - method_name: Ident, - self_ty: Ty<'tcx>, - call_expr: &'tcx hir::Expr<'tcx>, - scope: ProbeScope, - ) -> probe::PickResult<'tcx> { - let mode = probe::Mode::MethodCall; - let self_ty = self.resolve_vars_if_possible(self_ty); - self.probe_for_name( - span, - mode, - method_name, - IsSuggestion(false), - self_ty, - call_expr.hir_id, - scope, - ) - } - - pub(super) fn obligation_for_method( - &self, - span: Span, - trait_def_id: DefId, - self_ty: Ty<'tcx>, - opt_input_types: Option<&[Ty<'tcx>]>, - ) -> (traits::Obligation<'tcx, ty::Predicate<'tcx>>, &'tcx ty::List<ty::subst::GenericArg<'tcx>>) - { - // Construct a trait-reference `self_ty : Trait<input_tys>` - let substs = InternalSubsts::for_item(self.tcx, trait_def_id, |param, _| { - match param.kind { - GenericParamDefKind::Lifetime | GenericParamDefKind::Const { .. } => {} - GenericParamDefKind::Type { .. } => { - if param.index == 0 { - return self_ty.into(); - } else if let Some(input_types) = opt_input_types { - return input_types[param.index as usize - 1].into(); - } - } - } - self.var_for_def(span, param) - }); - - let trait_ref = ty::TraitRef::new(trait_def_id, substs); - - // Construct an obligation - let poly_trait_ref = ty::Binder::dummy(trait_ref); - ( - traits::Obligation::misc( - span, - self.body_id, - self.param_env, - poly_trait_ref.without_const().to_predicate(self.tcx), - ), - substs, - ) - } - - pub(super) fn obligation_for_op_method( - &self, - span: Span, - trait_def_id: DefId, - self_ty: Ty<'tcx>, - opt_input_type: Option<Ty<'tcx>>, - opt_input_expr: Option<&'tcx hir::Expr<'tcx>>, - expected: Expectation<'tcx>, - ) -> (traits::Obligation<'tcx, ty::Predicate<'tcx>>, &'tcx ty::List<ty::subst::GenericArg<'tcx>>) - { - // Construct a trait-reference `self_ty : Trait<input_tys>` - let substs = InternalSubsts::for_item(self.tcx, trait_def_id, |param, _| { - match param.kind { - GenericParamDefKind::Lifetime | GenericParamDefKind::Const { .. } => {} - GenericParamDefKind::Type { .. } => { - if param.index == 0 { - return self_ty.into(); - } else if let Some(input_type) = opt_input_type { - return input_type.into(); - } - } - } - self.var_for_def(span, param) - }); - - let trait_ref = ty::TraitRef::new(trait_def_id, substs); - - // Construct an obligation - let poly_trait_ref = ty::Binder::dummy(trait_ref); - let output_ty = expected.only_has_type(self).and_then(|ty| (!ty.needs_infer()).then(|| ty)); - - ( - traits::Obligation::new( - traits::ObligationCause::new( - span, - self.body_id, - traits::BinOp { - rhs_span: opt_input_expr.map(|expr| expr.span), - is_lit: opt_input_expr - .map_or(false, |expr| matches!(expr.kind, hir::ExprKind::Lit(_))), - output_ty, - }, - ), - self.param_env, - poly_trait_ref.without_const().to_predicate(self.tcx), - ), - substs, - ) - } - - /// `lookup_method_in_trait` is used for overloaded operators. - /// It does a very narrow slice of what the normal probe/confirm path does. - /// In particular, it doesn't really do any probing: it simply constructs - /// an obligation for a particular trait with the given self type and checks - /// whether that trait is implemented. - #[instrument(level = "debug", skip(self, span))] - pub(super) fn lookup_method_in_trait( - &self, - span: Span, - m_name: Ident, - trait_def_id: DefId, - self_ty: Ty<'tcx>, - opt_input_types: Option<&[Ty<'tcx>]>, - ) -> Option<InferOk<'tcx, MethodCallee<'tcx>>> { - let (obligation, substs) = - self.obligation_for_method(span, trait_def_id, self_ty, opt_input_types); - self.construct_obligation_for_trait( - span, - m_name, - trait_def_id, - obligation, - substs, - None, - false, - ) - } - - pub(super) fn lookup_op_method_in_trait( - &self, - span: Span, - m_name: Ident, - trait_def_id: DefId, - self_ty: Ty<'tcx>, - opt_input_type: Option<Ty<'tcx>>, - opt_input_expr: Option<&'tcx hir::Expr<'tcx>>, - expected: Expectation<'tcx>, - ) -> Option<InferOk<'tcx, MethodCallee<'tcx>>> { - let (obligation, substs) = self.obligation_for_op_method( - span, - trait_def_id, - self_ty, - opt_input_type, - opt_input_expr, - expected, - ); - self.construct_obligation_for_trait( - span, - m_name, - trait_def_id, - obligation, - substs, - opt_input_expr, - true, - ) - } - - // FIXME(#18741): it seems likely that we can consolidate some of this - // code with the other method-lookup code. In particular, the second half - // of this method is basically the same as confirmation. - fn construct_obligation_for_trait( - &self, - span: Span, - m_name: Ident, - trait_def_id: DefId, - obligation: traits::PredicateObligation<'tcx>, - substs: &'tcx ty::List<ty::subst::GenericArg<'tcx>>, - opt_input_expr: Option<&'tcx hir::Expr<'tcx>>, - is_op: bool, - ) -> Option<InferOk<'tcx, MethodCallee<'tcx>>> { - debug!(?obligation); - - // Now we want to know if this can be matched - if !self.predicate_may_hold(&obligation) { - debug!("--> Cannot match obligation"); - // Cannot be matched, no such method resolution is possible. - return None; - } - - // Trait must have a method named `m_name` and it should not have - // type parameters or early-bound regions. - let tcx = self.tcx; - let Some(method_item) = self.associated_value(trait_def_id, m_name) else { - tcx.sess.delay_span_bug( - span, - "operator trait does not have corresponding operator method", - ); - return None; - }; - let def_id = method_item.def_id; - let generics = tcx.generics_of(def_id); - assert_eq!(generics.params.len(), 0); - - debug!("lookup_in_trait_adjusted: method_item={:?}", method_item); - let mut obligations = vec![]; - - // Instantiate late-bound regions and substitute the trait - // parameters into the method type to get the actual method type. - // - // N.B., instantiate late-bound regions first so that - // `instantiate_type_scheme` can normalize associated types that - // may reference those regions. - let fn_sig = tcx.bound_fn_sig(def_id); - let fn_sig = fn_sig.subst(self.tcx, substs); - let fn_sig = self.replace_bound_vars_with_fresh_vars(span, infer::FnCall, fn_sig); - - let InferOk { value, obligations: o } = if is_op { - self.normalize_op_associated_types_in_as_infer_ok(span, fn_sig, opt_input_expr) - } else { - self.normalize_associated_types_in_as_infer_ok(span, fn_sig) - }; - let fn_sig = { - obligations.extend(o); - value - }; - - // Register obligations for the parameters. This will include the - // `Self` parameter, which in turn has a bound of the main trait, - // so this also effectively registers `obligation` as well. (We - // used to register `obligation` explicitly, but that resulted in - // double error messages being reported.) - // - // Note that as the method comes from a trait, it should not have - // any late-bound regions appearing in its bounds. - let bounds = self.tcx.predicates_of(def_id).instantiate(self.tcx, substs); - - let InferOk { value, obligations: o } = if is_op { - self.normalize_op_associated_types_in_as_infer_ok(span, bounds, opt_input_expr) - } else { - self.normalize_associated_types_in_as_infer_ok(span, bounds) - }; - let bounds = { - obligations.extend(o); - value - }; - - assert!(!bounds.has_escaping_bound_vars()); - - let cause = if is_op { - ObligationCause::new( - span, - self.body_id, - traits::BinOp { - rhs_span: opt_input_expr.map(|expr| expr.span), - is_lit: opt_input_expr - .map_or(false, |expr| matches!(expr.kind, hir::ExprKind::Lit(_))), - output_ty: None, - }, - ) - } else { - traits::ObligationCause::misc(span, self.body_id) - }; - let predicates_cause = cause.clone(); - obligations.extend(traits::predicates_for_generics( - move |_, _| predicates_cause.clone(), - self.param_env, - bounds, - )); - - // Also add an obligation for the method type being well-formed. - let method_ty = tcx.mk_fn_ptr(ty::Binder::dummy(fn_sig)); - debug!( - "lookup_in_trait_adjusted: matched method method_ty={:?} obligation={:?}", - method_ty, obligation - ); - obligations.push(traits::Obligation::new( - cause, - self.param_env, - ty::Binder::dummy(ty::PredicateKind::WellFormed(method_ty.into())).to_predicate(tcx), - )); - - let callee = MethodCallee { def_id, substs, sig: fn_sig }; - - debug!("callee = {:?}", callee); - - Some(InferOk { obligations, value: callee }) - } - - /// Performs a [full-qualified function call] (formerly "universal function call") lookup. If - /// lookup is successful, it will return the type of definition and the [`DefId`] of the found - /// function definition. - /// - /// [full-qualified function call]: https://doc.rust-lang.org/reference/expressions/call-expr.html#disambiguating-function-calls - /// - /// # Arguments - /// - /// Given a function call like `Foo::bar::<T1,...Tn>(...)`: - /// - /// * `self`: the surrounding `FnCtxt` (!) - /// * `span`: the span of the call, excluding arguments (`Foo::bar::<T1, ...Tn>`) - /// * `method_name`: the identifier of the function within the container type (`bar`) - /// * `self_ty`: the type to search within (`Foo`) - /// * `self_ty_span` the span for the type being searched within (span of `Foo`) - /// * `expr_id`: the [`hir::HirId`] of the expression composing the entire call - #[instrument(level = "debug", skip(self), ret)] - pub fn resolve_fully_qualified_call( - &self, - span: Span, - method_name: Ident, - self_ty: Ty<'tcx>, - self_ty_span: Span, - expr_id: hir::HirId, - ) -> Result<(DefKind, DefId), MethodError<'tcx>> { - let tcx = self.tcx; - - // Check if we have an enum variant. - if let ty::Adt(adt_def, _) = self_ty.kind() { - if adt_def.is_enum() { - let variant_def = adt_def - .variants() - .iter() - .find(|vd| tcx.hygienic_eq(method_name, vd.ident(tcx), adt_def.did())); - if let Some(variant_def) = variant_def { - // Braced variants generate unusable names in value namespace (reserved for - // possible future use), so variants resolved as associated items may refer to - // them as well. It's ok to use the variant's id as a ctor id since an - // error will be reported on any use of such resolution anyway. - let ctor_def_id = variant_def.ctor_def_id.unwrap_or(variant_def.def_id); - tcx.check_stability(ctor_def_id, Some(expr_id), span, Some(method_name.span)); - return Ok(( - DefKind::Ctor(CtorOf::Variant, variant_def.ctor_kind), - ctor_def_id, - )); - } - } - } - - let pick = self.probe_for_name( - span, - probe::Mode::Path, - method_name, - IsSuggestion(false), - self_ty, - expr_id, - ProbeScope::TraitsInScope, - )?; - - self.lint_fully_qualified_call_from_2018( - span, - method_name, - self_ty, - self_ty_span, - expr_id, - &pick, - ); - - debug!(?pick); - { - let mut typeck_results = self.typeck_results.borrow_mut(); - let used_trait_imports = Lrc::get_mut(&mut typeck_results.used_trait_imports).unwrap(); - for import_id in pick.import_ids { - debug!(used_trait_import=?import_id); - used_trait_imports.insert(import_id); - } - } - - let def_kind = pick.item.kind.as_def_kind(); - tcx.check_stability(pick.item.def_id, Some(expr_id), span, Some(method_name.span)); - Ok((def_kind, pick.item.def_id)) - } - - /// Finds item with name `item_name` defined in impl/trait `def_id` - /// and return it, or `None`, if no such item was defined there. - pub fn associated_value(&self, def_id: DefId, item_name: Ident) -> Option<ty::AssocItem> { - self.tcx - .associated_items(def_id) - .find_by_name_and_namespace(self.tcx, item_name, Namespace::ValueNS, def_id) - .copied() - } -} diff --git a/compiler/rustc_typeck/src/check/method/prelude2021.rs b/compiler/rustc_typeck/src/check/method/prelude2021.rs deleted file mode 100644 index 392695cca..000000000 --- a/compiler/rustc_typeck/src/check/method/prelude2021.rs +++ /dev/null @@ -1,418 +0,0 @@ -use hir::def_id::DefId; -use hir::HirId; -use hir::ItemKind; -use rustc_ast::Mutability; -use rustc_errors::Applicability; -use rustc_hir as hir; -use rustc_middle::ty::subst::InternalSubsts; -use rustc_middle::ty::{Adt, Array, Ref, Ty}; -use rustc_session::lint::builtin::RUST_2021_PRELUDE_COLLISIONS; -use rustc_span::symbol::kw::{Empty, Underscore}; -use rustc_span::symbol::{sym, Ident}; -use rustc_span::Span; -use rustc_trait_selection::infer::InferCtxtExt; - -use crate::check::{ - method::probe::{self, Pick}, - FnCtxt, -}; - -impl<'a, 'tcx> FnCtxt<'a, 'tcx> { - pub(super) fn lint_dot_call_from_2018( - &self, - self_ty: Ty<'tcx>, - segment: &hir::PathSegment<'_>, - span: Span, - call_expr: &'tcx hir::Expr<'tcx>, - self_expr: &'tcx hir::Expr<'tcx>, - pick: &Pick<'tcx>, - args: &'tcx [hir::Expr<'tcx>], - ) { - debug!( - "lookup(method_name={}, self_ty={:?}, call_expr={:?}, self_expr={:?})", - segment.ident, self_ty, call_expr, self_expr - ); - - // Rust 2021 and later is already using the new prelude - if span.rust_2021() { - return; - } - - let prelude_or_array_lint = match segment.ident.name { - // `try_into` was added to the prelude in Rust 2021. - sym::try_into => RUST_2021_PRELUDE_COLLISIONS, - // `into_iter` wasn't added to the prelude, - // but `[T; N].into_iter()` doesn't resolve to IntoIterator::into_iter - // before Rust 2021, which results in the same problem. - // It is only a problem for arrays. - sym::into_iter if let Array(..) = self_ty.kind() => { - // In this case, it wasn't really a prelude addition that was the problem. - // Instead, the problem is that the array-into_iter hack will no longer apply in Rust 2021. - rustc_lint::ARRAY_INTO_ITER - } - _ => return, - }; - - // No need to lint if method came from std/core, as that will now be in the prelude - if matches!(self.tcx.crate_name(pick.item.def_id.krate), sym::std | sym::core) { - return; - } - - if matches!(pick.kind, probe::PickKind::InherentImplPick | probe::PickKind::ObjectPick) { - // avoid repeatedly adding unneeded `&*`s - if pick.autoderefs == 1 - && matches!( - pick.autoref_or_ptr_adjustment, - Some(probe::AutorefOrPtrAdjustment::Autoref { .. }) - ) - && matches!(self_ty.kind(), Ref(..)) - { - return; - } - - // if it's an inherent `self` method (not `&self` or `&mut self`), it will take - // precedence over the `TryInto` impl, and thus won't break in 2021 edition - if pick.autoderefs == 0 && pick.autoref_or_ptr_adjustment.is_none() { - return; - } - - // Inherent impls only require not relying on autoref and autoderef in order to - // ensure that the trait implementation won't be used - self.tcx.struct_span_lint_hir( - prelude_or_array_lint, - self_expr.hir_id, - self_expr.span, - |lint| { - let sp = self_expr.span; - - let mut lint = lint.build(&format!( - "trait method `{}` will become ambiguous in Rust 2021", - segment.ident.name - )); - - let derefs = "*".repeat(pick.autoderefs); - - let autoref = match pick.autoref_or_ptr_adjustment { - Some(probe::AutorefOrPtrAdjustment::Autoref { - mutbl: Mutability::Mut, - .. - }) => "&mut ", - Some(probe::AutorefOrPtrAdjustment::Autoref { - mutbl: Mutability::Not, - .. - }) => "&", - Some(probe::AutorefOrPtrAdjustment::ToConstPtr) | None => "", - }; - if let Ok(self_expr) = self.sess().source_map().span_to_snippet(self_expr.span) - { - let self_adjusted = if let Some(probe::AutorefOrPtrAdjustment::ToConstPtr) = - pick.autoref_or_ptr_adjustment - { - format!("{}{} as *const _", derefs, self_expr) - } else { - format!("{}{}{}", autoref, derefs, self_expr) - }; - - lint.span_suggestion( - sp, - "disambiguate the method call", - format!("({})", self_adjusted), - Applicability::MachineApplicable, - ); - } else { - let self_adjusted = if let Some(probe::AutorefOrPtrAdjustment::ToConstPtr) = - pick.autoref_or_ptr_adjustment - { - format!("{}(...) as *const _", derefs) - } else { - format!("{}{}...", autoref, derefs) - }; - lint.span_help( - sp, - &format!("disambiguate the method call with `({})`", self_adjusted,), - ); - } - - lint.emit(); - }, - ); - } else { - // trait implementations require full disambiguation to not clash with the new prelude - // additions (i.e. convert from dot-call to fully-qualified call) - self.tcx.struct_span_lint_hir( - prelude_or_array_lint, - call_expr.hir_id, - call_expr.span, - |lint| { - let sp = call_expr.span; - let trait_name = self.trait_path_or_bare_name( - span, - call_expr.hir_id, - pick.item.container_id(self.tcx), - ); - - let mut lint = lint.build(&format!( - "trait method `{}` will become ambiguous in Rust 2021", - segment.ident.name - )); - - let (self_adjusted, precise) = self.adjust_expr(pick, self_expr, sp); - if precise { - let args = args - .iter() - .map(|arg| { - let span = arg.span.find_ancestor_inside(sp).unwrap_or_default(); - format!( - ", {}", - self.sess().source_map().span_to_snippet(span).unwrap() - ) - }) - .collect::<String>(); - - lint.span_suggestion( - sp, - "disambiguate the associated function", - format!( - "{}::{}{}({}{})", - trait_name, - segment.ident.name, - if let Some(args) = segment.args.as_ref().and_then(|args| self - .sess() - .source_map() - .span_to_snippet(args.span_ext) - .ok()) - { - // Keep turbofish. - format!("::{}", args) - } else { - String::new() - }, - self_adjusted, - args, - ), - Applicability::MachineApplicable, - ); - } else { - lint.span_help( - sp, - &format!( - "disambiguate the associated function with `{}::{}(...)`", - trait_name, segment.ident, - ), - ); - } - - lint.emit(); - }, - ); - } - } - - pub(super) fn lint_fully_qualified_call_from_2018( - &self, - span: Span, - method_name: Ident, - self_ty: Ty<'tcx>, - self_ty_span: Span, - expr_id: hir::HirId, - pick: &Pick<'tcx>, - ) { - // Rust 2021 and later is already using the new prelude - if span.rust_2021() { - return; - } - - // These are the fully qualified methods added to prelude in Rust 2021 - if !matches!(method_name.name, sym::try_into | sym::try_from | sym::from_iter) { - return; - } - - // No need to lint if method came from std/core, as that will now be in the prelude - if matches!(self.tcx.crate_name(pick.item.def_id.krate), sym::std | sym::core) { - return; - } - - // For from_iter, check if the type actually implements FromIterator. - // If we know it does not, we don't need to warn. - if method_name.name == sym::from_iter { - if let Some(trait_def_id) = self.tcx.get_diagnostic_item(sym::FromIterator) { - if !self - .infcx - .type_implements_trait( - trait_def_id, - self_ty, - InternalSubsts::empty(), - self.param_env, - ) - .may_apply() - { - return; - } - } - } - - // No need to lint if this is an inherent method called on a specific type, like `Vec::foo(...)`, - // since such methods take precedence over trait methods. - if matches!(pick.kind, probe::PickKind::InherentImplPick) { - return; - } - - self.tcx.struct_span_lint_hir(RUST_2021_PRELUDE_COLLISIONS, expr_id, span, |lint| { - // "type" refers to either a type or, more likely, a trait from which - // the associated function or method is from. - let container_id = pick.item.container_id(self.tcx); - let trait_path = self.trait_path_or_bare_name(span, expr_id, container_id); - let trait_generics = self.tcx.generics_of(container_id); - - let trait_name = - if trait_generics.params.len() <= trait_generics.has_self as usize { - trait_path - } else { - let counts = trait_generics.own_counts(); - format!( - "{}<{}>", - trait_path, - std::iter::repeat("'_") - .take(counts.lifetimes) - .chain(std::iter::repeat("_").take( - counts.types + counts.consts - trait_generics.has_self as usize - )) - .collect::<Vec<_>>() - .join(", ") - ) - }; - - let mut lint = lint.build(&format!( - "trait-associated function `{}` will become ambiguous in Rust 2021", - method_name.name - )); - - let mut self_ty_name = self_ty_span - .find_ancestor_inside(span) - .and_then(|span| self.sess().source_map().span_to_snippet(span).ok()) - .unwrap_or_else(|| self_ty.to_string()); - - // Get the number of generics the self type has (if an Adt) unless we can determine that - // the user has written the self type with generics already which we (naively) do by looking - // for a "<" in `self_ty_name`. - if !self_ty_name.contains('<') { - if let Adt(def, _) = self_ty.kind() { - let generics = self.tcx.generics_of(def.did()); - if !generics.params.is_empty() { - let counts = generics.own_counts(); - self_ty_name += &format!( - "<{}>", - std::iter::repeat("'_") - .take(counts.lifetimes) - .chain(std::iter::repeat("_").take(counts.types + counts.consts)) - .collect::<Vec<_>>() - .join(", ") - ); - } - } - } - lint.span_suggestion( - span, - "disambiguate the associated function", - format!("<{} as {}>::{}", self_ty_name, trait_name, method_name.name,), - Applicability::MachineApplicable, - ); - - lint.emit(); - }); - } - - fn trait_path_or_bare_name( - &self, - span: Span, - expr_hir_id: HirId, - trait_def_id: DefId, - ) -> String { - self.trait_path(span, expr_hir_id, trait_def_id).unwrap_or_else(|| { - let key = self.tcx.def_key(trait_def_id); - format!("{}", key.disambiguated_data.data) - }) - } - - fn trait_path(&self, span: Span, expr_hir_id: HirId, trait_def_id: DefId) -> Option<String> { - let applicable_traits = self.tcx.in_scope_traits(expr_hir_id)?; - let applicable_trait = applicable_traits.iter().find(|t| t.def_id == trait_def_id)?; - if applicable_trait.import_ids.is_empty() { - // The trait was declared within the module, we only need to use its name. - return None; - } - - let import_items: Vec<_> = applicable_trait - .import_ids - .iter() - .map(|&import_id| self.tcx.hir().expect_item(import_id)) - .collect(); - - // Find an identifier with which this trait was imported (note that `_` doesn't count). - let any_id = import_items - .iter() - .filter_map(|item| if item.ident.name != Underscore { Some(item.ident) } else { None }) - .next(); - if let Some(any_id) = any_id { - if any_id.name == Empty { - // Glob import, so just use its name. - return None; - } else { - return Some(format!("{}", any_id)); - } - } - - // All that is left is `_`! We need to use the full path. It doesn't matter which one we pick, - // so just take the first one. - match import_items[0].kind { - ItemKind::Use(path, _) => Some( - path.segments - .iter() - .map(|segment| segment.ident.to_string()) - .collect::<Vec<_>>() - .join("::"), - ), - _ => { - span_bug!(span, "unexpected item kind, expected a use: {:?}", import_items[0].kind); - } - } - } - - /// Creates a string version of the `expr` that includes explicit adjustments. - /// Returns the string and also a bool indicating whether this is a *precise* - /// suggestion. - fn adjust_expr( - &self, - pick: &Pick<'tcx>, - expr: &hir::Expr<'tcx>, - outer: Span, - ) -> (String, bool) { - let derefs = "*".repeat(pick.autoderefs); - - let autoref = match pick.autoref_or_ptr_adjustment { - Some(probe::AutorefOrPtrAdjustment::Autoref { mutbl: Mutability::Mut, .. }) => "&mut ", - Some(probe::AutorefOrPtrAdjustment::Autoref { mutbl: Mutability::Not, .. }) => "&", - Some(probe::AutorefOrPtrAdjustment::ToConstPtr) | None => "", - }; - - let (expr_text, precise) = if let Some(expr_text) = expr - .span - .find_ancestor_inside(outer) - .and_then(|span| self.sess().source_map().span_to_snippet(span).ok()) - { - (expr_text, true) - } else { - ("(..)".to_string(), false) - }; - - let adjusted_text = if let Some(probe::AutorefOrPtrAdjustment::ToConstPtr) = - pick.autoref_or_ptr_adjustment - { - format!("{}{} as *const _", derefs, expr_text) - } else { - format!("{}{}{}", autoref, derefs, expr_text) - }; - - (adjusted_text, precise) - } -} diff --git a/compiler/rustc_typeck/src/check/method/probe.rs b/compiler/rustc_typeck/src/check/method/probe.rs deleted file mode 100644 index e9f55ab34..000000000 --- a/compiler/rustc_typeck/src/check/method/probe.rs +++ /dev/null @@ -1,1927 +0,0 @@ -use super::suggest; -use super::CandidateSource; -use super::MethodError; -use super::NoMatchData; - -use crate::check::FnCtxt; -use crate::errors::MethodCallOnUnknownType; -use crate::hir::def::DefKind; -use crate::hir::def_id::DefId; - -use rustc_data_structures::fx::FxHashSet; -use rustc_errors::Applicability; -use rustc_hir as hir; -use rustc_hir::def::Namespace; -use rustc_infer::infer::canonical::OriginalQueryValues; -use rustc_infer::infer::canonical::{Canonical, QueryResponse}; -use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; -use rustc_infer::infer::{self, InferOk, TyCtxtInferExt}; -use rustc_middle::infer::unify_key::{ConstVariableOrigin, ConstVariableOriginKind}; -use rustc_middle::middle::stability; -use rustc_middle::ty::fast_reject::{simplify_type, TreatParams}; -use rustc_middle::ty::subst::{InternalSubsts, Subst, SubstsRef}; -use rustc_middle::ty::GenericParamDefKind; -use rustc_middle::ty::{self, ParamEnvAnd, ToPredicate, Ty, TyCtxt, TypeFoldable, TypeVisitable}; -use rustc_session::lint; -use rustc_span::def_id::LocalDefId; -use rustc_span::lev_distance::{ - find_best_match_for_name_with_substrings, lev_distance_with_substrings, -}; -use rustc_span::symbol::sym; -use rustc_span::{symbol::Ident, Span, Symbol, DUMMY_SP}; -use rustc_trait_selection::autoderef::{self, Autoderef}; -use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt; -use rustc_trait_selection::traits::query::method_autoderef::MethodAutoderefBadTy; -use rustc_trait_selection::traits::query::method_autoderef::{ - CandidateStep, MethodAutoderefStepsResult, -}; -use rustc_trait_selection::traits::query::CanonicalTyGoal; -use rustc_trait_selection::traits::{self, ObligationCause}; -use std::cmp::max; -use std::iter; -use std::mem; -use std::ops::Deref; - -use smallvec::{smallvec, SmallVec}; - -use self::CandidateKind::*; -pub use self::PickKind::*; - -/// Boolean flag used to indicate if this search is for a suggestion -/// or not. If true, we can allow ambiguity and so forth. -#[derive(Clone, Copy, Debug)] -pub struct IsSuggestion(pub bool); - -struct ProbeContext<'a, 'tcx> { - fcx: &'a FnCtxt<'a, 'tcx>, - span: Span, - mode: Mode, - method_name: Option<Ident>, - return_type: Option<Ty<'tcx>>, - - /// This is the OriginalQueryValues for the steps queries - /// that are answered in steps. - orig_steps_var_values: OriginalQueryValues<'tcx>, - steps: &'tcx [CandidateStep<'tcx>], - - inherent_candidates: Vec<Candidate<'tcx>>, - extension_candidates: Vec<Candidate<'tcx>>, - impl_dups: FxHashSet<DefId>, - - /// Collects near misses when the candidate functions are missing a `self` keyword and is only - /// used for error reporting - static_candidates: Vec<CandidateSource>, - - /// When probing for names, include names that are close to the - /// requested name (by Levensthein distance) - allow_similar_names: bool, - - /// Some(candidate) if there is a private candidate - private_candidate: Option<(DefKind, DefId)>, - - /// Collects near misses when trait bounds for type parameters are unsatisfied and is only used - /// for error reporting - unsatisfied_predicates: - Vec<(ty::Predicate<'tcx>, Option<ty::Predicate<'tcx>>, Option<ObligationCause<'tcx>>)>, - - is_suggestion: IsSuggestion, - - scope_expr_id: hir::HirId, -} - -impl<'a, 'tcx> Deref for ProbeContext<'a, 'tcx> { - type Target = FnCtxt<'a, 'tcx>; - fn deref(&self) -> &Self::Target { - self.fcx - } -} - -#[derive(Debug, Clone)] -struct Candidate<'tcx> { - // Candidates are (I'm not quite sure, but they are mostly) basically - // some metadata on top of a `ty::AssocItem` (without substs). - // - // However, method probing wants to be able to evaluate the predicates - // for a function with the substs applied - for example, if a function - // has `where Self: Sized`, we don't want to consider it unless `Self` - // is actually `Sized`, and similarly, return-type suggestions want - // to consider the "actual" return type. - // - // The way this is handled is through `xform_self_ty`. It contains - // the receiver type of this candidate, but `xform_self_ty`, - // `xform_ret_ty` and `kind` (which contains the predicates) have the - // generic parameters of this candidate substituted with the *same set* - // of inference variables, which acts as some weird sort of "query". - // - // When we check out a candidate, we require `xform_self_ty` to be - // a subtype of the passed-in self-type, and this equates the type - // variables in the rest of the fields. - // - // For example, if we have this candidate: - // ``` - // trait Foo { - // fn foo(&self) where Self: Sized; - // } - // ``` - // - // Then `xform_self_ty` will be `&'erased ?X` and `kind` will contain - // the predicate `?X: Sized`, so if we are evaluating `Foo` for a - // the receiver `&T`, we'll do the subtyping which will make `?X` - // get the right value, then when we evaluate the predicate we'll check - // if `T: Sized`. - xform_self_ty: Ty<'tcx>, - xform_ret_ty: Option<Ty<'tcx>>, - item: ty::AssocItem, - kind: CandidateKind<'tcx>, - import_ids: SmallVec<[LocalDefId; 1]>, -} - -#[derive(Debug, Clone)] -enum CandidateKind<'tcx> { - InherentImplCandidate( - SubstsRef<'tcx>, - // Normalize obligations - Vec<traits::PredicateObligation<'tcx>>, - ), - ObjectCandidate, - TraitCandidate(ty::TraitRef<'tcx>), - WhereClauseCandidate( - // Trait - ty::PolyTraitRef<'tcx>, - ), -} - -#[derive(Debug, PartialEq, Eq, Copy, Clone)] -enum ProbeResult { - NoMatch, - BadReturnType, - Match, -} - -/// When adjusting a receiver we often want to do one of -/// -/// - Add a `&` (or `&mut`), converting the receiver from `T` to `&T` (or `&mut T`) -/// - If the receiver has type `*mut T`, convert it to `*const T` -/// -/// This type tells us which one to do. -/// -/// Note that in principle we could do both at the same time. For example, when the receiver has -/// type `T`, we could autoref it to `&T`, then convert to `*const T`. Or, when it has type `*mut -/// T`, we could convert it to `*const T`, then autoref to `&*const T`. However, currently we do -/// (at most) one of these. Either the receiver has type `T` and we convert it to `&T` (or with -/// `mut`), or it has type `*mut T` and we convert it to `*const T`. -#[derive(Debug, PartialEq, Copy, Clone)] -pub enum AutorefOrPtrAdjustment { - /// Receiver has type `T`, add `&` or `&mut` (it `T` is `mut`), and maybe also "unsize" it. - /// Unsizing is used to convert a `[T; N]` to `[T]`, which only makes sense when autorefing. - Autoref { - mutbl: hir::Mutability, - - /// Indicates that the source expression should be "unsized" to a target type. - /// This is special-cased for just arrays unsizing to slices. - unsize: bool, - }, - /// Receiver has type `*mut T`, convert to `*const T` - ToConstPtr, -} - -impl AutorefOrPtrAdjustment { - fn get_unsize(&self) -> bool { - match self { - AutorefOrPtrAdjustment::Autoref { mutbl: _, unsize } => *unsize, - AutorefOrPtrAdjustment::ToConstPtr => false, - } - } -} - -#[derive(Debug, PartialEq, Clone)] -pub struct Pick<'tcx> { - pub item: ty::AssocItem, - pub kind: PickKind<'tcx>, - pub import_ids: SmallVec<[LocalDefId; 1]>, - - /// Indicates that the source expression should be autoderef'd N times - /// ```ignore (not-rust) - /// A = expr | *expr | **expr | ... - /// ``` - pub autoderefs: usize, - - /// Indicates that we want to add an autoref (and maybe also unsize it), or if the receiver is - /// `*mut T`, convert it to `*const T`. - pub autoref_or_ptr_adjustment: Option<AutorefOrPtrAdjustment>, - pub self_ty: Ty<'tcx>, -} - -#[derive(Clone, Debug, PartialEq, Eq)] -pub enum PickKind<'tcx> { - InherentImplPick, - ObjectPick, - TraitPick, - WhereClausePick( - // Trait - ty::PolyTraitRef<'tcx>, - ), -} - -pub type PickResult<'tcx> = Result<Pick<'tcx>, MethodError<'tcx>>; - -#[derive(PartialEq, Eq, Copy, Clone, Debug)] -pub enum Mode { - // An expression of the form `receiver.method_name(...)`. - // Autoderefs are performed on `receiver`, lookup is done based on the - // `self` argument of the method, and static methods aren't considered. - MethodCall, - // An expression of the form `Type::item` or `<T>::item`. - // No autoderefs are performed, lookup is done based on the type each - // implementation is for, and static methods are included. - Path, -} - -#[derive(PartialEq, Eq, Copy, Clone, Debug)] -pub enum ProbeScope { - // Assemble candidates coming only from traits in scope. - TraitsInScope, - - // Assemble candidates coming from all traits. - AllTraits, -} - -impl<'a, 'tcx> FnCtxt<'a, 'tcx> { - /// This is used to offer suggestions to users. It returns methods - /// that could have been called which have the desired return - /// type. Some effort is made to rule out methods that, if called, - /// would result in an error (basically, the same criteria we - /// would use to decide if a method is a plausible fit for - /// ambiguity purposes). - #[instrument(level = "debug", skip(self))] - pub fn probe_for_return_type( - &self, - span: Span, - mode: Mode, - return_type: Ty<'tcx>, - self_ty: Ty<'tcx>, - scope_expr_id: hir::HirId, - ) -> Vec<ty::AssocItem> { - let method_names = self - .probe_op( - span, - mode, - None, - Some(return_type), - IsSuggestion(true), - self_ty, - scope_expr_id, - ProbeScope::AllTraits, - |probe_cx| Ok(probe_cx.candidate_method_names()), - ) - .unwrap_or_default(); - method_names - .iter() - .flat_map(|&method_name| { - self.probe_op( - span, - mode, - Some(method_name), - Some(return_type), - IsSuggestion(true), - self_ty, - scope_expr_id, - ProbeScope::AllTraits, - |probe_cx| probe_cx.pick(), - ) - .ok() - .map(|pick| pick.item) - }) - .collect() - } - - #[instrument(level = "debug", skip(self))] - pub fn probe_for_name( - &self, - span: Span, - mode: Mode, - item_name: Ident, - is_suggestion: IsSuggestion, - self_ty: Ty<'tcx>, - scope_expr_id: hir::HirId, - scope: ProbeScope, - ) -> PickResult<'tcx> { - self.probe_op( - span, - mode, - Some(item_name), - None, - is_suggestion, - self_ty, - scope_expr_id, - scope, - |probe_cx| probe_cx.pick(), - ) - } - - fn probe_op<OP, R>( - &'a self, - span: Span, - mode: Mode, - method_name: Option<Ident>, - return_type: Option<Ty<'tcx>>, - is_suggestion: IsSuggestion, - self_ty: Ty<'tcx>, - scope_expr_id: hir::HirId, - scope: ProbeScope, - op: OP, - ) -> Result<R, MethodError<'tcx>> - where - OP: FnOnce(ProbeContext<'a, 'tcx>) -> Result<R, MethodError<'tcx>>, - { - let mut orig_values = OriginalQueryValues::default(); - let param_env_and_self_ty = self.canonicalize_query( - ParamEnvAnd { param_env: self.param_env, value: self_ty }, - &mut orig_values, - ); - - let steps = if mode == Mode::MethodCall { - self.tcx.method_autoderef_steps(param_env_and_self_ty) - } else { - self.probe(|_| { - // Mode::Path - the deref steps is "trivial". This turns - // our CanonicalQuery into a "trivial" QueryResponse. This - // is a bit inefficient, but I don't think that writing - // special handling for this "trivial case" is a good idea. - - let infcx = &self.infcx; - let (ParamEnvAnd { param_env: _, value: self_ty }, canonical_inference_vars) = - infcx.instantiate_canonical_with_fresh_inference_vars( - span, - ¶m_env_and_self_ty, - ); - debug!( - "probe_op: Mode::Path, param_env_and_self_ty={:?} self_ty={:?}", - param_env_and_self_ty, self_ty - ); - MethodAutoderefStepsResult { - steps: infcx.tcx.arena.alloc_from_iter([CandidateStep { - self_ty: self.make_query_response_ignoring_pending_obligations( - canonical_inference_vars, - self_ty, - ), - autoderefs: 0, - from_unsafe_deref: false, - unsize: false, - }]), - opt_bad_ty: None, - reached_recursion_limit: false, - } - }) - }; - - // If our autoderef loop had reached the recursion limit, - // report an overflow error, but continue going on with - // the truncated autoderef list. - if steps.reached_recursion_limit { - self.probe(|_| { - let ty = &steps - .steps - .last() - .unwrap_or_else(|| span_bug!(span, "reached the recursion limit in 0 steps?")) - .self_ty; - let ty = self - .probe_instantiate_query_response(span, &orig_values, ty) - .unwrap_or_else(|_| span_bug!(span, "instantiating {:?} failed?", ty)); - autoderef::report_autoderef_recursion_limit_error(self.tcx, span, ty.value); - }); - } - - // If we encountered an `_` type or an error type during autoderef, this is - // ambiguous. - if let Some(bad_ty) = &steps.opt_bad_ty { - if is_suggestion.0 { - // Ambiguity was encountered during a suggestion. Just keep going. - debug!("ProbeContext: encountered ambiguity in suggestion"); - } else if bad_ty.reached_raw_pointer && !self.tcx.features().arbitrary_self_types { - // this case used to be allowed by the compiler, - // so we do a future-compat lint here for the 2015 edition - // (see https://github.com/rust-lang/rust/issues/46906) - if self.tcx.sess.rust_2018() { - self.tcx.sess.emit_err(MethodCallOnUnknownType { span }); - } else { - self.tcx.struct_span_lint_hir( - lint::builtin::TYVAR_BEHIND_RAW_POINTER, - scope_expr_id, - span, - |lint| { - lint.build("type annotations needed").emit(); - }, - ); - } - } else { - // Encountered a real ambiguity, so abort the lookup. If `ty` is not - // an `Err`, report the right "type annotations needed" error pointing - // to it. - let ty = &bad_ty.ty; - let ty = self - .probe_instantiate_query_response(span, &orig_values, ty) - .unwrap_or_else(|_| span_bug!(span, "instantiating {:?} failed?", ty)); - let ty = self.structurally_resolved_type(span, ty.value); - assert!(matches!(ty.kind(), ty::Error(_))); - return Err(MethodError::NoMatch(NoMatchData { - static_candidates: Vec::new(), - unsatisfied_predicates: Vec::new(), - out_of_scope_traits: Vec::new(), - lev_candidate: None, - mode, - })); - } - } - - debug!("ProbeContext: steps for self_ty={:?} are {:?}", self_ty, steps); - - // this creates one big transaction so that all type variables etc - // that we create during the probe process are removed later - self.probe(|_| { - let mut probe_cx = ProbeContext::new( - self, - span, - mode, - method_name, - return_type, - orig_values, - steps.steps, - is_suggestion, - scope_expr_id, - ); - - probe_cx.assemble_inherent_candidates(); - match scope { - ProbeScope::TraitsInScope => { - probe_cx.assemble_extension_candidates_for_traits_in_scope(scope_expr_id) - } - ProbeScope::AllTraits => probe_cx.assemble_extension_candidates_for_all_traits(), - }; - op(probe_cx) - }) - } -} - -pub fn provide(providers: &mut ty::query::Providers) { - providers.method_autoderef_steps = method_autoderef_steps; -} - -fn method_autoderef_steps<'tcx>( - tcx: TyCtxt<'tcx>, - goal: CanonicalTyGoal<'tcx>, -) -> MethodAutoderefStepsResult<'tcx> { - debug!("method_autoderef_steps({:?})", goal); - - tcx.infer_ctxt().enter_with_canonical(DUMMY_SP, &goal, |ref infcx, goal, inference_vars| { - let ParamEnvAnd { param_env, value: self_ty } = goal; - - let mut autoderef = - Autoderef::new(infcx, param_env, hir::CRATE_HIR_ID, DUMMY_SP, self_ty, DUMMY_SP) - .include_raw_pointers() - .silence_errors(); - let mut reached_raw_pointer = false; - let mut steps: Vec<_> = autoderef - .by_ref() - .map(|(ty, d)| { - let step = CandidateStep { - self_ty: infcx.make_query_response_ignoring_pending_obligations( - inference_vars.clone(), - ty, - ), - autoderefs: d, - from_unsafe_deref: reached_raw_pointer, - unsize: false, - }; - if let ty::RawPtr(_) = ty.kind() { - // all the subsequent steps will be from_unsafe_deref - reached_raw_pointer = true; - } - step - }) - .collect(); - - let final_ty = autoderef.final_ty(true); - let opt_bad_ty = match final_ty.kind() { - ty::Infer(ty::TyVar(_)) | ty::Error(_) => Some(MethodAutoderefBadTy { - reached_raw_pointer, - ty: infcx - .make_query_response_ignoring_pending_obligations(inference_vars, final_ty), - }), - ty::Array(elem_ty, _) => { - let dereferences = steps.len() - 1; - - steps.push(CandidateStep { - self_ty: infcx.make_query_response_ignoring_pending_obligations( - inference_vars, - infcx.tcx.mk_slice(*elem_ty), - ), - autoderefs: dereferences, - // this could be from an unsafe deref if we had - // a *mut/const [T; N] - from_unsafe_deref: reached_raw_pointer, - unsize: true, - }); - - None - } - _ => None, - }; - - debug!("method_autoderef_steps: steps={:?} opt_bad_ty={:?}", steps, opt_bad_ty); - - MethodAutoderefStepsResult { - steps: tcx.arena.alloc_from_iter(steps), - opt_bad_ty: opt_bad_ty.map(|ty| &*tcx.arena.alloc(ty)), - reached_recursion_limit: autoderef.reached_recursion_limit(), - } - }) -} - -impl<'a, 'tcx> ProbeContext<'a, 'tcx> { - fn new( - fcx: &'a FnCtxt<'a, 'tcx>, - span: Span, - mode: Mode, - method_name: Option<Ident>, - return_type: Option<Ty<'tcx>>, - orig_steps_var_values: OriginalQueryValues<'tcx>, - steps: &'tcx [CandidateStep<'tcx>], - is_suggestion: IsSuggestion, - scope_expr_id: hir::HirId, - ) -> ProbeContext<'a, 'tcx> { - ProbeContext { - fcx, - span, - mode, - method_name, - return_type, - inherent_candidates: Vec::new(), - extension_candidates: Vec::new(), - impl_dups: FxHashSet::default(), - orig_steps_var_values, - steps, - static_candidates: Vec::new(), - allow_similar_names: false, - private_candidate: None, - unsatisfied_predicates: Vec::new(), - is_suggestion, - scope_expr_id, - } - } - - fn reset(&mut self) { - self.inherent_candidates.clear(); - self.extension_candidates.clear(); - self.impl_dups.clear(); - self.static_candidates.clear(); - self.private_candidate = None; - } - - /////////////////////////////////////////////////////////////////////////// - // CANDIDATE ASSEMBLY - - fn push_candidate(&mut self, candidate: Candidate<'tcx>, is_inherent: bool) { - let is_accessible = if let Some(name) = self.method_name { - let item = candidate.item; - let def_scope = self - .tcx - .adjust_ident_and_get_scope(name, item.container_id(self.tcx), self.body_id) - .1; - item.visibility(self.tcx).is_accessible_from(def_scope, self.tcx) - } else { - true - }; - if is_accessible { - if is_inherent { - self.inherent_candidates.push(candidate); - } else { - self.extension_candidates.push(candidate); - } - } else if self.private_candidate.is_none() { - self.private_candidate = - Some((candidate.item.kind.as_def_kind(), candidate.item.def_id)); - } - } - - fn assemble_inherent_candidates(&mut self) { - for step in self.steps.iter() { - self.assemble_probe(&step.self_ty); - } - } - - fn assemble_probe(&mut self, self_ty: &Canonical<'tcx, QueryResponse<'tcx, Ty<'tcx>>>) { - debug!("assemble_probe: self_ty={:?}", self_ty); - let raw_self_ty = self_ty.value.value; - match *raw_self_ty.kind() { - ty::Dynamic(data, ..) if let Some(p) = data.principal() => { - // Subtle: we can't use `instantiate_query_response` here: using it will - // commit to all of the type equalities assumed by inference going through - // autoderef (see the `method-probe-no-guessing` test). - // - // However, in this code, it is OK if we end up with an object type that is - // "more general" than the object type that we are evaluating. For *every* - // object type `MY_OBJECT`, a function call that goes through a trait-ref - // of the form `<MY_OBJECT as SuperTraitOf(MY_OBJECT)>::func` is a valid - // `ObjectCandidate`, and it should be discoverable "exactly" through one - // of the iterations in the autoderef loop, so there is no problem with it - // being discoverable in another one of these iterations. - // - // Using `instantiate_canonical_with_fresh_inference_vars` on our - // `Canonical<QueryResponse<Ty<'tcx>>>` and then *throwing away* the - // `CanonicalVarValues` will exactly give us such a generalization - it - // will still match the original object type, but it won't pollute our - // type variables in any form, so just do that! - let (QueryResponse { value: generalized_self_ty, .. }, _ignored_var_values) = - self.fcx - .instantiate_canonical_with_fresh_inference_vars(self.span, self_ty); - - self.assemble_inherent_candidates_from_object(generalized_self_ty); - self.assemble_inherent_impl_candidates_for_type(p.def_id()); - if self.tcx.has_attr(p.def_id(), sym::rustc_has_incoherent_inherent_impls) { - self.assemble_inherent_candidates_for_incoherent_ty(raw_self_ty); - } - } - ty::Adt(def, _) => { - let def_id = def.did(); - self.assemble_inherent_impl_candidates_for_type(def_id); - if self.tcx.has_attr(def_id, sym::rustc_has_incoherent_inherent_impls) { - self.assemble_inherent_candidates_for_incoherent_ty(raw_self_ty); - } - } - ty::Foreign(did) => { - self.assemble_inherent_impl_candidates_for_type(did); - if self.tcx.has_attr(did, sym::rustc_has_incoherent_inherent_impls) { - self.assemble_inherent_candidates_for_incoherent_ty(raw_self_ty); - } - } - ty::Param(p) => { - self.assemble_inherent_candidates_from_param(p); - } - ty::Bool - | ty::Char - | ty::Int(_) - | ty::Uint(_) - | ty::Float(_) - | ty::Str - | ty::Array(..) - | ty::Slice(_) - | ty::RawPtr(_) - | ty::Ref(..) - | ty::Never - | ty::Tuple(..) => self.assemble_inherent_candidates_for_incoherent_ty(raw_self_ty), - _ => {} - } - } - - fn assemble_inherent_candidates_for_incoherent_ty(&mut self, self_ty: Ty<'tcx>) { - let Some(simp) = simplify_type(self.tcx, self_ty, TreatParams::AsInfer) else { - bug!("unexpected incoherent type: {:?}", self_ty) - }; - for &impl_def_id in self.tcx.incoherent_impls(simp) { - self.assemble_inherent_impl_probe(impl_def_id); - } - } - - fn assemble_inherent_impl_candidates_for_type(&mut self, def_id: DefId) { - let impl_def_ids = self.tcx.at(self.span).inherent_impls(def_id); - for &impl_def_id in impl_def_ids.iter() { - self.assemble_inherent_impl_probe(impl_def_id); - } - } - - fn assemble_inherent_impl_probe(&mut self, impl_def_id: DefId) { - if !self.impl_dups.insert(impl_def_id) { - return; // already visited - } - - debug!("assemble_inherent_impl_probe {:?}", impl_def_id); - - for item in self.impl_or_trait_item(impl_def_id) { - if !self.has_applicable_self(&item) { - // No receiver declared. Not a candidate. - self.record_static_candidate(CandidateSource::Impl(impl_def_id)); - continue; - } - - let (impl_ty, impl_substs) = self.impl_ty_and_substs(impl_def_id); - let impl_ty = impl_ty.subst(self.tcx, impl_substs); - - debug!("impl_ty: {:?}", impl_ty); - - // Determine the receiver type that the method itself expects. - let (xform_self_ty, xform_ret_ty) = self.xform_self_ty(&item, impl_ty, impl_substs); - debug!("xform_self_ty: {:?}, xform_ret_ty: {:?}", xform_self_ty, xform_ret_ty); - - // We can't use normalize_associated_types_in as it will pollute the - // fcx's fulfillment context after this probe is over. - // Note: we only normalize `xform_self_ty` here since the normalization - // of the return type can lead to inference results that prohibit - // valid candidates from being found, see issue #85671 - // FIXME Postponing the normalization of the return type likely only hides a deeper bug, - // which might be caused by the `param_env` itself. The clauses of the `param_env` - // maybe shouldn't include `Param`s, but rather fresh variables or be canonicalized, - // see issue #89650 - let cause = traits::ObligationCause::misc(self.span, self.body_id); - let selcx = &mut traits::SelectionContext::new(self.fcx); - let traits::Normalized { value: xform_self_ty, obligations } = - traits::normalize(selcx, self.param_env, cause, xform_self_ty); - debug!( - "assemble_inherent_impl_probe after normalization: xform_self_ty = {:?}/{:?}", - xform_self_ty, xform_ret_ty - ); - - self.push_candidate( - Candidate { - xform_self_ty, - xform_ret_ty, - item, - kind: InherentImplCandidate(impl_substs, obligations), - import_ids: smallvec![], - }, - true, - ); - } - } - - fn assemble_inherent_candidates_from_object(&mut self, self_ty: Ty<'tcx>) { - debug!("assemble_inherent_candidates_from_object(self_ty={:?})", self_ty); - - let principal = match self_ty.kind() { - ty::Dynamic(ref data, ..) => Some(data), - _ => None, - } - .and_then(|data| data.principal()) - .unwrap_or_else(|| { - span_bug!( - self.span, - "non-object {:?} in assemble_inherent_candidates_from_object", - self_ty - ) - }); - - // It is illegal to invoke a method on a trait instance that refers to - // the `Self` type. An [`ObjectSafetyViolation::SupertraitSelf`] error - // will be reported by `object_safety.rs` if the method refers to the - // `Self` type anywhere other than the receiver. Here, we use a - // substitution that replaces `Self` with the object type itself. Hence, - // a `&self` method will wind up with an argument type like `&dyn Trait`. - let trait_ref = principal.with_self_ty(self.tcx, self_ty); - self.elaborate_bounds(iter::once(trait_ref), |this, new_trait_ref, item| { - let new_trait_ref = this.erase_late_bound_regions(new_trait_ref); - - let (xform_self_ty, xform_ret_ty) = - this.xform_self_ty(&item, new_trait_ref.self_ty(), new_trait_ref.substs); - this.push_candidate( - Candidate { - xform_self_ty, - xform_ret_ty, - item, - kind: ObjectCandidate, - import_ids: smallvec![], - }, - true, - ); - }); - } - - fn assemble_inherent_candidates_from_param(&mut self, param_ty: ty::ParamTy) { - // FIXME: do we want to commit to this behavior for param bounds? - debug!("assemble_inherent_candidates_from_param(param_ty={:?})", param_ty); - - let bounds = self.param_env.caller_bounds().iter().filter_map(|predicate| { - let bound_predicate = predicate.kind(); - match bound_predicate.skip_binder() { - ty::PredicateKind::Trait(trait_predicate) => { - match *trait_predicate.trait_ref.self_ty().kind() { - ty::Param(p) if p == param_ty => { - Some(bound_predicate.rebind(trait_predicate.trait_ref)) - } - _ => None, - } - } - ty::PredicateKind::Subtype(..) - | ty::PredicateKind::Coerce(..) - | ty::PredicateKind::Projection(..) - | ty::PredicateKind::RegionOutlives(..) - | ty::PredicateKind::WellFormed(..) - | ty::PredicateKind::ObjectSafe(..) - | ty::PredicateKind::ClosureKind(..) - | ty::PredicateKind::TypeOutlives(..) - | ty::PredicateKind::ConstEvaluatable(..) - | ty::PredicateKind::ConstEquate(..) - | ty::PredicateKind::TypeWellFormedFromEnv(..) => None, - } - }); - - self.elaborate_bounds(bounds, |this, poly_trait_ref, item| { - let trait_ref = this.erase_late_bound_regions(poly_trait_ref); - - let (xform_self_ty, xform_ret_ty) = - this.xform_self_ty(&item, trait_ref.self_ty(), trait_ref.substs); - - // Because this trait derives from a where-clause, it - // should not contain any inference variables or other - // artifacts. This means it is safe to put into the - // `WhereClauseCandidate` and (eventually) into the - // `WhereClausePick`. - assert!(!trait_ref.substs.needs_infer()); - - this.push_candidate( - Candidate { - xform_self_ty, - xform_ret_ty, - item, - kind: WhereClauseCandidate(poly_trait_ref), - import_ids: smallvec![], - }, - true, - ); - }); - } - - // Do a search through a list of bounds, using a callback to actually - // create the candidates. - fn elaborate_bounds<F>( - &mut self, - bounds: impl Iterator<Item = ty::PolyTraitRef<'tcx>>, - mut mk_cand: F, - ) where - F: for<'b> FnMut(&mut ProbeContext<'b, 'tcx>, ty::PolyTraitRef<'tcx>, ty::AssocItem), - { - let tcx = self.tcx; - for bound_trait_ref in traits::transitive_bounds(tcx, bounds) { - debug!("elaborate_bounds(bound_trait_ref={:?})", bound_trait_ref); - for item in self.impl_or_trait_item(bound_trait_ref.def_id()) { - if !self.has_applicable_self(&item) { - self.record_static_candidate(CandidateSource::Trait(bound_trait_ref.def_id())); - } else { - mk_cand(self, bound_trait_ref, item); - } - } - } - } - - fn assemble_extension_candidates_for_traits_in_scope(&mut self, expr_hir_id: hir::HirId) { - let mut duplicates = FxHashSet::default(); - let opt_applicable_traits = self.tcx.in_scope_traits(expr_hir_id); - if let Some(applicable_traits) = opt_applicable_traits { - for trait_candidate in applicable_traits.iter() { - let trait_did = trait_candidate.def_id; - if duplicates.insert(trait_did) { - self.assemble_extension_candidates_for_trait( - &trait_candidate.import_ids, - trait_did, - ); - } - } - } - } - - fn assemble_extension_candidates_for_all_traits(&mut self) { - let mut duplicates = FxHashSet::default(); - for trait_info in suggest::all_traits(self.tcx) { - if duplicates.insert(trait_info.def_id) { - self.assemble_extension_candidates_for_trait(&smallvec![], trait_info.def_id); - } - } - } - - pub fn matches_return_type( - &self, - method: &ty::AssocItem, - self_ty: Option<Ty<'tcx>>, - expected: Ty<'tcx>, - ) -> bool { - match method.kind { - ty::AssocKind::Fn => { - let fty = self.tcx.bound_fn_sig(method.def_id); - self.probe(|_| { - let substs = self.fresh_substs_for_item(self.span, method.def_id); - let fty = fty.subst(self.tcx, substs); - let fty = - self.replace_bound_vars_with_fresh_vars(self.span, infer::FnCall, fty); - - if let Some(self_ty) = self_ty { - if self - .at(&ObligationCause::dummy(), self.param_env) - .sup(fty.inputs()[0], self_ty) - .is_err() - { - return false; - } - } - self.can_sub(self.param_env, fty.output(), expected).is_ok() - }) - } - _ => false, - } - } - - fn assemble_extension_candidates_for_trait( - &mut self, - import_ids: &SmallVec<[LocalDefId; 1]>, - trait_def_id: DefId, - ) { - debug!("assemble_extension_candidates_for_trait(trait_def_id={:?})", trait_def_id); - let trait_substs = self.fresh_item_substs(trait_def_id); - let trait_ref = ty::TraitRef::new(trait_def_id, trait_substs); - - if self.tcx.is_trait_alias(trait_def_id) { - // For trait aliases, assume all supertraits are relevant. - let bounds = iter::once(ty::Binder::dummy(trait_ref)); - self.elaborate_bounds(bounds, |this, new_trait_ref, item| { - let new_trait_ref = this.erase_late_bound_regions(new_trait_ref); - - let (xform_self_ty, xform_ret_ty) = - this.xform_self_ty(&item, new_trait_ref.self_ty(), new_trait_ref.substs); - this.push_candidate( - Candidate { - xform_self_ty, - xform_ret_ty, - item, - import_ids: import_ids.clone(), - kind: TraitCandidate(new_trait_ref), - }, - false, - ); - }); - } else { - debug_assert!(self.tcx.is_trait(trait_def_id)); - for item in self.impl_or_trait_item(trait_def_id) { - // Check whether `trait_def_id` defines a method with suitable name. - if !self.has_applicable_self(&item) { - debug!("method has inapplicable self"); - self.record_static_candidate(CandidateSource::Trait(trait_def_id)); - continue; - } - - let (xform_self_ty, xform_ret_ty) = - self.xform_self_ty(&item, trait_ref.self_ty(), trait_substs); - self.push_candidate( - Candidate { - xform_self_ty, - xform_ret_ty, - item, - import_ids: import_ids.clone(), - kind: TraitCandidate(trait_ref), - }, - false, - ); - } - } - } - - fn candidate_method_names(&self) -> Vec<Ident> { - let mut set = FxHashSet::default(); - let mut names: Vec<_> = self - .inherent_candidates - .iter() - .chain(&self.extension_candidates) - .filter(|candidate| { - if let Some(return_ty) = self.return_type { - self.matches_return_type(&candidate.item, None, return_ty) - } else { - true - } - }) - .map(|candidate| candidate.item.ident(self.tcx)) - .filter(|&name| set.insert(name)) - .collect(); - - // Sort them by the name so we have a stable result. - names.sort_by(|a, b| a.as_str().partial_cmp(b.as_str()).unwrap()); - names - } - - /////////////////////////////////////////////////////////////////////////// - // THE ACTUAL SEARCH - - fn pick(mut self) -> PickResult<'tcx> { - assert!(self.method_name.is_some()); - - if let Some(r) = self.pick_core() { - return r; - } - - debug!("pick: actual search failed, assemble diagnostics"); - - let static_candidates = mem::take(&mut self.static_candidates); - let private_candidate = self.private_candidate.take(); - let unsatisfied_predicates = mem::take(&mut self.unsatisfied_predicates); - - // things failed, so lets look at all traits, for diagnostic purposes now: - self.reset(); - - let span = self.span; - let tcx = self.tcx; - - self.assemble_extension_candidates_for_all_traits(); - - let out_of_scope_traits = match self.pick_core() { - Some(Ok(p)) => vec![p.item.container_id(self.tcx)], - //Some(Ok(p)) => p.iter().map(|p| p.item.container().id()).collect(), - Some(Err(MethodError::Ambiguity(v))) => v - .into_iter() - .map(|source| match source { - CandidateSource::Trait(id) => id, - CandidateSource::Impl(impl_id) => match tcx.trait_id_of_impl(impl_id) { - Some(id) => id, - None => span_bug!(span, "found inherent method when looking at traits"), - }, - }) - .collect(), - Some(Err(MethodError::NoMatch(NoMatchData { - out_of_scope_traits: others, .. - }))) => { - assert!(others.is_empty()); - vec![] - } - _ => vec![], - }; - - if let Some((kind, def_id)) = private_candidate { - return Err(MethodError::PrivateMatch(kind, def_id, out_of_scope_traits)); - } - let lev_candidate = self.probe_for_lev_candidate()?; - - Err(MethodError::NoMatch(NoMatchData { - static_candidates, - unsatisfied_predicates, - out_of_scope_traits, - lev_candidate, - mode: self.mode, - })) - } - - fn pick_core(&mut self) -> Option<PickResult<'tcx>> { - let mut unstable_candidates = Vec::new(); - let pick = self.pick_all_method(Some(&mut unstable_candidates)); - - // In this case unstable picking is done by `pick_method`. - if !self.tcx.sess.opts.unstable_opts.pick_stable_methods_before_any_unstable { - return pick; - } - - match pick { - // Emit a lint if there are unstable candidates alongside the stable ones. - // - // We suppress warning if we're picking the method only because it is a - // suggestion. - Some(Ok(ref p)) if !self.is_suggestion.0 && !unstable_candidates.is_empty() => { - self.emit_unstable_name_collision_hint(p, &unstable_candidates); - pick - } - Some(_) => pick, - None => self.pick_all_method(None), - } - } - - fn pick_all_method( - &mut self, - mut unstable_candidates: Option<&mut Vec<(Candidate<'tcx>, Symbol)>>, - ) -> Option<PickResult<'tcx>> { - let steps = self.steps.clone(); - steps - .iter() - .filter(|step| { - debug!("pick_all_method: step={:?}", step); - // skip types that are from a type error or that would require dereferencing - // a raw pointer - !step.self_ty.references_error() && !step.from_unsafe_deref - }) - .flat_map(|step| { - let InferOk { value: self_ty, obligations: _ } = self - .fcx - .probe_instantiate_query_response( - self.span, - &self.orig_steps_var_values, - &step.self_ty, - ) - .unwrap_or_else(|_| { - span_bug!(self.span, "{:?} was applicable but now isn't?", step.self_ty) - }); - self.pick_by_value_method(step, self_ty, unstable_candidates.as_deref_mut()) - .or_else(|| { - self.pick_autorefd_method( - step, - self_ty, - hir::Mutability::Not, - unstable_candidates.as_deref_mut(), - ) - .or_else(|| { - self.pick_autorefd_method( - step, - self_ty, - hir::Mutability::Mut, - unstable_candidates.as_deref_mut(), - ) - }) - .or_else(|| { - self.pick_const_ptr_method( - step, - self_ty, - unstable_candidates.as_deref_mut(), - ) - }) - }) - }) - .next() - } - - /// For each type `T` in the step list, this attempts to find a method where - /// the (transformed) self type is exactly `T`. We do however do one - /// transformation on the adjustment: if we are passing a region pointer in, - /// we will potentially *reborrow* it to a shorter lifetime. This allows us - /// to transparently pass `&mut` pointers, in particular, without consuming - /// them for their entire lifetime. - fn pick_by_value_method( - &mut self, - step: &CandidateStep<'tcx>, - self_ty: Ty<'tcx>, - unstable_candidates: Option<&mut Vec<(Candidate<'tcx>, Symbol)>>, - ) -> Option<PickResult<'tcx>> { - if step.unsize { - return None; - } - - self.pick_method(self_ty, unstable_candidates).map(|r| { - r.map(|mut pick| { - pick.autoderefs = step.autoderefs; - - // Insert a `&*` or `&mut *` if this is a reference type: - if let ty::Ref(_, _, mutbl) = *step.self_ty.value.value.kind() { - pick.autoderefs += 1; - pick.autoref_or_ptr_adjustment = Some(AutorefOrPtrAdjustment::Autoref { - mutbl, - unsize: pick.autoref_or_ptr_adjustment.map_or(false, |a| a.get_unsize()), - }) - } - - pick - }) - }) - } - - fn pick_autorefd_method( - &mut self, - step: &CandidateStep<'tcx>, - self_ty: Ty<'tcx>, - mutbl: hir::Mutability, - unstable_candidates: Option<&mut Vec<(Candidate<'tcx>, Symbol)>>, - ) -> Option<PickResult<'tcx>> { - let tcx = self.tcx; - - // In general, during probing we erase regions. - let region = tcx.lifetimes.re_erased; - - let autoref_ty = tcx.mk_ref(region, ty::TypeAndMut { ty: self_ty, mutbl }); - self.pick_method(autoref_ty, unstable_candidates).map(|r| { - r.map(|mut pick| { - pick.autoderefs = step.autoderefs; - pick.autoref_or_ptr_adjustment = - Some(AutorefOrPtrAdjustment::Autoref { mutbl, unsize: step.unsize }); - pick - }) - }) - } - - /// If `self_ty` is `*mut T` then this picks `*const T` methods. The reason why we have a - /// special case for this is because going from `*mut T` to `*const T` with autoderefs and - /// autorefs would require dereferencing the pointer, which is not safe. - fn pick_const_ptr_method( - &mut self, - step: &CandidateStep<'tcx>, - self_ty: Ty<'tcx>, - unstable_candidates: Option<&mut Vec<(Candidate<'tcx>, Symbol)>>, - ) -> Option<PickResult<'tcx>> { - // Don't convert an unsized reference to ptr - if step.unsize { - return None; - } - - let &ty::RawPtr(ty::TypeAndMut { ty, mutbl: hir::Mutability::Mut }) = self_ty.kind() else { - return None; - }; - - let const_self_ty = ty::TypeAndMut { ty, mutbl: hir::Mutability::Not }; - let const_ptr_ty = self.tcx.mk_ptr(const_self_ty); - self.pick_method(const_ptr_ty, unstable_candidates).map(|r| { - r.map(|mut pick| { - pick.autoderefs = step.autoderefs; - pick.autoref_or_ptr_adjustment = Some(AutorefOrPtrAdjustment::ToConstPtr); - pick - }) - }) - } - - fn pick_method_with_unstable(&mut self, self_ty: Ty<'tcx>) -> Option<PickResult<'tcx>> { - debug!("pick_method_with_unstable(self_ty={})", self.ty_to_string(self_ty)); - - let mut possibly_unsatisfied_predicates = Vec::new(); - let mut unstable_candidates = Vec::new(); - - for (kind, candidates) in - &[("inherent", &self.inherent_candidates), ("extension", &self.extension_candidates)] - { - debug!("searching {} candidates", kind); - let res = self.consider_candidates( - self_ty, - candidates.iter(), - &mut possibly_unsatisfied_predicates, - Some(&mut unstable_candidates), - ); - if let Some(pick) = res { - if !self.is_suggestion.0 && !unstable_candidates.is_empty() { - if let Ok(p) = &pick { - // Emit a lint if there are unstable candidates alongside the stable ones. - // - // We suppress warning if we're picking the method only because it is a - // suggestion. - self.emit_unstable_name_collision_hint(p, &unstable_candidates); - } - } - return Some(pick); - } - } - - debug!("searching unstable candidates"); - let res = self.consider_candidates( - self_ty, - unstable_candidates.iter().map(|(c, _)| c), - &mut possibly_unsatisfied_predicates, - None, - ); - if res.is_none() { - self.unsatisfied_predicates.extend(possibly_unsatisfied_predicates); - } - res - } - - fn pick_method( - &mut self, - self_ty: Ty<'tcx>, - mut unstable_candidates: Option<&mut Vec<(Candidate<'tcx>, Symbol)>>, - ) -> Option<PickResult<'tcx>> { - if !self.tcx.sess.opts.unstable_opts.pick_stable_methods_before_any_unstable { - return self.pick_method_with_unstable(self_ty); - } - - debug!("pick_method(self_ty={})", self.ty_to_string(self_ty)); - - let mut possibly_unsatisfied_predicates = Vec::new(); - - for (kind, candidates) in - &[("inherent", &self.inherent_candidates), ("extension", &self.extension_candidates)] - { - debug!("searching {} candidates", kind); - let res = self.consider_candidates( - self_ty, - candidates.iter(), - &mut possibly_unsatisfied_predicates, - unstable_candidates.as_deref_mut(), - ); - if let Some(pick) = res { - return Some(pick); - } - } - - // `pick_method` may be called twice for the same self_ty if no stable methods - // match. Only extend once. - if unstable_candidates.is_some() { - self.unsatisfied_predicates.extend(possibly_unsatisfied_predicates); - } - None - } - - fn consider_candidates<'b, ProbesIter>( - &self, - self_ty: Ty<'tcx>, - probes: ProbesIter, - possibly_unsatisfied_predicates: &mut Vec<( - ty::Predicate<'tcx>, - Option<ty::Predicate<'tcx>>, - Option<ObligationCause<'tcx>>, - )>, - unstable_candidates: Option<&mut Vec<(Candidate<'tcx>, Symbol)>>, - ) -> Option<PickResult<'tcx>> - where - ProbesIter: Iterator<Item = &'b Candidate<'tcx>> + Clone, - 'tcx: 'b, - { - let mut applicable_candidates: Vec<_> = probes - .clone() - .map(|probe| { - (probe, self.consider_probe(self_ty, probe, possibly_unsatisfied_predicates)) - }) - .filter(|&(_, status)| status != ProbeResult::NoMatch) - .collect(); - - debug!("applicable_candidates: {:?}", applicable_candidates); - - if applicable_candidates.len() > 1 { - if let Some(pick) = - self.collapse_candidates_to_trait_pick(self_ty, &applicable_candidates) - { - return Some(Ok(pick)); - } - } - - if let Some(uc) = unstable_candidates { - applicable_candidates.retain(|&(p, _)| { - if let stability::EvalResult::Deny { feature, .. } = - self.tcx.eval_stability(p.item.def_id, None, self.span, None) - { - uc.push((p.clone(), feature)); - return false; - } - true - }); - } - - if applicable_candidates.len() > 1 { - let sources = probes.map(|p| self.candidate_source(p, self_ty)).collect(); - return Some(Err(MethodError::Ambiguity(sources))); - } - - applicable_candidates.pop().map(|(probe, status)| { - if status == ProbeResult::Match { - Ok(probe.to_unadjusted_pick(self_ty)) - } else { - Err(MethodError::BadReturnType) - } - }) - } - - fn emit_unstable_name_collision_hint( - &self, - stable_pick: &Pick<'_>, - unstable_candidates: &[(Candidate<'tcx>, Symbol)], - ) { - self.tcx.struct_span_lint_hir( - lint::builtin::UNSTABLE_NAME_COLLISIONS, - self.scope_expr_id, - self.span, - |lint| { - let def_kind = stable_pick.item.kind.as_def_kind(); - let mut diag = lint.build(&format!( - "{} {} with this name may be added to the standard library in the future", - def_kind.article(), - def_kind.descr(stable_pick.item.def_id), - )); - match (stable_pick.item.kind, stable_pick.item.container) { - (ty::AssocKind::Fn, _) => { - // FIXME: This should be a `span_suggestion` instead of `help` - // However `self.span` only - // highlights the method name, so we can't use it. Also consider reusing - // the code from `report_method_error()`. - diag.help(&format!( - "call with fully qualified syntax `{}(...)` to keep using the current \ - method", - self.tcx.def_path_str(stable_pick.item.def_id), - )); - } - (ty::AssocKind::Const, ty::AssocItemContainer::TraitContainer) => { - let def_id = stable_pick.item.container_id(self.tcx); - diag.span_suggestion( - self.span, - "use the fully qualified path to the associated const", - format!( - "<{} as {}>::{}", - stable_pick.self_ty, - self.tcx.def_path_str(def_id), - stable_pick.item.name - ), - Applicability::MachineApplicable, - ); - } - _ => {} - } - if self.tcx.sess.is_nightly_build() { - for (candidate, feature) in unstable_candidates { - diag.help(&format!( - "add `#![feature({})]` to the crate attributes to enable `{}`", - feature, - self.tcx.def_path_str(candidate.item.def_id), - )); - } - } - - diag.emit(); - }, - ); - } - - fn select_trait_candidate( - &self, - trait_ref: ty::TraitRef<'tcx>, - ) -> traits::SelectionResult<'tcx, traits::Selection<'tcx>> { - let cause = traits::ObligationCause::misc(self.span, self.body_id); - let predicate = ty::Binder::dummy(trait_ref).to_poly_trait_predicate(); - let obligation = traits::Obligation::new(cause, self.param_env, predicate); - traits::SelectionContext::new(self).select(&obligation) - } - - fn candidate_source(&self, candidate: &Candidate<'tcx>, self_ty: Ty<'tcx>) -> CandidateSource { - match candidate.kind { - InherentImplCandidate(..) => { - CandidateSource::Impl(candidate.item.container_id(self.tcx)) - } - ObjectCandidate | WhereClauseCandidate(_) => { - CandidateSource::Trait(candidate.item.container_id(self.tcx)) - } - TraitCandidate(trait_ref) => self.probe(|_| { - let _ = self - .at(&ObligationCause::dummy(), self.param_env) - .define_opaque_types(false) - .sup(candidate.xform_self_ty, self_ty); - match self.select_trait_candidate(trait_ref) { - Ok(Some(traits::ImplSource::UserDefined(ref impl_data))) => { - // If only a single impl matches, make the error message point - // to that impl. - CandidateSource::Impl(impl_data.impl_def_id) - } - _ => CandidateSource::Trait(candidate.item.container_id(self.tcx)), - } - }), - } - } - - fn consider_probe( - &self, - self_ty: Ty<'tcx>, - probe: &Candidate<'tcx>, - possibly_unsatisfied_predicates: &mut Vec<( - ty::Predicate<'tcx>, - Option<ty::Predicate<'tcx>>, - Option<ObligationCause<'tcx>>, - )>, - ) -> ProbeResult { - debug!("consider_probe: self_ty={:?} probe={:?}", self_ty, probe); - - self.probe(|_| { - // First check that the self type can be related. - let sub_obligations = match self - .at(&ObligationCause::dummy(), self.param_env) - .define_opaque_types(false) - .sup(probe.xform_self_ty, self_ty) - { - Ok(InferOk { obligations, value: () }) => obligations, - Err(err) => { - debug!("--> cannot relate self-types {:?}", err); - return ProbeResult::NoMatch; - } - }; - - let mut result = ProbeResult::Match; - let mut xform_ret_ty = probe.xform_ret_ty; - debug!(?xform_ret_ty); - - let selcx = &mut traits::SelectionContext::new(self); - let cause = traits::ObligationCause::misc(self.span, self.body_id); - - let mut parent_pred = None; - - // If so, impls may carry other conditions (e.g., where - // clauses) that must be considered. Make sure that those - // match as well (or at least may match, sometimes we - // don't have enough information to fully evaluate). - match probe.kind { - InherentImplCandidate(ref substs, ref ref_obligations) => { - // `xform_ret_ty` hasn't been normalized yet, only `xform_self_ty`, - // see the reasons mentioned in the comments in `assemble_inherent_impl_probe` - // for why this is necessary - let traits::Normalized { - value: normalized_xform_ret_ty, - obligations: normalization_obligations, - } = traits::normalize(selcx, self.param_env, cause.clone(), probe.xform_ret_ty); - xform_ret_ty = normalized_xform_ret_ty; - debug!("xform_ret_ty after normalization: {:?}", xform_ret_ty); - - // Check whether the impl imposes obligations we have to worry about. - let impl_def_id = probe.item.container_id(self.tcx); - let impl_bounds = self.tcx.predicates_of(impl_def_id); - let impl_bounds = impl_bounds.instantiate(self.tcx, substs); - let traits::Normalized { value: impl_bounds, obligations: norm_obligations } = - traits::normalize(selcx, self.param_env, cause.clone(), impl_bounds); - - // Convert the bounds into obligations. - let impl_obligations = traits::predicates_for_generics( - move |_, _| cause.clone(), - self.param_env, - impl_bounds, - ); - - let candidate_obligations = impl_obligations - .chain(norm_obligations.into_iter()) - .chain(ref_obligations.iter().cloned()) - .chain(normalization_obligations.into_iter()); - - // Evaluate those obligations to see if they might possibly hold. - for o in candidate_obligations { - let o = self.resolve_vars_if_possible(o); - if !self.predicate_may_hold(&o) { - result = ProbeResult::NoMatch; - possibly_unsatisfied_predicates.push(( - o.predicate, - None, - Some(o.cause), - )); - } - } - } - - ObjectCandidate | WhereClauseCandidate(..) => { - // These have no additional conditions to check. - } - - TraitCandidate(trait_ref) => { - if let Some(method_name) = self.method_name { - // Some trait methods are excluded for arrays before 2021. - // (`array.into_iter()` wants a slice iterator for compatibility.) - if self_ty.is_array() && !method_name.span.rust_2021() { - let trait_def = self.tcx.trait_def(trait_ref.def_id); - if trait_def.skip_array_during_method_dispatch { - return ProbeResult::NoMatch; - } - } - } - let predicate = - ty::Binder::dummy(trait_ref).without_const().to_predicate(self.tcx); - parent_pred = Some(predicate); - let obligation = traits::Obligation::new(cause, self.param_env, predicate); - if !self.predicate_may_hold(&obligation) { - result = ProbeResult::NoMatch; - if self.probe(|_| { - match self.select_trait_candidate(trait_ref) { - Err(_) => return true, - Ok(Some(impl_source)) - if !impl_source.borrow_nested_obligations().is_empty() => - { - for obligation in impl_source.borrow_nested_obligations() { - // Determine exactly which obligation wasn't met, so - // that we can give more context in the error. - if !self.predicate_may_hold(obligation) { - let nested_predicate = - self.resolve_vars_if_possible(obligation.predicate); - let predicate = - self.resolve_vars_if_possible(predicate); - let p = if predicate == nested_predicate { - // Avoid "`MyStruct: Foo` which is required by - // `MyStruct: Foo`" in E0599. - None - } else { - Some(predicate) - }; - possibly_unsatisfied_predicates.push(( - nested_predicate, - p, - Some(obligation.cause.clone()), - )); - } - } - } - _ => { - // Some nested subobligation of this predicate - // failed. - let predicate = self.resolve_vars_if_possible(predicate); - possibly_unsatisfied_predicates.push((predicate, None, None)); - } - } - false - }) { - // This candidate's primary obligation doesn't even - // select - don't bother registering anything in - // `potentially_unsatisfied_predicates`. - return ProbeResult::NoMatch; - } - } - } - } - - // Evaluate those obligations to see if they might possibly hold. - for o in sub_obligations { - let o = self.resolve_vars_if_possible(o); - if !self.predicate_may_hold(&o) { - result = ProbeResult::NoMatch; - possibly_unsatisfied_predicates.push((o.predicate, parent_pred, Some(o.cause))); - } - } - - if let ProbeResult::Match = result { - if let (Some(return_ty), Some(xform_ret_ty)) = (self.return_type, xform_ret_ty) { - let xform_ret_ty = self.resolve_vars_if_possible(xform_ret_ty); - debug!( - "comparing return_ty {:?} with xform ret ty {:?}", - return_ty, probe.xform_ret_ty - ); - if self - .at(&ObligationCause::dummy(), self.param_env) - .define_opaque_types(false) - .sup(return_ty, xform_ret_ty) - .is_err() - { - return ProbeResult::BadReturnType; - } - } - } - - result - }) - } - - /// Sometimes we get in a situation where we have multiple probes that are all impls of the - /// same trait, but we don't know which impl to use. In this case, since in all cases the - /// external interface of the method can be determined from the trait, it's ok not to decide. - /// We can basically just collapse all of the probes for various impls into one where-clause - /// probe. This will result in a pending obligation so when more type-info is available we can - /// make the final decision. - /// - /// Example (`src/test/ui/method-two-trait-defer-resolution-1.rs`): - /// - /// ```ignore (illustrative) - /// trait Foo { ... } - /// impl Foo for Vec<i32> { ... } - /// impl Foo for Vec<usize> { ... } - /// ``` - /// - /// Now imagine the receiver is `Vec<_>`. It doesn't really matter at this time which impl we - /// use, so it's ok to just commit to "using the method from the trait Foo". - fn collapse_candidates_to_trait_pick( - &self, - self_ty: Ty<'tcx>, - probes: &[(&Candidate<'tcx>, ProbeResult)], - ) -> Option<Pick<'tcx>> { - // Do all probes correspond to the same trait? - let container = probes[0].0.item.trait_container(self.tcx)?; - for (p, _) in &probes[1..] { - let p_container = p.item.trait_container(self.tcx)?; - if p_container != container { - return None; - } - } - - // FIXME: check the return type here somehow. - // If so, just use this trait and call it a day. - Some(Pick { - item: probes[0].0.item, - kind: TraitPick, - import_ids: probes[0].0.import_ids.clone(), - autoderefs: 0, - autoref_or_ptr_adjustment: None, - self_ty, - }) - } - - /// Similarly to `probe_for_return_type`, this method attempts to find the best matching - /// candidate method where the method name may have been misspelled. Similarly to other - /// Levenshtein based suggestions, we provide at most one such suggestion. - fn probe_for_lev_candidate(&mut self) -> Result<Option<ty::AssocItem>, MethodError<'tcx>> { - debug!("probing for method names similar to {:?}", self.method_name); - - let steps = self.steps.clone(); - self.probe(|_| { - let mut pcx = ProbeContext::new( - self.fcx, - self.span, - self.mode, - self.method_name, - self.return_type, - self.orig_steps_var_values.clone(), - steps, - IsSuggestion(true), - self.scope_expr_id, - ); - pcx.allow_similar_names = true; - pcx.assemble_inherent_candidates(); - - let method_names = pcx.candidate_method_names(); - pcx.allow_similar_names = false; - let applicable_close_candidates: Vec<ty::AssocItem> = method_names - .iter() - .filter_map(|&method_name| { - pcx.reset(); - pcx.method_name = Some(method_name); - pcx.assemble_inherent_candidates(); - pcx.pick_core().and_then(|pick| pick.ok()).map(|pick| pick.item) - }) - .collect(); - - if applicable_close_candidates.is_empty() { - Ok(None) - } else { - let best_name = { - let names = applicable_close_candidates - .iter() - .map(|cand| cand.name) - .collect::<Vec<Symbol>>(); - find_best_match_for_name_with_substrings( - &names, - self.method_name.unwrap().name, - None, - ) - } - .unwrap(); - Ok(applicable_close_candidates.into_iter().find(|method| method.name == best_name)) - } - }) - } - - /////////////////////////////////////////////////////////////////////////// - // MISCELLANY - fn has_applicable_self(&self, item: &ty::AssocItem) -> bool { - // "Fast track" -- check for usage of sugar when in method call - // mode. - // - // In Path mode (i.e., resolving a value like `T::next`), consider any - // associated value (i.e., methods, constants) but not types. - match self.mode { - Mode::MethodCall => item.fn_has_self_parameter, - Mode::Path => match item.kind { - ty::AssocKind::Type => false, - ty::AssocKind::Fn | ty::AssocKind::Const => true, - }, - } - // FIXME -- check for types that deref to `Self`, - // like `Rc<Self>` and so on. - // - // Note also that the current code will break if this type - // includes any of the type parameters defined on the method - // -- but this could be overcome. - } - - fn record_static_candidate(&mut self, source: CandidateSource) { - self.static_candidates.push(source); - } - - #[instrument(level = "debug", skip(self))] - fn xform_self_ty( - &self, - item: &ty::AssocItem, - impl_ty: Ty<'tcx>, - substs: SubstsRef<'tcx>, - ) -> (Ty<'tcx>, Option<Ty<'tcx>>) { - if item.kind == ty::AssocKind::Fn && self.mode == Mode::MethodCall { - let sig = self.xform_method_sig(item.def_id, substs); - (sig.inputs()[0], Some(sig.output())) - } else { - (impl_ty, None) - } - } - - #[instrument(level = "debug", skip(self))] - fn xform_method_sig(&self, method: DefId, substs: SubstsRef<'tcx>) -> ty::FnSig<'tcx> { - let fn_sig = self.tcx.bound_fn_sig(method); - debug!(?fn_sig); - - assert!(!substs.has_escaping_bound_vars()); - - // It is possible for type parameters or early-bound lifetimes - // to appear in the signature of `self`. The substitutions we - // are given do not include type/lifetime parameters for the - // method yet. So create fresh variables here for those too, - // if there are any. - let generics = self.tcx.generics_of(method); - assert_eq!(substs.len(), generics.parent_count as usize); - - let xform_fn_sig = if generics.params.is_empty() { - fn_sig.subst(self.tcx, substs) - } else { - let substs = InternalSubsts::for_item(self.tcx, method, |param, _| { - let i = param.index as usize; - if i < substs.len() { - substs[i] - } else { - match param.kind { - GenericParamDefKind::Lifetime => { - // In general, during probe we erase regions. - self.tcx.lifetimes.re_erased.into() - } - GenericParamDefKind::Type { .. } | GenericParamDefKind::Const { .. } => { - self.var_for_def(self.span, param) - } - } - } - }); - fn_sig.subst(self.tcx, substs) - }; - - self.erase_late_bound_regions(xform_fn_sig) - } - - /// Gets the type of an impl and generate substitutions with inference vars. - fn impl_ty_and_substs( - &self, - impl_def_id: DefId, - ) -> (ty::EarlyBinder<Ty<'tcx>>, SubstsRef<'tcx>) { - (self.tcx.bound_type_of(impl_def_id), self.fresh_item_substs(impl_def_id)) - } - - fn fresh_item_substs(&self, def_id: DefId) -> SubstsRef<'tcx> { - InternalSubsts::for_item(self.tcx, def_id, |param, _| match param.kind { - GenericParamDefKind::Lifetime => self.tcx.lifetimes.re_erased.into(), - GenericParamDefKind::Type { .. } => self - .next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::SubstitutionPlaceholder, - span: self.tcx.def_span(def_id), - }) - .into(), - GenericParamDefKind::Const { .. } => { - let span = self.tcx.def_span(def_id); - let origin = ConstVariableOrigin { - kind: ConstVariableOriginKind::SubstitutionPlaceholder, - span, - }; - self.next_const_var(self.tcx.type_of(param.def_id), origin).into() - } - }) - } - - /// Replaces late-bound-regions bound by `value` with `'static` using - /// `ty::erase_late_bound_regions`. - /// - /// This is only a reasonable thing to do during the *probe* phase, not the *confirm* phase, of - /// method matching. It is reasonable during the probe phase because we don't consider region - /// relationships at all. Therefore, we can just replace all the region variables with 'static - /// rather than creating fresh region variables. This is nice for two reasons: - /// - /// 1. Because the numbers of the region variables would otherwise be fairly unique to this - /// particular method call, it winds up creating fewer types overall, which helps for memory - /// usage. (Admittedly, this is a rather small effect, though measurable.) - /// - /// 2. It makes it easier to deal with higher-ranked trait bounds, because we can replace any - /// late-bound regions with 'static. Otherwise, if we were going to replace late-bound - /// regions with actual region variables as is proper, we'd have to ensure that the same - /// region got replaced with the same variable, which requires a bit more coordination - /// and/or tracking the substitution and - /// so forth. - fn erase_late_bound_regions<T>(&self, value: ty::Binder<'tcx, T>) -> T - where - T: TypeFoldable<'tcx>, - { - self.tcx.erase_late_bound_regions(value) - } - - /// Finds the method with the appropriate name (or return type, as the case may be). If - /// `allow_similar_names` is set, find methods with close-matching names. - // The length of the returned iterator is nearly always 0 or 1 and this - // method is fairly hot. - fn impl_or_trait_item(&self, def_id: DefId) -> SmallVec<[ty::AssocItem; 1]> { - if let Some(name) = self.method_name { - if self.allow_similar_names { - let max_dist = max(name.as_str().len(), 3) / 3; - self.tcx - .associated_items(def_id) - .in_definition_order() - .filter(|x| { - if x.kind.namespace() != Namespace::ValueNS { - return false; - } - match lev_distance_with_substrings(name.as_str(), x.name.as_str(), max_dist) - { - Some(d) => d > 0, - None => false, - } - }) - .copied() - .collect() - } else { - self.fcx - .associated_value(def_id, name) - .map_or_else(SmallVec::new, |x| SmallVec::from_buf([x])) - } - } else { - self.tcx.associated_items(def_id).in_definition_order().copied().collect() - } - } -} - -impl<'tcx> Candidate<'tcx> { - fn to_unadjusted_pick(&self, self_ty: Ty<'tcx>) -> Pick<'tcx> { - Pick { - item: self.item, - kind: match self.kind { - InherentImplCandidate(..) => InherentImplPick, - ObjectCandidate => ObjectPick, - TraitCandidate(_) => TraitPick, - WhereClauseCandidate(ref trait_ref) => { - // Only trait derived from where-clauses should - // appear here, so they should not contain any - // inference variables or other artifacts. This - // means they are safe to put into the - // `WhereClausePick`. - assert!( - !trait_ref.skip_binder().substs.needs_infer() - && !trait_ref.skip_binder().substs.has_placeholders() - ); - - WhereClausePick(*trait_ref) - } - }, - import_ids: self.import_ids.clone(), - autoderefs: 0, - autoref_or_ptr_adjustment: None, - self_ty, - } - } -} diff --git a/compiler/rustc_typeck/src/check/method/suggest.rs b/compiler/rustc_typeck/src/check/method/suggest.rs deleted file mode 100644 index 2d459b2cc..000000000 --- a/compiler/rustc_typeck/src/check/method/suggest.rs +++ /dev/null @@ -1,2365 +0,0 @@ -//! Give useful errors and suggestions to users when an item can't be -//! found or is otherwise invalid. - -use crate::check::FnCtxt; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_errors::{ - pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, - MultiSpan, -}; -use rustc_hir as hir; -use rustc_hir::def::DefKind; -use rustc_hir::def_id::DefId; -use rustc_hir::lang_items::LangItem; -use rustc_hir::{ExprKind, Node, QPath}; -use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; -use rustc_middle::traits::util::supertraits; -use rustc_middle::ty::fast_reject::{simplify_type, TreatParams}; -use rustc_middle::ty::print::with_crate_prefix; -use rustc_middle::ty::{self, DefIdTree, ToPredicate, Ty, TyCtxt, TypeVisitable}; -use rustc_middle::ty::{IsSuggestable, ToPolyTraitRef}; -use rustc_span::symbol::{kw, sym, Ident}; -use rustc_span::Symbol; -use rustc_span::{lev_distance, source_map, ExpnKind, FileName, MacroKind, Span}; -use rustc_trait_selection::traits::error_reporting::on_unimplemented::InferCtxtExt as _; -use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; -use rustc_trait_selection::traits::{ - FulfillmentError, Obligation, ObligationCause, ObligationCauseCode, OnUnimplementedNote, -}; - -use std::cmp::Ordering; -use std::iter; - -use super::probe::{IsSuggestion, Mode, ProbeScope}; -use super::{CandidateSource, MethodError, NoMatchData}; - -impl<'a, 'tcx> FnCtxt<'a, 'tcx> { - fn is_fn_ty(&self, ty: Ty<'tcx>, span: Span) -> bool { - let tcx = self.tcx; - match ty.kind() { - // Not all of these (e.g., unsafe fns) implement `FnOnce`, - // so we look for these beforehand. - ty::Closure(..) | ty::FnDef(..) | ty::FnPtr(_) => true, - // If it's not a simple function, look for things which implement `FnOnce`. - _ => { - let Some(fn_once) = tcx.lang_items().fn_once_trait() else { - return false; - }; - - // This conditional prevents us from asking to call errors and unresolved types. - // It might seem that we can use `predicate_must_hold_modulo_regions`, - // but since a Dummy binder is used to fill in the FnOnce trait's arguments, - // type resolution always gives a "maybe" here. - if self.autoderef(span, ty).any(|(ty, _)| { - info!("check deref {:?} error", ty); - matches!(ty.kind(), ty::Error(_) | ty::Infer(_)) - }) { - return false; - } - - self.autoderef(span, ty).any(|(ty, _)| { - info!("check deref {:?} impl FnOnce", ty); - self.probe(|_| { - let fn_once_substs = tcx.mk_substs_trait( - ty, - &[self - .next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::MiscVariable, - span, - }) - .into()], - ); - let trait_ref = ty::TraitRef::new(fn_once, fn_once_substs); - let poly_trait_ref = ty::Binder::dummy(trait_ref); - let obligation = Obligation::misc( - span, - self.body_id, - self.param_env, - poly_trait_ref.without_const().to_predicate(tcx), - ); - self.predicate_may_hold(&obligation) - }) - }) - } - } - } - - fn is_slice_ty(&self, ty: Ty<'tcx>, span: Span) -> bool { - self.autoderef(span, ty).any(|(ty, _)| matches!(ty.kind(), ty::Slice(..) | ty::Array(..))) - } - - pub fn report_method_error( - &self, - mut span: Span, - rcvr_ty: Ty<'tcx>, - item_name: Ident, - source: SelfSource<'tcx>, - error: MethodError<'tcx>, - args: Option<(&'tcx hir::Expr<'tcx>, &'tcx [hir::Expr<'tcx>])>, - ) -> Option<DiagnosticBuilder<'_, ErrorGuaranteed>> { - // Avoid suggestions when we don't know what's going on. - if rcvr_ty.references_error() { - return None; - } - - let report_candidates = |span: Span, - err: &mut Diagnostic, - mut sources: Vec<CandidateSource>, - sugg_span: Span| { - sources.sort(); - sources.dedup(); - // Dynamic limit to avoid hiding just one candidate, which is silly. - let limit = if sources.len() == 5 { 5 } else { 4 }; - - for (idx, source) in sources.iter().take(limit).enumerate() { - match *source { - CandidateSource::Impl(impl_did) => { - // Provide the best span we can. Use the item, if local to crate, else - // the impl, if local to crate (item may be defaulted), else nothing. - let Some(item) = self.associated_value(impl_did, item_name).or_else(|| { - let impl_trait_ref = self.tcx.impl_trait_ref(impl_did)?; - self.associated_value(impl_trait_ref.def_id, item_name) - }) else { - continue; - }; - - let note_span = if item.def_id.is_local() { - Some(self.tcx.def_span(item.def_id)) - } else if impl_did.is_local() { - Some(self.tcx.def_span(impl_did)) - } else { - None - }; - - let impl_ty = self.tcx.at(span).type_of(impl_did); - - let insertion = match self.tcx.impl_trait_ref(impl_did) { - None => String::new(), - Some(trait_ref) => format!( - " of the trait `{}`", - self.tcx.def_path_str(trait_ref.def_id) - ), - }; - - let (note_str, idx) = if sources.len() > 1 { - ( - format!( - "candidate #{} is defined in an impl{} for the type `{}`", - idx + 1, - insertion, - impl_ty, - ), - Some(idx + 1), - ) - } else { - ( - format!( - "the candidate is defined in an impl{} for the type `{}`", - insertion, impl_ty, - ), - None, - ) - }; - if let Some(note_span) = note_span { - // We have a span pointing to the method. Show note with snippet. - err.span_note(note_span, ¬e_str); - } else { - err.note(¬e_str); - } - if let Some(trait_ref) = self.tcx.impl_trait_ref(impl_did) { - let path = self.tcx.def_path_str(trait_ref.def_id); - - let ty = match item.kind { - ty::AssocKind::Const | ty::AssocKind::Type => rcvr_ty, - ty::AssocKind::Fn => self - .tcx - .fn_sig(item.def_id) - .inputs() - .skip_binder() - .get(0) - .filter(|ty| ty.is_region_ptr() && !rcvr_ty.is_region_ptr()) - .copied() - .unwrap_or(rcvr_ty), - }; - print_disambiguation_help( - item_name, - args, - err, - path, - ty, - item.kind, - item.def_id, - sugg_span, - idx, - self.tcx.sess.source_map(), - item.fn_has_self_parameter, - ); - } - } - CandidateSource::Trait(trait_did) => { - let Some(item) = self.associated_value(trait_did, item_name) else { continue }; - let item_span = self.tcx.def_span(item.def_id); - let idx = if sources.len() > 1 { - let msg = &format!( - "candidate #{} is defined in the trait `{}`", - idx + 1, - self.tcx.def_path_str(trait_did) - ); - err.span_note(item_span, msg); - Some(idx + 1) - } else { - let msg = &format!( - "the candidate is defined in the trait `{}`", - self.tcx.def_path_str(trait_did) - ); - err.span_note(item_span, msg); - None - }; - let path = self.tcx.def_path_str(trait_did); - print_disambiguation_help( - item_name, - args, - err, - path, - rcvr_ty, - item.kind, - item.def_id, - sugg_span, - idx, - self.tcx.sess.source_map(), - item.fn_has_self_parameter, - ); - } - } - } - if sources.len() > limit { - err.note(&format!("and {} others", sources.len() - limit)); - } - }; - - let sugg_span = if let SelfSource::MethodCall(expr) = source { - // Given `foo.bar(baz)`, `expr` is `bar`, but we want to point to the whole thing. - self.tcx.hir().expect_expr(self.tcx.hir().get_parent_node(expr.hir_id)).span - } else { - span - }; - - match error { - MethodError::NoMatch(NoMatchData { - static_candidates: static_sources, - unsatisfied_predicates, - out_of_scope_traits, - lev_candidate, - mode, - }) => { - let tcx = self.tcx; - - let actual = self.resolve_vars_if_possible(rcvr_ty); - let ty_str = self.ty_to_string(actual); - let is_method = mode == Mode::MethodCall; - let item_kind = if is_method { - "method" - } else if actual.is_enum() { - "variant or associated item" - } else { - match (item_name.as_str().chars().next(), actual.is_fresh_ty()) { - (Some(name), false) if name.is_lowercase() => "function or associated item", - (Some(_), false) => "associated item", - (Some(_), true) | (None, false) => "variant or associated item", - (None, true) => "variant", - } - }; - - if self.suggest_constraining_numerical_ty( - tcx, actual, source, span, item_kind, item_name, &ty_str, - ) { - return None; - } - - span = item_name.span; - - // Don't show generic arguments when the method can't be found in any implementation (#81576). - let mut ty_str_reported = ty_str.clone(); - if let ty::Adt(_, generics) = actual.kind() { - if generics.len() > 0 { - let mut autoderef = self.autoderef(span, actual); - let candidate_found = autoderef.any(|(ty, _)| { - if let ty::Adt(adt_deref, _) = ty.kind() { - self.tcx - .inherent_impls(adt_deref.did()) - .iter() - .filter_map(|def_id| self.associated_value(*def_id, item_name)) - .count() - >= 1 - } else { - false - } - }); - let has_deref = autoderef.step_count() > 0; - if !candidate_found && !has_deref && unsatisfied_predicates.is_empty() { - if let Some((path_string, _)) = ty_str.split_once('<') { - ty_str_reported = path_string.to_string(); - } - } - } - } - - let mut err = struct_span_err!( - tcx.sess, - span, - E0599, - "no {} named `{}` found for {} `{}` in the current scope", - item_kind, - item_name, - actual.prefix_string(self.tcx), - ty_str_reported, - ); - if actual.references_error() { - err.downgrade_to_delayed_bug(); - } - - if let Mode::MethodCall = mode && let SelfSource::MethodCall(cal) = source { - self.suggest_await_before_method( - &mut err, item_name, actual, cal, span, - ); - } - if let Some(span) = tcx.resolutions(()).confused_type_with_std_module.get(&span) { - err.span_suggestion( - span.shrink_to_lo(), - "you are looking for the module in `std`, not the primitive type", - "std::", - Applicability::MachineApplicable, - ); - } - if let ty::RawPtr(_) = &actual.kind() { - err.note( - "try using `<*const T>::as_ref()` to get a reference to the \ - type behind the pointer: https://doc.rust-lang.org/std/\ - primitive.pointer.html#method.as_ref", - ); - err.note( - "using `<*const T>::as_ref()` on a pointer which is unaligned or points \ - to invalid or uninitialized memory is undefined behavior", - ); - } - - let ty_span = match actual.kind() { - ty::Param(param_type) => { - let generics = self.tcx.generics_of(self.body_id.owner.to_def_id()); - let type_param = generics.type_param(param_type, self.tcx); - Some(self.tcx.def_span(type_param.def_id)) - } - ty::Adt(def, _) if def.did().is_local() => Some(tcx.def_span(def.did())), - _ => None, - }; - - if let Some(span) = ty_span { - err.span_label( - span, - format!( - "{item_kind} `{item_name}` not found for this {}", - actual.prefix_string(self.tcx) - ), - ); - } - - if let SelfSource::MethodCall(rcvr_expr) = source { - self.suggest_fn_call(&mut err, rcvr_expr, rcvr_ty, |output_ty| { - let call_expr = self - .tcx - .hir() - .expect_expr(self.tcx.hir().get_parent_node(rcvr_expr.hir_id)); - let probe = self.lookup_probe( - span, - item_name, - output_ty, - call_expr, - ProbeScope::AllTraits, - ); - probe.is_ok() - }); - } - - let mut custom_span_label = false; - - if !static_sources.is_empty() { - err.note( - "found the following associated functions; to be used as methods, \ - functions must have a `self` parameter", - ); - err.span_label(span, "this is an associated function, not a method"); - custom_span_label = true; - } - if static_sources.len() == 1 { - let ty_str = - if let Some(CandidateSource::Impl(impl_did)) = static_sources.get(0) { - // When the "method" is resolved through dereferencing, we really want the - // original type that has the associated function for accurate suggestions. - // (#61411) - let ty = tcx.at(span).type_of(*impl_did); - match (&ty.peel_refs().kind(), &actual.peel_refs().kind()) { - (ty::Adt(def, _), ty::Adt(def_actual, _)) if def == def_actual => { - // Use `actual` as it will have more `substs` filled in. - self.ty_to_value_string(actual.peel_refs()) - } - _ => self.ty_to_value_string(ty.peel_refs()), - } - } else { - self.ty_to_value_string(actual.peel_refs()) - }; - if let SelfSource::MethodCall(expr) = source { - err.span_suggestion( - expr.span.to(span), - "use associated function syntax instead", - format!("{}::{}", ty_str, item_name), - Applicability::MachineApplicable, - ); - } else { - err.help(&format!("try with `{}::{}`", ty_str, item_name,)); - } - - report_candidates(span, &mut err, static_sources, sugg_span); - } else if static_sources.len() > 1 { - report_candidates(span, &mut err, static_sources, sugg_span); - } - - let mut bound_spans = vec![]; - let mut restrict_type_params = false; - let mut unsatisfied_bounds = false; - if item_name.name == sym::count && self.is_slice_ty(actual, span) { - let msg = "consider using `len` instead"; - if let SelfSource::MethodCall(_expr) = source { - err.span_suggestion_short( - span, - msg, - "len", - Applicability::MachineApplicable, - ); - } else { - err.span_label(span, msg); - } - if let Some(iterator_trait) = self.tcx.get_diagnostic_item(sym::Iterator) { - let iterator_trait = self.tcx.def_path_str(iterator_trait); - err.note(&format!("`count` is defined on `{iterator_trait}`, which `{actual}` does not implement")); - } - } else if !unsatisfied_predicates.is_empty() { - let mut type_params = FxHashMap::default(); - - // Pick out the list of unimplemented traits on the receiver. - // This is used for custom error messages with the `#[rustc_on_unimplemented]` attribute. - let mut unimplemented_traits = FxHashMap::default(); - let mut unimplemented_traits_only = true; - for (predicate, _parent_pred, cause) in &unsatisfied_predicates { - if let (ty::PredicateKind::Trait(p), Some(cause)) = - (predicate.kind().skip_binder(), cause.as_ref()) - { - if p.trait_ref.self_ty() != rcvr_ty { - // This is necessary, not just to keep the errors clean, but also - // because our derived obligations can wind up with a trait ref that - // requires a different param_env to be correctly compared. - continue; - } - unimplemented_traits.entry(p.trait_ref.def_id).or_insert(( - predicate.kind().rebind(p.trait_ref), - Obligation { - cause: cause.clone(), - param_env: self.param_env, - predicate: *predicate, - recursion_depth: 0, - }, - )); - } - } - - // Make sure that, if any traits other than the found ones were involved, - // we don't don't report an unimplemented trait. - // We don't want to say that `iter::Cloned` is not an iterator, just - // because of some non-Clone item being iterated over. - for (predicate, _parent_pred, _cause) in &unsatisfied_predicates { - match predicate.kind().skip_binder() { - ty::PredicateKind::Trait(p) - if unimplemented_traits.contains_key(&p.trait_ref.def_id) => {} - _ => { - unimplemented_traits_only = false; - break; - } - } - } - - let mut collect_type_param_suggestions = - |self_ty: Ty<'tcx>, parent_pred: ty::Predicate<'tcx>, obligation: &str| { - // We don't care about regions here, so it's fine to skip the binder here. - if let (ty::Param(_), ty::PredicateKind::Trait(p)) = - (self_ty.kind(), parent_pred.kind().skip_binder()) - { - let node = match p.trait_ref.self_ty().kind() { - ty::Param(_) => { - // Account for `fn` items like in `issue-35677.rs` to - // suggest restricting its type params. - let did = self.tcx.hir().body_owner_def_id(hir::BodyId { - hir_id: self.body_id, - }); - Some( - self.tcx - .hir() - .get(self.tcx.hir().local_def_id_to_hir_id(did)), - ) - } - ty::Adt(def, _) => def.did().as_local().map(|def_id| { - self.tcx - .hir() - .get(self.tcx.hir().local_def_id_to_hir_id(def_id)) - }), - _ => None, - }; - if let Some(hir::Node::Item(hir::Item { kind, .. })) = node { - if let Some(g) = kind.generics() { - let key = ( - g.tail_span_for_predicate_suggestion(), - g.add_where_or_trailing_comma(), - ); - type_params - .entry(key) - .or_insert_with(FxHashSet::default) - .insert(obligation.to_owned()); - } - } - } - }; - let mut bound_span_label = |self_ty: Ty<'_>, obligation: &str, quiet: &str| { - let msg = format!( - "doesn't satisfy `{}`", - if obligation.len() > 50 { quiet } else { obligation } - ); - match &self_ty.kind() { - // Point at the type that couldn't satisfy the bound. - ty::Adt(def, _) => { - bound_spans.push((self.tcx.def_span(def.did()), msg)) - } - // Point at the trait object that couldn't satisfy the bound. - ty::Dynamic(preds, _, _) => { - for pred in preds.iter() { - match pred.skip_binder() { - ty::ExistentialPredicate::Trait(tr) => bound_spans - .push((self.tcx.def_span(tr.def_id), msg.clone())), - ty::ExistentialPredicate::Projection(_) - | ty::ExistentialPredicate::AutoTrait(_) => {} - } - } - } - // Point at the closure that couldn't satisfy the bound. - ty::Closure(def_id, _) => bound_spans.push(( - tcx.def_span(*def_id), - format!("doesn't satisfy `{}`", quiet), - )), - _ => {} - } - }; - let mut format_pred = |pred: ty::Predicate<'tcx>| { - let bound_predicate = pred.kind(); - match bound_predicate.skip_binder() { - ty::PredicateKind::Projection(pred) => { - let pred = bound_predicate.rebind(pred); - // `<Foo as Iterator>::Item = String`. - let projection_ty = pred.skip_binder().projection_ty; - - let substs_with_infer_self = tcx.mk_substs( - iter::once(tcx.mk_ty_var(ty::TyVid::from_u32(0)).into()) - .chain(projection_ty.substs.iter().skip(1)), - ); - - let quiet_projection_ty = ty::ProjectionTy { - substs: substs_with_infer_self, - item_def_id: projection_ty.item_def_id, - }; - - let term = pred.skip_binder().term; - - let obligation = format!("{} = {}", projection_ty, term); - let quiet = format!("{} = {}", quiet_projection_ty, term); - - bound_span_label(projection_ty.self_ty(), &obligation, &quiet); - Some((obligation, projection_ty.self_ty())) - } - ty::PredicateKind::Trait(poly_trait_ref) => { - let p = poly_trait_ref.trait_ref; - let self_ty = p.self_ty(); - let path = p.print_only_trait_path(); - let obligation = format!("{}: {}", self_ty, path); - let quiet = format!("_: {}", path); - bound_span_label(self_ty, &obligation, &quiet); - Some((obligation, self_ty)) - } - _ => None, - } - }; - - // Find all the requirements that come from a local `impl` block. - let mut skip_list: FxHashSet<_> = Default::default(); - let mut spanned_predicates: FxHashMap<MultiSpan, _> = Default::default(); - for (data, p, parent_p, impl_def_id, cause) in unsatisfied_predicates - .iter() - .filter_map(|(p, parent, c)| c.as_ref().map(|c| (p, parent, c))) - .filter_map(|(p, parent, c)| match c.code() { - ObligationCauseCode::ImplDerivedObligation(ref data) => { - Some((&data.derived, p, parent, data.impl_def_id, data)) - } - _ => None, - }) - { - let parent_trait_ref = data.parent_trait_pred; - let path = parent_trait_ref.print_modifiers_and_trait_path(); - let tr_self_ty = parent_trait_ref.skip_binder().self_ty(); - let unsatisfied_msg = "unsatisfied trait bound introduced here"; - let derive_msg = - "unsatisfied trait bound introduced in this `derive` macro"; - match self.tcx.hir().get_if_local(impl_def_id) { - // Unmet obligation comes from a `derive` macro, point at it once to - // avoid multiple span labels pointing at the same place. - Some(Node::Item(hir::Item { - kind: hir::ItemKind::Trait(..), - ident, - .. - })) if matches!( - ident.span.ctxt().outer_expn_data().kind, - ExpnKind::Macro(MacroKind::Derive, _) - ) => - { - let span = ident.span.ctxt().outer_expn_data().call_site; - let mut spans: MultiSpan = span.into(); - spans.push_span_label(span, derive_msg); - let entry = spanned_predicates.entry(spans); - entry.or_insert_with(|| (path, tr_self_ty, Vec::new())).2.push(p); - } - - Some(Node::Item(hir::Item { - kind: hir::ItemKind::Impl(hir::Impl { of_trait, self_ty, .. }), - .. - })) if matches!( - self_ty.span.ctxt().outer_expn_data().kind, - ExpnKind::Macro(MacroKind::Derive, _) - ) || matches!( - of_trait.as_ref().map(|t| t - .path - .span - .ctxt() - .outer_expn_data() - .kind), - Some(ExpnKind::Macro(MacroKind::Derive, _)) - ) => - { - let span = self_ty.span.ctxt().outer_expn_data().call_site; - let mut spans: MultiSpan = span.into(); - spans.push_span_label(span, derive_msg); - let entry = spanned_predicates.entry(spans); - entry.or_insert_with(|| (path, tr_self_ty, Vec::new())).2.push(p); - } - - // Unmet obligation coming from a `trait`. - Some(Node::Item(hir::Item { - kind: hir::ItemKind::Trait(..), - ident, - span: item_span, - .. - })) if !matches!( - ident.span.ctxt().outer_expn_data().kind, - ExpnKind::Macro(MacroKind::Derive, _) - ) => - { - if let Some(pred) = parent_p { - // Done to add the "doesn't satisfy" `span_label`. - let _ = format_pred(*pred); - } - skip_list.insert(p); - let mut spans = if cause.span != *item_span { - let mut spans: MultiSpan = cause.span.into(); - spans.push_span_label(cause.span, unsatisfied_msg); - spans - } else { - ident.span.into() - }; - spans.push_span_label(ident.span, "in this trait"); - let entry = spanned_predicates.entry(spans); - entry.or_insert_with(|| (path, tr_self_ty, Vec::new())).2.push(p); - } - - // Unmet obligation coming from an `impl`. - Some(Node::Item(hir::Item { - kind: - hir::ItemKind::Impl(hir::Impl { - of_trait, self_ty, generics, .. - }), - span: item_span, - .. - })) if !matches!( - self_ty.span.ctxt().outer_expn_data().kind, - ExpnKind::Macro(MacroKind::Derive, _) - ) && !matches!( - of_trait.as_ref().map(|t| t - .path - .span - .ctxt() - .outer_expn_data() - .kind), - Some(ExpnKind::Macro(MacroKind::Derive, _)) - ) => - { - let sized_pred = - unsatisfied_predicates.iter().any(|(pred, _, _)| { - match pred.kind().skip_binder() { - ty::PredicateKind::Trait(pred) => { - Some(pred.def_id()) - == self.tcx.lang_items().sized_trait() - && pred.polarity == ty::ImplPolarity::Positive - } - _ => false, - } - }); - for param in generics.params { - if param.span == cause.span && sized_pred { - let (sp, sugg) = match param.colon_span { - Some(sp) => (sp.shrink_to_hi(), " ?Sized +"), - None => (param.span.shrink_to_hi(), ": ?Sized"), - }; - err.span_suggestion_verbose( - sp, - "consider relaxing the type parameter's implicit \ - `Sized` bound", - sugg, - Applicability::MachineApplicable, - ); - } - } - if let Some(pred) = parent_p { - // Done to add the "doesn't satisfy" `span_label`. - let _ = format_pred(*pred); - } - skip_list.insert(p); - let mut spans = if cause.span != *item_span { - let mut spans: MultiSpan = cause.span.into(); - spans.push_span_label(cause.span, unsatisfied_msg); - spans - } else { - let mut spans = Vec::with_capacity(2); - if let Some(trait_ref) = of_trait { - spans.push(trait_ref.path.span); - } - spans.push(self_ty.span); - spans.into() - }; - if let Some(trait_ref) = of_trait { - spans.push_span_label(trait_ref.path.span, ""); - } - spans.push_span_label(self_ty.span, ""); - - let entry = spanned_predicates.entry(spans); - entry.or_insert_with(|| (path, tr_self_ty, Vec::new())).2.push(p); - } - _ => {} - } - } - let mut spanned_predicates: Vec<_> = spanned_predicates.into_iter().collect(); - spanned_predicates.sort_by_key(|(span, (_, _, _))| span.primary_span()); - for (span, (_path, _self_ty, preds)) in spanned_predicates { - let mut preds: Vec<_> = preds - .into_iter() - .filter_map(|pred| format_pred(*pred)) - .map(|(p, _)| format!("`{}`", p)) - .collect(); - preds.sort(); - preds.dedup(); - let msg = if let [pred] = &preds[..] { - format!("trait bound {} was not satisfied", pred) - } else { - format!( - "the following trait bounds were not satisfied:\n{}", - preds.join("\n"), - ) - }; - err.span_note(span, &msg); - unsatisfied_bounds = true; - } - - // The requirements that didn't have an `impl` span to show. - let mut bound_list = unsatisfied_predicates - .iter() - .filter_map(|(pred, parent_pred, _cause)| { - format_pred(*pred).map(|(p, self_ty)| { - collect_type_param_suggestions(self_ty, *pred, &p); - ( - match parent_pred { - None => format!("`{}`", &p), - Some(parent_pred) => match format_pred(*parent_pred) { - None => format!("`{}`", &p), - Some((parent_p, _)) => { - collect_type_param_suggestions( - self_ty, - *parent_pred, - &p, - ); - format!( - "`{}`\nwhich is required by `{}`", - p, parent_p - ) - } - }, - }, - *pred, - ) - }) - }) - .filter(|(_, pred)| !skip_list.contains(&pred)) - .map(|(t, _)| t) - .enumerate() - .collect::<Vec<(usize, String)>>(); - - for ((span, add_where_or_comma), obligations) in type_params.into_iter() { - restrict_type_params = true; - // #74886: Sort here so that the output is always the same. - let mut obligations = obligations.into_iter().collect::<Vec<_>>(); - obligations.sort(); - err.span_suggestion_verbose( - span, - &format!( - "consider restricting the type parameter{s} to satisfy the \ - trait bound{s}", - s = pluralize!(obligations.len()) - ), - format!("{} {}", add_where_or_comma, obligations.join(", ")), - Applicability::MaybeIncorrect, - ); - } - - bound_list.sort_by(|(_, a), (_, b)| a.cmp(b)); // Sort alphabetically. - bound_list.dedup_by(|(_, a), (_, b)| a == b); // #35677 - bound_list.sort_by_key(|(pos, _)| *pos); // Keep the original predicate order. - - if !bound_list.is_empty() || !skip_list.is_empty() { - let bound_list = bound_list - .into_iter() - .map(|(_, path)| path) - .collect::<Vec<_>>() - .join("\n"); - let actual_prefix = actual.prefix_string(self.tcx); - info!("unimplemented_traits.len() == {}", unimplemented_traits.len()); - let (primary_message, label) = - if unimplemented_traits.len() == 1 && unimplemented_traits_only { - unimplemented_traits - .into_iter() - .next() - .map(|(_, (trait_ref, obligation))| { - if trait_ref.self_ty().references_error() - || actual.references_error() - { - // Avoid crashing. - return (None, None); - } - let OnUnimplementedNote { message, label, .. } = - self.on_unimplemented_note(trait_ref, &obligation); - (message, label) - }) - .unwrap_or((None, None)) - } else { - (None, None) - }; - let primary_message = primary_message.unwrap_or_else(|| format!( - "the {item_kind} `{item_name}` exists for {actual_prefix} `{ty_str}`, but its trait bounds were not satisfied" - )); - err.set_primary_message(&primary_message); - if let Some(label) = label { - custom_span_label = true; - err.span_label(span, label); - } - if !bound_list.is_empty() { - err.note(&format!( - "the following trait bounds were not satisfied:\n{bound_list}" - )); - } - self.suggest_derive(&mut err, &unsatisfied_predicates); - - unsatisfied_bounds = true; - } - } - - let label_span_not_found = |err: &mut Diagnostic| { - if unsatisfied_predicates.is_empty() { - err.span_label(span, format!("{item_kind} not found in `{ty_str}`")); - let is_string_or_ref_str = match actual.kind() { - ty::Ref(_, ty, _) => { - ty.is_str() - || matches!( - ty.kind(), - ty::Adt(adt, _) if self.tcx.is_diagnostic_item(sym::String, adt.did()) - ) - } - ty::Adt(adt, _) => self.tcx.is_diagnostic_item(sym::String, adt.did()), - _ => false, - }; - if is_string_or_ref_str && item_name.name == sym::iter { - err.span_suggestion_verbose( - item_name.span, - "because of the in-memory representation of `&str`, to obtain \ - an `Iterator` over each of its codepoint use method `chars`", - "chars", - Applicability::MachineApplicable, - ); - } - if let ty::Adt(adt, _) = rcvr_ty.kind() { - let mut inherent_impls_candidate = self - .tcx - .inherent_impls(adt.did()) - .iter() - .copied() - .filter(|def_id| { - if let Some(assoc) = self.associated_value(*def_id, item_name) { - // Check for both mode is the same so we avoid suggesting - // incorrect associated item. - match (mode, assoc.fn_has_self_parameter, source) { - (Mode::MethodCall, true, SelfSource::MethodCall(_)) => { - // We check that the suggest type is actually - // different from the received one - // So we avoid suggestion method with Box<Self> - // for instance - self.tcx.at(span).type_of(*def_id) != actual - && self.tcx.at(span).type_of(*def_id) != rcvr_ty - } - (Mode::Path, false, _) => true, - _ => false, - } - } else { - false - } - }) - .collect::<Vec<_>>(); - if !inherent_impls_candidate.is_empty() { - inherent_impls_candidate.sort(); - inherent_impls_candidate.dedup(); - - // number of type to shows at most. - let limit = if inherent_impls_candidate.len() == 5 { 5 } else { 4 }; - let type_candidates = inherent_impls_candidate - .iter() - .take(limit) - .map(|impl_item| { - format!("- `{}`", self.tcx.at(span).type_of(*impl_item)) - }) - .collect::<Vec<_>>() - .join("\n"); - let additional_types = if inherent_impls_candidate.len() > limit { - format!( - "\nand {} more types", - inherent_impls_candidate.len() - limit - ) - } else { - "".to_string() - }; - err.note(&format!( - "the {item_kind} was found for\n{}{}", - type_candidates, additional_types - )); - } - } - } else { - err.span_label(span, format!("{item_kind} cannot be called on `{ty_str}` due to unsatisfied trait bounds")); - } - }; - - // If the method name is the name of a field with a function or closure type, - // give a helping note that it has to be called as `(x.f)(...)`. - if let SelfSource::MethodCall(expr) = source { - if !self.suggest_field_call(span, rcvr_ty, expr, item_name, &mut err) - && lev_candidate.is_none() - && !custom_span_label - { - label_span_not_found(&mut err); - } - } else if !custom_span_label { - label_span_not_found(&mut err); - } - - // Don't suggest (for example) `expr.field.method()` if `expr.method()` - // doesn't exist due to unsatisfied predicates. - if unsatisfied_predicates.is_empty() { - self.check_for_field_method(&mut err, source, span, actual, item_name); - } - - self.check_for_unwrap_self(&mut err, source, span, actual, item_name); - - bound_spans.sort(); - bound_spans.dedup(); - for (span, msg) in bound_spans.into_iter() { - err.span_label(span, &msg); - } - - if actual.is_numeric() && actual.is_fresh() || restrict_type_params { - } else { - self.suggest_traits_to_import( - &mut err, - span, - rcvr_ty, - item_name, - args.map(|(_, args)| args.len() + 1), - source, - out_of_scope_traits, - &unsatisfied_predicates, - unsatisfied_bounds, - ); - } - - // Don't emit a suggestion if we found an actual method - // that had unsatisfied trait bounds - if unsatisfied_predicates.is_empty() && actual.is_enum() { - let adt_def = actual.ty_adt_def().expect("enum is not an ADT"); - if let Some(suggestion) = lev_distance::find_best_match_for_name( - &adt_def.variants().iter().map(|s| s.name).collect::<Vec<_>>(), - item_name.name, - None, - ) { - err.span_suggestion( - span, - "there is a variant with a similar name", - suggestion, - Applicability::MaybeIncorrect, - ); - } - } - - if item_name.name == sym::as_str && actual.peel_refs().is_str() { - let msg = "remove this method call"; - let mut fallback_span = true; - if let SelfSource::MethodCall(expr) = source { - let call_expr = - self.tcx.hir().expect_expr(self.tcx.hir().get_parent_node(expr.hir_id)); - if let Some(span) = call_expr.span.trim_start(expr.span) { - err.span_suggestion(span, msg, "", Applicability::MachineApplicable); - fallback_span = false; - } - } - if fallback_span { - err.span_label(span, msg); - } - } else if let Some(lev_candidate) = lev_candidate { - // Don't emit a suggestion if we found an actual method - // that had unsatisfied trait bounds - if unsatisfied_predicates.is_empty() { - let def_kind = lev_candidate.kind.as_def_kind(); - // Methods are defined within the context of a struct and their first parameter is always self, - // which represents the instance of the struct the method is being called on - // Associated functions don’t take self as a parameter and - // they are not methods because they don’t have an instance of the struct to work with. - if def_kind == DefKind::AssocFn && lev_candidate.fn_has_self_parameter { - err.span_suggestion( - span, - &format!("there is a method with a similar name",), - lev_candidate.name, - Applicability::MaybeIncorrect, - ); - } else { - err.span_suggestion( - span, - &format!( - "there is {} {} with a similar name", - def_kind.article(), - def_kind.descr(lev_candidate.def_id), - ), - lev_candidate.name, - Applicability::MaybeIncorrect, - ); - } - } - } - - self.check_for_deref_method(&mut err, source, rcvr_ty, item_name); - - return Some(err); - } - - MethodError::Ambiguity(sources) => { - let mut err = struct_span_err!( - self.sess(), - item_name.span, - E0034, - "multiple applicable items in scope" - ); - err.span_label(item_name.span, format!("multiple `{}` found", item_name)); - - report_candidates(span, &mut err, sources, sugg_span); - err.emit(); - } - - MethodError::PrivateMatch(kind, def_id, out_of_scope_traits) => { - let kind = kind.descr(def_id); - let mut err = struct_span_err!( - self.tcx.sess, - item_name.span, - E0624, - "{} `{}` is private", - kind, - item_name - ); - err.span_label(item_name.span, &format!("private {}", kind)); - let sp = self - .tcx - .hir() - .span_if_local(def_id) - .unwrap_or_else(|| self.tcx.def_span(def_id)); - err.span_label(sp, &format!("private {} defined here", kind)); - self.suggest_valid_traits(&mut err, out_of_scope_traits); - err.emit(); - } - - MethodError::IllegalSizedBound(candidates, needs_mut, bound_span) => { - let msg = format!("the `{}` method cannot be invoked on a trait object", item_name); - let mut err = self.sess().struct_span_err(span, &msg); - err.span_label(bound_span, "this has a `Sized` requirement"); - if !candidates.is_empty() { - let help = format!( - "{an}other candidate{s} {were} found in the following trait{s}, perhaps \ - add a `use` for {one_of_them}:", - an = if candidates.len() == 1 { "an" } else { "" }, - s = pluralize!(candidates.len()), - were = pluralize!("was", candidates.len()), - one_of_them = if candidates.len() == 1 { "it" } else { "one_of_them" }, - ); - self.suggest_use_candidates(&mut err, help, candidates); - } - if let ty::Ref(region, t_type, mutability) = rcvr_ty.kind() { - if needs_mut { - let trait_type = self.tcx.mk_ref( - *region, - ty::TypeAndMut { ty: *t_type, mutbl: mutability.invert() }, - ); - err.note(&format!("you need `{}` instead of `{}`", trait_type, rcvr_ty)); - } - } - err.emit(); - } - - MethodError::BadReturnType => bug!("no return type expectations but got BadReturnType"), - } - None - } - - fn suggest_field_call( - &self, - span: Span, - rcvr_ty: Ty<'tcx>, - expr: &hir::Expr<'_>, - item_name: Ident, - err: &mut Diagnostic, - ) -> bool { - let tcx = self.tcx; - let field_receiver = self.autoderef(span, rcvr_ty).find_map(|(ty, _)| match ty.kind() { - ty::Adt(def, substs) if !def.is_enum() => { - let variant = &def.non_enum_variant(); - tcx.find_field_index(item_name, variant).map(|index| { - let field = &variant.fields[index]; - let field_ty = field.ty(tcx, substs); - (field, field_ty) - }) - } - _ => None, - }); - if let Some((field, field_ty)) = field_receiver { - let scope = tcx.parent_module(self.body_id); - let is_accessible = field.vis.is_accessible_from(scope, tcx); - - if is_accessible { - if self.is_fn_ty(field_ty, span) { - let expr_span = expr.span.to(item_name.span); - err.multipart_suggestion( - &format!( - "to call the function stored in `{}`, \ - surround the field access with parentheses", - item_name, - ), - vec![ - (expr_span.shrink_to_lo(), '('.to_string()), - (expr_span.shrink_to_hi(), ')'.to_string()), - ], - Applicability::MachineApplicable, - ); - } else { - let call_expr = tcx.hir().expect_expr(tcx.hir().get_parent_node(expr.hir_id)); - - if let Some(span) = call_expr.span.trim_start(item_name.span) { - err.span_suggestion( - span, - "remove the arguments", - "", - Applicability::MaybeIncorrect, - ); - } - } - } - - let field_kind = if is_accessible { "field" } else { "private field" }; - err.span_label(item_name.span, format!("{}, not a method", field_kind)); - return true; - } - false - } - - fn suggest_constraining_numerical_ty( - &self, - tcx: TyCtxt<'tcx>, - actual: Ty<'tcx>, - source: SelfSource<'_>, - span: Span, - item_kind: &str, - item_name: Ident, - ty_str: &str, - ) -> bool { - let found_candidate = all_traits(self.tcx) - .into_iter() - .any(|info| self.associated_value(info.def_id, item_name).is_some()); - let found_assoc = |ty: Ty<'tcx>| { - simplify_type(tcx, ty, TreatParams::AsInfer) - .and_then(|simp| { - tcx.incoherent_impls(simp) - .iter() - .find_map(|&id| self.associated_value(id, item_name)) - }) - .is_some() - }; - let found_candidate = found_candidate - || found_assoc(tcx.types.i8) - || found_assoc(tcx.types.i16) - || found_assoc(tcx.types.i32) - || found_assoc(tcx.types.i64) - || found_assoc(tcx.types.i128) - || found_assoc(tcx.types.u8) - || found_assoc(tcx.types.u16) - || found_assoc(tcx.types.u32) - || found_assoc(tcx.types.u64) - || found_assoc(tcx.types.u128) - || found_assoc(tcx.types.f32) - || found_assoc(tcx.types.f32); - if found_candidate - && actual.is_numeric() - && !actual.has_concrete_skeleton() - && let SelfSource::MethodCall(expr) = source - { - let mut err = struct_span_err!( - tcx.sess, - span, - E0689, - "can't call {} `{}` on ambiguous numeric type `{}`", - item_kind, - item_name, - ty_str - ); - let concrete_type = if actual.is_integral() { "i32" } else { "f32" }; - match expr.kind { - ExprKind::Lit(ref lit) => { - // numeric literal - let snippet = tcx - .sess - .source_map() - .span_to_snippet(lit.span) - .unwrap_or_else(|_| "<numeric literal>".to_owned()); - - // If this is a floating point literal that ends with '.', - // get rid of it to stop this from becoming a member access. - let snippet = snippet.strip_suffix('.').unwrap_or(&snippet); - - err.span_suggestion( - lit.span, - &format!( - "you must specify a concrete type for this numeric value, \ - like `{}`", - concrete_type - ), - format!("{snippet}_{concrete_type}"), - Applicability::MaybeIncorrect, - ); - } - ExprKind::Path(QPath::Resolved(_, path)) => { - // local binding - if let hir::def::Res::Local(hir_id) = path.res { - let span = tcx.hir().span(hir_id); - let filename = tcx.sess.source_map().span_to_filename(span); - - let parent_node = - self.tcx.hir().get(self.tcx.hir().get_parent_node(hir_id)); - let msg = format!( - "you must specify a type for this binding, like `{}`", - concrete_type, - ); - - match (filename, parent_node) { - ( - FileName::Real(_), - Node::Local(hir::Local { - source: hir::LocalSource::Normal, - ty, - .. - }), - ) => { - let type_span = ty.map(|ty| ty.span.with_lo(span.hi())).unwrap_or(span.shrink_to_hi()); - err.span_suggestion( - // account for `let x: _ = 42;` - // ^^^ - type_span, - &msg, - format!(": {concrete_type}"), - Applicability::MaybeIncorrect, - ); - } - _ => { - err.span_label(span, msg); - } - } - } - } - _ => {} - } - err.emit(); - return true; - } - false - } - - fn check_for_field_method( - &self, - err: &mut Diagnostic, - source: SelfSource<'tcx>, - span: Span, - actual: Ty<'tcx>, - item_name: Ident, - ) { - if let SelfSource::MethodCall(expr) = source - && let mod_id = self.tcx.parent_module(expr.hir_id).to_def_id() - && let Some((fields, substs)) = - self.get_field_candidates_considering_privacy(span, actual, mod_id) - { - let call_expr = self.tcx.hir().expect_expr(self.tcx.hir().get_parent_node(expr.hir_id)); - - let lang_items = self.tcx.lang_items(); - let never_mention_traits = [ - lang_items.clone_trait(), - lang_items.deref_trait(), - lang_items.deref_mut_trait(), - self.tcx.get_diagnostic_item(sym::AsRef), - self.tcx.get_diagnostic_item(sym::AsMut), - self.tcx.get_diagnostic_item(sym::Borrow), - self.tcx.get_diagnostic_item(sym::BorrowMut), - ]; - let candidate_fields: Vec<_> = fields - .filter_map(|candidate_field| { - self.check_for_nested_field_satisfying( - span, - &|_, field_ty| { - self.lookup_probe( - span, - item_name, - field_ty, - call_expr, - ProbeScope::TraitsInScope, - ) - .map_or(false, |pick| { - !never_mention_traits - .iter() - .flatten() - .any(|def_id| self.tcx.parent(pick.item.def_id) == *def_id) - }) - }, - candidate_field, - substs, - vec![], - mod_id, - ) - }) - .map(|field_path| { - field_path - .iter() - .map(|id| id.name.to_ident_string()) - .collect::<Vec<String>>() - .join(".") - }) - .collect(); - - let len = candidate_fields.len(); - if len > 0 { - err.span_suggestions( - item_name.span.shrink_to_lo(), - format!( - "{} of the expressions' fields {} a method of the same name", - if len > 1 { "some" } else { "one" }, - if len > 1 { "have" } else { "has" }, - ), - candidate_fields.iter().map(|path| format!("{path}.")), - Applicability::MaybeIncorrect, - ); - } - } - } - - fn check_for_unwrap_self( - &self, - err: &mut Diagnostic, - source: SelfSource<'tcx>, - span: Span, - actual: Ty<'tcx>, - item_name: Ident, - ) { - let tcx = self.tcx; - let SelfSource::MethodCall(expr) = source else { return; }; - let call_expr = tcx.hir().expect_expr(tcx.hir().get_parent_node(expr.hir_id)); - - let ty::Adt(kind, substs) = actual.kind() else { return; }; - if !kind.is_enum() { - return; - } - - let matching_variants: Vec<_> = kind - .variants() - .iter() - .flat_map(|variant| { - let [field] = &variant.fields[..] else { return None; }; - let field_ty = field.ty(tcx, substs); - - // Skip `_`, since that'll just lead to ambiguity. - if self.resolve_vars_if_possible(field_ty).is_ty_var() { - return None; - } - - self.lookup_probe(span, item_name, field_ty, call_expr, ProbeScope::AllTraits) - .ok() - .map(|pick| (variant, field, pick)) - }) - .collect(); - - let ret_ty_matches = |diagnostic_item| { - if let Some(ret_ty) = self - .ret_coercion - .as_ref() - .map(|c| self.resolve_vars_if_possible(c.borrow().expected_ty())) - && let ty::Adt(kind, _) = ret_ty.kind() - && tcx.get_diagnostic_item(diagnostic_item) == Some(kind.did()) - { - true - } else { - false - } - }; - - match &matching_variants[..] { - [(_, field, pick)] => { - let self_ty = field.ty(tcx, substs); - err.span_note( - tcx.def_span(pick.item.def_id), - &format!("the method `{item_name}` exists on the type `{self_ty}`"), - ); - let (article, kind, variant, question) = - if Some(kind.did()) == tcx.get_diagnostic_item(sym::Result) { - ("a", "Result", "Err", ret_ty_matches(sym::Result)) - } else if Some(kind.did()) == tcx.get_diagnostic_item(sym::Option) { - ("an", "Option", "None", ret_ty_matches(sym::Option)) - } else { - return; - }; - if question { - err.span_suggestion_verbose( - expr.span.shrink_to_hi(), - format!( - "use the `?` operator to extract the `{self_ty}` value, propagating \ - {article} `{kind}::{variant}` value to the caller" - ), - "?", - Applicability::MachineApplicable, - ); - } else { - err.span_suggestion_verbose( - expr.span.shrink_to_hi(), - format!( - "consider using `{kind}::expect` to unwrap the `{self_ty}` value, \ - panicking if the value is {article} `{kind}::{variant}`" - ), - ".expect(\"REASON\")", - Applicability::HasPlaceholders, - ); - } - } - // FIXME(compiler-errors): Support suggestions for other matching enum variants - _ => {} - } - } - - pub(crate) fn note_unmet_impls_on_type( - &self, - err: &mut Diagnostic, - errors: Vec<FulfillmentError<'tcx>>, - ) { - let all_local_types_needing_impls = - errors.iter().all(|e| match e.obligation.predicate.kind().skip_binder() { - ty::PredicateKind::Trait(pred) => match pred.self_ty().kind() { - ty::Adt(def, _) => def.did().is_local(), - _ => false, - }, - _ => false, - }); - let mut preds: Vec<_> = errors - .iter() - .filter_map(|e| match e.obligation.predicate.kind().skip_binder() { - ty::PredicateKind::Trait(pred) => Some(pred), - _ => None, - }) - .collect(); - preds.sort_by_key(|pred| (pred.def_id(), pred.self_ty())); - let def_ids = preds - .iter() - .filter_map(|pred| match pred.self_ty().kind() { - ty::Adt(def, _) => Some(def.did()), - _ => None, - }) - .collect::<FxHashSet<_>>(); - let mut spans: MultiSpan = def_ids - .iter() - .filter_map(|def_id| { - let span = self.tcx.def_span(*def_id); - if span.is_dummy() { None } else { Some(span) } - }) - .collect::<Vec<_>>() - .into(); - - for pred in &preds { - match pred.self_ty().kind() { - ty::Adt(def, _) if def.did().is_local() => { - spans.push_span_label( - self.tcx.def_span(def.did()), - format!("must implement `{}`", pred.trait_ref.print_only_trait_path()), - ); - } - _ => {} - } - } - - if all_local_types_needing_impls && spans.primary_span().is_some() { - let msg = if preds.len() == 1 { - format!( - "an implementation of `{}` might be missing for `{}`", - preds[0].trait_ref.print_only_trait_path(), - preds[0].self_ty() - ) - } else { - format!( - "the following type{} would have to `impl` {} required trait{} for this \ - operation to be valid", - pluralize!(def_ids.len()), - if def_ids.len() == 1 { "its" } else { "their" }, - pluralize!(preds.len()), - ) - }; - err.span_note(spans, &msg); - } - - let preds: Vec<_> = errors - .iter() - .map(|e| (e.obligation.predicate, None, Some(e.obligation.cause.clone()))) - .collect(); - self.suggest_derive(err, &preds); - } - - fn suggest_derive( - &self, - err: &mut Diagnostic, - unsatisfied_predicates: &[( - ty::Predicate<'tcx>, - Option<ty::Predicate<'tcx>>, - Option<ObligationCause<'tcx>>, - )], - ) { - let mut derives = Vec::<(String, Span, Symbol)>::new(); - let mut traits = Vec::<Span>::new(); - for (pred, _, _) in unsatisfied_predicates { - let ty::PredicateKind::Trait(trait_pred) = pred.kind().skip_binder() else { continue }; - let adt = match trait_pred.self_ty().ty_adt_def() { - Some(adt) if adt.did().is_local() => adt, - _ => continue, - }; - if let Some(diagnostic_name) = self.tcx.get_diagnostic_name(trait_pred.def_id()) { - let can_derive = match diagnostic_name { - sym::Default => !adt.is_enum(), - sym::Eq - | sym::PartialEq - | sym::Ord - | sym::PartialOrd - | sym::Clone - | sym::Copy - | sym::Hash - | sym::Debug => true, - _ => false, - }; - if can_derive { - let self_name = trait_pred.self_ty().to_string(); - let self_span = self.tcx.def_span(adt.did()); - if let Some(poly_trait_ref) = pred.to_opt_poly_trait_pred() { - for super_trait in supertraits(self.tcx, poly_trait_ref.to_poly_trait_ref()) - { - if let Some(parent_diagnostic_name) = - self.tcx.get_diagnostic_name(super_trait.def_id()) - { - derives.push(( - self_name.clone(), - self_span, - parent_diagnostic_name, - )); - } - } - } - derives.push((self_name, self_span, diagnostic_name)); - } else { - traits.push(self.tcx.def_span(trait_pred.def_id())); - } - } else { - traits.push(self.tcx.def_span(trait_pred.def_id())); - } - } - traits.sort(); - traits.dedup(); - - derives.sort(); - derives.dedup(); - - let mut derives_grouped = Vec::<(String, Span, String)>::new(); - for (self_name, self_span, trait_name) in derives.into_iter() { - if let Some((last_self_name, _, ref mut last_trait_names)) = derives_grouped.last_mut() - { - if last_self_name == &self_name { - last_trait_names.push_str(format!(", {}", trait_name).as_str()); - continue; - } - } - derives_grouped.push((self_name, self_span, trait_name.to_string())); - } - - let len = traits.len(); - if len > 0 { - let span: MultiSpan = traits.into(); - err.span_note( - span, - &format!("the following trait{} must be implemented", pluralize!(len),), - ); - } - - for (self_name, self_span, traits) in &derives_grouped { - err.span_suggestion_verbose( - self_span.shrink_to_lo(), - &format!("consider annotating `{}` with `#[derive({})]`", self_name, traits), - format!("#[derive({})]\n", traits), - Applicability::MaybeIncorrect, - ); - } - } - - fn check_for_deref_method( - &self, - err: &mut Diagnostic, - self_source: SelfSource<'tcx>, - rcvr_ty: Ty<'tcx>, - item_name: Ident, - ) { - let SelfSource::QPath(ty) = self_source else { return; }; - for (deref_ty, _) in self.autoderef(rustc_span::DUMMY_SP, rcvr_ty).skip(1) { - if let Ok(pick) = self.probe_for_name( - ty.span, - Mode::Path, - item_name, - IsSuggestion(true), - deref_ty, - ty.hir_id, - ProbeScope::TraitsInScope, - ) { - if deref_ty.is_suggestable(self.tcx, true) - // If this method receives `&self`, then the provided - // argument _should_ coerce, so it's valid to suggest - // just changing the path. - && pick.item.fn_has_self_parameter - && let Some(self_ty) = - self.tcx.fn_sig(pick.item.def_id).inputs().skip_binder().get(0) - && self_ty.is_ref() - { - let suggested_path = match deref_ty.kind() { - ty::Bool - | ty::Char - | ty::Int(_) - | ty::Uint(_) - | ty::Float(_) - | ty::Adt(_, _) - | ty::Str - | ty::Projection(_) - | ty::Param(_) => format!("{deref_ty}"), - _ => format!("<{deref_ty}>"), - }; - err.span_suggestion_verbose( - ty.span, - format!("the function `{item_name}` is implemented on `{deref_ty}`"), - suggested_path, - Applicability::MaybeIncorrect, - ); - } else { - err.span_note( - ty.span, - format!("the function `{item_name}` is implemented on `{deref_ty}`"), - ); - } - return; - } - } - } - - /// Print out the type for use in value namespace. - fn ty_to_value_string(&self, ty: Ty<'tcx>) -> String { - match ty.kind() { - ty::Adt(def, substs) => format!("{}", ty::Instance::new(def.did(), substs)), - _ => self.ty_to_string(ty), - } - } - - fn suggest_await_before_method( - &self, - err: &mut Diagnostic, - item_name: Ident, - ty: Ty<'tcx>, - call: &hir::Expr<'_>, - span: Span, - ) { - let output_ty = match self.get_impl_future_output_ty(ty) { - Some(output_ty) => self.resolve_vars_if_possible(output_ty).skip_binder(), - _ => return, - }; - let method_exists = self.method_exists(item_name, output_ty, call.hir_id, true); - debug!("suggest_await_before_method: is_method_exist={}", method_exists); - if method_exists { - err.span_suggestion_verbose( - span.shrink_to_lo(), - "consider `await`ing on the `Future` and calling the method on its `Output`", - "await.", - Applicability::MaybeIncorrect, - ); - } - } - - fn suggest_use_candidates(&self, err: &mut Diagnostic, msg: String, candidates: Vec<DefId>) { - let parent_map = self.tcx.visible_parent_map(()); - - // Separate out candidates that must be imported with a glob, because they are named `_` - // and cannot be referred with their identifier. - let (candidates, globs): (Vec<_>, Vec<_>) = candidates.into_iter().partition(|trait_did| { - if let Some(parent_did) = parent_map.get(trait_did) { - // If the item is re-exported as `_`, we should suggest a glob-import instead. - if *parent_did != self.tcx.parent(*trait_did) - && self - .tcx - .module_children(*parent_did) - .iter() - .filter(|child| child.res.opt_def_id() == Some(*trait_did)) - .all(|child| child.ident.name == kw::Underscore) - { - return false; - } - } - - true - }); - - let module_did = self.tcx.parent_module(self.body_id); - let (module, _, _) = self.tcx.hir().get_module(module_did); - let span = module.spans.inject_use_span; - - let path_strings = candidates.iter().map(|trait_did| { - format!("use {};\n", with_crate_prefix!(self.tcx.def_path_str(*trait_did)),) - }); - - let glob_path_strings = globs.iter().map(|trait_did| { - let parent_did = parent_map.get(trait_did).unwrap(); - format!( - "use {}::*; // trait {}\n", - with_crate_prefix!(self.tcx.def_path_str(*parent_did)), - self.tcx.item_name(*trait_did), - ) - }); - - err.span_suggestions( - span, - &msg, - path_strings.chain(glob_path_strings), - Applicability::MaybeIncorrect, - ); - } - - fn suggest_valid_traits( - &self, - err: &mut Diagnostic, - valid_out_of_scope_traits: Vec<DefId>, - ) -> bool { - if !valid_out_of_scope_traits.is_empty() { - let mut candidates = valid_out_of_scope_traits; - candidates.sort(); - candidates.dedup(); - - // `TryFrom` and `FromIterator` have no methods - let edition_fix = candidates - .iter() - .find(|did| self.tcx.is_diagnostic_item(sym::TryInto, **did)) - .copied(); - - err.help("items from traits can only be used if the trait is in scope"); - let msg = format!( - "the following {traits_are} implemented but not in scope; \ - perhaps add a `use` for {one_of_them}:", - traits_are = if candidates.len() == 1 { "trait is" } else { "traits are" }, - one_of_them = if candidates.len() == 1 { "it" } else { "one of them" }, - ); - - self.suggest_use_candidates(err, msg, candidates); - if let Some(did) = edition_fix { - err.note(&format!( - "'{}' is included in the prelude starting in Edition 2021", - with_crate_prefix!(self.tcx.def_path_str(did)) - )); - } - - true - } else { - false - } - } - - fn suggest_traits_to_import( - &self, - err: &mut Diagnostic, - span: Span, - rcvr_ty: Ty<'tcx>, - item_name: Ident, - inputs_len: Option<usize>, - source: SelfSource<'tcx>, - valid_out_of_scope_traits: Vec<DefId>, - unsatisfied_predicates: &[( - ty::Predicate<'tcx>, - Option<ty::Predicate<'tcx>>, - Option<ObligationCause<'tcx>>, - )], - unsatisfied_bounds: bool, - ) { - let mut alt_rcvr_sugg = false; - if let (SelfSource::MethodCall(rcvr), false) = (source, unsatisfied_bounds) { - debug!(?span, ?item_name, ?rcvr_ty, ?rcvr); - let skippable = [ - self.tcx.lang_items().clone_trait(), - self.tcx.lang_items().deref_trait(), - self.tcx.lang_items().deref_mut_trait(), - self.tcx.lang_items().drop_trait(), - self.tcx.get_diagnostic_item(sym::AsRef), - ]; - // Try alternative arbitrary self types that could fulfill this call. - // FIXME: probe for all types that *could* be arbitrary self-types, not - // just this list. - for (rcvr_ty, post) in &[ - (rcvr_ty, ""), - (self.tcx.mk_mut_ref(self.tcx.lifetimes.re_erased, rcvr_ty), "&mut "), - (self.tcx.mk_imm_ref(self.tcx.lifetimes.re_erased, rcvr_ty), "&"), - ] { - match self.lookup_probe(span, item_name, *rcvr_ty, rcvr, ProbeScope::AllTraits) { - Ok(pick) => { - // If the method is defined for the receiver we have, it likely wasn't `use`d. - // We point at the method, but we just skip the rest of the check for arbitrary - // self types and rely on the suggestion to `use` the trait from - // `suggest_valid_traits`. - let did = Some(pick.item.container_id(self.tcx)); - let skip = skippable.contains(&did); - if pick.autoderefs == 0 && !skip { - err.span_label( - pick.item.ident(self.tcx).span, - &format!("the method is available for `{}` here", rcvr_ty), - ); - } - break; - } - Err(MethodError::Ambiguity(_)) => { - // If the method is defined (but ambiguous) for the receiver we have, it is also - // likely we haven't `use`d it. It may be possible that if we `Box`/`Pin`/etc. - // the receiver, then it might disambiguate this method, but I think these - // suggestions are generally misleading (see #94218). - break; - } - _ => {} - } - - for (rcvr_ty, pre) in &[ - (self.tcx.mk_lang_item(*rcvr_ty, LangItem::OwnedBox), "Box::new"), - (self.tcx.mk_lang_item(*rcvr_ty, LangItem::Pin), "Pin::new"), - (self.tcx.mk_diagnostic_item(*rcvr_ty, sym::Arc), "Arc::new"), - (self.tcx.mk_diagnostic_item(*rcvr_ty, sym::Rc), "Rc::new"), - ] { - if let Some(new_rcvr_t) = *rcvr_ty - && let Ok(pick) = self.lookup_probe( - span, - item_name, - new_rcvr_t, - rcvr, - ProbeScope::AllTraits, - ) - { - debug!("try_alt_rcvr: pick candidate {:?}", pick); - let did = Some(pick.item.container_id(self.tcx)); - // We don't want to suggest a container type when the missing - // method is `.clone()` or `.deref()` otherwise we'd suggest - // `Arc::new(foo).clone()`, which is far from what the user wants. - // Explicitly ignore the `Pin::as_ref()` method as `Pin` does not - // implement the `AsRef` trait. - let skip = skippable.contains(&did) - || (("Pin::new" == *pre) && (sym::as_ref == item_name.name)) - || inputs_len.map_or(false, |inputs_len| pick.item.kind == ty::AssocKind::Fn && self.tcx.fn_sig(pick.item.def_id).skip_binder().inputs().len() != inputs_len); - // Make sure the method is defined for the *actual* receiver: we don't - // want to treat `Box<Self>` as a receiver if it only works because of - // an autoderef to `&self` - if pick.autoderefs == 0 && !skip { - err.span_label( - pick.item.ident(self.tcx).span, - &format!("the method is available for `{}` here", new_rcvr_t), - ); - err.multipart_suggestion( - "consider wrapping the receiver expression with the \ - appropriate type", - vec![ - (rcvr.span.shrink_to_lo(), format!("{}({}", pre, post)), - (rcvr.span.shrink_to_hi(), ")".to_string()), - ], - Applicability::MaybeIncorrect, - ); - // We don't care about the other suggestions. - alt_rcvr_sugg = true; - } - } - } - } - } - if self.suggest_valid_traits(err, valid_out_of_scope_traits) { - return; - } - - let type_is_local = self.type_derefs_to_local(span, rcvr_ty, source); - - let mut arbitrary_rcvr = vec![]; - // There are no traits implemented, so lets suggest some traits to - // implement, by finding ones that have the item name, and are - // legal to implement. - let mut candidates = all_traits(self.tcx) - .into_iter() - // Don't issue suggestions for unstable traits since they're - // unlikely to be implementable anyway - .filter(|info| match self.tcx.lookup_stability(info.def_id) { - Some(attr) => attr.level.is_stable(), - None => true, - }) - .filter(|info| { - // We approximate the coherence rules to only suggest - // traits that are legal to implement by requiring that - // either the type or trait is local. Multi-dispatch means - // this isn't perfect (that is, there are cases when - // implementing a trait would be legal but is rejected - // here). - unsatisfied_predicates.iter().all(|(p, _, _)| { - match p.kind().skip_binder() { - // Hide traits if they are present in predicates as they can be fixed without - // having to implement them. - ty::PredicateKind::Trait(t) => t.def_id() == info.def_id, - ty::PredicateKind::Projection(p) => { - p.projection_ty.item_def_id == info.def_id - } - _ => false, - } - }) && (type_is_local || info.def_id.is_local()) - && self - .associated_value(info.def_id, item_name) - .filter(|item| { - if let ty::AssocKind::Fn = item.kind { - let id = item - .def_id - .as_local() - .map(|def_id| self.tcx.hir().local_def_id_to_hir_id(def_id)); - if let Some(hir::Node::TraitItem(hir::TraitItem { - kind: hir::TraitItemKind::Fn(fn_sig, method), - .. - })) = id.map(|id| self.tcx.hir().get(id)) - { - let self_first_arg = match method { - hir::TraitFn::Required([ident, ..]) => { - ident.name == kw::SelfLower - } - hir::TraitFn::Provided(body_id) => { - self.tcx.hir().body(*body_id).params.first().map_or( - false, - |param| { - matches!( - param.pat.kind, - hir::PatKind::Binding(_, _, ident, _) - if ident.name == kw::SelfLower - ) - }, - ) - } - _ => false, - }; - - if !fn_sig.decl.implicit_self.has_implicit_self() - && self_first_arg - { - if let Some(ty) = fn_sig.decl.inputs.get(0) { - arbitrary_rcvr.push(ty.span); - } - return false; - } - } - } - // We only want to suggest public or local traits (#45781). - item.visibility(self.tcx).is_public() || info.def_id.is_local() - }) - .is_some() - }) - .collect::<Vec<_>>(); - for span in &arbitrary_rcvr { - err.span_label( - *span, - "the method might not be found because of this arbitrary self type", - ); - } - if alt_rcvr_sugg { - return; - } - - if !candidates.is_empty() { - // Sort from most relevant to least relevant. - candidates.sort_by(|a, b| a.cmp(b).reverse()); - candidates.dedup(); - - let param_type = match rcvr_ty.kind() { - ty::Param(param) => Some(param), - ty::Ref(_, ty, _) => match ty.kind() { - ty::Param(param) => Some(param), - _ => None, - }, - _ => None, - }; - err.help(if param_type.is_some() { - "items from traits can only be used if the type parameter is bounded by the trait" - } else { - "items from traits can only be used if the trait is implemented and in scope" - }); - let candidates_len = candidates.len(); - let message = |action| { - format!( - "the following {traits_define} an item `{name}`, perhaps you need to {action} \ - {one_of_them}:", - traits_define = - if candidates_len == 1 { "trait defines" } else { "traits define" }, - action = action, - one_of_them = if candidates_len == 1 { "it" } else { "one of them" }, - name = item_name, - ) - }; - // Obtain the span for `param` and use it for a structured suggestion. - if let Some(param) = param_type { - let generics = self.tcx.generics_of(self.body_id.owner.to_def_id()); - let type_param = generics.type_param(param, self.tcx); - let hir = self.tcx.hir(); - if let Some(def_id) = type_param.def_id.as_local() { - let id = hir.local_def_id_to_hir_id(def_id); - // Get the `hir::Param` to verify whether it already has any bounds. - // We do this to avoid suggesting code that ends up as `T: FooBar`, - // instead we suggest `T: Foo + Bar` in that case. - match hir.get(id) { - Node::GenericParam(param) => { - enum Introducer { - Plus, - Colon, - Nothing, - } - let ast_generics = hir.get_generics(id.owner).unwrap(); - let (sp, mut introducer) = if let Some(span) = - ast_generics.bounds_span_for_suggestions(def_id) - { - (span, Introducer::Plus) - } else if let Some(colon_span) = param.colon_span { - (colon_span.shrink_to_hi(), Introducer::Nothing) - } else { - (param.span.shrink_to_hi(), Introducer::Colon) - }; - if matches!( - param.kind, - hir::GenericParamKind::Type { synthetic: true, .. }, - ) { - introducer = Introducer::Plus - } - let trait_def_ids: FxHashSet<DefId> = ast_generics - .bounds_for_param(def_id) - .flat_map(|bp| bp.bounds.iter()) - .filter_map(|bound| bound.trait_ref()?.trait_def_id()) - .collect(); - if !candidates.iter().any(|t| trait_def_ids.contains(&t.def_id)) { - err.span_suggestions( - sp, - &message(format!( - "restrict type parameter `{}` with", - param.name.ident(), - )), - candidates.iter().map(|t| { - format!( - "{} {}", - match introducer { - Introducer::Plus => " +", - Introducer::Colon => ":", - Introducer::Nothing => "", - }, - self.tcx.def_path_str(t.def_id), - ) - }), - Applicability::MaybeIncorrect, - ); - } - return; - } - Node::Item(hir::Item { - kind: hir::ItemKind::Trait(.., bounds, _), - ident, - .. - }) => { - let (sp, sep, article) = if bounds.is_empty() { - (ident.span.shrink_to_hi(), ":", "a") - } else { - (bounds.last().unwrap().span().shrink_to_hi(), " +", "another") - }; - err.span_suggestions( - sp, - &message(format!("add {} supertrait for", article)), - candidates.iter().map(|t| { - format!("{} {}", sep, self.tcx.def_path_str(t.def_id),) - }), - Applicability::MaybeIncorrect, - ); - return; - } - _ => {} - } - } - } - - let (potential_candidates, explicitly_negative) = if param_type.is_some() { - // FIXME: Even though negative bounds are not implemented, we could maybe handle - // cases where a positive bound implies a negative impl. - (candidates, Vec::new()) - } else if let Some(simp_rcvr_ty) = - simplify_type(self.tcx, rcvr_ty, TreatParams::AsPlaceholder) - { - let mut potential_candidates = Vec::new(); - let mut explicitly_negative = Vec::new(); - for candidate in candidates { - // Check if there's a negative impl of `candidate` for `rcvr_ty` - if self - .tcx - .all_impls(candidate.def_id) - .filter(|imp_did| { - self.tcx.impl_polarity(*imp_did) == ty::ImplPolarity::Negative - }) - .any(|imp_did| { - let imp = self.tcx.impl_trait_ref(imp_did).unwrap(); - let imp_simp = - simplify_type(self.tcx, imp.self_ty(), TreatParams::AsPlaceholder); - imp_simp.map_or(false, |s| s == simp_rcvr_ty) - }) - { - explicitly_negative.push(candidate); - } else { - potential_candidates.push(candidate); - } - } - (potential_candidates, explicitly_negative) - } else { - // We don't know enough about `recv_ty` to make proper suggestions. - (candidates, Vec::new()) - }; - - let action = if let Some(param) = param_type { - format!("restrict type parameter `{}` with", param) - } else { - // FIXME: it might only need to be imported into scope, not implemented. - "implement".to_string() - }; - match &potential_candidates[..] { - [] => {} - [trait_info] if trait_info.def_id.is_local() => { - err.span_note( - self.tcx.def_span(trait_info.def_id), - &format!( - "`{}` defines an item `{}`, perhaps you need to {} it", - self.tcx.def_path_str(trait_info.def_id), - item_name, - action - ), - ); - } - trait_infos => { - let mut msg = message(action); - for (i, trait_info) in trait_infos.iter().enumerate() { - msg.push_str(&format!( - "\ncandidate #{}: `{}`", - i + 1, - self.tcx.def_path_str(trait_info.def_id), - )); - } - err.note(&msg); - } - } - match &explicitly_negative[..] { - [] => {} - [trait_info] => { - let msg = format!( - "the trait `{}` defines an item `{}`, but is explicitly unimplemented", - self.tcx.def_path_str(trait_info.def_id), - item_name - ); - err.note(&msg); - } - trait_infos => { - let mut msg = format!( - "the following traits define an item `{}`, but are explicitly unimplemented:", - item_name - ); - for trait_info in trait_infos { - msg.push_str(&format!("\n{}", self.tcx.def_path_str(trait_info.def_id))); - } - err.note(&msg); - } - } - } - } - - /// Checks whether there is a local type somewhere in the chain of - /// autoderefs of `rcvr_ty`. - fn type_derefs_to_local( - &self, - span: Span, - rcvr_ty: Ty<'tcx>, - source: SelfSource<'tcx>, - ) -> bool { - fn is_local(ty: Ty<'_>) -> bool { - match ty.kind() { - ty::Adt(def, _) => def.did().is_local(), - ty::Foreign(did) => did.is_local(), - ty::Dynamic(tr, ..) => tr.principal().map_or(false, |d| d.def_id().is_local()), - ty::Param(_) => true, - - // Everything else (primitive types, etc.) is effectively - // non-local (there are "edge" cases, e.g., `(LocalType,)`, but - // the noise from these sort of types is usually just really - // annoying, rather than any sort of help). - _ => false, - } - } - - // This occurs for UFCS desugaring of `T::method`, where there is no - // receiver expression for the method call, and thus no autoderef. - if let SelfSource::QPath(_) = source { - return is_local(self.resolve_vars_with_obligations(rcvr_ty)); - } - - self.autoderef(span, rcvr_ty).any(|(ty, _)| is_local(ty)) - } -} - -#[derive(Copy, Clone, Debug)] -pub enum SelfSource<'a> { - QPath(&'a hir::Ty<'a>), - MethodCall(&'a hir::Expr<'a> /* rcvr */), -} - -#[derive(Copy, Clone)] -pub struct TraitInfo { - pub def_id: DefId, -} - -impl PartialEq for TraitInfo { - fn eq(&self, other: &TraitInfo) -> bool { - self.cmp(other) == Ordering::Equal - } -} -impl Eq for TraitInfo {} -impl PartialOrd for TraitInfo { - fn partial_cmp(&self, other: &TraitInfo) -> Option<Ordering> { - Some(self.cmp(other)) - } -} -impl Ord for TraitInfo { - fn cmp(&self, other: &TraitInfo) -> Ordering { - // Local crates are more important than remote ones (local: - // `cnum == 0`), and otherwise we throw in the defid for totality. - - let lhs = (other.def_id.krate, other.def_id); - let rhs = (self.def_id.krate, self.def_id); - lhs.cmp(&rhs) - } -} - -/// Retrieves all traits in this crate and any dependent crates, -/// and wraps them into `TraitInfo` for custom sorting. -pub fn all_traits(tcx: TyCtxt<'_>) -> Vec<TraitInfo> { - tcx.all_traits().map(|def_id| TraitInfo { def_id }).collect() -} - -fn print_disambiguation_help<'tcx>( - item_name: Ident, - args: Option<(&'tcx hir::Expr<'tcx>, &'tcx [hir::Expr<'tcx>])>, - err: &mut Diagnostic, - trait_name: String, - rcvr_ty: Ty<'_>, - kind: ty::AssocKind, - def_id: DefId, - span: Span, - candidate: Option<usize>, - source_map: &source_map::SourceMap, - fn_has_self_parameter: bool, -) { - let mut applicability = Applicability::MachineApplicable; - let (span, sugg) = if let (ty::AssocKind::Fn, Some((receiver, args))) = (kind, args) { - let args = format!( - "({}{})", - if rcvr_ty.is_region_ptr() { - if rcvr_ty.is_mutable_ptr() { "&mut " } else { "&" } - } else { - "" - }, - std::iter::once(receiver) - .chain(args.iter()) - .map(|arg| source_map.span_to_snippet(arg.span).unwrap_or_else(|_| { - applicability = Applicability::HasPlaceholders; - "_".to_owned() - })) - .collect::<Vec<_>>() - .join(", "), - ); - let trait_name = if !fn_has_self_parameter { - format!("<{} as {}>", rcvr_ty, trait_name) - } else { - trait_name - }; - (span, format!("{}::{}{}", trait_name, item_name, args)) - } else { - (span.with_hi(item_name.span.lo()), format!("<{} as {}>::", rcvr_ty, trait_name)) - }; - err.span_suggestion_verbose( - span, - &format!( - "disambiguate the {} for {}", - kind.as_def_kind().descr(def_id), - if let Some(candidate) = candidate { - format!("candidate #{}", candidate) - } else { - "the candidate".to_string() - }, - ), - sugg, - applicability, - ); -} diff --git a/compiler/rustc_typeck/src/check/mod.rs b/compiler/rustc_typeck/src/check/mod.rs deleted file mode 100644 index cfae63e4a..000000000 --- a/compiler/rustc_typeck/src/check/mod.rs +++ /dev/null @@ -1,967 +0,0 @@ -/*! - -# typeck: check phase - -Within the check phase of type check, we check each item one at a time -(bodies of function expressions are checked as part of the containing -function). Inference is used to supply types wherever they are unknown. - -By far the most complex case is checking the body of a function. This -can be broken down into several distinct phases: - -- gather: creates type variables to represent the type of each local - variable and pattern binding. - -- main: the main pass does the lion's share of the work: it - determines the types of all expressions, resolves - methods, checks for most invalid conditions, and so forth. In - some cases, where a type is unknown, it may create a type or region - variable and use that as the type of an expression. - - In the process of checking, various constraints will be placed on - these type variables through the subtyping relationships requested - through the `demand` module. The `infer` module is in charge - of resolving those constraints. - -- regionck: after main is complete, the regionck pass goes over all - types looking for regions and making sure that they did not escape - into places where they are not in scope. This may also influence the - final assignments of the various region variables if there is some - flexibility. - -- writeback: writes the final types within a function body, replacing - type variables with their final inferred types. These final types - are written into the `tcx.node_types` table, which should *never* contain - any reference to a type variable. - -## Intermediate types - -While type checking a function, the intermediate types for the -expressions, blocks, and so forth contained within the function are -stored in `fcx.node_types` and `fcx.node_substs`. These types -may contain unresolved type variables. After type checking is -complete, the functions in the writeback module are used to take the -types from this table, resolve them, and then write them into their -permanent home in the type context `tcx`. - -This means that during inferencing you should use `fcx.write_ty()` -and `fcx.expr_ty()` / `fcx.node_ty()` to write/obtain the types of -nodes within the function. - -The types of top-level items, which never contain unbound type -variables, are stored directly into the `tcx` typeck_results. - -N.B., a type variable is not the same thing as a type parameter. A -type variable is an instance of a type parameter. That is, -given a generic function `fn foo<T>(t: T)`, while checking the -function `foo`, the type `ty_param(0)` refers to the type `T`, which -is treated in abstract. However, when `foo()` is called, `T` will be -substituted for a fresh type variable `N`. This variable will -eventually be resolved to some concrete type (which might itself be -a type parameter). - -*/ - -pub mod _match; -mod autoderef; -mod callee; -pub mod cast; -mod check; -mod closure; -pub mod coercion; -mod compare_method; -pub mod demand; -mod diverges; -pub mod dropck; -mod expectation; -mod expr; -mod fallback; -mod fn_ctxt; -mod gather_locals; -mod generator_interior; -mod inherited; -pub mod intrinsic; -mod intrinsicck; -pub mod method; -mod op; -mod pat; -mod place_op; -mod region; -pub mod rvalue_scopes; -mod upvar; -pub mod wfcheck; -pub mod writeback; - -use check::{check_abi, check_fn, check_mod_item_types}; -pub use diverges::Diverges; -pub use expectation::Expectation; -pub use fn_ctxt::*; -pub use inherited::{Inherited, InheritedBuilder}; - -use crate::astconv::AstConv; -use crate::check::gather_locals::GatherLocalsVisitor; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_errors::{ - pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, MultiSpan, -}; -use rustc_hir as hir; -use rustc_hir::def::Res; -use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_hir::intravisit::Visitor; -use rustc_hir::{HirIdMap, ImplicitSelfKind, Node}; -use rustc_index::bit_set::BitSet; -use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; -use rustc_middle::ty::query::Providers; -use rustc_middle::ty::subst::{InternalSubsts, Subst, SubstsRef}; -use rustc_middle::ty::{self, Ty, TyCtxt, UserType}; -use rustc_session::config; -use rustc_session::parse::feature_err; -use rustc_session::Session; -use rustc_span::source_map::DUMMY_SP; -use rustc_span::symbol::{kw, Ident}; -use rustc_span::{self, BytePos, Span, Symbol}; -use rustc_target::abi::VariantIdx; -use rustc_target::spec::abi::Abi; -use rustc_trait_selection::traits; -use rustc_trait_selection::traits::error_reporting::recursive_type_with_infinite_size_error; -use rustc_trait_selection::traits::error_reporting::suggestions::ReturnsVisitor; -use std::cell::RefCell; -use std::num::NonZeroU32; - -use crate::require_c_abi_if_c_variadic; -use crate::util::common::indenter; - -use self::coercion::DynamicCoerceMany; -use self::compare_method::collect_trait_impl_trait_tys; -use self::region::region_scope_tree; -pub use self::Expectation::*; - -#[macro_export] -macro_rules! type_error_struct { - ($session:expr, $span:expr, $typ:expr, $code:ident, $($message:tt)*) => ({ - let mut err = rustc_errors::struct_span_err!($session, $span, $code, $($message)*); - - if $typ.references_error() { - err.downgrade_to_delayed_bug(); - } - - err - }) -} - -/// The type of a local binding, including the revealed type for anon types. -#[derive(Copy, Clone, Debug)] -pub struct LocalTy<'tcx> { - decl_ty: Ty<'tcx>, - revealed_ty: Ty<'tcx>, -} - -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub enum Needs { - MutPlace, - None, -} - -impl Needs { - fn maybe_mut_place(m: hir::Mutability) -> Self { - match m { - hir::Mutability::Mut => Needs::MutPlace, - hir::Mutability::Not => Needs::None, - } - } -} - -#[derive(Copy, Clone)] -pub struct UnsafetyState { - pub def: hir::HirId, - pub unsafety: hir::Unsafety, - from_fn: bool, -} - -impl UnsafetyState { - pub fn function(unsafety: hir::Unsafety, def: hir::HirId) -> UnsafetyState { - UnsafetyState { def, unsafety, from_fn: true } - } - - pub fn recurse(self, blk: &hir::Block<'_>) -> UnsafetyState { - use hir::BlockCheckMode; - match self.unsafety { - // If this unsafe, then if the outer function was already marked as - // unsafe we shouldn't attribute the unsafe'ness to the block. This - // way the block can be warned about instead of ignoring this - // extraneous block (functions are never warned about). - hir::Unsafety::Unsafe if self.from_fn => self, - - unsafety => { - let (unsafety, def) = match blk.rules { - BlockCheckMode::UnsafeBlock(..) => (hir::Unsafety::Unsafe, blk.hir_id), - BlockCheckMode::DefaultBlock => (unsafety, self.def), - }; - UnsafetyState { def, unsafety, from_fn: false } - } - } - } -} - -#[derive(Debug, Copy, Clone)] -pub enum PlaceOp { - Deref, - Index, -} - -pub struct BreakableCtxt<'tcx> { - may_break: bool, - - // this is `null` for loops where break with a value is illegal, - // such as `while`, `for`, and `while let` - coerce: Option<DynamicCoerceMany<'tcx>>, -} - -pub struct EnclosingBreakables<'tcx> { - stack: Vec<BreakableCtxt<'tcx>>, - by_id: HirIdMap<usize>, -} - -impl<'tcx> EnclosingBreakables<'tcx> { - fn find_breakable(&mut self, target_id: hir::HirId) -> &mut BreakableCtxt<'tcx> { - self.opt_find_breakable(target_id).unwrap_or_else(|| { - bug!("could not find enclosing breakable with id {}", target_id); - }) - } - - fn opt_find_breakable(&mut self, target_id: hir::HirId) -> Option<&mut BreakableCtxt<'tcx>> { - match self.by_id.get(&target_id) { - Some(ix) => Some(&mut self.stack[*ix]), - None => None, - } - } -} - -pub fn provide(providers: &mut Providers) { - method::provide(providers); - wfcheck::provide(providers); - *providers = Providers { - typeck_item_bodies, - typeck_const_arg, - typeck, - diagnostic_only_typeck, - has_typeck_results, - adt_destructor, - used_trait_imports, - check_mod_item_types, - region_scope_tree, - collect_trait_impl_trait_tys, - ..*providers - }; -} - -fn adt_destructor(tcx: TyCtxt<'_>, def_id: DefId) -> Option<ty::Destructor> { - tcx.calculate_dtor(def_id, dropck::check_drop_impl) -} - -/// If this `DefId` is a "primary tables entry", returns -/// `Some((body_id, body_ty, fn_sig))`. Otherwise, returns `None`. -/// -/// If this function returns `Some`, then `typeck_results(def_id)` will -/// succeed; if it returns `None`, then `typeck_results(def_id)` may or -/// may not succeed. In some cases where this function returns `None` -/// (notably closures), `typeck_results(def_id)` would wind up -/// redirecting to the owning function. -fn primary_body_of( - tcx: TyCtxt<'_>, - id: hir::HirId, -) -> Option<(hir::BodyId, Option<&hir::Ty<'_>>, Option<&hir::FnSig<'_>>)> { - match tcx.hir().get(id) { - Node::Item(item) => match item.kind { - hir::ItemKind::Const(ty, body) | hir::ItemKind::Static(ty, _, body) => { - Some((body, Some(ty), None)) - } - hir::ItemKind::Fn(ref sig, .., body) => Some((body, None, Some(sig))), - _ => None, - }, - Node::TraitItem(item) => match item.kind { - hir::TraitItemKind::Const(ty, Some(body)) => Some((body, Some(ty), None)), - hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Provided(body)) => { - Some((body, None, Some(sig))) - } - _ => None, - }, - Node::ImplItem(item) => match item.kind { - hir::ImplItemKind::Const(ty, body) => Some((body, Some(ty), None)), - hir::ImplItemKind::Fn(ref sig, body) => Some((body, None, Some(sig))), - _ => None, - }, - Node::AnonConst(constant) => Some((constant.body, None, None)), - _ => None, - } -} - -fn has_typeck_results(tcx: TyCtxt<'_>, def_id: DefId) -> bool { - // Closures' typeck results come from their outermost function, - // as they are part of the same "inference environment". - let typeck_root_def_id = tcx.typeck_root_def_id(def_id); - if typeck_root_def_id != def_id { - return tcx.has_typeck_results(typeck_root_def_id); - } - - if let Some(def_id) = def_id.as_local() { - let id = tcx.hir().local_def_id_to_hir_id(def_id); - primary_body_of(tcx, id).is_some() - } else { - false - } -} - -fn used_trait_imports(tcx: TyCtxt<'_>, def_id: LocalDefId) -> &FxHashSet<LocalDefId> { - &*tcx.typeck(def_id).used_trait_imports -} - -fn typeck_const_arg<'tcx>( - tcx: TyCtxt<'tcx>, - (did, param_did): (LocalDefId, DefId), -) -> &ty::TypeckResults<'tcx> { - let fallback = move || tcx.type_of(param_did); - typeck_with_fallback(tcx, did, fallback) -} - -fn typeck<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &ty::TypeckResults<'tcx> { - if let Some(param_did) = tcx.opt_const_param_of(def_id) { - tcx.typeck_const_arg((def_id, param_did)) - } else { - let fallback = move || tcx.type_of(def_id.to_def_id()); - typeck_with_fallback(tcx, def_id, fallback) - } -} - -/// Used only to get `TypeckResults` for type inference during error recovery. -/// Currently only used for type inference of `static`s and `const`s to avoid type cycle errors. -fn diagnostic_only_typeck<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> &ty::TypeckResults<'tcx> { - let fallback = move || { - let span = tcx.hir().span(tcx.hir().local_def_id_to_hir_id(def_id)); - tcx.ty_error_with_message(span, "diagnostic only typeck table used") - }; - typeck_with_fallback(tcx, def_id, fallback) -} - -fn typeck_with_fallback<'tcx>( - tcx: TyCtxt<'tcx>, - def_id: LocalDefId, - fallback: impl Fn() -> Ty<'tcx> + 'tcx, -) -> &'tcx ty::TypeckResults<'tcx> { - // Closures' typeck results come from their outermost function, - // as they are part of the same "inference environment". - let typeck_root_def_id = tcx.typeck_root_def_id(def_id.to_def_id()).expect_local(); - if typeck_root_def_id != def_id { - return tcx.typeck(typeck_root_def_id); - } - - let id = tcx.hir().local_def_id_to_hir_id(def_id); - let span = tcx.hir().span(id); - - // Figure out what primary body this item has. - let (body_id, body_ty, fn_sig) = primary_body_of(tcx, id).unwrap_or_else(|| { - span_bug!(span, "can't type-check body of {:?}", def_id); - }); - let body = tcx.hir().body(body_id); - - let typeck_results = Inherited::build(tcx, def_id).enter(|inh| { - let param_env = tcx.param_env(def_id); - let fcx = if let Some(hir::FnSig { header, decl, .. }) = fn_sig { - let fn_sig = if crate::collect::get_infer_ret_ty(&decl.output).is_some() { - let fcx = FnCtxt::new(&inh, param_env, body.value.hir_id); - <dyn AstConv<'_>>::ty_of_fn(&fcx, id, header.unsafety, header.abi, decl, None, None) - } else { - tcx.fn_sig(def_id) - }; - - check_abi(tcx, id, span, fn_sig.abi()); - - // Compute the function signature from point of view of inside the fn. - let fn_sig = tcx.liberate_late_bound_regions(def_id.to_def_id(), fn_sig); - let fn_sig = inh.normalize_associated_types_in( - body.value.span, - body_id.hir_id, - param_env, - fn_sig, - ); - check_fn(&inh, param_env, fn_sig, decl, id, body, None, true).0 - } else { - let fcx = FnCtxt::new(&inh, param_env, body.value.hir_id); - let expected_type = body_ty - .and_then(|ty| match ty.kind { - hir::TyKind::Infer => Some(<dyn AstConv<'_>>::ast_ty_to_ty(&fcx, ty)), - _ => None, - }) - .unwrap_or_else(|| match tcx.hir().get(id) { - Node::AnonConst(_) => match tcx.hir().get(tcx.hir().get_parent_node(id)) { - Node::Expr(&hir::Expr { - kind: hir::ExprKind::ConstBlock(ref anon_const), - .. - }) if anon_const.hir_id == id => fcx.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::TypeInference, - span, - }), - Node::Ty(&hir::Ty { - kind: hir::TyKind::Typeof(ref anon_const), .. - }) if anon_const.hir_id == id => fcx.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::TypeInference, - span, - }), - Node::Expr(&hir::Expr { kind: hir::ExprKind::InlineAsm(asm), .. }) - | Node::Item(&hir::Item { kind: hir::ItemKind::GlobalAsm(asm), .. }) => { - let operand_ty = asm - .operands - .iter() - .filter_map(|(op, _op_sp)| match op { - hir::InlineAsmOperand::Const { anon_const } - if anon_const.hir_id == id => - { - // Inline assembly constants must be integers. - Some(fcx.next_int_var()) - } - hir::InlineAsmOperand::SymFn { anon_const } - if anon_const.hir_id == id => - { - Some(fcx.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::MiscVariable, - span, - })) - } - _ => None, - }) - .next(); - operand_ty.unwrap_or_else(fallback) - } - _ => fallback(), - }, - _ => fallback(), - }); - - let expected_type = fcx.normalize_associated_types_in(body.value.span, expected_type); - fcx.require_type_is_sized(expected_type, body.value.span, traits::ConstSized); - - // Gather locals in statics (because of block expressions). - GatherLocalsVisitor::new(&fcx).visit_body(body); - - fcx.check_expr_coercable_to_type(&body.value, expected_type, None); - - fcx.write_ty(id, expected_type); - - fcx - }; - - let fallback_has_occurred = fcx.type_inference_fallback(); - - // Even though coercion casts provide type hints, we check casts after fallback for - // backwards compatibility. This makes fallback a stronger type hint than a cast coercion. - fcx.check_casts(); - fcx.select_obligations_where_possible(fallback_has_occurred, |_| {}); - - // Closure and generator analysis may run after fallback - // because they don't constrain other type variables. - fcx.closure_analyze(body); - assert!(fcx.deferred_call_resolutions.borrow().is_empty()); - // Before the generator analysis, temporary scopes shall be marked to provide more - // precise information on types to be captured. - fcx.resolve_rvalue_scopes(def_id.to_def_id()); - fcx.resolve_generator_interiors(def_id.to_def_id()); - - for (ty, span, code) in fcx.deferred_sized_obligations.borrow_mut().drain(..) { - let ty = fcx.normalize_ty(span, ty); - fcx.require_type_is_sized(ty, span, code); - } - - fcx.select_all_obligations_or_error(); - - if !fcx.infcx.is_tainted_by_errors() { - fcx.check_transmutes(); - } - - fcx.check_asms(); - - fcx.infcx.skip_region_resolution(); - - fcx.resolve_type_vars_in_body(body) - }); - - // Consistency check our TypeckResults instance can hold all ItemLocalIds - // it will need to hold. - assert_eq!(typeck_results.hir_owner, id.owner); - - typeck_results -} - -/// When `check_fn` is invoked on a generator (i.e., a body that -/// includes yield), it returns back some information about the yield -/// points. -struct GeneratorTypes<'tcx> { - /// Type of generator argument / values returned by `yield`. - resume_ty: Ty<'tcx>, - - /// Type of value that is yielded. - yield_ty: Ty<'tcx>, - - /// Types that are captured (see `GeneratorInterior` for more). - interior: Ty<'tcx>, - - /// Indicates if the generator is movable or static (immovable). - movability: hir::Movability, -} - -/// Given a `DefId` for an opaque type in return position, find its parent item's return -/// expressions. -fn get_owner_return_paths<'tcx>( - tcx: TyCtxt<'tcx>, - def_id: LocalDefId, -) -> Option<(LocalDefId, ReturnsVisitor<'tcx>)> { - let hir_id = tcx.hir().local_def_id_to_hir_id(def_id); - let parent_id = tcx.hir().get_parent_item(hir_id); - tcx.hir().find_by_def_id(parent_id).and_then(|node| node.body_id()).map(|body_id| { - let body = tcx.hir().body(body_id); - let mut visitor = ReturnsVisitor::default(); - visitor.visit_body(body); - (parent_id, visitor) - }) -} - -// Forbid defining intrinsics in Rust code, -// as they must always be defined by the compiler. -fn fn_maybe_err(tcx: TyCtxt<'_>, sp: Span, abi: Abi) { - if let Abi::RustIntrinsic | Abi::PlatformIntrinsic = abi { - tcx.sess.span_err(sp, "intrinsic must be in `extern \"rust-intrinsic\" { ... }` block"); - } -} - -fn maybe_check_static_with_link_section(tcx: TyCtxt<'_>, id: LocalDefId) { - // Only restricted on wasm target for now - if !tcx.sess.target.is_like_wasm { - return; - } - - // If `#[link_section]` is missing, then nothing to verify - let attrs = tcx.codegen_fn_attrs(id); - if attrs.link_section.is_none() { - return; - } - - // For the wasm32 target statics with `#[link_section]` are placed into custom - // sections of the final output file, but this isn't link custom sections of - // other executable formats. Namely we can only embed a list of bytes, - // nothing with provenance (pointers to anything else). If any provenance - // show up, reject it here. - // `#[link_section]` may contain arbitrary, or even undefined bytes, but it is - // the consumer's responsibility to ensure all bytes that have been read - // have defined values. - if let Ok(alloc) = tcx.eval_static_initializer(id.to_def_id()) - && alloc.inner().provenance().len() != 0 - { - let msg = "statics with a custom `#[link_section]` must be a \ - simple list of bytes on the wasm target with no \ - extra levels of indirection such as references"; - tcx.sess.span_err(tcx.def_span(id), msg); - } -} - -fn report_forbidden_specialization( - tcx: TyCtxt<'_>, - impl_item: &hir::ImplItemRef, - parent_impl: DefId, -) { - let mut err = struct_span_err!( - tcx.sess, - impl_item.span, - E0520, - "`{}` specializes an item from a parent `impl`, but \ - that item is not marked `default`", - impl_item.ident - ); - err.span_label(impl_item.span, format!("cannot specialize default item `{}`", impl_item.ident)); - - match tcx.span_of_impl(parent_impl) { - Ok(span) => { - err.span_label(span, "parent `impl` is here"); - err.note(&format!( - "to specialize, `{}` in the parent `impl` must be marked `default`", - impl_item.ident - )); - } - Err(cname) => { - err.note(&format!("parent implementation is in crate `{cname}`")); - } - } - - err.emit(); -} - -fn missing_items_err( - tcx: TyCtxt<'_>, - impl_span: Span, - missing_items: &[&ty::AssocItem], - full_impl_span: Span, -) { - let missing_items_msg = missing_items - .iter() - .map(|trait_item| trait_item.name.to_string()) - .collect::<Vec<_>>() - .join("`, `"); - - let mut err = struct_span_err!( - tcx.sess, - impl_span, - E0046, - "not all trait items implemented, missing: `{missing_items_msg}`", - ); - err.span_label(impl_span, format!("missing `{missing_items_msg}` in implementation")); - - // `Span` before impl block closing brace. - let hi = full_impl_span.hi() - BytePos(1); - // Point at the place right before the closing brace of the relevant `impl` to suggest - // adding the associated item at the end of its body. - let sugg_sp = full_impl_span.with_lo(hi).with_hi(hi); - // Obtain the level of indentation ending in `sugg_sp`. - let padding = - tcx.sess.source_map().indentation_before(sugg_sp).unwrap_or_else(|| String::new()); - - for trait_item in missing_items { - let snippet = suggestion_signature(trait_item, tcx); - let code = format!("{}{}\n{}", padding, snippet, padding); - let msg = format!("implement the missing item: `{snippet}`"); - let appl = Applicability::HasPlaceholders; - if let Some(span) = tcx.hir().span_if_local(trait_item.def_id) { - err.span_label(span, format!("`{}` from trait", trait_item.name)); - err.tool_only_span_suggestion(sugg_sp, &msg, code, appl); - } else { - err.span_suggestion_hidden(sugg_sp, &msg, code, appl); - } - } - err.emit(); -} - -fn missing_items_must_implement_one_of_err( - tcx: TyCtxt<'_>, - impl_span: Span, - missing_items: &[Ident], - annotation_span: Option<Span>, -) { - let missing_items_msg = - missing_items.iter().map(Ident::to_string).collect::<Vec<_>>().join("`, `"); - - let mut err = struct_span_err!( - tcx.sess, - impl_span, - E0046, - "not all trait items implemented, missing one of: `{missing_items_msg}`", - ); - err.span_label(impl_span, format!("missing one of `{missing_items_msg}` in implementation")); - - if let Some(annotation_span) = annotation_span { - err.span_note(annotation_span, "required because of this annotation"); - } - - err.emit(); -} - -fn default_body_is_unstable( - tcx: TyCtxt<'_>, - impl_span: Span, - item_did: DefId, - feature: Symbol, - reason: Option<Symbol>, - issue: Option<NonZeroU32>, -) { - let missing_item_name = &tcx.associated_item(item_did).name; - let use_of_unstable_library_feature_note = match reason { - Some(r) => format!("use of unstable library feature '{feature}': {r}"), - None => format!("use of unstable library feature '{feature}'"), - }; - - let mut err = struct_span_err!( - tcx.sess, - impl_span, - E0046, - "not all trait items implemented, missing: `{missing_item_name}`", - ); - err.note(format!("default implementation of `{missing_item_name}` is unstable")); - err.note(use_of_unstable_library_feature_note); - rustc_session::parse::add_feature_diagnostics_for_issue( - &mut err, - &tcx.sess.parse_sess, - feature, - rustc_feature::GateIssue::Library(issue), - ); - err.emit(); -} - -/// Re-sugar `ty::GenericPredicates` in a way suitable to be used in structured suggestions. -fn bounds_from_generic_predicates<'tcx>( - tcx: TyCtxt<'tcx>, - predicates: ty::GenericPredicates<'tcx>, -) -> (String, String) { - let mut types: FxHashMap<Ty<'tcx>, Vec<DefId>> = FxHashMap::default(); - let mut projections = vec![]; - for (predicate, _) in predicates.predicates { - debug!("predicate {:?}", predicate); - let bound_predicate = predicate.kind(); - match bound_predicate.skip_binder() { - ty::PredicateKind::Trait(trait_predicate) => { - let entry = types.entry(trait_predicate.self_ty()).or_default(); - let def_id = trait_predicate.def_id(); - if Some(def_id) != tcx.lang_items().sized_trait() { - // Type params are `Sized` by default, do not add that restriction to the list - // if it is a positive requirement. - entry.push(trait_predicate.def_id()); - } - } - ty::PredicateKind::Projection(projection_pred) => { - projections.push(bound_predicate.rebind(projection_pred)); - } - _ => {} - } - } - let generics = if types.is_empty() { - "".to_string() - } else { - format!( - "<{}>", - types - .keys() - .filter_map(|t| match t.kind() { - ty::Param(_) => Some(t.to_string()), - // Avoid suggesting the following: - // fn foo<T, <T as Trait>::Bar>(_: T) where T: Trait, <T as Trait>::Bar: Other {} - _ => None, - }) - .collect::<Vec<_>>() - .join(", ") - ) - }; - let mut where_clauses = vec![]; - for (ty, bounds) in types { - where_clauses - .extend(bounds.into_iter().map(|bound| format!("{}: {}", ty, tcx.def_path_str(bound)))); - } - for projection in &projections { - let p = projection.skip_binder(); - // FIXME: this is not currently supported syntax, we should be looking at the `types` and - // insert the associated types where they correspond, but for now let's be "lazy" and - // propose this instead of the following valid resugaring: - // `T: Trait, Trait::Assoc = K` → `T: Trait<Assoc = K>` - where_clauses.push(format!( - "{} = {}", - tcx.def_path_str(p.projection_ty.item_def_id), - p.term, - )); - } - let where_clauses = if where_clauses.is_empty() { - String::new() - } else { - format!(" where {}", where_clauses.join(", ")) - }; - (generics, where_clauses) -} - -/// Return placeholder code for the given function. -fn fn_sig_suggestion<'tcx>( - tcx: TyCtxt<'tcx>, - sig: ty::FnSig<'tcx>, - ident: Ident, - predicates: ty::GenericPredicates<'tcx>, - assoc: &ty::AssocItem, -) -> String { - let args = sig - .inputs() - .iter() - .enumerate() - .map(|(i, ty)| { - Some(match ty.kind() { - ty::Param(_) if assoc.fn_has_self_parameter && i == 0 => "self".to_string(), - ty::Ref(reg, ref_ty, mutability) if i == 0 => { - let reg = format!("{reg} "); - let reg = match ®[..] { - "'_ " | " " => "", - reg => reg, - }; - if assoc.fn_has_self_parameter { - match ref_ty.kind() { - ty::Param(param) if param.name == kw::SelfUpper => { - format!("&{}{}self", reg, mutability.prefix_str()) - } - - _ => format!("self: {ty}"), - } - } else { - format!("_: {ty}") - } - } - _ => { - if assoc.fn_has_self_parameter && i == 0 { - format!("self: {ty}") - } else { - format!("_: {ty}") - } - } - }) - }) - .chain(std::iter::once(if sig.c_variadic { Some("...".to_string()) } else { None })) - .flatten() - .collect::<Vec<String>>() - .join(", "); - let output = sig.output(); - let output = if !output.is_unit() { format!(" -> {output}") } else { String::new() }; - - let unsafety = sig.unsafety.prefix_str(); - let (generics, where_clauses) = bounds_from_generic_predicates(tcx, predicates); - - // FIXME: this is not entirely correct, as the lifetimes from borrowed params will - // not be present in the `fn` definition, not will we account for renamed - // lifetimes between the `impl` and the `trait`, but this should be good enough to - // fill in a significant portion of the missing code, and other subsequent - // suggestions can help the user fix the code. - format!("{unsafety}fn {ident}{generics}({args}){output}{where_clauses} {{ todo!() }}") -} - -/// Return placeholder code for the given associated item. -/// Similar to `ty::AssocItem::suggestion`, but appropriate for use as the code snippet of a -/// structured suggestion. -fn suggestion_signature(assoc: &ty::AssocItem, tcx: TyCtxt<'_>) -> String { - match assoc.kind { - ty::AssocKind::Fn => { - // We skip the binder here because the binder would deanonymize all - // late-bound regions, and we don't want method signatures to show up - // `as for<'r> fn(&'r MyType)`. Pretty-printing handles late-bound - // regions just fine, showing `fn(&MyType)`. - fn_sig_suggestion( - tcx, - tcx.fn_sig(assoc.def_id).skip_binder(), - assoc.ident(tcx), - tcx.predicates_of(assoc.def_id), - assoc, - ) - } - ty::AssocKind::Type => format!("type {} = Type;", assoc.name), - ty::AssocKind::Const => { - let ty = tcx.type_of(assoc.def_id); - let val = expr::ty_kind_suggestion(ty).unwrap_or("value"); - format!("const {}: {} = {};", assoc.name, ty, val) - } - } -} - -/// Emit an error when encountering two or more variants in a transparent enum. -fn bad_variant_count<'tcx>(tcx: TyCtxt<'tcx>, adt: ty::AdtDef<'tcx>, sp: Span, did: DefId) { - let variant_spans: Vec<_> = adt - .variants() - .iter() - .map(|variant| tcx.hir().span_if_local(variant.def_id).unwrap()) - .collect(); - let msg = format!("needs exactly one variant, but has {}", adt.variants().len(),); - let mut err = struct_span_err!(tcx.sess, sp, E0731, "transparent enum {msg}"); - err.span_label(sp, &msg); - if let [start @ .., end] = &*variant_spans { - for variant_span in start { - err.span_label(*variant_span, ""); - } - err.span_label(*end, &format!("too many variants in `{}`", tcx.def_path_str(did))); - } - err.emit(); -} - -/// Emit an error when encountering two or more non-zero-sized fields in a transparent -/// enum. -fn bad_non_zero_sized_fields<'tcx>( - tcx: TyCtxt<'tcx>, - adt: ty::AdtDef<'tcx>, - field_count: usize, - field_spans: impl Iterator<Item = Span>, - sp: Span, -) { - let msg = format!("needs at most one non-zero-sized field, but has {field_count}"); - let mut err = struct_span_err!( - tcx.sess, - sp, - E0690, - "{}transparent {} {}", - if adt.is_enum() { "the variant of a " } else { "" }, - adt.descr(), - msg, - ); - err.span_label(sp, &msg); - for sp in field_spans { - err.span_label(sp, "this field is non-zero-sized"); - } - err.emit(); -} - -fn report_unexpected_variant_res(tcx: TyCtxt<'_>, res: Res, qpath: &hir::QPath<'_>, span: Span) { - struct_span_err!( - tcx.sess, - span, - E0533, - "expected unit struct, unit variant or constant, found {} `{}`", - res.descr(), - rustc_hir_pretty::qpath_to_string(qpath), - ) - .emit(); -} - -/// Controls whether the arguments are tupled. This is used for the call -/// operator. -/// -/// Tupling means that all call-side arguments are packed into a tuple and -/// passed as a single parameter. For example, if tupling is enabled, this -/// function: -/// ``` -/// fn f(x: (isize, isize)) {} -/// ``` -/// Can be called as: -/// ```ignore UNSOLVED (can this be done in user code?) -/// # fn f(x: (isize, isize)) {} -/// f(1, 2); -/// ``` -/// Instead of: -/// ``` -/// # fn f(x: (isize, isize)) {} -/// f((1, 2)); -/// ``` -#[derive(Clone, Eq, PartialEq)] -enum TupleArgumentsFlag { - DontTupleArguments, - TupleArguments, -} - -fn typeck_item_bodies(tcx: TyCtxt<'_>, (): ()) { - tcx.hir().par_body_owners(|body_owner_def_id| tcx.ensure().typeck(body_owner_def_id)); -} - -fn fatally_break_rust(sess: &Session) { - let handler = sess.diagnostic(); - handler.span_bug_no_panic( - MultiSpan::new(), - "It looks like you're trying to break rust; would you like some ICE?", - ); - handler.note_without_error("the compiler expectedly panicked. this is a feature."); - handler.note_without_error( - "we would appreciate a joke overview: \ - https://github.com/rust-lang/rust/issues/43162#issuecomment-320764675", - ); - handler.note_without_error(&format!( - "rustc {} running on {}", - option_env!("CFG_VERSION").unwrap_or("unknown_version"), - config::host_triple(), - )); -} - -fn potentially_plural_count(count: usize, word: &str) -> String { - format!("{} {}{}", count, word, pluralize!(count)) -} - -fn has_expected_num_generic_args<'tcx>( - tcx: TyCtxt<'tcx>, - trait_did: Option<DefId>, - expected: usize, -) -> bool { - trait_did.map_or(true, |trait_did| { - let generics = tcx.generics_of(trait_did); - generics.count() == expected + if generics.has_self { 1 } else { 0 } - }) -} diff --git a/compiler/rustc_typeck/src/check/op.rs b/compiler/rustc_typeck/src/check/op.rs deleted file mode 100644 index 4754717c2..000000000 --- a/compiler/rustc_typeck/src/check/op.rs +++ /dev/null @@ -1,993 +0,0 @@ -//! Code related to processing overloaded binary and unary operators. - -use super::method::MethodCallee; -use super::{has_expected_num_generic_args, FnCtxt}; -use crate::check::Expectation; -use rustc_ast as ast; -use rustc_errors::{self, struct_span_err, Applicability, Diagnostic}; -use rustc_hir as hir; -use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; -use rustc_infer::traits::ObligationCauseCode; -use rustc_middle::ty::adjustment::{ - Adjust, Adjustment, AllowTwoPhase, AutoBorrow, AutoBorrowMutability, -}; -use rustc_middle::ty::print::with_no_trimmed_paths; -use rustc_middle::ty::{self, DefIdTree, Ty, TyCtxt, TypeFolder, TypeSuperFoldable, TypeVisitable}; -use rustc_span::source_map::Spanned; -use rustc_span::symbol::{sym, Ident}; -use rustc_span::Span; -use rustc_trait_selection::infer::InferCtxtExt; -use rustc_trait_selection::traits::error_reporting::suggestions::InferCtxtExt as _; -use rustc_trait_selection::traits::{FulfillmentError, TraitEngine, TraitEngineExt}; -use rustc_type_ir::sty::TyKind::*; - -impl<'a, 'tcx> FnCtxt<'a, 'tcx> { - /// Checks a `a <op>= b` - pub fn check_binop_assign( - &self, - expr: &'tcx hir::Expr<'tcx>, - op: hir::BinOp, - lhs: &'tcx hir::Expr<'tcx>, - rhs: &'tcx hir::Expr<'tcx>, - expected: Expectation<'tcx>, - ) -> Ty<'tcx> { - let (lhs_ty, rhs_ty, return_ty) = - self.check_overloaded_binop(expr, lhs, rhs, op, IsAssign::Yes, expected); - - let ty = - if !lhs_ty.is_ty_var() && !rhs_ty.is_ty_var() && is_builtin_binop(lhs_ty, rhs_ty, op) { - self.enforce_builtin_binop_types(lhs.span, lhs_ty, rhs.span, rhs_ty, op); - self.tcx.mk_unit() - } else { - return_ty - }; - - self.check_lhs_assignable(lhs, "E0067", op.span, |err| { - if let Some(lhs_deref_ty) = self.deref_once_mutably_for_diagnostic(lhs_ty) { - if self - .lookup_op_method( - lhs_deref_ty, - Some(rhs_ty), - Some(rhs), - Op::Binary(op, IsAssign::Yes), - expected, - ) - .is_ok() - { - // If LHS += RHS is an error, but *LHS += RHS is successful, then we will have - // emitted a better suggestion during error handling in check_overloaded_binop. - if self - .lookup_op_method( - lhs_ty, - Some(rhs_ty), - Some(rhs), - Op::Binary(op, IsAssign::Yes), - expected, - ) - .is_err() - { - err.downgrade_to_delayed_bug(); - } else { - // Otherwise, it's valid to suggest dereferencing the LHS here. - err.span_suggestion_verbose( - lhs.span.shrink_to_lo(), - "consider dereferencing the left-hand side of this operation", - "*", - Applicability::MaybeIncorrect, - ); - } - } - } - }); - - ty - } - - /// Checks a potentially overloaded binary operator. - pub fn check_binop( - &self, - expr: &'tcx hir::Expr<'tcx>, - op: hir::BinOp, - lhs_expr: &'tcx hir::Expr<'tcx>, - rhs_expr: &'tcx hir::Expr<'tcx>, - expected: Expectation<'tcx>, - ) -> Ty<'tcx> { - let tcx = self.tcx; - - debug!( - "check_binop(expr.hir_id={}, expr={:?}, op={:?}, lhs_expr={:?}, rhs_expr={:?})", - expr.hir_id, expr, op, lhs_expr, rhs_expr - ); - - match BinOpCategory::from(op) { - BinOpCategory::Shortcircuit => { - // && and || are a simple case. - self.check_expr_coercable_to_type(lhs_expr, tcx.types.bool, None); - let lhs_diverges = self.diverges.get(); - self.check_expr_coercable_to_type(rhs_expr, tcx.types.bool, None); - - // Depending on the LHS' value, the RHS can never execute. - self.diverges.set(lhs_diverges); - - tcx.types.bool - } - _ => { - // Otherwise, we always treat operators as if they are - // overloaded. This is the way to be most flexible w/r/t - // types that get inferred. - let (lhs_ty, rhs_ty, return_ty) = self.check_overloaded_binop( - expr, - lhs_expr, - rhs_expr, - op, - IsAssign::No, - expected, - ); - - // Supply type inference hints if relevant. Probably these - // hints should be enforced during select as part of the - // `consider_unification_despite_ambiguity` routine, but this - // more convenient for now. - // - // The basic idea is to help type inference by taking - // advantage of things we know about how the impls for - // scalar types are arranged. This is important in a - // scenario like `1_u32 << 2`, because it lets us quickly - // deduce that the result type should be `u32`, even - // though we don't know yet what type 2 has and hence - // can't pin this down to a specific impl. - if !lhs_ty.is_ty_var() - && !rhs_ty.is_ty_var() - && is_builtin_binop(lhs_ty, rhs_ty, op) - { - let builtin_return_ty = self.enforce_builtin_binop_types( - lhs_expr.span, - lhs_ty, - rhs_expr.span, - rhs_ty, - op, - ); - self.demand_suptype(expr.span, builtin_return_ty, return_ty); - } - - return_ty - } - } - } - - fn enforce_builtin_binop_types( - &self, - lhs_span: Span, - lhs_ty: Ty<'tcx>, - rhs_span: Span, - rhs_ty: Ty<'tcx>, - op: hir::BinOp, - ) -> Ty<'tcx> { - debug_assert!(is_builtin_binop(lhs_ty, rhs_ty, op)); - - // Special-case a single layer of referencing, so that things like `5.0 + &6.0f32` work. - // (See https://github.com/rust-lang/rust/issues/57447.) - let (lhs_ty, rhs_ty) = (deref_ty_if_possible(lhs_ty), deref_ty_if_possible(rhs_ty)); - - let tcx = self.tcx; - match BinOpCategory::from(op) { - BinOpCategory::Shortcircuit => { - self.demand_suptype(lhs_span, tcx.types.bool, lhs_ty); - self.demand_suptype(rhs_span, tcx.types.bool, rhs_ty); - tcx.types.bool - } - - BinOpCategory::Shift => { - // result type is same as LHS always - lhs_ty - } - - BinOpCategory::Math | BinOpCategory::Bitwise => { - // both LHS and RHS and result will have the same type - self.demand_suptype(rhs_span, lhs_ty, rhs_ty); - lhs_ty - } - - BinOpCategory::Comparison => { - // both LHS and RHS and result will have the same type - self.demand_suptype(rhs_span, lhs_ty, rhs_ty); - tcx.types.bool - } - } - } - - fn check_overloaded_binop( - &self, - expr: &'tcx hir::Expr<'tcx>, - lhs_expr: &'tcx hir::Expr<'tcx>, - rhs_expr: &'tcx hir::Expr<'tcx>, - op: hir::BinOp, - is_assign: IsAssign, - expected: Expectation<'tcx>, - ) -> (Ty<'tcx>, Ty<'tcx>, Ty<'tcx>) { - debug!( - "check_overloaded_binop(expr.hir_id={}, op={:?}, is_assign={:?})", - expr.hir_id, op, is_assign - ); - - let lhs_ty = match is_assign { - IsAssign::No => { - // Find a suitable supertype of the LHS expression's type, by coercing to - // a type variable, to pass as the `Self` to the trait, avoiding invariant - // trait matching creating lifetime constraints that are too strict. - // e.g., adding `&'a T` and `&'b T`, given `&'x T: Add<&'x T>`, will result - // in `&'a T <: &'x T` and `&'b T <: &'x T`, instead of `'a = 'b = 'x`. - let lhs_ty = self.check_expr(lhs_expr); - let fresh_var = self.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::MiscVariable, - span: lhs_expr.span, - }); - self.demand_coerce(lhs_expr, lhs_ty, fresh_var, Some(rhs_expr), AllowTwoPhase::No) - } - IsAssign::Yes => { - // rust-lang/rust#52126: We have to use strict - // equivalence on the LHS of an assign-op like `+=`; - // overwritten or mutably-borrowed places cannot be - // coerced to a supertype. - self.check_expr(lhs_expr) - } - }; - let lhs_ty = self.resolve_vars_with_obligations(lhs_ty); - - // N.B., as we have not yet type-checked the RHS, we don't have the - // type at hand. Make a variable to represent it. The whole reason - // for this indirection is so that, below, we can check the expr - // using this variable as the expected type, which sometimes lets - // us do better coercions than we would be able to do otherwise, - // particularly for things like `String + &String`. - let rhs_ty_var = self.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::MiscVariable, - span: rhs_expr.span, - }); - - let result = self.lookup_op_method( - lhs_ty, - Some(rhs_ty_var), - Some(rhs_expr), - Op::Binary(op, is_assign), - expected, - ); - - // see `NB` above - let rhs_ty = self.check_expr_coercable_to_type(rhs_expr, rhs_ty_var, Some(lhs_expr)); - let rhs_ty = self.resolve_vars_with_obligations(rhs_ty); - - let return_ty = match result { - Ok(method) => { - let by_ref_binop = !op.node.is_by_value(); - if is_assign == IsAssign::Yes || by_ref_binop { - if let ty::Ref(region, _, mutbl) = method.sig.inputs()[0].kind() { - let mutbl = match mutbl { - hir::Mutability::Not => AutoBorrowMutability::Not, - hir::Mutability::Mut => AutoBorrowMutability::Mut { - // Allow two-phase borrows for binops in initial deployment - // since they desugar to methods - allow_two_phase_borrow: AllowTwoPhase::Yes, - }, - }; - let autoref = Adjustment { - kind: Adjust::Borrow(AutoBorrow::Ref(*region, mutbl)), - target: method.sig.inputs()[0], - }; - self.apply_adjustments(lhs_expr, vec![autoref]); - } - } - if by_ref_binop { - if let ty::Ref(region, _, mutbl) = method.sig.inputs()[1].kind() { - let mutbl = match mutbl { - hir::Mutability::Not => AutoBorrowMutability::Not, - hir::Mutability::Mut => AutoBorrowMutability::Mut { - // Allow two-phase borrows for binops in initial deployment - // since they desugar to methods - allow_two_phase_borrow: AllowTwoPhase::Yes, - }, - }; - let autoref = Adjustment { - kind: Adjust::Borrow(AutoBorrow::Ref(*region, mutbl)), - target: method.sig.inputs()[1], - }; - // HACK(eddyb) Bypass checks due to reborrows being in - // some cases applied on the RHS, on top of which we need - // to autoref, which is not allowed by apply_adjustments. - // self.apply_adjustments(rhs_expr, vec![autoref]); - self.typeck_results - .borrow_mut() - .adjustments_mut() - .entry(rhs_expr.hir_id) - .or_default() - .push(autoref); - } - } - self.write_method_call(expr.hir_id, method); - - method.sig.output() - } - // error types are considered "builtin" - Err(_) if lhs_ty.references_error() || rhs_ty.references_error() => self.tcx.ty_error(), - Err(errors) => { - let (_, trait_def_id) = - lang_item_for_op(self.tcx, Op::Binary(op, is_assign), op.span); - let missing_trait = trait_def_id - .map(|def_id| with_no_trimmed_paths!(self.tcx.def_path_str(def_id))); - let (mut err, output_def_id) = match is_assign { - IsAssign::Yes => { - let mut err = struct_span_err!( - self.tcx.sess, - expr.span, - E0368, - "binary assignment operation `{}=` cannot be applied to type `{}`", - op.node.as_str(), - lhs_ty, - ); - err.span_label( - lhs_expr.span, - format!("cannot use `{}=` on type `{}`", op.node.as_str(), lhs_ty), - ); - self.note_unmet_impls_on_type(&mut err, errors); - (err, None) - } - IsAssign::No => { - let message = match op.node { - hir::BinOpKind::Add => { - format!("cannot add `{rhs_ty}` to `{lhs_ty}`") - } - hir::BinOpKind::Sub => { - format!("cannot subtract `{rhs_ty}` from `{lhs_ty}`") - } - hir::BinOpKind::Mul => { - format!("cannot multiply `{lhs_ty}` by `{rhs_ty}`") - } - hir::BinOpKind::Div => { - format!("cannot divide `{lhs_ty}` by `{rhs_ty}`") - } - hir::BinOpKind::Rem => { - format!("cannot mod `{lhs_ty}` by `{rhs_ty}`") - } - hir::BinOpKind::BitAnd => { - format!("no implementation for `{lhs_ty} & {rhs_ty}`") - } - hir::BinOpKind::BitXor => { - format!("no implementation for `{lhs_ty} ^ {rhs_ty}`") - } - hir::BinOpKind::BitOr => { - format!("no implementation for `{lhs_ty} | {rhs_ty}`") - } - hir::BinOpKind::Shl => { - format!("no implementation for `{lhs_ty} << {rhs_ty}`") - } - hir::BinOpKind::Shr => { - format!("no implementation for `{lhs_ty} >> {rhs_ty}`") - } - _ => format!( - "binary operation `{}` cannot be applied to type `{}`", - op.node.as_str(), - lhs_ty - ), - }; - let output_def_id = trait_def_id.and_then(|def_id| { - self.tcx - .associated_item_def_ids(def_id) - .iter() - .find(|item_def_id| { - self.tcx.associated_item(*item_def_id).name == sym::Output - }) - .cloned() - }); - let mut err = struct_span_err!(self.tcx.sess, op.span, E0369, "{message}"); - if !lhs_expr.span.eq(&rhs_expr.span) { - err.span_label(lhs_expr.span, lhs_ty.to_string()); - err.span_label(rhs_expr.span, rhs_ty.to_string()); - } - self.note_unmet_impls_on_type(&mut err, errors); - (err, output_def_id) - } - }; - - let mut suggest_deref_binop = |lhs_deref_ty: Ty<'tcx>| { - if self - .lookup_op_method( - lhs_deref_ty, - Some(rhs_ty), - Some(rhs_expr), - Op::Binary(op, is_assign), - expected, - ) - .is_ok() - { - let msg = &format!( - "`{}{}` can be used on `{}` if you dereference the left-hand side", - op.node.as_str(), - match is_assign { - IsAssign::Yes => "=", - IsAssign::No => "", - }, - lhs_deref_ty, - ); - err.span_suggestion_verbose( - lhs_expr.span.shrink_to_lo(), - msg, - "*", - rustc_errors::Applicability::MachineApplicable, - ); - } - }; - - let is_compatible = |lhs_ty, rhs_ty| { - self.lookup_op_method( - lhs_ty, - Some(rhs_ty), - Some(rhs_expr), - Op::Binary(op, is_assign), - expected, - ) - .is_ok() - }; - - // We should suggest `a + b` => `*a + b` if `a` is copy, and suggest - // `a += b` => `*a += b` if a is a mut ref. - if !op.span.can_be_used_for_suggestions() { - // Suppress suggestions when lhs and rhs are not in the same span as the error - } else if is_assign == IsAssign::Yes - && let Some(lhs_deref_ty) = self.deref_once_mutably_for_diagnostic(lhs_ty) - { - suggest_deref_binop(lhs_deref_ty); - } else if is_assign == IsAssign::No - && let Ref(_, lhs_deref_ty, _) = lhs_ty.kind() - { - if self.type_is_copy_modulo_regions( - self.param_env, - *lhs_deref_ty, - lhs_expr.span, - ) { - suggest_deref_binop(*lhs_deref_ty); - } - } else if self.suggest_fn_call(&mut err, lhs_expr, lhs_ty, |lhs_ty| { - is_compatible(lhs_ty, rhs_ty) - }) || self.suggest_fn_call(&mut err, rhs_expr, rhs_ty, |rhs_ty| { - is_compatible(lhs_ty, rhs_ty) - }) || self.suggest_two_fn_call( - &mut err, - rhs_expr, - rhs_ty, - lhs_expr, - lhs_ty, - |lhs_ty, rhs_ty| is_compatible(lhs_ty, rhs_ty), - ) { - // Cool - } - - if let Some(missing_trait) = missing_trait { - if op.node == hir::BinOpKind::Add - && self.check_str_addition( - lhs_expr, rhs_expr, lhs_ty, rhs_ty, &mut err, is_assign, op, - ) - { - // This has nothing here because it means we did string - // concatenation (e.g., "Hello " + "World!"). This means - // we don't want the note in the else clause to be emitted - } else if lhs_ty.has_param_types_or_consts() { - // Look for a TraitPredicate in the Fulfillment errors, - // and use it to generate a suggestion. - // - // Note that lookup_op_method must be called again but - // with a specific rhs_ty instead of a placeholder so - // the resulting predicate generates a more specific - // suggestion for the user. - let errors = self - .lookup_op_method( - lhs_ty, - Some(rhs_ty), - Some(rhs_expr), - Op::Binary(op, is_assign), - expected, - ) - .unwrap_err(); - if !errors.is_empty() { - for error in errors { - if let Some(trait_pred) = - error.obligation.predicate.to_opt_poly_trait_pred() - { - let output_associated_item = match error.obligation.cause.code() - { - ObligationCauseCode::BinOp { - output_ty: Some(output_ty), - .. - } => { - // Make sure that we're attaching `Output = ..` to the right trait predicate - if let Some(output_def_id) = output_def_id - && let Some(trait_def_id) = trait_def_id - && self.tcx.parent(output_def_id) == trait_def_id - { - Some(("Output", *output_ty)) - } else { - None - } - } - _ => None, - }; - - self.suggest_restricting_param_bound( - &mut err, - trait_pred, - output_associated_item, - self.body_id, - ); - } - } - } else { - // When we know that a missing bound is responsible, we don't show - // this note as it is redundant. - err.note(&format!( - "the trait `{missing_trait}` is not implemented for `{lhs_ty}`" - )); - } - } - } - err.emit(); - self.tcx.ty_error() - } - }; - - (lhs_ty, rhs_ty, return_ty) - } - - /// Provide actionable suggestions when trying to add two strings with incorrect types, - /// like `&str + &str`, `String + String` and `&str + &String`. - /// - /// If this function returns `true` it means a note was printed, so we don't need - /// to print the normal "implementation of `std::ops::Add` might be missing" note - fn check_str_addition( - &self, - lhs_expr: &'tcx hir::Expr<'tcx>, - rhs_expr: &'tcx hir::Expr<'tcx>, - lhs_ty: Ty<'tcx>, - rhs_ty: Ty<'tcx>, - err: &mut Diagnostic, - is_assign: IsAssign, - op: hir::BinOp, - ) -> bool { - let str_concat_note = "string concatenation requires an owned `String` on the left"; - let rm_borrow_msg = "remove the borrow to obtain an owned `String`"; - let to_owned_msg = "create an owned `String` from a string reference"; - - let is_std_string = |ty: Ty<'tcx>| { - ty.ty_adt_def() - .map_or(false, |ty_def| self.tcx.is_diagnostic_item(sym::String, ty_def.did())) - }; - - match (lhs_ty.kind(), rhs_ty.kind()) { - (&Ref(_, l_ty, _), &Ref(_, r_ty, _)) // &str or &String + &str, &String or &&str - if (*l_ty.kind() == Str || is_std_string(l_ty)) - && (*r_ty.kind() == Str - || is_std_string(r_ty) - || matches!( - r_ty.kind(), Ref(_, inner_ty, _) if *inner_ty.kind() == Str - )) => - { - if let IsAssign::No = is_assign { // Do not supply this message if `&str += &str` - err.span_label(op.span, "`+` cannot be used to concatenate two `&str` strings"); - err.note(str_concat_note); - if let hir::ExprKind::AddrOf(_, _, lhs_inner_expr) = lhs_expr.kind { - err.span_suggestion_verbose( - lhs_expr.span.until(lhs_inner_expr.span), - rm_borrow_msg, - "", - Applicability::MachineApplicable - ); - } else { - err.span_suggestion_verbose( - lhs_expr.span.shrink_to_hi(), - to_owned_msg, - ".to_owned()", - Applicability::MachineApplicable - ); - } - } - true - } - (&Ref(_, l_ty, _), &Adt(..)) // Handle `&str` & `&String` + `String` - if (*l_ty.kind() == Str || is_std_string(l_ty)) && is_std_string(rhs_ty) => - { - err.span_label( - op.span, - "`+` cannot be used to concatenate a `&str` with a `String`", - ); - match is_assign { - IsAssign::No => { - let sugg_msg; - let lhs_sugg = if let hir::ExprKind::AddrOf(_, _, lhs_inner_expr) = lhs_expr.kind { - sugg_msg = "remove the borrow on the left and add one on the right"; - (lhs_expr.span.until(lhs_inner_expr.span), "".to_owned()) - } else { - sugg_msg = "create an owned `String` on the left and add a borrow on the right"; - (lhs_expr.span.shrink_to_hi(), ".to_owned()".to_owned()) - }; - let suggestions = vec![ - lhs_sugg, - (rhs_expr.span.shrink_to_lo(), "&".to_owned()), - ]; - err.multipart_suggestion_verbose( - sugg_msg, - suggestions, - Applicability::MachineApplicable, - ); - } - IsAssign::Yes => { - err.note(str_concat_note); - } - } - true - } - _ => false, - } - } - - pub fn check_user_unop( - &self, - ex: &'tcx hir::Expr<'tcx>, - operand_ty: Ty<'tcx>, - op: hir::UnOp, - expected: Expectation<'tcx>, - ) -> Ty<'tcx> { - assert!(op.is_by_value()); - match self.lookup_op_method(operand_ty, None, None, Op::Unary(op, ex.span), expected) { - Ok(method) => { - self.write_method_call(ex.hir_id, method); - method.sig.output() - } - Err(errors) => { - let actual = self.resolve_vars_if_possible(operand_ty); - if !actual.references_error() { - let mut err = struct_span_err!( - self.tcx.sess, - ex.span, - E0600, - "cannot apply unary operator `{}` to type `{}`", - op.as_str(), - actual - ); - err.span_label( - ex.span, - format!("cannot apply unary operator `{}`", op.as_str()), - ); - - if operand_ty.has_param_types_or_consts() { - let predicates = errors.iter().filter_map(|error| { - error.obligation.predicate.to_opt_poly_trait_pred() - }); - for pred in predicates { - self.suggest_restricting_param_bound( - &mut err, - pred, - None, - self.body_id, - ); - } - } - - let sp = self.tcx.sess.source_map().start_point(ex.span); - if let Some(sp) = - self.tcx.sess.parse_sess.ambiguous_block_expr_parse.borrow().get(&sp) - { - // If the previous expression was a block expression, suggest parentheses - // (turning this into a binary subtraction operation instead.) - // for example, `{2} - 2` -> `({2}) - 2` (see src\test\ui\parser\expr-as-stmt.rs) - self.tcx.sess.parse_sess.expr_parentheses_needed(&mut err, *sp); - } else { - match actual.kind() { - Uint(_) if op == hir::UnOp::Neg => { - err.note("unsigned values cannot be negated"); - - if let hir::ExprKind::Unary( - _, - hir::Expr { - kind: - hir::ExprKind::Lit(Spanned { - node: ast::LitKind::Int(1, _), - .. - }), - .. - }, - ) = ex.kind - { - err.span_suggestion( - ex.span, - &format!( - "you may have meant the maximum value of `{actual}`", - ), - format!("{actual}::MAX"), - Applicability::MaybeIncorrect, - ); - } - } - Str | Never | Char | Tuple(_) | Array(_, _) => {} - Ref(_, lty, _) if *lty.kind() == Str => {} - _ => { - self.note_unmet_impls_on_type(&mut err, errors); - } - } - } - err.emit(); - } - self.tcx.ty_error() - } - } - } - - fn lookup_op_method( - &self, - lhs_ty: Ty<'tcx>, - other_ty: Option<Ty<'tcx>>, - other_ty_expr: Option<&'tcx hir::Expr<'tcx>>, - op: Op, - expected: Expectation<'tcx>, - ) -> Result<MethodCallee<'tcx>, Vec<FulfillmentError<'tcx>>> { - let span = match op { - Op::Binary(op, _) => op.span, - Op::Unary(_, span) => span, - }; - let (opname, trait_did) = lang_item_for_op(self.tcx, op, span); - - debug!( - "lookup_op_method(lhs_ty={:?}, op={:?}, opname={:?}, trait_did={:?})", - lhs_ty, op, opname, trait_did - ); - - // Catches cases like #83893, where a lang item is declared with the - // wrong number of generic arguments. Should have yielded an error - // elsewhere by now, but we have to catch it here so that we do not - // index `other_tys` out of bounds (if the lang item has too many - // generic arguments, `other_tys` is too short). - if !has_expected_num_generic_args( - self.tcx, - trait_did, - match op { - // Binary ops have a generic right-hand side, unary ops don't - Op::Binary(..) => 1, - Op::Unary(..) => 0, - }, - ) { - return Err(vec![]); - } - - let opname = Ident::with_dummy_span(opname); - let method = trait_did.and_then(|trait_did| { - self.lookup_op_method_in_trait( - span, - opname, - trait_did, - lhs_ty, - other_ty, - other_ty_expr, - expected, - ) - }); - - match (method, trait_did) { - (Some(ok), _) => { - let method = self.register_infer_ok_obligations(ok); - self.select_obligations_where_possible(false, |_| {}); - Ok(method) - } - (None, None) => Err(vec![]), - (None, Some(trait_did)) => { - let (obligation, _) = self.obligation_for_op_method( - span, - trait_did, - lhs_ty, - other_ty, - other_ty_expr, - expected, - ); - let mut fulfill = <dyn TraitEngine<'_>>::new(self.tcx); - fulfill.register_predicate_obligation(self, obligation); - Err(fulfill.select_where_possible(&self.infcx)) - } - } - } -} - -fn lang_item_for_op( - tcx: TyCtxt<'_>, - op: Op, - span: Span, -) -> (rustc_span::Symbol, Option<hir::def_id::DefId>) { - let lang = tcx.lang_items(); - if let Op::Binary(op, IsAssign::Yes) = op { - match op.node { - hir::BinOpKind::Add => (sym::add_assign, lang.add_assign_trait()), - hir::BinOpKind::Sub => (sym::sub_assign, lang.sub_assign_trait()), - hir::BinOpKind::Mul => (sym::mul_assign, lang.mul_assign_trait()), - hir::BinOpKind::Div => (sym::div_assign, lang.div_assign_trait()), - hir::BinOpKind::Rem => (sym::rem_assign, lang.rem_assign_trait()), - hir::BinOpKind::BitXor => (sym::bitxor_assign, lang.bitxor_assign_trait()), - hir::BinOpKind::BitAnd => (sym::bitand_assign, lang.bitand_assign_trait()), - hir::BinOpKind::BitOr => (sym::bitor_assign, lang.bitor_assign_trait()), - hir::BinOpKind::Shl => (sym::shl_assign, lang.shl_assign_trait()), - hir::BinOpKind::Shr => (sym::shr_assign, lang.shr_assign_trait()), - hir::BinOpKind::Lt - | hir::BinOpKind::Le - | hir::BinOpKind::Ge - | hir::BinOpKind::Gt - | hir::BinOpKind::Eq - | hir::BinOpKind::Ne - | hir::BinOpKind::And - | hir::BinOpKind::Or => { - span_bug!(span, "impossible assignment operation: {}=", op.node.as_str()) - } - } - } else if let Op::Binary(op, IsAssign::No) = op { - match op.node { - hir::BinOpKind::Add => (sym::add, lang.add_trait()), - hir::BinOpKind::Sub => (sym::sub, lang.sub_trait()), - hir::BinOpKind::Mul => (sym::mul, lang.mul_trait()), - hir::BinOpKind::Div => (sym::div, lang.div_trait()), - hir::BinOpKind::Rem => (sym::rem, lang.rem_trait()), - hir::BinOpKind::BitXor => (sym::bitxor, lang.bitxor_trait()), - hir::BinOpKind::BitAnd => (sym::bitand, lang.bitand_trait()), - hir::BinOpKind::BitOr => (sym::bitor, lang.bitor_trait()), - hir::BinOpKind::Shl => (sym::shl, lang.shl_trait()), - hir::BinOpKind::Shr => (sym::shr, lang.shr_trait()), - hir::BinOpKind::Lt => (sym::lt, lang.partial_ord_trait()), - hir::BinOpKind::Le => (sym::le, lang.partial_ord_trait()), - hir::BinOpKind::Ge => (sym::ge, lang.partial_ord_trait()), - hir::BinOpKind::Gt => (sym::gt, lang.partial_ord_trait()), - hir::BinOpKind::Eq => (sym::eq, lang.eq_trait()), - hir::BinOpKind::Ne => (sym::ne, lang.eq_trait()), - hir::BinOpKind::And | hir::BinOpKind::Or => { - span_bug!(span, "&& and || are not overloadable") - } - } - } else if let Op::Unary(hir::UnOp::Not, _) = op { - (sym::not, lang.not_trait()) - } else if let Op::Unary(hir::UnOp::Neg, _) = op { - (sym::neg, lang.neg_trait()) - } else { - bug!("lookup_op_method: op not supported: {:?}", op) - } -} - -// Binary operator categories. These categories summarize the behavior -// with respect to the builtin operations supported. -enum BinOpCategory { - /// &&, || -- cannot be overridden - Shortcircuit, - - /// <<, >> -- when shifting a single integer, rhs can be any - /// integer type. For simd, types must match. - Shift, - - /// +, -, etc -- takes equal types, produces same type as input, - /// applicable to ints/floats/simd - Math, - - /// &, |, ^ -- takes equal types, produces same type as input, - /// applicable to ints/floats/simd/bool - Bitwise, - - /// ==, !=, etc -- takes equal types, produces bools, except for simd, - /// which produce the input type - Comparison, -} - -impl BinOpCategory { - fn from(op: hir::BinOp) -> BinOpCategory { - match op.node { - hir::BinOpKind::Shl | hir::BinOpKind::Shr => BinOpCategory::Shift, - - hir::BinOpKind::Add - | hir::BinOpKind::Sub - | hir::BinOpKind::Mul - | hir::BinOpKind::Div - | hir::BinOpKind::Rem => BinOpCategory::Math, - - hir::BinOpKind::BitXor | hir::BinOpKind::BitAnd | hir::BinOpKind::BitOr => { - BinOpCategory::Bitwise - } - - hir::BinOpKind::Eq - | hir::BinOpKind::Ne - | hir::BinOpKind::Lt - | hir::BinOpKind::Le - | hir::BinOpKind::Ge - | hir::BinOpKind::Gt => BinOpCategory::Comparison, - - hir::BinOpKind::And | hir::BinOpKind::Or => BinOpCategory::Shortcircuit, - } - } -} - -/// Whether the binary operation is an assignment (`a += b`), or not (`a + b`) -#[derive(Clone, Copy, Debug, PartialEq)] -enum IsAssign { - No, - Yes, -} - -#[derive(Clone, Copy, Debug)] -enum Op { - Binary(hir::BinOp, IsAssign), - Unary(hir::UnOp, Span), -} - -/// Dereferences a single level of immutable referencing. -fn deref_ty_if_possible<'tcx>(ty: Ty<'tcx>) -> Ty<'tcx> { - match ty.kind() { - ty::Ref(_, ty, hir::Mutability::Not) => *ty, - _ => ty, - } -} - -/// Returns `true` if this is a built-in arithmetic operation (e.g., u32 -/// + u32, i16x4 == i16x4) and false if these types would have to be -/// overloaded to be legal. There are two reasons that we distinguish -/// builtin operations from overloaded ones (vs trying to drive -/// everything uniformly through the trait system and intrinsics or -/// something like that): -/// -/// 1. Builtin operations can trivially be evaluated in constants. -/// 2. For comparison operators applied to SIMD types the result is -/// not of type `bool`. For example, `i16x4 == i16x4` yields a -/// type like `i16x4`. This means that the overloaded trait -/// `PartialEq` is not applicable. -/// -/// Reason #2 is the killer. I tried for a while to always use -/// overloaded logic and just check the types in constants/codegen after -/// the fact, and it worked fine, except for SIMD types. -nmatsakis -fn is_builtin_binop<'tcx>(lhs: Ty<'tcx>, rhs: Ty<'tcx>, op: hir::BinOp) -> bool { - // Special-case a single layer of referencing, so that things like `5.0 + &6.0f32` work. - // (See https://github.com/rust-lang/rust/issues/57447.) - let (lhs, rhs) = (deref_ty_if_possible(lhs), deref_ty_if_possible(rhs)); - - match BinOpCategory::from(op) { - BinOpCategory::Shortcircuit => true, - - BinOpCategory::Shift => { - lhs.references_error() - || rhs.references_error() - || lhs.is_integral() && rhs.is_integral() - } - - BinOpCategory::Math => { - lhs.references_error() - || rhs.references_error() - || lhs.is_integral() && rhs.is_integral() - || lhs.is_floating_point() && rhs.is_floating_point() - } - - BinOpCategory::Bitwise => { - lhs.references_error() - || rhs.references_error() - || lhs.is_integral() && rhs.is_integral() - || lhs.is_floating_point() && rhs.is_floating_point() - || lhs.is_bool() && rhs.is_bool() - } - - BinOpCategory::Comparison => { - lhs.references_error() || rhs.references_error() || lhs.is_scalar() && rhs.is_scalar() - } - } -} - -struct TypeParamEraser<'a, 'tcx>(&'a FnCtxt<'a, 'tcx>, Span); - -impl<'tcx> TypeFolder<'tcx> for TypeParamEraser<'_, 'tcx> { - fn tcx(&self) -> TyCtxt<'tcx> { - self.0.tcx - } - - fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { - match ty.kind() { - ty::Param(_) => self.0.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::MiscVariable, - span: self.1, - }), - _ => ty.super_fold_with(self), - } - } -} diff --git a/compiler/rustc_typeck/src/check/pat.rs b/compiler/rustc_typeck/src/check/pat.rs deleted file mode 100644 index 8906b622b..000000000 --- a/compiler/rustc_typeck/src/check/pat.rs +++ /dev/null @@ -1,2187 +0,0 @@ -use crate::check::FnCtxt; -use rustc_ast as ast; - -use rustc_data_structures::fx::FxHashMap; -use rustc_errors::{ - pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, - MultiSpan, -}; -use rustc_hir as hir; -use rustc_hir::def::{CtorKind, DefKind, Res}; -use rustc_hir::pat_util::EnumerateAndAdjustIterator; -use rustc_hir::{HirId, Pat, PatKind}; -use rustc_infer::infer; -use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; -use rustc_middle::middle::stability::EvalResult; -use rustc_middle::ty::{self, Adt, BindingMode, Ty, TypeVisitable}; -use rustc_session::lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS; -use rustc_span::hygiene::DesugaringKind; -use rustc_span::lev_distance::find_best_match_for_name; -use rustc_span::source_map::{Span, Spanned}; -use rustc_span::symbol::{kw, sym, Ident}; -use rustc_span::{BytePos, DUMMY_SP}; -use rustc_trait_selection::autoderef::Autoderef; -use rustc_trait_selection::traits::{ObligationCause, Pattern}; -use ty::VariantDef; - -use std::cmp; -use std::collections::hash_map::Entry::{Occupied, Vacant}; - -use super::report_unexpected_variant_res; - -const CANNOT_IMPLICITLY_DEREF_POINTER_TRAIT_OBJ: &str = "\ -This error indicates that a pointer to a trait type cannot be implicitly dereferenced by a \ -pattern. Every trait defines a type, but because the size of trait implementors isn't fixed, \ -this type has no compile-time size. Therefore, all accesses to trait types must be through \ -pointers. If you encounter this error you should try to avoid dereferencing the pointer. - -You can read more about trait objects in the Trait Objects section of the Reference: \ -https://doc.rust-lang.org/reference/types.html#trait-objects"; - -/// Information about the expected type at the top level of type checking a pattern. -/// -/// **NOTE:** This is only for use by diagnostics. Do NOT use for type checking logic! -#[derive(Copy, Clone)] -struct TopInfo<'tcx> { - /// The `expected` type at the top level of type checking a pattern. - expected: Ty<'tcx>, - /// Was the origin of the `span` from a scrutinee expression? - /// - /// Otherwise there is no scrutinee and it could be e.g. from the type of a formal parameter. - origin_expr: bool, - /// The span giving rise to the `expected` type, if one could be provided. - /// - /// If `origin_expr` is `true`, then this is the span of the scrutinee as in: - /// - /// - `match scrutinee { ... }` - /// - `let _ = scrutinee;` - /// - /// This is used to point to add context in type errors. - /// In the following example, `span` corresponds to the `a + b` expression: - /// - /// ```text - /// error[E0308]: mismatched types - /// --> src/main.rs:L:C - /// | - /// L | let temp: usize = match a + b { - /// | ----- this expression has type `usize` - /// L | Ok(num) => num, - /// | ^^^^^^^ expected `usize`, found enum `std::result::Result` - /// | - /// = note: expected type `usize` - /// found type `std::result::Result<_, _>` - /// ``` - span: Option<Span>, -} - -impl<'tcx> FnCtxt<'_, 'tcx> { - fn pattern_cause(&self, ti: TopInfo<'tcx>, cause_span: Span) -> ObligationCause<'tcx> { - let code = Pattern { span: ti.span, root_ty: ti.expected, origin_expr: ti.origin_expr }; - self.cause(cause_span, code) - } - - fn demand_eqtype_pat_diag( - &self, - cause_span: Span, - expected: Ty<'tcx>, - actual: Ty<'tcx>, - ti: TopInfo<'tcx>, - ) -> Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>> { - self.demand_eqtype_with_origin(&self.pattern_cause(ti, cause_span), expected, actual) - } - - fn demand_eqtype_pat( - &self, - cause_span: Span, - expected: Ty<'tcx>, - actual: Ty<'tcx>, - ti: TopInfo<'tcx>, - ) { - if let Some(mut err) = self.demand_eqtype_pat_diag(cause_span, expected, actual, ti) { - err.emit(); - } - } -} - -const INITIAL_BM: BindingMode = BindingMode::BindByValue(hir::Mutability::Not); - -/// Mode for adjusting the expected type and binding mode. -enum AdjustMode { - /// Peel off all immediate reference types. - Peel, - /// Reset binding mode to the initial mode. - Reset, - /// Pass on the input binding mode and expected type. - Pass, -} - -impl<'a, 'tcx> FnCtxt<'a, 'tcx> { - /// Type check the given top level pattern against the `expected` type. - /// - /// If a `Some(span)` is provided and `origin_expr` holds, - /// then the `span` represents the scrutinee's span. - /// The scrutinee is found in e.g. `match scrutinee { ... }` and `let pat = scrutinee;`. - /// - /// Otherwise, `Some(span)` represents the span of a type expression - /// which originated the `expected` type. - pub fn check_pat_top( - &self, - pat: &'tcx Pat<'tcx>, - expected: Ty<'tcx>, - span: Option<Span>, - origin_expr: bool, - ) { - let info = TopInfo { expected, origin_expr, span }; - self.check_pat(pat, expected, INITIAL_BM, info); - } - - /// Type check the given `pat` against the `expected` type - /// with the provided `def_bm` (default binding mode). - /// - /// Outside of this module, `check_pat_top` should always be used. - /// Conversely, inside this module, `check_pat_top` should never be used. - #[instrument(level = "debug", skip(self, ti))] - fn check_pat( - &self, - pat: &'tcx Pat<'tcx>, - expected: Ty<'tcx>, - def_bm: BindingMode, - ti: TopInfo<'tcx>, - ) { - let path_res = match &pat.kind { - PatKind::Path(qpath) => { - Some(self.resolve_ty_and_res_fully_qualified_call(qpath, pat.hir_id, pat.span)) - } - _ => None, - }; - let adjust_mode = self.calc_adjust_mode(pat, path_res.map(|(res, ..)| res)); - let (expected, def_bm) = self.calc_default_binding_mode(pat, expected, def_bm, adjust_mode); - - let ty = match pat.kind { - PatKind::Wild => expected, - PatKind::Lit(lt) => self.check_pat_lit(pat.span, lt, expected, ti), - PatKind::Range(lhs, rhs, _) => self.check_pat_range(pat.span, lhs, rhs, expected, ti), - PatKind::Binding(ba, var_id, _, sub) => { - self.check_pat_ident(pat, ba, var_id, sub, expected, def_bm, ti) - } - PatKind::TupleStruct(ref qpath, subpats, ddpos) => { - self.check_pat_tuple_struct(pat, qpath, subpats, ddpos, expected, def_bm, ti) - } - PatKind::Path(ref qpath) => { - self.check_pat_path(pat, qpath, path_res.unwrap(), expected, ti) - } - PatKind::Struct(ref qpath, fields, has_rest_pat) => { - self.check_pat_struct(pat, qpath, fields, has_rest_pat, expected, def_bm, ti) - } - PatKind::Or(pats) => { - for pat in pats { - self.check_pat(pat, expected, def_bm, ti); - } - expected - } - PatKind::Tuple(elements, ddpos) => { - self.check_pat_tuple(pat.span, elements, ddpos, expected, def_bm, ti) - } - PatKind::Box(inner) => self.check_pat_box(pat.span, inner, expected, def_bm, ti), - PatKind::Ref(inner, mutbl) => { - self.check_pat_ref(pat, inner, mutbl, expected, def_bm, ti) - } - PatKind::Slice(before, slice, after) => { - self.check_pat_slice(pat.span, before, slice, after, expected, def_bm, ti) - } - }; - - self.write_ty(pat.hir_id, ty); - - // (note_1): In most of the cases where (note_1) is referenced - // (literals and constants being the exception), we relate types - // using strict equality, even though subtyping would be sufficient. - // There are a few reasons for this, some of which are fairly subtle - // and which cost me (nmatsakis) an hour or two debugging to remember, - // so I thought I'd write them down this time. - // - // 1. There is no loss of expressiveness here, though it does - // cause some inconvenience. What we are saying is that the type - // of `x` becomes *exactly* what is expected. This can cause unnecessary - // errors in some cases, such as this one: - // - // ``` - // fn foo<'x>(x: &'x i32) { - // let a = 1; - // let mut z = x; - // z = &a; - // } - // ``` - // - // The reason we might get an error is that `z` might be - // assigned a type like `&'x i32`, and then we would have - // a problem when we try to assign `&a` to `z`, because - // the lifetime of `&a` (i.e., the enclosing block) is - // shorter than `'x`. - // - // HOWEVER, this code works fine. The reason is that the - // expected type here is whatever type the user wrote, not - // the initializer's type. In this case the user wrote - // nothing, so we are going to create a type variable `Z`. - // Then we will assign the type of the initializer (`&'x i32`) - // as a subtype of `Z`: `&'x i32 <: Z`. And hence we - // will instantiate `Z` as a type `&'0 i32` where `'0` is - // a fresh region variable, with the constraint that `'x : '0`. - // So basically we're all set. - // - // Note that there are two tests to check that this remains true - // (`regions-reassign-{match,let}-bound-pointer.rs`). - // - // 2. Things go horribly wrong if we use subtype. The reason for - // THIS is a fairly subtle case involving bound regions. See the - // `givens` field in `region_constraints`, as well as the test - // `regions-relate-bound-regions-on-closures-to-inference-variables.rs`, - // for details. Short version is that we must sometimes detect - // relationships between specific region variables and regions - // bound in a closure signature, and that detection gets thrown - // off when we substitute fresh region variables here to enable - // subtyping. - } - - /// Compute the new expected type and default binding mode from the old ones - /// as well as the pattern form we are currently checking. - fn calc_default_binding_mode( - &self, - pat: &'tcx Pat<'tcx>, - expected: Ty<'tcx>, - def_bm: BindingMode, - adjust_mode: AdjustMode, - ) -> (Ty<'tcx>, BindingMode) { - match adjust_mode { - AdjustMode::Pass => (expected, def_bm), - AdjustMode::Reset => (expected, INITIAL_BM), - AdjustMode::Peel => self.peel_off_references(pat, expected, def_bm), - } - } - - /// How should the binding mode and expected type be adjusted? - /// - /// When the pattern is a path pattern, `opt_path_res` must be `Some(res)`. - fn calc_adjust_mode(&self, pat: &'tcx Pat<'tcx>, opt_path_res: Option<Res>) -> AdjustMode { - // When we perform destructuring assignment, we disable default match bindings, which are - // unintuitive in this context. - if !pat.default_binding_modes { - return AdjustMode::Reset; - } - match &pat.kind { - // Type checking these product-like types successfully always require - // that the expected type be of those types and not reference types. - PatKind::Struct(..) - | PatKind::TupleStruct(..) - | PatKind::Tuple(..) - | PatKind::Box(_) - | PatKind::Range(..) - | PatKind::Slice(..) => AdjustMode::Peel, - // String and byte-string literals result in types `&str` and `&[u8]` respectively. - // All other literals result in non-reference types. - // As a result, we allow `if let 0 = &&0 {}` but not `if let "foo" = &&"foo {}`. - // - // Call `resolve_vars_if_possible` here for inline const blocks. - PatKind::Lit(lt) => match self.resolve_vars_if_possible(self.check_expr(lt)).kind() { - ty::Ref(..) => AdjustMode::Pass, - _ => AdjustMode::Peel, - }, - PatKind::Path(_) => match opt_path_res.unwrap() { - // These constants can be of a reference type, e.g. `const X: &u8 = &0;`. - // Peeling the reference types too early will cause type checking failures. - // Although it would be possible to *also* peel the types of the constants too. - Res::Def(DefKind::Const | DefKind::AssocConst, _) => AdjustMode::Pass, - // In the `ValueNS`, we have `SelfCtor(..) | Ctor(_, Const), _)` remaining which - // could successfully compile. The former being `Self` requires a unit struct. - // In either case, and unlike constants, the pattern itself cannot be - // a reference type wherefore peeling doesn't give up any expressiveness. - _ => AdjustMode::Peel, - }, - // When encountering a `& mut? pat` pattern, reset to "by value". - // This is so that `x` and `y` here are by value, as they appear to be: - // - // ``` - // match &(&22, &44) { - // (&x, &y) => ... - // } - // ``` - // - // See issue #46688. - PatKind::Ref(..) => AdjustMode::Reset, - // A `_` pattern works with any expected type, so there's no need to do anything. - PatKind::Wild - // Bindings also work with whatever the expected type is, - // and moreover if we peel references off, that will give us the wrong binding type. - // Also, we can have a subpattern `binding @ pat`. - // Each side of the `@` should be treated independently (like with OR-patterns). - | PatKind::Binding(..) - // An OR-pattern just propagates to each individual alternative. - // This is maximally flexible, allowing e.g., `Some(mut x) | &Some(mut x)`. - // In that example, `Some(mut x)` results in `Peel` whereas `&Some(mut x)` in `Reset`. - | PatKind::Or(_) => AdjustMode::Pass, - } - } - - /// Peel off as many immediately nested `& mut?` from the expected type as possible - /// and return the new expected type and binding default binding mode. - /// The adjustments vector, if non-empty is stored in a table. - fn peel_off_references( - &self, - pat: &'tcx Pat<'tcx>, - expected: Ty<'tcx>, - mut def_bm: BindingMode, - ) -> (Ty<'tcx>, BindingMode) { - let mut expected = self.resolve_vars_with_obligations(expected); - - // Peel off as many `&` or `&mut` from the scrutinee type as possible. For example, - // for `match &&&mut Some(5)` the loop runs three times, aborting when it reaches - // the `Some(5)` which is not of type Ref. - // - // For each ampersand peeled off, update the binding mode and push the original - // type into the adjustments vector. - // - // See the examples in `ui/match-defbm*.rs`. - let mut pat_adjustments = vec![]; - while let ty::Ref(_, inner_ty, inner_mutability) = *expected.kind() { - debug!("inspecting {:?}", expected); - - debug!("current discriminant is Ref, inserting implicit deref"); - // Preserve the reference type. We'll need it later during THIR lowering. - pat_adjustments.push(expected); - - expected = inner_ty; - def_bm = ty::BindByReference(match def_bm { - // If default binding mode is by value, make it `ref` or `ref mut` - // (depending on whether we observe `&` or `&mut`). - ty::BindByValue(_) | - // When `ref mut`, stay a `ref mut` (on `&mut`) or downgrade to `ref` (on `&`). - ty::BindByReference(hir::Mutability::Mut) => inner_mutability, - // Once a `ref`, always a `ref`. - // This is because a `& &mut` cannot mutate the underlying value. - ty::BindByReference(m @ hir::Mutability::Not) => m, - }); - } - - if !pat_adjustments.is_empty() { - debug!("default binding mode is now {:?}", def_bm); - self.inh - .typeck_results - .borrow_mut() - .pat_adjustments_mut() - .insert(pat.hir_id, pat_adjustments); - } - - (expected, def_bm) - } - - fn check_pat_lit( - &self, - span: Span, - lt: &hir::Expr<'tcx>, - expected: Ty<'tcx>, - ti: TopInfo<'tcx>, - ) -> Ty<'tcx> { - // We've already computed the type above (when checking for a non-ref pat), - // so avoid computing it again. - let ty = self.node_ty(lt.hir_id); - - // Byte string patterns behave the same way as array patterns - // They can denote both statically and dynamically-sized byte arrays. - let mut pat_ty = ty; - if let hir::ExprKind::Lit(Spanned { node: ast::LitKind::ByteStr(_), .. }) = lt.kind { - let expected = self.structurally_resolved_type(span, expected); - if let ty::Ref(_, inner_ty, _) = expected.kind() - && matches!(inner_ty.kind(), ty::Slice(_)) - { - let tcx = self.tcx; - trace!(?lt.hir_id.local_id, "polymorphic byte string lit"); - self.typeck_results - .borrow_mut() - .treat_byte_string_as_slice - .insert(lt.hir_id.local_id); - pat_ty = tcx.mk_imm_ref(tcx.lifetimes.re_static, tcx.mk_slice(tcx.types.u8)); - } - } - - // Somewhat surprising: in this case, the subtyping relation goes the - // opposite way as the other cases. Actually what we really want is not - // a subtyping relation at all but rather that there exists a LUB - // (so that they can be compared). However, in practice, constants are - // always scalars or strings. For scalars subtyping is irrelevant, - // and for strings `ty` is type is `&'static str`, so if we say that - // - // &'static str <: expected - // - // then that's equivalent to there existing a LUB. - let cause = self.pattern_cause(ti, span); - if let Some(mut err) = self.demand_suptype_with_origin(&cause, expected, pat_ty) { - err.emit_unless( - ti.span - .filter(|&s| { - // In the case of `if`- and `while`-expressions we've already checked - // that `scrutinee: bool`. We know that the pattern is `true`, - // so an error here would be a duplicate and from the wrong POV. - s.is_desugaring(DesugaringKind::CondTemporary) - }) - .is_some(), - ); - } - - pat_ty - } - - fn check_pat_range( - &self, - span: Span, - lhs: Option<&'tcx hir::Expr<'tcx>>, - rhs: Option<&'tcx hir::Expr<'tcx>>, - expected: Ty<'tcx>, - ti: TopInfo<'tcx>, - ) -> Ty<'tcx> { - let calc_side = |opt_expr: Option<&'tcx hir::Expr<'tcx>>| match opt_expr { - None => None, - Some(expr) => { - let ty = self.check_expr(expr); - // Check that the end-point is possibly of numeric or char type. - // The early check here is not for correctness, but rather better - // diagnostics (e.g. when `&str` is being matched, `expected` will - // be peeled to `str` while ty here is still `&str`, if we don't - // err early here, a rather confusing unification error will be - // emitted instead). - let fail = - !(ty.is_numeric() || ty.is_char() || ty.is_ty_var() || ty.references_error()); - Some((fail, ty, expr.span)) - } - }; - let mut lhs = calc_side(lhs); - let mut rhs = calc_side(rhs); - - if let (Some((true, ..)), _) | (_, Some((true, ..))) = (lhs, rhs) { - // There exists a side that didn't meet our criteria that the end-point - // be of a numeric or char type, as checked in `calc_side` above. - self.emit_err_pat_range(span, lhs, rhs); - return self.tcx.ty_error(); - } - - // Unify each side with `expected`. - // Subtyping doesn't matter here, as the value is some kind of scalar. - let demand_eqtype = |x: &mut _, y| { - if let Some((ref mut fail, x_ty, x_span)) = *x - && let Some(mut err) = self.demand_eqtype_pat_diag(x_span, expected, x_ty, ti) - { - if let Some((_, y_ty, y_span)) = y { - self.endpoint_has_type(&mut err, y_span, y_ty); - } - err.emit(); - *fail = true; - } - }; - demand_eqtype(&mut lhs, rhs); - demand_eqtype(&mut rhs, lhs); - - if let (Some((true, ..)), _) | (_, Some((true, ..))) = (lhs, rhs) { - return self.tcx.ty_error(); - } - - // Find the unified type and check if it's of numeric or char type again. - // This check is needed if both sides are inference variables. - // We require types to be resolved here so that we emit inference failure - // rather than "_ is not a char or numeric". - let ty = self.structurally_resolved_type(span, expected); - if !(ty.is_numeric() || ty.is_char() || ty.references_error()) { - if let Some((ref mut fail, _, _)) = lhs { - *fail = true; - } - if let Some((ref mut fail, _, _)) = rhs { - *fail = true; - } - self.emit_err_pat_range(span, lhs, rhs); - return self.tcx.ty_error(); - } - ty - } - - fn endpoint_has_type(&self, err: &mut Diagnostic, span: Span, ty: Ty<'_>) { - if !ty.references_error() { - err.span_label(span, &format!("this is of type `{}`", ty)); - } - } - - fn emit_err_pat_range( - &self, - span: Span, - lhs: Option<(bool, Ty<'tcx>, Span)>, - rhs: Option<(bool, Ty<'tcx>, Span)>, - ) { - let span = match (lhs, rhs) { - (Some((true, ..)), Some((true, ..))) => span, - (Some((true, _, sp)), _) => sp, - (_, Some((true, _, sp))) => sp, - _ => span_bug!(span, "emit_err_pat_range: no side failed or exists but still error?"), - }; - let mut err = struct_span_err!( - self.tcx.sess, - span, - E0029, - "only `char` and numeric types are allowed in range patterns" - ); - let msg = |ty| { - let ty = self.resolve_vars_if_possible(ty); - format!("this is of type `{}` but it should be `char` or numeric", ty) - }; - let mut one_side_err = |first_span, first_ty, second: Option<(bool, Ty<'tcx>, Span)>| { - err.span_label(first_span, &msg(first_ty)); - if let Some((_, ty, sp)) = second { - let ty = self.resolve_vars_if_possible(ty); - self.endpoint_has_type(&mut err, sp, ty); - } - }; - match (lhs, rhs) { - (Some((true, lhs_ty, lhs_sp)), Some((true, rhs_ty, rhs_sp))) => { - err.span_label(lhs_sp, &msg(lhs_ty)); - err.span_label(rhs_sp, &msg(rhs_ty)); - } - (Some((true, lhs_ty, lhs_sp)), rhs) => one_side_err(lhs_sp, lhs_ty, rhs), - (lhs, Some((true, rhs_ty, rhs_sp))) => one_side_err(rhs_sp, rhs_ty, lhs), - _ => span_bug!(span, "Impossible, verified above."), - } - if self.tcx.sess.teach(&err.get_code().unwrap()) { - err.note( - "In a match expression, only numbers and characters can be matched \ - against a range. This is because the compiler checks that the range \ - is non-empty at compile-time, and is unable to evaluate arbitrary \ - comparison functions. If you want to capture values of an orderable \ - type between two end-points, you can use a guard.", - ); - } - err.emit(); - } - - fn check_pat_ident( - &self, - pat: &'tcx Pat<'tcx>, - ba: hir::BindingAnnotation, - var_id: HirId, - sub: Option<&'tcx Pat<'tcx>>, - expected: Ty<'tcx>, - def_bm: BindingMode, - ti: TopInfo<'tcx>, - ) -> Ty<'tcx> { - // Determine the binding mode... - let bm = match ba { - hir::BindingAnnotation::NONE => def_bm, - _ => BindingMode::convert(ba), - }; - // ...and store it in a side table: - self.inh.typeck_results.borrow_mut().pat_binding_modes_mut().insert(pat.hir_id, bm); - - debug!("check_pat_ident: pat.hir_id={:?} bm={:?}", pat.hir_id, bm); - - let local_ty = self.local_ty(pat.span, pat.hir_id).decl_ty; - let eq_ty = match bm { - ty::BindByReference(mutbl) => { - // If the binding is like `ref x | ref mut x`, - // then `x` is assigned a value of type `&M T` where M is the - // mutability and T is the expected type. - // - // `x` is assigned a value of type `&M T`, hence `&M T <: typeof(x)` - // is required. However, we use equality, which is stronger. - // See (note_1) for an explanation. - self.new_ref_ty(pat.span, mutbl, expected) - } - // Otherwise, the type of x is the expected type `T`. - ty::BindByValue(_) => { - // As above, `T <: typeof(x)` is required, but we use equality, see (note_1). - expected - } - }; - self.demand_eqtype_pat(pat.span, eq_ty, local_ty, ti); - - // If there are multiple arms, make sure they all agree on - // what the type of the binding `x` ought to be. - if var_id != pat.hir_id { - self.check_binding_alt_eq_ty(ba, pat.span, var_id, local_ty, ti); - } - - if let Some(p) = sub { - self.check_pat(p, expected, def_bm, ti); - } - - local_ty - } - - fn check_binding_alt_eq_ty( - &self, - ba: hir::BindingAnnotation, - span: Span, - var_id: HirId, - ty: Ty<'tcx>, - ti: TopInfo<'tcx>, - ) { - let var_ty = self.local_ty(span, var_id).decl_ty; - if let Some(mut err) = self.demand_eqtype_pat_diag(span, var_ty, ty, ti) { - let hir = self.tcx.hir(); - let var_ty = self.resolve_vars_with_obligations(var_ty); - let msg = format!("first introduced with type `{var_ty}` here"); - err.span_label(hir.span(var_id), msg); - let in_match = hir.parent_iter(var_id).any(|(_, n)| { - matches!( - n, - hir::Node::Expr(hir::Expr { - kind: hir::ExprKind::Match(.., hir::MatchSource::Normal), - .. - }) - ) - }); - let pre = if in_match { "in the same arm, " } else { "" }; - err.note(&format!("{}a binding must have the same type in all alternatives", pre)); - self.suggest_adding_missing_ref_or_removing_ref( - &mut err, - span, - var_ty, - self.resolve_vars_with_obligations(ty), - ba, - ); - err.emit(); - } - } - - fn suggest_adding_missing_ref_or_removing_ref( - &self, - err: &mut Diagnostic, - span: Span, - expected: Ty<'tcx>, - actual: Ty<'tcx>, - ba: hir::BindingAnnotation, - ) { - match (expected.kind(), actual.kind(), ba) { - (ty::Ref(_, inner_ty, _), _, hir::BindingAnnotation::NONE) - if self.can_eq(self.param_env, *inner_ty, actual).is_ok() => - { - err.span_suggestion_verbose( - span.shrink_to_lo(), - "consider adding `ref`", - "ref ", - Applicability::MaybeIncorrect, - ); - } - (_, ty::Ref(_, inner_ty, _), hir::BindingAnnotation::REF) - if self.can_eq(self.param_env, expected, *inner_ty).is_ok() => - { - err.span_suggestion_verbose( - span.with_hi(span.lo() + BytePos(4)), - "consider removing `ref`", - "", - Applicability::MaybeIncorrect, - ); - } - _ => (), - } - } - - // Precondition: pat is a Ref(_) pattern - fn borrow_pat_suggestion(&self, err: &mut Diagnostic, pat: &Pat<'_>) { - let tcx = self.tcx; - if let PatKind::Ref(inner, mutbl) = pat.kind - && let PatKind::Binding(_, _, binding, ..) = inner.kind { - let binding_parent_id = tcx.hir().get_parent_node(pat.hir_id); - let binding_parent = tcx.hir().get(binding_parent_id); - debug!(?inner, ?pat, ?binding_parent); - - let mutability = match mutbl { - ast::Mutability::Mut => "mut", - ast::Mutability::Not => "", - }; - - let mut_var_suggestion = 'block: { - if !matches!(mutbl, ast::Mutability::Mut) { - break 'block None; - } - - let ident_kind = match binding_parent { - hir::Node::Param(_) => "parameter", - hir::Node::Local(_) => "variable", - hir::Node::Arm(_) => "binding", - - // Provide diagnostics only if the parent pattern is struct-like, - // i.e. where `mut binding` makes sense - hir::Node::Pat(Pat { kind, .. }) => match kind { - PatKind::Struct(..) - | PatKind::TupleStruct(..) - | PatKind::Or(..) - | PatKind::Tuple(..) - | PatKind::Slice(..) => "binding", - - PatKind::Wild - | PatKind::Binding(..) - | PatKind::Path(..) - | PatKind::Box(..) - | PatKind::Ref(..) - | PatKind::Lit(..) - | PatKind::Range(..) => break 'block None, - }, - - // Don't provide suggestions in other cases - _ => break 'block None, - }; - - Some(( - pat.span, - format!("to declare a mutable {ident_kind} use"), - format!("mut {binding}"), - )) - - }; - - match binding_parent { - // Check that there is explicit type (ie this is not a closure param with inferred type) - // so we don't suggest moving something to the type that does not exist - hir::Node::Param(hir::Param { ty_span, .. }) if binding.span != *ty_span => { - err.multipart_suggestion_verbose( - format!("to take parameter `{binding}` by reference, move `&{mutability}` to the type"), - vec![ - (pat.span.until(inner.span), "".to_owned()), - (ty_span.shrink_to_lo(), format!("&{}", mutbl.prefix_str())), - ], - Applicability::MachineApplicable - ); - - if let Some((sp, msg, sugg)) = mut_var_suggestion { - err.span_note(sp, format!("{msg}: `{sugg}`")); - } - } - hir::Node::Param(_) | hir::Node::Arm(_) | hir::Node::Pat(_) => { - // rely on match ergonomics or it might be nested `&&pat` - err.span_suggestion_verbose( - pat.span.until(inner.span), - format!("consider removing `&{mutability}` from the pattern"), - "", - Applicability::MaybeIncorrect, - ); - - if let Some((sp, msg, sugg)) = mut_var_suggestion { - err.span_note(sp, format!("{msg}: `{sugg}`")); - } - } - _ if let Some((sp, msg, sugg)) = mut_var_suggestion => { - err.span_suggestion(sp, msg, sugg, Applicability::MachineApplicable); - } - _ => {} // don't provide suggestions in other cases #55175 - } - } - } - - pub fn check_dereferenceable(&self, span: Span, expected: Ty<'tcx>, inner: &Pat<'_>) -> bool { - if let PatKind::Binding(..) = inner.kind - && let Some(mt) = self.shallow_resolve(expected).builtin_deref(true) - && let ty::Dynamic(..) = mt.ty.kind() - { - // This is "x = SomeTrait" being reduced from - // "let &x = &SomeTrait" or "let box x = Box<SomeTrait>", an error. - let type_str = self.ty_to_string(expected); - let mut err = struct_span_err!( - self.tcx.sess, - span, - E0033, - "type `{}` cannot be dereferenced", - type_str - ); - err.span_label(span, format!("type `{type_str}` cannot be dereferenced")); - if self.tcx.sess.teach(&err.get_code().unwrap()) { - err.note(CANNOT_IMPLICITLY_DEREF_POINTER_TRAIT_OBJ); - } - err.emit(); - return false; - } - true - } - - fn check_pat_struct( - &self, - pat: &'tcx Pat<'tcx>, - qpath: &hir::QPath<'_>, - fields: &'tcx [hir::PatField<'tcx>], - has_rest_pat: bool, - expected: Ty<'tcx>, - def_bm: BindingMode, - ti: TopInfo<'tcx>, - ) -> Ty<'tcx> { - // Resolve the path and check the definition for errors. - let Some((variant, pat_ty)) = self.check_struct_path(qpath, pat.hir_id) else { - let err = self.tcx.ty_error(); - for field in fields { - let ti = ti; - self.check_pat(field.pat, err, def_bm, ti); - } - return err; - }; - - // Type-check the path. - self.demand_eqtype_pat(pat.span, expected, pat_ty, ti); - - // Type-check subpatterns. - if self.check_struct_pat_fields(pat_ty, &pat, variant, fields, has_rest_pat, def_bm, ti) { - pat_ty - } else { - self.tcx.ty_error() - } - } - - fn check_pat_path( - &self, - pat: &Pat<'tcx>, - qpath: &hir::QPath<'_>, - path_resolution: (Res, Option<Ty<'tcx>>, &'tcx [hir::PathSegment<'tcx>]), - expected: Ty<'tcx>, - ti: TopInfo<'tcx>, - ) -> Ty<'tcx> { - let tcx = self.tcx; - - // We have already resolved the path. - let (res, opt_ty, segments) = path_resolution; - match res { - Res::Err => { - self.set_tainted_by_errors(); - return tcx.ty_error(); - } - Res::Def(DefKind::AssocFn | DefKind::Ctor(_, CtorKind::Fictive | CtorKind::Fn), _) => { - report_unexpected_variant_res(tcx, res, qpath, pat.span); - return tcx.ty_error(); - } - Res::SelfCtor(..) - | Res::Def( - DefKind::Ctor(_, CtorKind::Const) - | DefKind::Const - | DefKind::AssocConst - | DefKind::ConstParam, - _, - ) => {} // OK - _ => bug!("unexpected pattern resolution: {:?}", res), - } - - // Type-check the path. - let (pat_ty, pat_res) = - self.instantiate_value_path(segments, opt_ty, res, pat.span, pat.hir_id); - if let Some(err) = - self.demand_suptype_with_origin(&self.pattern_cause(ti, pat.span), expected, pat_ty) - { - self.emit_bad_pat_path(err, pat, res, pat_res, pat_ty, segments); - } - pat_ty - } - - fn maybe_suggest_range_literal( - &self, - e: &mut Diagnostic, - opt_def_id: Option<hir::def_id::DefId>, - ident: Ident, - ) -> bool { - match opt_def_id { - Some(def_id) => match self.tcx.hir().get_if_local(def_id) { - Some(hir::Node::Item(hir::Item { - kind: hir::ItemKind::Const(_, body_id), .. - })) => match self.tcx.hir().get(body_id.hir_id) { - hir::Node::Expr(expr) => { - if hir::is_range_literal(expr) { - let span = self.tcx.hir().span(body_id.hir_id); - if let Ok(snip) = self.tcx.sess.source_map().span_to_snippet(span) { - e.span_suggestion_verbose( - ident.span, - "you may want to move the range into the match block", - snip, - Applicability::MachineApplicable, - ); - return true; - } - } - } - _ => (), - }, - _ => (), - }, - _ => (), - } - false - } - - fn emit_bad_pat_path( - &self, - mut e: DiagnosticBuilder<'_, ErrorGuaranteed>, - pat: &hir::Pat<'tcx>, - res: Res, - pat_res: Res, - pat_ty: Ty<'tcx>, - segments: &'tcx [hir::PathSegment<'tcx>], - ) { - let pat_span = pat.span; - if let Some(span) = self.tcx.hir().res_span(pat_res) { - e.span_label(span, &format!("{} defined here", res.descr())); - if let [hir::PathSegment { ident, .. }] = &*segments { - e.span_label( - pat_span, - &format!( - "`{}` is interpreted as {} {}, not a new binding", - ident, - res.article(), - res.descr(), - ), - ); - match self.tcx.hir().get(self.tcx.hir().get_parent_node(pat.hir_id)) { - hir::Node::PatField(..) => { - e.span_suggestion_verbose( - ident.span.shrink_to_hi(), - "bind the struct field to a different name instead", - format!(": other_{}", ident.as_str().to_lowercase()), - Applicability::HasPlaceholders, - ); - } - _ => { - let (type_def_id, item_def_id) = match pat_ty.kind() { - Adt(def, _) => match res { - Res::Def(DefKind::Const, def_id) => (Some(def.did()), Some(def_id)), - _ => (None, None), - }, - _ => (None, None), - }; - - let ranges = &[ - self.tcx.lang_items().range_struct(), - self.tcx.lang_items().range_from_struct(), - self.tcx.lang_items().range_to_struct(), - self.tcx.lang_items().range_full_struct(), - self.tcx.lang_items().range_inclusive_struct(), - self.tcx.lang_items().range_to_inclusive_struct(), - ]; - if type_def_id != None && ranges.contains(&type_def_id) { - if !self.maybe_suggest_range_literal(&mut e, item_def_id, *ident) { - let msg = "constants only support matching by type, \ - if you meant to match against a range of values, \ - consider using a range pattern like `min ..= max` in the match block"; - e.note(msg); - } - } else { - let msg = "introduce a new binding instead"; - let sugg = format!("other_{}", ident.as_str().to_lowercase()); - e.span_suggestion( - ident.span, - msg, - sugg, - Applicability::HasPlaceholders, - ); - } - } - }; - } - } - e.emit(); - } - - fn check_pat_tuple_struct( - &self, - pat: &'tcx Pat<'tcx>, - qpath: &'tcx hir::QPath<'tcx>, - subpats: &'tcx [Pat<'tcx>], - ddpos: hir::DotDotPos, - expected: Ty<'tcx>, - def_bm: BindingMode, - ti: TopInfo<'tcx>, - ) -> Ty<'tcx> { - let tcx = self.tcx; - let on_error = || { - for pat in subpats { - self.check_pat(pat, tcx.ty_error(), def_bm, ti); - } - }; - let report_unexpected_res = |res: Res| { - let sm = tcx.sess.source_map(); - let path_str = sm - .span_to_snippet(sm.span_until_char(pat.span, '(')) - .map_or_else(|_| String::new(), |s| format!(" `{}`", s.trim_end())); - let msg = format!( - "expected tuple struct or tuple variant, found {}{}", - res.descr(), - path_str - ); - - let mut err = struct_span_err!(tcx.sess, pat.span, E0164, "{msg}"); - match res { - Res::Def(DefKind::Fn | DefKind::AssocFn, _) => { - err.span_label(pat.span, "`fn` calls are not allowed in patterns"); - err.help( - "for more information, visit \ - https://doc.rust-lang.org/book/ch18-00-patterns.html", - ); - } - _ => { - err.span_label(pat.span, "not a tuple variant or struct"); - } - } - err.emit(); - on_error(); - }; - - // Resolve the path and check the definition for errors. - let (res, opt_ty, segments) = - self.resolve_ty_and_res_fully_qualified_call(qpath, pat.hir_id, pat.span); - if res == Res::Err { - self.set_tainted_by_errors(); - on_error(); - return self.tcx.ty_error(); - } - - // Type-check the path. - let (pat_ty, res) = - self.instantiate_value_path(segments, opt_ty, res, pat.span, pat.hir_id); - if !pat_ty.is_fn() { - report_unexpected_res(res); - return tcx.ty_error(); - } - - let variant = match res { - Res::Err => { - self.set_tainted_by_errors(); - on_error(); - return tcx.ty_error(); - } - Res::Def(DefKind::AssocConst | DefKind::AssocFn, _) => { - report_unexpected_res(res); - return tcx.ty_error(); - } - Res::Def(DefKind::Ctor(_, CtorKind::Fn), _) => tcx.expect_variant_res(res), - _ => bug!("unexpected pattern resolution: {:?}", res), - }; - - // Replace constructor type with constructed type for tuple struct patterns. - let pat_ty = pat_ty.fn_sig(tcx).output(); - let pat_ty = pat_ty.no_bound_vars().expect("expected fn type"); - - // Type-check the tuple struct pattern against the expected type. - let diag = self.demand_eqtype_pat_diag(pat.span, expected, pat_ty, ti); - let had_err = if let Some(mut err) = diag { - err.emit(); - true - } else { - false - }; - - // Type-check subpatterns. - if subpats.len() == variant.fields.len() - || subpats.len() < variant.fields.len() && ddpos.as_opt_usize().is_some() - { - let ty::Adt(_, substs) = pat_ty.kind() else { - bug!("unexpected pattern type {:?}", pat_ty); - }; - for (i, subpat) in subpats.iter().enumerate_and_adjust(variant.fields.len(), ddpos) { - let field_ty = self.field_ty(subpat.span, &variant.fields[i], substs); - self.check_pat(subpat, field_ty, def_bm, ti); - - self.tcx.check_stability( - variant.fields[i].did, - Some(pat.hir_id), - subpat.span, - None, - ); - } - } else { - // Pattern has wrong number of fields. - self.e0023(pat.span, res, qpath, subpats, &variant.fields, expected, had_err); - on_error(); - return tcx.ty_error(); - } - pat_ty - } - - fn e0023( - &self, - pat_span: Span, - res: Res, - qpath: &hir::QPath<'_>, - subpats: &'tcx [Pat<'tcx>], - fields: &'tcx [ty::FieldDef], - expected: Ty<'tcx>, - had_err: bool, - ) { - let subpats_ending = pluralize!(subpats.len()); - let fields_ending = pluralize!(fields.len()); - - let subpat_spans = if subpats.is_empty() { - vec![pat_span] - } else { - subpats.iter().map(|p| p.span).collect() - }; - let last_subpat_span = *subpat_spans.last().unwrap(); - let res_span = self.tcx.def_span(res.def_id()); - let def_ident_span = self.tcx.def_ident_span(res.def_id()).unwrap_or(res_span); - let field_def_spans = if fields.is_empty() { - vec![res_span] - } else { - fields.iter().map(|f| f.ident(self.tcx).span).collect() - }; - let last_field_def_span = *field_def_spans.last().unwrap(); - - let mut err = struct_span_err!( - self.tcx.sess, - MultiSpan::from_spans(subpat_spans), - E0023, - "this pattern has {} field{}, but the corresponding {} has {} field{}", - subpats.len(), - subpats_ending, - res.descr(), - fields.len(), - fields_ending, - ); - err.span_label( - last_subpat_span, - &format!("expected {} field{}, found {}", fields.len(), fields_ending, subpats.len()), - ); - if self.tcx.sess.source_map().is_multiline(qpath.span().between(last_subpat_span)) { - err.span_label(qpath.span(), ""); - } - if self.tcx.sess.source_map().is_multiline(def_ident_span.between(last_field_def_span)) { - err.span_label(def_ident_span, format!("{} defined here", res.descr())); - } - for span in &field_def_spans[..field_def_spans.len() - 1] { - err.span_label(*span, ""); - } - err.span_label( - last_field_def_span, - &format!("{} has {} field{}", res.descr(), fields.len(), fields_ending), - ); - - // Identify the case `Some(x, y)` where the expected type is e.g. `Option<(T, U)>`. - // More generally, the expected type wants a tuple variant with one field of an - // N-arity-tuple, e.g., `V_i((p_0, .., p_N))`. Meanwhile, the user supplied a pattern - // with the subpatterns directly in the tuple variant pattern, e.g., `V_i(p_0, .., p_N)`. - let missing_parentheses = match (&expected.kind(), fields, had_err) { - // #67037: only do this if we could successfully type-check the expected type against - // the tuple struct pattern. Otherwise the substs could get out of range on e.g., - // `let P() = U;` where `P != U` with `struct P<T>(T);`. - (ty::Adt(_, substs), [field], false) => { - let field_ty = self.field_ty(pat_span, field, substs); - match field_ty.kind() { - ty::Tuple(fields) => fields.len() == subpats.len(), - _ => false, - } - } - _ => false, - }; - if missing_parentheses { - let (left, right) = match subpats { - // This is the zero case; we aim to get the "hi" part of the `QPath`'s - // span as the "lo" and then the "hi" part of the pattern's span as the "hi". - // This looks like: - // - // help: missing parentheses - // | - // L | let A(()) = A(()); - // | ^ ^ - [] => (qpath.span().shrink_to_hi(), pat_span), - // Easy case. Just take the "lo" of the first sub-pattern and the "hi" of the - // last sub-pattern. In the case of `A(x)` the first and last may coincide. - // This looks like: - // - // help: missing parentheses - // | - // L | let A((x, y)) = A((1, 2)); - // | ^ ^ - [first, ..] => (first.span.shrink_to_lo(), subpats.last().unwrap().span), - }; - err.multipart_suggestion( - "missing parentheses", - vec![(left, "(".to_string()), (right.shrink_to_hi(), ")".to_string())], - Applicability::MachineApplicable, - ); - } else if fields.len() > subpats.len() && pat_span != DUMMY_SP { - let after_fields_span = pat_span.with_hi(pat_span.hi() - BytePos(1)).shrink_to_hi(); - let all_fields_span = match subpats { - [] => after_fields_span, - [field] => field.span, - [first, .., last] => first.span.to(last.span), - }; - - // Check if all the fields in the pattern are wildcards. - let all_wildcards = subpats.iter().all(|pat| matches!(pat.kind, PatKind::Wild)); - let first_tail_wildcard = - subpats.iter().enumerate().fold(None, |acc, (pos, pat)| match (acc, &pat.kind) { - (None, PatKind::Wild) => Some(pos), - (Some(_), PatKind::Wild) => acc, - _ => None, - }); - let tail_span = match first_tail_wildcard { - None => after_fields_span, - Some(0) => subpats[0].span.to(after_fields_span), - Some(pos) => subpats[pos - 1].span.shrink_to_hi().to(after_fields_span), - }; - - // FIXME: heuristic-based suggestion to check current types for where to add `_`. - let mut wildcard_sugg = vec!["_"; fields.len() - subpats.len()].join(", "); - if !subpats.is_empty() { - wildcard_sugg = String::from(", ") + &wildcard_sugg; - } - - err.span_suggestion_verbose( - after_fields_span, - "use `_` to explicitly ignore each field", - wildcard_sugg, - Applicability::MaybeIncorrect, - ); - - // Only suggest `..` if more than one field is missing - // or the pattern consists of all wildcards. - if fields.len() - subpats.len() > 1 || all_wildcards { - if subpats.is_empty() || all_wildcards { - err.span_suggestion_verbose( - all_fields_span, - "use `..` to ignore all fields", - "..", - Applicability::MaybeIncorrect, - ); - } else { - err.span_suggestion_verbose( - tail_span, - "use `..` to ignore the rest of the fields", - ", ..", - Applicability::MaybeIncorrect, - ); - } - } - } - - err.emit(); - } - - fn check_pat_tuple( - &self, - span: Span, - elements: &'tcx [Pat<'tcx>], - ddpos: hir::DotDotPos, - expected: Ty<'tcx>, - def_bm: BindingMode, - ti: TopInfo<'tcx>, - ) -> Ty<'tcx> { - let tcx = self.tcx; - let mut expected_len = elements.len(); - if ddpos.as_opt_usize().is_some() { - // Require known type only when `..` is present. - if let ty::Tuple(tys) = self.structurally_resolved_type(span, expected).kind() { - expected_len = tys.len(); - } - } - let max_len = cmp::max(expected_len, elements.len()); - - let element_tys_iter = (0..max_len).map(|_| { - self.next_ty_var( - // FIXME: `MiscVariable` for now -- obtaining the span and name information - // from all tuple elements isn't trivial. - TypeVariableOrigin { kind: TypeVariableOriginKind::TypeInference, span }, - ) - }); - let element_tys = tcx.mk_type_list(element_tys_iter); - let pat_ty = tcx.mk_ty(ty::Tuple(element_tys)); - if let Some(mut err) = self.demand_eqtype_pat_diag(span, expected, pat_ty, ti) { - err.emit(); - // Walk subpatterns with an expected type of `err` in this case to silence - // further errors being emitted when using the bindings. #50333 - let element_tys_iter = (0..max_len).map(|_| tcx.ty_error()); - for (_, elem) in elements.iter().enumerate_and_adjust(max_len, ddpos) { - self.check_pat(elem, tcx.ty_error(), def_bm, ti); - } - tcx.mk_tup(element_tys_iter) - } else { - for (i, elem) in elements.iter().enumerate_and_adjust(max_len, ddpos) { - self.check_pat(elem, element_tys[i], def_bm, ti); - } - pat_ty - } - } - - fn check_struct_pat_fields( - &self, - adt_ty: Ty<'tcx>, - pat: &'tcx Pat<'tcx>, - variant: &'tcx ty::VariantDef, - fields: &'tcx [hir::PatField<'tcx>], - has_rest_pat: bool, - def_bm: BindingMode, - ti: TopInfo<'tcx>, - ) -> bool { - let tcx = self.tcx; - - let ty::Adt(adt, substs) = adt_ty.kind() else { - span_bug!(pat.span, "struct pattern is not an ADT"); - }; - - // Index the struct fields' types. - let field_map = variant - .fields - .iter() - .enumerate() - .map(|(i, field)| (field.ident(self.tcx).normalize_to_macros_2_0(), (i, field))) - .collect::<FxHashMap<_, _>>(); - - // Keep track of which fields have already appeared in the pattern. - let mut used_fields = FxHashMap::default(); - let mut no_field_errors = true; - - let mut inexistent_fields = vec![]; - // Typecheck each field. - for field in fields { - let span = field.span; - let ident = tcx.adjust_ident(field.ident, variant.def_id); - let field_ty = match used_fields.entry(ident) { - Occupied(occupied) => { - self.error_field_already_bound(span, field.ident, *occupied.get()); - no_field_errors = false; - tcx.ty_error() - } - Vacant(vacant) => { - vacant.insert(span); - field_map - .get(&ident) - .map(|(i, f)| { - self.write_field_index(field.hir_id, *i); - self.tcx.check_stability(f.did, Some(pat.hir_id), span, None); - self.field_ty(span, f, substs) - }) - .unwrap_or_else(|| { - inexistent_fields.push(field); - no_field_errors = false; - tcx.ty_error() - }) - } - }; - - self.check_pat(field.pat, field_ty, def_bm, ti); - } - - let mut unmentioned_fields = variant - .fields - .iter() - .map(|field| (field, field.ident(self.tcx).normalize_to_macros_2_0())) - .filter(|(_, ident)| !used_fields.contains_key(ident)) - .collect::<Vec<_>>(); - - let inexistent_fields_err = if !(inexistent_fields.is_empty() || variant.is_recovered()) - && !inexistent_fields.iter().any(|field| field.ident.name == kw::Underscore) - { - Some(self.error_inexistent_fields( - adt.variant_descr(), - &inexistent_fields, - &mut unmentioned_fields, - variant, - substs, - )) - } else { - None - }; - - // Require `..` if struct has non_exhaustive attribute. - let non_exhaustive = variant.is_field_list_non_exhaustive() && !adt.did().is_local(); - if non_exhaustive && !has_rest_pat { - self.error_foreign_non_exhaustive_spat(pat, adt.variant_descr(), fields.is_empty()); - } - - let mut unmentioned_err = None; - // Report an error if an incorrect number of fields was specified. - if adt.is_union() { - if fields.len() != 1 { - tcx.sess - .struct_span_err(pat.span, "union patterns should have exactly one field") - .emit(); - } - if has_rest_pat { - tcx.sess.struct_span_err(pat.span, "`..` cannot be used in union patterns").emit(); - } - } else if !unmentioned_fields.is_empty() { - let accessible_unmentioned_fields: Vec<_> = unmentioned_fields - .iter() - .copied() - .filter(|(field, _)| { - field.vis.is_accessible_from(tcx.parent_module(pat.hir_id), tcx) - && !matches!( - tcx.eval_stability(field.did, None, DUMMY_SP, None), - EvalResult::Deny { .. } - ) - // We only want to report the error if it is hidden and not local - && !(tcx.is_doc_hidden(field.did) && !field.did.is_local()) - }) - .collect(); - - if !has_rest_pat { - if accessible_unmentioned_fields.is_empty() { - unmentioned_err = Some(self.error_no_accessible_fields(pat, fields)); - } else { - unmentioned_err = Some(self.error_unmentioned_fields( - pat, - &accessible_unmentioned_fields, - accessible_unmentioned_fields.len() != unmentioned_fields.len(), - fields, - )); - } - } else if non_exhaustive && !accessible_unmentioned_fields.is_empty() { - self.lint_non_exhaustive_omitted_patterns( - pat, - &accessible_unmentioned_fields, - adt_ty, - ) - } - } - match (inexistent_fields_err, unmentioned_err) { - (Some(mut i), Some(mut u)) => { - if let Some(mut e) = self.error_tuple_variant_as_struct_pat(pat, fields, variant) { - // We don't want to show the nonexistent fields error when this was - // `Foo { a, b }` when it should have been `Foo(a, b)`. - i.delay_as_bug(); - u.delay_as_bug(); - e.emit(); - } else { - i.emit(); - u.emit(); - } - } - (None, Some(mut u)) => { - if let Some(mut e) = self.error_tuple_variant_as_struct_pat(pat, fields, variant) { - u.delay_as_bug(); - e.emit(); - } else { - u.emit(); - } - } - (Some(mut err), None) => { - err.emit(); - } - (None, None) if let Some(mut err) = - self.error_tuple_variant_index_shorthand(variant, pat, fields) => - { - err.emit(); - } - (None, None) => {} - } - no_field_errors - } - - fn error_tuple_variant_index_shorthand( - &self, - variant: &VariantDef, - pat: &'_ Pat<'_>, - fields: &[hir::PatField<'_>], - ) -> Option<DiagnosticBuilder<'_, ErrorGuaranteed>> { - // if this is a tuple struct, then all field names will be numbers - // so if any fields in a struct pattern use shorthand syntax, they will - // be invalid identifiers (for example, Foo { 0, 1 }). - if let (CtorKind::Fn, PatKind::Struct(qpath, field_patterns, ..)) = - (variant.ctor_kind, &pat.kind) - { - let has_shorthand_field_name = field_patterns.iter().any(|field| field.is_shorthand); - if has_shorthand_field_name { - let path = rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| { - s.print_qpath(qpath, false) - }); - let mut err = struct_span_err!( - self.tcx.sess, - pat.span, - E0769, - "tuple variant `{path}` written as struct variant", - ); - err.span_suggestion_verbose( - qpath.span().shrink_to_hi().to(pat.span.shrink_to_hi()), - "use the tuple variant pattern syntax instead", - format!("({})", self.get_suggested_tuple_struct_pattern(fields, variant)), - Applicability::MaybeIncorrect, - ); - return Some(err); - } - } - None - } - - fn error_foreign_non_exhaustive_spat(&self, pat: &Pat<'_>, descr: &str, no_fields: bool) { - let sess = self.tcx.sess; - let sm = sess.source_map(); - let sp_brace = sm.end_point(pat.span); - let sp_comma = sm.end_point(pat.span.with_hi(sp_brace.hi())); - let sugg = if no_fields || sp_brace != sp_comma { ".. }" } else { ", .. }" }; - - let mut err = struct_span_err!( - sess, - pat.span, - E0638, - "`..` required with {descr} marked as non-exhaustive", - ); - err.span_suggestion_verbose( - sp_comma, - "add `..` at the end of the field list to ignore all other fields", - sugg, - Applicability::MachineApplicable, - ); - err.emit(); - } - - fn error_field_already_bound(&self, span: Span, ident: Ident, other_field: Span) { - struct_span_err!( - self.tcx.sess, - span, - E0025, - "field `{}` bound multiple times in the pattern", - ident - ) - .span_label(span, format!("multiple uses of `{ident}` in pattern")) - .span_label(other_field, format!("first use of `{ident}`")) - .emit(); - } - - fn error_inexistent_fields( - &self, - kind_name: &str, - inexistent_fields: &[&hir::PatField<'tcx>], - unmentioned_fields: &mut Vec<(&'tcx ty::FieldDef, Ident)>, - variant: &ty::VariantDef, - substs: &'tcx ty::List<ty::subst::GenericArg<'tcx>>, - ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { - let tcx = self.tcx; - let (field_names, t, plural) = if inexistent_fields.len() == 1 { - (format!("a field named `{}`", inexistent_fields[0].ident), "this", "") - } else { - ( - format!( - "fields named {}", - inexistent_fields - .iter() - .map(|field| format!("`{}`", field.ident)) - .collect::<Vec<String>>() - .join(", ") - ), - "these", - "s", - ) - }; - let spans = inexistent_fields.iter().map(|field| field.ident.span).collect::<Vec<_>>(); - let mut err = struct_span_err!( - tcx.sess, - spans, - E0026, - "{} `{}` does not have {}", - kind_name, - tcx.def_path_str(variant.def_id), - field_names - ); - if let Some(pat_field) = inexistent_fields.last() { - err.span_label( - pat_field.ident.span, - format!( - "{} `{}` does not have {} field{}", - kind_name, - tcx.def_path_str(variant.def_id), - t, - plural - ), - ); - - if unmentioned_fields.len() == 1 { - let input = - unmentioned_fields.iter().map(|(_, field)| field.name).collect::<Vec<_>>(); - let suggested_name = find_best_match_for_name(&input, pat_field.ident.name, None); - if let Some(suggested_name) = suggested_name { - err.span_suggestion( - pat_field.ident.span, - "a field with a similar name exists", - suggested_name, - Applicability::MaybeIncorrect, - ); - - // When we have a tuple struct used with struct we don't want to suggest using - // the (valid) struct syntax with numeric field names. Instead we want to - // suggest the expected syntax. We infer that this is the case by parsing the - // `Ident` into an unsized integer. The suggestion will be emitted elsewhere in - // `smart_resolve_context_dependent_help`. - if suggested_name.to_ident_string().parse::<usize>().is_err() { - // We don't want to throw `E0027` in case we have thrown `E0026` for them. - unmentioned_fields.retain(|&(_, x)| x.name != suggested_name); - } - } else if inexistent_fields.len() == 1 { - match pat_field.pat.kind { - PatKind::Lit(expr) - if !self.can_coerce( - self.typeck_results.borrow().expr_ty(expr), - self.field_ty( - unmentioned_fields[0].1.span, - unmentioned_fields[0].0, - substs, - ), - ) => {} - _ => { - let unmentioned_field = unmentioned_fields[0].1.name; - err.span_suggestion_short( - pat_field.ident.span, - &format!( - "`{}` has a field named `{}`", - tcx.def_path_str(variant.def_id), - unmentioned_field - ), - unmentioned_field.to_string(), - Applicability::MaybeIncorrect, - ); - } - } - } - } - } - if tcx.sess.teach(&err.get_code().unwrap()) { - err.note( - "This error indicates that a struct pattern attempted to \ - extract a non-existent field from a struct. Struct fields \ - are identified by the name used before the colon : so struct \ - patterns should resemble the declaration of the struct type \ - being matched.\n\n\ - If you are using shorthand field patterns but want to refer \ - to the struct field by a different name, you should rename \ - it explicitly.", - ); - } - err - } - - fn error_tuple_variant_as_struct_pat( - &self, - pat: &Pat<'_>, - fields: &'tcx [hir::PatField<'tcx>], - variant: &ty::VariantDef, - ) -> Option<DiagnosticBuilder<'tcx, ErrorGuaranteed>> { - if let (CtorKind::Fn, PatKind::Struct(qpath, ..)) = (variant.ctor_kind, &pat.kind) { - let path = rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| { - s.print_qpath(qpath, false) - }); - let mut err = struct_span_err!( - self.tcx.sess, - pat.span, - E0769, - "tuple variant `{}` written as struct variant", - path - ); - let (sugg, appl) = if fields.len() == variant.fields.len() { - ( - self.get_suggested_tuple_struct_pattern(fields, variant), - Applicability::MachineApplicable, - ) - } else { - ( - variant.fields.iter().map(|_| "_").collect::<Vec<&str>>().join(", "), - Applicability::MaybeIncorrect, - ) - }; - err.span_suggestion_verbose( - qpath.span().shrink_to_hi().to(pat.span.shrink_to_hi()), - "use the tuple variant pattern syntax instead", - format!("({})", sugg), - appl, - ); - return Some(err); - } - None - } - - fn get_suggested_tuple_struct_pattern( - &self, - fields: &[hir::PatField<'_>], - variant: &VariantDef, - ) -> String { - let variant_field_idents = - variant.fields.iter().map(|f| f.ident(self.tcx)).collect::<Vec<Ident>>(); - fields - .iter() - .map(|field| { - match self.tcx.sess.source_map().span_to_snippet(field.pat.span) { - Ok(f) => { - // Field names are numbers, but numbers - // are not valid identifiers - if variant_field_idents.contains(&field.ident) { - String::from("_") - } else { - f - } - } - Err(_) => rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| { - s.print_pat(field.pat) - }), - } - }) - .collect::<Vec<String>>() - .join(", ") - } - - /// Returns a diagnostic reporting a struct pattern which is missing an `..` due to - /// inaccessible fields. - /// - /// ```text - /// error: pattern requires `..` due to inaccessible fields - /// --> src/main.rs:10:9 - /// | - /// LL | let foo::Foo {} = foo::Foo::default(); - /// | ^^^^^^^^^^^ - /// | - /// help: add a `..` - /// | - /// LL | let foo::Foo { .. } = foo::Foo::default(); - /// | ^^^^^^ - /// ``` - fn error_no_accessible_fields( - &self, - pat: &Pat<'_>, - fields: &'tcx [hir::PatField<'tcx>], - ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { - let mut err = self - .tcx - .sess - .struct_span_err(pat.span, "pattern requires `..` due to inaccessible fields"); - - if let Some(field) = fields.last() { - err.span_suggestion_verbose( - field.span.shrink_to_hi(), - "ignore the inaccessible and unused fields", - ", ..", - Applicability::MachineApplicable, - ); - } else { - let qpath_span = if let PatKind::Struct(qpath, ..) = &pat.kind { - qpath.span() - } else { - bug!("`error_no_accessible_fields` called on non-struct pattern"); - }; - - // Shrink the span to exclude the `foo:Foo` in `foo::Foo { }`. - let span = pat.span.with_lo(qpath_span.shrink_to_hi().hi()); - err.span_suggestion_verbose( - span, - "ignore the inaccessible and unused fields", - " { .. }", - Applicability::MachineApplicable, - ); - } - err - } - - /// Report that a pattern for a `#[non_exhaustive]` struct marked with `non_exhaustive_omitted_patterns` - /// is not exhaustive enough. - /// - /// Nb: the partner lint for enums lives in `compiler/rustc_mir_build/src/thir/pattern/usefulness.rs`. - fn lint_non_exhaustive_omitted_patterns( - &self, - pat: &Pat<'_>, - unmentioned_fields: &[(&ty::FieldDef, Ident)], - ty: Ty<'tcx>, - ) { - fn joined_uncovered_patterns(witnesses: &[&Ident]) -> String { - const LIMIT: usize = 3; - match witnesses { - [] => bug!(), - [witness] => format!("`{}`", witness), - [head @ .., tail] if head.len() < LIMIT => { - let head: Vec<_> = head.iter().map(<_>::to_string).collect(); - format!("`{}` and `{}`", head.join("`, `"), tail) - } - _ => { - let (head, tail) = witnesses.split_at(LIMIT); - let head: Vec<_> = head.iter().map(<_>::to_string).collect(); - format!("`{}` and {} more", head.join("`, `"), tail.len()) - } - } - } - let joined_patterns = joined_uncovered_patterns( - &unmentioned_fields.iter().map(|(_, i)| i).collect::<Vec<_>>(), - ); - - self.tcx.struct_span_lint_hir(NON_EXHAUSTIVE_OMITTED_PATTERNS, pat.hir_id, pat.span, |build| { - let mut lint = build.build("some fields are not explicitly listed"); - lint.span_label(pat.span, format!("field{} {} not listed", rustc_errors::pluralize!(unmentioned_fields.len()), joined_patterns)); - - lint.help( - "ensure that all fields are mentioned explicitly by adding the suggested fields", - ); - lint.note(&format!( - "the pattern is of type `{}` and the `non_exhaustive_omitted_patterns` attribute was found", - ty, - )); - lint.emit(); - }); - } - - /// Returns a diagnostic reporting a struct pattern which does not mention some fields. - /// - /// ```text - /// error[E0027]: pattern does not mention field `bar` - /// --> src/main.rs:15:9 - /// | - /// LL | let foo::Foo {} = foo::Foo::new(); - /// | ^^^^^^^^^^^ missing field `bar` - /// ``` - fn error_unmentioned_fields( - &self, - pat: &Pat<'_>, - unmentioned_fields: &[(&ty::FieldDef, Ident)], - have_inaccessible_fields: bool, - fields: &'tcx [hir::PatField<'tcx>], - ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { - let inaccessible = if have_inaccessible_fields { " and inaccessible fields" } else { "" }; - let field_names = if unmentioned_fields.len() == 1 { - format!("field `{}`{}", unmentioned_fields[0].1, inaccessible) - } else { - let fields = unmentioned_fields - .iter() - .map(|(_, name)| format!("`{}`", name)) - .collect::<Vec<String>>() - .join(", "); - format!("fields {}{}", fields, inaccessible) - }; - let mut err = struct_span_err!( - self.tcx.sess, - pat.span, - E0027, - "pattern does not mention {}", - field_names - ); - err.span_label(pat.span, format!("missing {}", field_names)); - let len = unmentioned_fields.len(); - let (prefix, postfix, sp) = match fields { - [] => match &pat.kind { - PatKind::Struct(path, [], false) => { - (" { ", " }", path.span().shrink_to_hi().until(pat.span.shrink_to_hi())) - } - _ => return err, - }, - [.., field] => { - // Account for last field having a trailing comma or parse recovery at the tail of - // the pattern to avoid invalid suggestion (#78511). - let tail = field.span.shrink_to_hi().with_hi(pat.span.hi()); - match &pat.kind { - PatKind::Struct(..) => (", ", " }", tail), - _ => return err, - } - } - }; - err.span_suggestion( - sp, - &format!( - "include the missing field{} in the pattern{}", - pluralize!(len), - if have_inaccessible_fields { " and ignore the inaccessible fields" } else { "" } - ), - format!( - "{}{}{}{}", - prefix, - unmentioned_fields - .iter() - .map(|(_, name)| name.to_string()) - .collect::<Vec<_>>() - .join(", "), - if have_inaccessible_fields { ", .." } else { "" }, - postfix, - ), - Applicability::MachineApplicable, - ); - err.span_suggestion( - sp, - &format!( - "if you don't care about {these} missing field{s}, you can explicitly ignore {them}", - these = pluralize!("this", len), - s = pluralize!(len), - them = if len == 1 { "it" } else { "them" }, - ), - format!("{}..{}", prefix, postfix), - Applicability::MachineApplicable, - ); - err - } - - fn check_pat_box( - &self, - span: Span, - inner: &'tcx Pat<'tcx>, - expected: Ty<'tcx>, - def_bm: BindingMode, - ti: TopInfo<'tcx>, - ) -> Ty<'tcx> { - let tcx = self.tcx; - let (box_ty, inner_ty) = if self.check_dereferenceable(span, expected, inner) { - // Here, `demand::subtype` is good enough, but I don't - // think any errors can be introduced by using `demand::eqtype`. - let inner_ty = self.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::TypeInference, - span: inner.span, - }); - let box_ty = tcx.mk_box(inner_ty); - self.demand_eqtype_pat(span, expected, box_ty, ti); - (box_ty, inner_ty) - } else { - let err = tcx.ty_error(); - (err, err) - }; - self.check_pat(inner, inner_ty, def_bm, ti); - box_ty - } - - // Precondition: Pat is Ref(inner) - fn check_pat_ref( - &self, - pat: &'tcx Pat<'tcx>, - inner: &'tcx Pat<'tcx>, - mutbl: hir::Mutability, - expected: Ty<'tcx>, - def_bm: BindingMode, - ti: TopInfo<'tcx>, - ) -> Ty<'tcx> { - let tcx = self.tcx; - let expected = self.shallow_resolve(expected); - let (rptr_ty, inner_ty) = if self.check_dereferenceable(pat.span, expected, inner) { - // `demand::subtype` would be good enough, but using `eqtype` turns - // out to be equally general. See (note_1) for details. - - // Take region, inner-type from expected type if we can, - // to avoid creating needless variables. This also helps with - // the bad interactions of the given hack detailed in (note_1). - debug!("check_pat_ref: expected={:?}", expected); - match *expected.kind() { - ty::Ref(_, r_ty, r_mutbl) if r_mutbl == mutbl => (expected, r_ty), - _ => { - let inner_ty = self.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::TypeInference, - span: inner.span, - }); - let rptr_ty = self.new_ref_ty(pat.span, mutbl, inner_ty); - debug!("check_pat_ref: demanding {:?} = {:?}", expected, rptr_ty); - let err = self.demand_eqtype_pat_diag(pat.span, expected, rptr_ty, ti); - - // Look for a case like `fn foo(&foo: u32)` and suggest - // `fn foo(foo: &u32)` - if let Some(mut err) = err { - self.borrow_pat_suggestion(&mut err, pat); - err.emit(); - } - (rptr_ty, inner_ty) - } - } - } else { - let err = tcx.ty_error(); - (err, err) - }; - self.check_pat(inner, inner_ty, def_bm, ti); - rptr_ty - } - - /// Create a reference type with a fresh region variable. - fn new_ref_ty(&self, span: Span, mutbl: hir::Mutability, ty: Ty<'tcx>) -> Ty<'tcx> { - let region = self.next_region_var(infer::PatternRegion(span)); - let mt = ty::TypeAndMut { ty, mutbl }; - self.tcx.mk_ref(region, mt) - } - - /// Type check a slice pattern. - /// - /// Syntactically, these look like `[pat_0, ..., pat_n]`. - /// Semantically, we are type checking a pattern with structure: - /// ```ignore (not-rust) - /// [before_0, ..., before_n, (slice, after_0, ... after_n)?] - /// ``` - /// The type of `slice`, if it is present, depends on the `expected` type. - /// If `slice` is missing, then so is `after_i`. - /// If `slice` is present, it can still represent 0 elements. - fn check_pat_slice( - &self, - span: Span, - before: &'tcx [Pat<'tcx>], - slice: Option<&'tcx Pat<'tcx>>, - after: &'tcx [Pat<'tcx>], - expected: Ty<'tcx>, - def_bm: BindingMode, - ti: TopInfo<'tcx>, - ) -> Ty<'tcx> { - let expected = self.structurally_resolved_type(span, expected); - let (element_ty, opt_slice_ty, inferred) = match *expected.kind() { - // An array, so we might have something like `let [a, b, c] = [0, 1, 2];`. - ty::Array(element_ty, len) => { - let min = before.len() as u64 + after.len() as u64; - let (opt_slice_ty, expected) = - self.check_array_pat_len(span, element_ty, expected, slice, len, min); - // `opt_slice_ty.is_none()` => `slice.is_none()`. - // Note, though, that opt_slice_ty could be `Some(error_ty)`. - assert!(opt_slice_ty.is_some() || slice.is_none()); - (element_ty, opt_slice_ty, expected) - } - ty::Slice(element_ty) => (element_ty, Some(expected), expected), - // The expected type must be an array or slice, but was neither, so error. - _ => { - if !expected.references_error() { - self.error_expected_array_or_slice(span, expected, ti); - } - let err = self.tcx.ty_error(); - (err, Some(err), err) - } - }; - - // Type check all the patterns before `slice`. - for elt in before { - self.check_pat(elt, element_ty, def_bm, ti); - } - // Type check the `slice`, if present, against its expected type. - if let Some(slice) = slice { - self.check_pat(slice, opt_slice_ty.unwrap(), def_bm, ti); - } - // Type check the elements after `slice`, if present. - for elt in after { - self.check_pat(elt, element_ty, def_bm, ti); - } - inferred - } - - /// Type check the length of an array pattern. - /// - /// Returns both the type of the variable length pattern (or `None`), and the potentially - /// inferred array type. We only return `None` for the slice type if `slice.is_none()`. - fn check_array_pat_len( - &self, - span: Span, - element_ty: Ty<'tcx>, - arr_ty: Ty<'tcx>, - slice: Option<&'tcx Pat<'tcx>>, - len: ty::Const<'tcx>, - min_len: u64, - ) -> (Option<Ty<'tcx>>, Ty<'tcx>) { - if let Some(len) = len.try_eval_usize(self.tcx, self.param_env) { - // Now we know the length... - if slice.is_none() { - // ...and since there is no variable-length pattern, - // we require an exact match between the number of elements - // in the array pattern and as provided by the matched type. - if min_len == len { - return (None, arr_ty); - } - - self.error_scrutinee_inconsistent_length(span, min_len, len); - } else if let Some(pat_len) = len.checked_sub(min_len) { - // The variable-length pattern was there, - // so it has an array type with the remaining elements left as its size... - return (Some(self.tcx.mk_array(element_ty, pat_len)), arr_ty); - } else { - // ...however, in this case, there were no remaining elements. - // That is, the slice pattern requires more than the array type offers. - self.error_scrutinee_with_rest_inconsistent_length(span, min_len, len); - } - } else if slice.is_none() { - // We have a pattern with a fixed length, - // which we can use to infer the length of the array. - let updated_arr_ty = self.tcx.mk_array(element_ty, min_len); - self.demand_eqtype(span, updated_arr_ty, arr_ty); - return (None, updated_arr_ty); - } else { - // We have a variable-length pattern and don't know the array length. - // This happens if we have e.g., - // `let [a, b, ..] = arr` where `arr: [T; N]` where `const N: usize`. - self.error_scrutinee_unfixed_length(span); - } - - // If we get here, we must have emitted an error. - (Some(self.tcx.ty_error()), arr_ty) - } - - fn error_scrutinee_inconsistent_length(&self, span: Span, min_len: u64, size: u64) { - struct_span_err!( - self.tcx.sess, - span, - E0527, - "pattern requires {} element{} but array has {}", - min_len, - pluralize!(min_len), - size, - ) - .span_label(span, format!("expected {} element{}", size, pluralize!(size))) - .emit(); - } - - fn error_scrutinee_with_rest_inconsistent_length(&self, span: Span, min_len: u64, size: u64) { - struct_span_err!( - self.tcx.sess, - span, - E0528, - "pattern requires at least {} element{} but array has {}", - min_len, - pluralize!(min_len), - size, - ) - .span_label( - span, - format!("pattern cannot match array of {} element{}", size, pluralize!(size),), - ) - .emit(); - } - - fn error_scrutinee_unfixed_length(&self, span: Span) { - struct_span_err!( - self.tcx.sess, - span, - E0730, - "cannot pattern-match on an array without a fixed length", - ) - .emit(); - } - - fn error_expected_array_or_slice(&self, span: Span, expected_ty: Ty<'tcx>, ti: TopInfo<'tcx>) { - let mut err = struct_span_err!( - self.tcx.sess, - span, - E0529, - "expected an array or slice, found `{expected_ty}`" - ); - if let ty::Ref(_, ty, _) = expected_ty.kind() - && let ty::Array(..) | ty::Slice(..) = ty.kind() - { - err.help("the semantics of slice patterns changed recently; see issue #62254"); - } else if Autoderef::new(&self.infcx, self.param_env, self.body_id, span, expected_ty, span) - .any(|(ty, _)| matches!(ty.kind(), ty::Slice(..) | ty::Array(..))) - && let (Some(span), true) = (ti.span, ti.origin_expr) - && let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) - { - let ty = self.resolve_vars_if_possible(ti.expected); - let is_slice_or_array_or_vector = self.is_slice_or_array_or_vector(&mut err, snippet.clone(), ty); - match is_slice_or_array_or_vector.1.kind() { - ty::Adt(adt_def, _) - if self.tcx.is_diagnostic_item(sym::Option, adt_def.did()) - || self.tcx.is_diagnostic_item(sym::Result, adt_def.did()) => - { - // Slicing won't work here, but `.as_deref()` might (issue #91328). - err.span_suggestion( - span, - "consider using `as_deref` here", - format!("{snippet}.as_deref()"), - Applicability::MaybeIncorrect, - ); - } - _ => () - } - if is_slice_or_array_or_vector.0 { - err.span_suggestion( - span, - "consider slicing here", - format!("{snippet}[..]"), - Applicability::MachineApplicable, - ); - } - } - err.span_label(span, format!("pattern cannot match with input type `{expected_ty}`")); - err.emit(); - } - - fn is_slice_or_array_or_vector( - &self, - err: &mut Diagnostic, - snippet: String, - ty: Ty<'tcx>, - ) -> (bool, Ty<'tcx>) { - match ty.kind() { - ty::Adt(adt_def, _) if self.tcx.is_diagnostic_item(sym::Vec, adt_def.did()) => { - (true, ty) - } - ty::Ref(_, ty, _) => self.is_slice_or_array_or_vector(err, snippet, *ty), - ty::Slice(..) | ty::Array(..) => (true, ty), - _ => (false, ty), - } - } -} diff --git a/compiler/rustc_typeck/src/check/place_op.rs b/compiler/rustc_typeck/src/check/place_op.rs deleted file mode 100644 index 2e0f37eba..000000000 --- a/compiler/rustc_typeck/src/check/place_op.rs +++ /dev/null @@ -1,451 +0,0 @@ -use crate::check::method::MethodCallee; -use crate::check::{has_expected_num_generic_args, FnCtxt, PlaceOp}; -use rustc_ast as ast; -use rustc_errors::Applicability; -use rustc_hir as hir; -use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind}; -use rustc_infer::infer::InferOk; -use rustc_middle::ty::adjustment::{Adjust, Adjustment, OverloadedDeref, PointerCast}; -use rustc_middle::ty::adjustment::{AllowTwoPhase, AutoBorrow, AutoBorrowMutability}; -use rustc_middle::ty::{self, Ty}; -use rustc_span::symbol::{sym, Ident}; -use rustc_span::Span; -use rustc_trait_selection::autoderef::Autoderef; -use std::slice; - -impl<'a, 'tcx> FnCtxt<'a, 'tcx> { - /// Type-check `*oprnd_expr` with `oprnd_expr` type-checked already. - pub(super) fn lookup_derefing( - &self, - expr: &hir::Expr<'_>, - oprnd_expr: &'tcx hir::Expr<'tcx>, - oprnd_ty: Ty<'tcx>, - ) -> Option<Ty<'tcx>> { - if let Some(mt) = oprnd_ty.builtin_deref(true) { - return Some(mt.ty); - } - - let ok = self.try_overloaded_deref(expr.span, oprnd_ty)?; - let method = self.register_infer_ok_obligations(ok); - if let ty::Ref(region, _, hir::Mutability::Not) = method.sig.inputs()[0].kind() { - self.apply_adjustments( - oprnd_expr, - vec![Adjustment { - kind: Adjust::Borrow(AutoBorrow::Ref(*region, AutoBorrowMutability::Not)), - target: method.sig.inputs()[0], - }], - ); - } else { - span_bug!(expr.span, "input to deref is not a ref?"); - } - let ty = self.make_overloaded_place_return_type(method).ty; - self.write_method_call(expr.hir_id, method); - Some(ty) - } - - /// Type-check `*base_expr[index_expr]` with `base_expr` and `index_expr` type-checked already. - pub(super) fn lookup_indexing( - &self, - expr: &hir::Expr<'_>, - base_expr: &'tcx hir::Expr<'tcx>, - base_ty: Ty<'tcx>, - index_expr: &'tcx hir::Expr<'tcx>, - idx_ty: Ty<'tcx>, - ) -> Option<(/*index type*/ Ty<'tcx>, /*element type*/ Ty<'tcx>)> { - // FIXME(#18741) -- this is almost but not quite the same as the - // autoderef that normal method probing does. They could likely be - // consolidated. - - let mut autoderef = self.autoderef(base_expr.span, base_ty); - let mut result = None; - while result.is_none() && autoderef.next().is_some() { - result = self.try_index_step(expr, base_expr, &autoderef, idx_ty, index_expr); - } - self.register_predicates(autoderef.into_obligations()); - result - } - - fn negative_index( - &self, - ty: Ty<'tcx>, - span: Span, - base_expr: &hir::Expr<'_>, - ) -> Option<(Ty<'tcx>, Ty<'tcx>)> { - let ty = self.resolve_vars_if_possible(ty); - let mut err = self.tcx.sess.struct_span_err( - span, - &format!("negative integers cannot be used to index on a `{ty}`"), - ); - err.span_label(span, &format!("cannot use a negative integer for indexing on `{ty}`")); - if let (hir::ExprKind::Path(..), Ok(snippet)) = - (&base_expr.kind, self.tcx.sess.source_map().span_to_snippet(base_expr.span)) - { - // `foo[-1]` to `foo[foo.len() - 1]` - err.span_suggestion_verbose( - span.shrink_to_lo(), - &format!( - "to access an element starting from the end of the `{ty}`, compute the index", - ), - format!("{snippet}.len() "), - Applicability::MachineApplicable, - ); - } - err.emit(); - Some((self.tcx.ty_error(), self.tcx.ty_error())) - } - - /// To type-check `base_expr[index_expr]`, we progressively autoderef - /// (and otherwise adjust) `base_expr`, looking for a type which either - /// supports builtin indexing or overloaded indexing. - /// This loop implements one step in that search; the autoderef loop - /// is implemented by `lookup_indexing`. - fn try_index_step( - &self, - expr: &hir::Expr<'_>, - base_expr: &hir::Expr<'_>, - autoderef: &Autoderef<'a, 'tcx>, - index_ty: Ty<'tcx>, - index_expr: &hir::Expr<'_>, - ) -> Option<(/*index type*/ Ty<'tcx>, /*element type*/ Ty<'tcx>)> { - let adjusted_ty = - self.structurally_resolved_type(autoderef.span(), autoderef.final_ty(false)); - debug!( - "try_index_step(expr={:?}, base_expr={:?}, adjusted_ty={:?}, \ - index_ty={:?})", - expr, base_expr, adjusted_ty, index_ty - ); - - if let hir::ExprKind::Unary( - hir::UnOp::Neg, - hir::Expr { - kind: hir::ExprKind::Lit(hir::Lit { node: ast::LitKind::Int(..), .. }), - .. - }, - ) = index_expr.kind - { - match adjusted_ty.kind() { - ty::Adt(def, _) if self.tcx.is_diagnostic_item(sym::Vec, def.did()) => { - return self.negative_index(adjusted_ty, index_expr.span, base_expr); - } - ty::Slice(_) | ty::Array(_, _) => { - return self.negative_index(adjusted_ty, index_expr.span, base_expr); - } - _ => {} - } - } - - for unsize in [false, true] { - let mut self_ty = adjusted_ty; - if unsize { - // We only unsize arrays here. - if let ty::Array(element_ty, _) = adjusted_ty.kind() { - self_ty = self.tcx.mk_slice(*element_ty); - } else { - continue; - } - } - - // If some lookup succeeds, write callee into table and extract index/element - // type from the method signature. - // If some lookup succeeded, install method in table - let input_ty = self.next_ty_var(TypeVariableOrigin { - kind: TypeVariableOriginKind::AutoDeref, - span: base_expr.span, - }); - let method = - self.try_overloaded_place_op(expr.span, self_ty, &[input_ty], PlaceOp::Index); - - if let Some(result) = method { - debug!("try_index_step: success, using overloaded indexing"); - let method = self.register_infer_ok_obligations(result); - - let mut adjustments = self.adjust_steps(autoderef); - if let ty::Ref(region, _, hir::Mutability::Not) = method.sig.inputs()[0].kind() { - adjustments.push(Adjustment { - kind: Adjust::Borrow(AutoBorrow::Ref(*region, AutoBorrowMutability::Not)), - target: self.tcx.mk_ref( - *region, - ty::TypeAndMut { mutbl: hir::Mutability::Not, ty: adjusted_ty }, - ), - }); - } else { - span_bug!(expr.span, "input to index is not a ref?"); - } - if unsize { - adjustments.push(Adjustment { - kind: Adjust::Pointer(PointerCast::Unsize), - target: method.sig.inputs()[0], - }); - } - self.apply_adjustments(base_expr, adjustments); - - self.write_method_call(expr.hir_id, method); - - return Some((input_ty, self.make_overloaded_place_return_type(method).ty)); - } - } - - None - } - - /// Try to resolve an overloaded place op. We only deal with the immutable - /// variant here (Deref/Index). In some contexts we would need the mutable - /// variant (DerefMut/IndexMut); those would be later converted by - /// `convert_place_derefs_to_mutable`. - pub(super) fn try_overloaded_place_op( - &self, - span: Span, - base_ty: Ty<'tcx>, - arg_tys: &[Ty<'tcx>], - op: PlaceOp, - ) -> Option<InferOk<'tcx, MethodCallee<'tcx>>> { - debug!("try_overloaded_place_op({:?},{:?},{:?})", span, base_ty, op); - - let (imm_tr, imm_op) = match op { - PlaceOp::Deref => (self.tcx.lang_items().deref_trait(), sym::deref), - PlaceOp::Index => (self.tcx.lang_items().index_trait(), sym::index), - }; - - // If the lang item was declared incorrectly, stop here so that we don't - // run into an ICE (#83893). The error is reported where the lang item is - // declared. - if !has_expected_num_generic_args( - self.tcx, - imm_tr, - match op { - PlaceOp::Deref => 0, - PlaceOp::Index => 1, - }, - ) { - return None; - } - - imm_tr.and_then(|trait_did| { - self.lookup_method_in_trait( - span, - Ident::with_dummy_span(imm_op), - trait_did, - base_ty, - Some(arg_tys), - ) - }) - } - - fn try_mutable_overloaded_place_op( - &self, - span: Span, - base_ty: Ty<'tcx>, - arg_tys: &[Ty<'tcx>], - op: PlaceOp, - ) -> Option<InferOk<'tcx, MethodCallee<'tcx>>> { - debug!("try_mutable_overloaded_place_op({:?},{:?},{:?})", span, base_ty, op); - - let (mut_tr, mut_op) = match op { - PlaceOp::Deref => (self.tcx.lang_items().deref_mut_trait(), sym::deref_mut), - PlaceOp::Index => (self.tcx.lang_items().index_mut_trait(), sym::index_mut), - }; - - // If the lang item was declared incorrectly, stop here so that we don't - // run into an ICE (#83893). The error is reported where the lang item is - // declared. - if !has_expected_num_generic_args( - self.tcx, - mut_tr, - match op { - PlaceOp::Deref => 0, - PlaceOp::Index => 1, - }, - ) { - return None; - } - - mut_tr.and_then(|trait_did| { - self.lookup_method_in_trait( - span, - Ident::with_dummy_span(mut_op), - trait_did, - base_ty, - Some(arg_tys), - ) - }) - } - - /// Convert auto-derefs, indices, etc of an expression from `Deref` and `Index` - /// into `DerefMut` and `IndexMut` respectively. - /// - /// This is a second pass of typechecking derefs/indices. We need this because we do not - /// always know whether a place needs to be mutable or not in the first pass. - /// This happens whether there is an implicit mutable reborrow, e.g. when the type - /// is used as the receiver of a method call. - pub fn convert_place_derefs_to_mutable(&self, expr: &hir::Expr<'_>) { - // Gather up expressions we want to munge. - let mut exprs = vec![expr]; - - while let hir::ExprKind::Field(ref expr, _) - | hir::ExprKind::Index(ref expr, _) - | hir::ExprKind::Unary(hir::UnOp::Deref, ref expr) = exprs.last().unwrap().kind - { - exprs.push(expr); - } - - debug!("convert_place_derefs_to_mutable: exprs={:?}", exprs); - - // Fix up autoderefs and derefs. - let mut inside_union = false; - for (i, &expr) in exprs.iter().rev().enumerate() { - debug!("convert_place_derefs_to_mutable: i={} expr={:?}", i, expr); - - let mut source = self.node_ty(expr.hir_id); - if matches!(expr.kind, hir::ExprKind::Unary(hir::UnOp::Deref, _)) { - // Clear previous flag; after a pointer indirection it does not apply any more. - inside_union = false; - } - if source.is_union() { - inside_union = true; - } - // Fix up the autoderefs. Autorefs can only occur immediately preceding - // overloaded place ops, and will be fixed by them in order to get - // the correct region. - // Do not mutate adjustments in place, but rather take them, - // and replace them after mutating them, to avoid having the - // typeck results borrowed during (`deref_mut`) method resolution. - let previous_adjustments = - self.typeck_results.borrow_mut().adjustments_mut().remove(expr.hir_id); - if let Some(mut adjustments) = previous_adjustments { - for adjustment in &mut adjustments { - if let Adjust::Deref(Some(ref mut deref)) = adjustment.kind - && let Some(ok) = self.try_mutable_overloaded_place_op( - expr.span, - source, - &[], - PlaceOp::Deref, - ) - { - let method = self.register_infer_ok_obligations(ok); - if let ty::Ref(region, _, mutbl) = *method.sig.output().kind() { - *deref = OverloadedDeref { region, mutbl, span: deref.span }; - } - // If this is a union field, also throw an error for `DerefMut` of `ManuallyDrop` (see RFC 2514). - // This helps avoid accidental drops. - if inside_union - && source.ty_adt_def().map_or(false, |adt| adt.is_manually_drop()) - { - let mut err = self.tcx.sess.struct_span_err( - expr.span, - "not automatically applying `DerefMut` on `ManuallyDrop` union field", - ); - err.help( - "writing to this reference calls the destructor for the old value", - ); - err.help("add an explicit `*` if that is desired, or call `ptr::write` to not run the destructor"); - err.emit(); - } - } - source = adjustment.target; - } - self.typeck_results.borrow_mut().adjustments_mut().insert(expr.hir_id, adjustments); - } - - match expr.kind { - hir::ExprKind::Index(base_expr, ..) => { - self.convert_place_op_to_mutable(PlaceOp::Index, expr, base_expr); - } - hir::ExprKind::Unary(hir::UnOp::Deref, base_expr) => { - self.convert_place_op_to_mutable(PlaceOp::Deref, expr, base_expr); - } - _ => {} - } - } - } - - fn convert_place_op_to_mutable( - &self, - op: PlaceOp, - expr: &hir::Expr<'_>, - base_expr: &hir::Expr<'_>, - ) { - debug!("convert_place_op_to_mutable({:?}, {:?}, {:?})", op, expr, base_expr); - if !self.typeck_results.borrow().is_method_call(expr) { - debug!("convert_place_op_to_mutable - builtin, nothing to do"); - return; - } - - // Need to deref because overloaded place ops take self by-reference. - let base_ty = self - .typeck_results - .borrow() - .expr_ty_adjusted(base_expr) - .builtin_deref(false) - .expect("place op takes something that is not a ref") - .ty; - - let arg_ty = match op { - PlaceOp::Deref => None, - PlaceOp::Index => { - // We would need to recover the `T` used when we resolve `<_ as Index<T>>::index` - // in try_index_step. This is the subst at index 1. - // - // Note: we should *not* use `expr_ty` of index_expr here because autoderef - // during coercions can cause type of index_expr to differ from `T` (#72002). - // We also could not use `expr_ty_adjusted` of index_expr because reborrowing - // during coercions can also cause type of index_expr to differ from `T`, - // which can potentially cause regionck failure (#74933). - Some(self.typeck_results.borrow().node_substs(expr.hir_id).type_at(1)) - } - }; - let arg_tys = match arg_ty { - None => &[], - Some(ref ty) => slice::from_ref(ty), - }; - - let method = self.try_mutable_overloaded_place_op(expr.span, base_ty, arg_tys, op); - let method = match method { - Some(ok) => self.register_infer_ok_obligations(ok), - // Couldn't find the mutable variant of the place op, keep the - // current, immutable version. - None => return, - }; - debug!("convert_place_op_to_mutable: method={:?}", method); - self.write_method_call(expr.hir_id, method); - - let ty::Ref(region, _, hir::Mutability::Mut) = method.sig.inputs()[0].kind() else { - span_bug!(expr.span, "input to mutable place op is not a mut ref?"); - }; - - // Convert the autoref in the base expr to mutable with the correct - // region and mutability. - let base_expr_ty = self.node_ty(base_expr.hir_id); - if let Some(adjustments) = - self.typeck_results.borrow_mut().adjustments_mut().get_mut(base_expr.hir_id) - { - let mut source = base_expr_ty; - for adjustment in &mut adjustments[..] { - if let Adjust::Borrow(AutoBorrow::Ref(..)) = adjustment.kind { - debug!("convert_place_op_to_mutable: converting autoref {:?}", adjustment); - let mutbl = AutoBorrowMutability::Mut { - // Deref/indexing can be desugared to a method call, - // so maybe we could use two-phase here. - // See the documentation of AllowTwoPhase for why that's - // not the case today. - allow_two_phase_borrow: AllowTwoPhase::No, - }; - adjustment.kind = Adjust::Borrow(AutoBorrow::Ref(*region, mutbl)); - adjustment.target = self - .tcx - .mk_ref(*region, ty::TypeAndMut { ty: source, mutbl: mutbl.into() }); - } - source = adjustment.target; - } - - // If we have an autoref followed by unsizing at the end, fix the unsize target. - if let [ - .., - Adjustment { kind: Adjust::Borrow(AutoBorrow::Ref(..)), .. }, - Adjustment { kind: Adjust::Pointer(PointerCast::Unsize), ref mut target }, - ] = adjustments[..] - { - *target = method.sig.inputs()[0]; - } - } - } -} diff --git a/compiler/rustc_typeck/src/check/region.rs b/compiler/rustc_typeck/src/check/region.rs deleted file mode 100644 index b89db79be..000000000 --- a/compiler/rustc_typeck/src/check/region.rs +++ /dev/null @@ -1,852 +0,0 @@ -//! This file builds up the `ScopeTree`, which describes -//! the parent links in the region hierarchy. -//! -//! For more information about how MIR-based region-checking works, -//! see the [rustc dev guide]. -//! -//! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/borrow_check.html - -use rustc_ast::walk_list; -use rustc_data_structures::fx::FxHashSet; -use rustc_hir as hir; -use rustc_hir::def_id::DefId; -use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::{Arm, Block, Expr, Local, Pat, PatKind, Stmt}; -use rustc_index::vec::Idx; -use rustc_middle::middle::region::*; -use rustc_middle::ty::TyCtxt; -use rustc_span::source_map; -use rustc_span::Span; - -use std::mem; - -#[derive(Debug, Copy, Clone)] -pub struct Context { - /// The scope that contains any new variables declared, plus its depth in - /// the scope tree. - var_parent: Option<(Scope, ScopeDepth)>, - - /// Region parent of expressions, etc., plus its depth in the scope tree. - parent: Option<(Scope, ScopeDepth)>, -} - -struct RegionResolutionVisitor<'tcx> { - tcx: TyCtxt<'tcx>, - - // The number of expressions and patterns visited in the current body. - expr_and_pat_count: usize, - // When this is `true`, we record the `Scopes` we encounter - // when processing a Yield expression. This allows us to fix - // up their indices. - pessimistic_yield: bool, - // Stores scopes when `pessimistic_yield` is `true`. - fixup_scopes: Vec<Scope>, - // The generated scope tree. - scope_tree: ScopeTree, - - cx: Context, - - /// `terminating_scopes` is a set containing the ids of each - /// statement, or conditional/repeating expression. These scopes - /// are calling "terminating scopes" because, when attempting to - /// find the scope of a temporary, by default we search up the - /// enclosing scopes until we encounter the terminating scope. A - /// conditional/repeating expression is one which is not - /// guaranteed to execute exactly once upon entering the parent - /// scope. This could be because the expression only executes - /// conditionally, such as the expression `b` in `a && b`, or - /// because the expression may execute many times, such as a loop - /// body. The reason that we distinguish such expressions is that, - /// upon exiting the parent scope, we cannot statically know how - /// many times the expression executed, and thus if the expression - /// creates temporaries we cannot know statically how many such - /// temporaries we would have to cleanup. Therefore, we ensure that - /// the temporaries never outlast the conditional/repeating - /// expression, preventing the need for dynamic checks and/or - /// arbitrary amounts of stack space. Terminating scopes end - /// up being contained in a DestructionScope that contains the - /// destructor's execution. - terminating_scopes: FxHashSet<hir::ItemLocalId>, -} - -/// Records the lifetime of a local variable as `cx.var_parent` -fn record_var_lifetime( - visitor: &mut RegionResolutionVisitor<'_>, - var_id: hir::ItemLocalId, - _sp: Span, -) { - match visitor.cx.var_parent { - None => { - // this can happen in extern fn declarations like - // - // extern fn isalnum(c: c_int) -> c_int - } - Some((parent_scope, _)) => visitor.scope_tree.record_var_scope(var_id, parent_scope), - } -} - -fn resolve_block<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, blk: &'tcx hir::Block<'tcx>) { - debug!("resolve_block(blk.hir_id={:?})", blk.hir_id); - - let prev_cx = visitor.cx; - - // We treat the tail expression in the block (if any) somewhat - // differently from the statements. The issue has to do with - // temporary lifetimes. Consider the following: - // - // quux({ - // let inner = ... (&bar()) ...; - // - // (... (&foo()) ...) // (the tail expression) - // }, other_argument()); - // - // Each of the statements within the block is a terminating - // scope, and thus a temporary (e.g., the result of calling - // `bar()` in the initializer expression for `let inner = ...;`) - // will be cleaned up immediately after its corresponding - // statement (i.e., `let inner = ...;`) executes. - // - // On the other hand, temporaries associated with evaluating the - // tail expression for the block are assigned lifetimes so that - // they will be cleaned up as part of the terminating scope - // *surrounding* the block expression. Here, the terminating - // scope for the block expression is the `quux(..)` call; so - // those temporaries will only be cleaned up *after* both - // `other_argument()` has run and also the call to `quux(..)` - // itself has returned. - - visitor.enter_node_scope_with_dtor(blk.hir_id.local_id); - visitor.cx.var_parent = visitor.cx.parent; - - { - // This block should be kept approximately in sync with - // `intravisit::walk_block`. (We manually walk the block, rather - // than call `walk_block`, in order to maintain precise - // index information.) - - 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 - // suffix of the block. Each subscope in a block has the - // previous subscope in the block as a parent, except for - // the first such subscope, which has the block itself as a - // parent. - 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) - } - hir::StmtKind::Expr(..) | hir::StmtKind::Semi(..) => visitor.visit_stmt(statement), - } - } - walk_list!(visitor, visit_expr, &blk.expr); - } - - visitor.cx = prev_cx; -} - -fn resolve_arm<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, arm: &'tcx hir::Arm<'tcx>) { - let prev_cx = visitor.cx; - - visitor.enter_scope(Scope { id: arm.hir_id.local_id, data: ScopeData::Node }); - visitor.cx.var_parent = visitor.cx.parent; - - visitor.terminating_scopes.insert(arm.body.hir_id.local_id); - - if let Some(hir::Guard::If(ref expr)) = arm.guard { - visitor.terminating_scopes.insert(expr.hir_id.local_id); - } - - intravisit::walk_arm(visitor, arm); - - visitor.cx = prev_cx; -} - -fn resolve_pat<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, pat: &'tcx hir::Pat<'tcx>) { - visitor.record_child_scope(Scope { id: pat.hir_id.local_id, data: ScopeData::Node }); - - // If this is a binding then record the lifetime of that binding. - if let PatKind::Binding(..) = pat.kind { - record_var_lifetime(visitor, pat.hir_id.local_id, pat.span); - } - - debug!("resolve_pat - pre-increment {} pat = {:?}", visitor.expr_and_pat_count, pat); - - intravisit::walk_pat(visitor, pat); - - visitor.expr_and_pat_count += 1; - - debug!("resolve_pat - post-increment {} pat = {:?}", visitor.expr_and_pat_count, pat); -} - -fn resolve_stmt<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, stmt: &'tcx hir::Stmt<'tcx>) { - let stmt_id = stmt.hir_id.local_id; - debug!("resolve_stmt(stmt.id={:?})", stmt_id); - - // Every statement will clean up the temporaries created during - // execution of that statement. Therefore each statement has an - // associated destruction scope that represents the scope of the - // statement plus its destructors, and thus the scope for which - // regions referenced by the destructors need to survive. - visitor.terminating_scopes.insert(stmt_id); - - let prev_parent = visitor.cx.parent; - visitor.enter_node_scope_with_dtor(stmt_id); - - intravisit::walk_stmt(visitor, stmt); - - visitor.cx.parent = prev_parent; -} - -fn resolve_expr<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, expr: &'tcx hir::Expr<'tcx>) { - debug!("resolve_expr - pre-increment {} expr = {:?}", visitor.expr_and_pat_count, expr); - - let prev_cx = visitor.cx; - visitor.enter_node_scope_with_dtor(expr.hir_id.local_id); - - { - let terminating_scopes = &mut visitor.terminating_scopes; - let mut terminating = |id: hir::ItemLocalId| { - terminating_scopes.insert(id); - }; - match expr.kind { - // Conditional or repeating scopes are always terminating - // scopes, meaning that temporaries cannot outlive them. - // This ensures fixed size stacks. - hir::ExprKind::Binary( - source_map::Spanned { node: hir::BinOpKind::And, .. }, - _, - ref r, - ) - | hir::ExprKind::Binary( - source_map::Spanned { node: hir::BinOpKind::Or, .. }, - _, - ref r, - ) => { - // For shortcircuiting operators, mark the RHS as a terminating - // scope since it only executes conditionally. - 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); - } - - hir::ExprKind::If(_, ref then, None) => { - terminating(then.hir_id.local_id); - } - - hir::ExprKind::Loop(ref body, _, _, _) => { - terminating(body.hir_id.local_id); - } - - hir::ExprKind::DropTemps(ref expr) => { - // `DropTemps(expr)` does not denote a conditional scope. - // Rather, we want to achieve the same behavior as `{ let _t = expr; _t }`. - terminating(expr.hir_id.local_id); - } - - hir::ExprKind::AssignOp(..) - | hir::ExprKind::Index(..) - | hir::ExprKind::Unary(..) - | hir::ExprKind::Call(..) - | hir::ExprKind::MethodCall(..) => { - // FIXME(https://github.com/rust-lang/rfcs/issues/811) Nested method calls - // - // The lifetimes for a call or method call look as follows: - // - // call.id - // - arg0.id - // - ... - // - argN.id - // - call.callee_id - // - // The idea is that call.callee_id represents *the time when - // the invoked function is actually running* and call.id - // represents *the time to prepare the arguments and make the - // call*. See the section "Borrows in Calls" borrowck/README.md - // for an extended explanation of why this distinction is - // important. - // - // record_superlifetime(new_cx, expr.callee_id); - } - - _ => {} - } - } - - let prev_pessimistic = visitor.pessimistic_yield; - - // Ordinarily, we can rely on the visit order of HIR intravisit - // to correspond to the actual execution order of statements. - // However, there's a weird corner case with compound assignment - // operators (e.g. `a += b`). The evaluation order depends on whether - // or not the operator is overloaded (e.g. whether or not a trait - // like AddAssign is implemented). - - // For primitive types (which, despite having a trait impl, don't actually - // end up calling it), the evaluation order is right-to-left. For example, - // the following code snippet: - // - // let y = &mut 0; - // *{println!("LHS!"); y} += {println!("RHS!"); 1}; - // - // will print: - // - // RHS! - // LHS! - // - // However, if the operator is used on a non-primitive type, - // the evaluation order will be left-to-right, since the operator - // actually get desugared to a method call. For example, this - // nearly identical code snippet: - // - // let y = &mut String::new(); - // *{println!("LHS String"); y} += {println!("RHS String"); "hi"}; - // - // will print: - // LHS String - // RHS String - // - // To determine the actual execution order, we need to perform - // trait resolution. Unfortunately, we need to be able to compute - // yield_in_scope before type checking is even done, as it gets - // used by AST borrowcheck. - // - // Fortunately, we don't need to know the actual execution order. - // It suffices to know the 'worst case' order with respect to yields. - // Specifically, we need to know the highest 'expr_and_pat_count' - // that we could assign to the yield expression. To do this, - // we pick the greater of the two values from the left-hand - // and right-hand expressions. This makes us overly conservative - // about what types could possibly live across yield points, - // but we will never fail to detect that a type does actually - // live across a yield point. The latter part is critical - - // we're already overly conservative about what types will live - // across yield points, as the generated MIR will determine - // when things are actually live. However, for typecheck to work - // properly, we can't miss any types. - - match expr.kind { - // Manually recurse over closures and inline consts, because they are the only - // case of nested bodies that share the parent environment. - hir::ExprKind::Closure(&hir::Closure { body, .. }) - | hir::ExprKind::ConstBlock(hir::AnonConst { body, .. }) => { - let body = visitor.tcx.hir().body(body); - visitor.visit_body(body); - } - hir::ExprKind::AssignOp(_, ref left_expr, ref right_expr) => { - debug!( - "resolve_expr - enabling pessimistic_yield, was previously {}", - prev_pessimistic - ); - - let start_point = visitor.fixup_scopes.len(); - visitor.pessimistic_yield = true; - - // If the actual execution order turns out to be right-to-left, - // then we're fine. However, if the actual execution order is left-to-right, - // then we'll assign too low a count to any `yield` expressions - // we encounter in 'right_expression' - they should really occur after all of the - // expressions in 'left_expression'. - visitor.visit_expr(&right_expr); - visitor.pessimistic_yield = prev_pessimistic; - - debug!("resolve_expr - restoring pessimistic_yield to {}", prev_pessimistic); - visitor.visit_expr(&left_expr); - debug!("resolve_expr - fixing up counts to {}", visitor.expr_and_pat_count); - - // Remove and process any scopes pushed by the visitor - let target_scopes = visitor.fixup_scopes.drain(start_point..); - - for scope in target_scopes { - let mut yield_data = - visitor.scope_tree.yield_in_scope.get_mut(&scope).unwrap().last_mut().unwrap(); - let count = yield_data.expr_and_pat_count; - let span = yield_data.span; - - // expr_and_pat_count never decreases. Since we recorded counts in yield_in_scope - // before walking the left-hand side, it should be impossible for the recorded - // count to be greater than the left-hand side count. - if count > visitor.expr_and_pat_count { - bug!( - "Encountered greater count {} at span {:?} - expected no greater than {}", - count, - span, - visitor.expr_and_pat_count - ); - } - let new_count = visitor.expr_and_pat_count; - debug!( - "resolve_expr - increasing count for scope {:?} from {} to {} at span {:?}", - scope, count, new_count, span - ); - - yield_data.expr_and_pat_count = new_count; - } - } - - hir::ExprKind::If(ref cond, ref then, Some(ref otherwise)) => { - let expr_cx = visitor.cx; - visitor.enter_scope(Scope { id: then.hir_id.local_id, data: ScopeData::IfThen }); - visitor.cx.var_parent = visitor.cx.parent; - visitor.visit_expr(cond); - visitor.visit_expr(then); - visitor.cx = expr_cx; - visitor.visit_expr(otherwise); - } - - hir::ExprKind::If(ref cond, ref then, None) => { - let expr_cx = visitor.cx; - visitor.enter_scope(Scope { id: then.hir_id.local_id, data: ScopeData::IfThen }); - visitor.cx.var_parent = visitor.cx.parent; - visitor.visit_expr(cond); - visitor.visit_expr(then); - visitor.cx = expr_cx; - } - - _ => intravisit::walk_expr(visitor, expr), - } - - visitor.expr_and_pat_count += 1; - - debug!("resolve_expr post-increment {}, expr = {:?}", visitor.expr_and_pat_count, expr); - - if let hir::ExprKind::Yield(_, source) = &expr.kind { - // Mark this expr's scope and all parent scopes as containing `yield`. - let mut scope = Scope { id: expr.hir_id.local_id, data: ScopeData::Node }; - loop { - let span = match expr.kind { - hir::ExprKind::Yield(expr, hir::YieldSource::Await { .. }) => { - expr.span.shrink_to_hi().to(expr.span) - } - _ => expr.span, - }; - let data = - YieldData { span, expr_and_pat_count: visitor.expr_and_pat_count, source: *source }; - match visitor.scope_tree.yield_in_scope.get_mut(&scope) { - Some(yields) => yields.push(data), - None => { - visitor.scope_tree.yield_in_scope.insert(scope, vec![data]); - } - } - - if visitor.pessimistic_yield { - debug!("resolve_expr in pessimistic_yield - marking scope {:?} for fixup", scope); - visitor.fixup_scopes.push(scope); - } - - // Keep traversing up while we can. - match visitor.scope_tree.parent_map.get(&scope) { - // Don't cross from closure bodies to their parent. - Some(&(superscope, _)) => match superscope.data { - ScopeData::CallSite => break, - _ => scope = superscope, - }, - None => break, - } - } - } - - visitor.cx = prev_cx; -} - -fn resolve_local<'tcx>( - visitor: &mut RegionResolutionVisitor<'tcx>, - pat: Option<&'tcx hir::Pat<'tcx>>, - init: Option<&'tcx hir::Expr<'tcx>>, -) { - debug!("resolve_local(pat={:?}, init={:?})", pat, init); - - let blk_scope = visitor.cx.var_parent.map(|(p, _)| p); - - // As an exception to the normal rules governing temporary - // lifetimes, initializers in a let have a temporary lifetime - // of the enclosing block. This means that e.g., a program - // like the following is legal: - // - // let ref x = HashMap::new(); - // - // Because the hash map will be freed in the enclosing block. - // - // We express the rules more formally based on 3 grammars (defined - // fully in the helpers below that implement them): - // - // 1. `E&`, which matches expressions like `&<rvalue>` that - // own a pointer into the stack. - // - // 2. `P&`, which matches patterns like `ref x` or `(ref x, ref - // y)` that produce ref bindings into the value they are - // matched against or something (at least partially) owned by - // the value they are matched against. (By partially owned, - // I mean that creating a binding into a ref-counted or managed value - // would still count.) - // - // 3. `ET`, which matches both rvalues like `foo()` as well as places - // based on rvalues like `foo().x[2].y`. - // - // A subexpression `<rvalue>` that appears in a let initializer - // `let pat [: ty] = expr` has an extended temporary lifetime if - // any of the following conditions are met: - // - // A. `pat` matches `P&` and `expr` matches `ET` - // (covers cases where `pat` creates ref bindings into an rvalue - // produced by `expr`) - // B. `ty` is a borrowed pointer and `expr` matches `ET` - // (covers cases where coercion creates a borrow) - // C. `expr` matches `E&` - // (covers cases `expr` borrows an rvalue that is then assigned - // to memory (at least partially) owned by the binding) - // - // Here are some examples hopefully giving an intuition where each - // rule comes into play and why: - // - // Rule A. `let (ref x, ref y) = (foo().x, 44)`. The rvalue `(22, 44)` - // would have an extended lifetime, but not `foo()`. - // - // Rule B. `let x = &foo().x`. The rvalue `foo()` would have extended - // lifetime. - // - // In some cases, multiple rules may apply (though not to the same - // rvalue). For example: - // - // let ref x = [&a(), &b()]; - // - // Here, the expression `[...]` has an extended lifetime due to rule - // A, but the inner rvalues `a()` and `b()` have an extended lifetime - // due to rule C. - - if let Some(expr) = init { - record_rvalue_scope_if_borrow_expr(visitor, &expr, blk_scope); - - if let Some(pat) = pat { - if is_binding_pat(pat) { - visitor.scope_tree.record_rvalue_candidate( - expr.hir_id, - RvalueCandidateType::Pattern { - target: expr.hir_id.local_id, - lifetime: blk_scope, - }, - ); - } - } - } - - // Make sure we visit the initializer first, so expr_and_pat_count remains correct. - // The correct order, as shared between generator_interior, drop_ranges and intravisitor, - // is to walk initializer, followed by pattern bindings, finally followed by the `else` block. - if let Some(expr) = init { - visitor.visit_expr(expr); - } - if let Some(pat) = pat { - visitor.visit_pat(pat); - } - - /// Returns `true` if `pat` match the `P&` non-terminal. - /// - /// ```text - /// P& = ref X - /// | StructName { ..., P&, ... } - /// | VariantName(..., P&, ...) - /// | [ ..., P&, ... ] - /// | ( ..., P&, ... ) - /// | ... "|" P& "|" ... - /// | box P& - /// ``` - fn is_binding_pat(pat: &hir::Pat<'_>) -> bool { - // Note that the code below looks for *explicit* refs only, that is, it won't - // know about *implicit* refs as introduced in #42640. - // - // This is not a problem. For example, consider - // - // let (ref x, ref y) = (Foo { .. }, Bar { .. }); - // - // Due to the explicit refs on the left hand side, the below code would signal - // that the temporary value on the right hand side should live until the end of - // the enclosing block (as opposed to being dropped after the let is complete). - // - // To create an implicit ref, however, you must have a borrowed value on the RHS - // already, as in this example (which won't compile before #42640): - // - // let Foo { x, .. } = &Foo { x: ..., ... }; - // - // in place of - // - // let Foo { ref x, .. } = Foo { ... }; - // - // In the former case (the implicit ref version), the temporary is created by the - // & 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(hir::ByRef::Yes, _), ..) => true, - - PatKind::Struct(_, ref field_pats, _) => { - field_pats.iter().any(|fp| is_binding_pat(&fp.pat)) - } - - PatKind::Slice(ref pats1, ref pats2, ref pats3) => { - pats1.iter().any(|p| is_binding_pat(&p)) - || pats2.iter().any(|p| is_binding_pat(&p)) - || pats3.iter().any(|p| is_binding_pat(&p)) - } - - PatKind::Or(ref subpats) - | PatKind::TupleStruct(_, ref subpats, _) - | PatKind::Tuple(ref subpats, _) => subpats.iter().any(|p| is_binding_pat(&p)), - - PatKind::Box(ref subpat) => is_binding_pat(&subpat), - - PatKind::Ref(_, _) - | PatKind::Binding(hir::BindingAnnotation(hir::ByRef::No, _), ..) - | PatKind::Wild - | PatKind::Path(_) - | PatKind::Lit(_) - | PatKind::Range(_, _, _) => false, - } - } - - /// If `expr` matches the `E&` grammar, then records an extended rvalue scope as appropriate: - /// - /// ```text - /// E& = & ET - /// | StructName { ..., f: E&, ... } - /// | [ ..., E&, ... ] - /// | ( ..., E&, ... ) - /// | {...; E&} - /// | box E& - /// | E& as ... - /// | ( E& ) - /// ``` - fn record_rvalue_scope_if_borrow_expr<'tcx>( - visitor: &mut RegionResolutionVisitor<'tcx>, - expr: &hir::Expr<'_>, - blk_id: Option<Scope>, - ) { - match expr.kind { - hir::ExprKind::AddrOf(_, _, subexpr) => { - record_rvalue_scope_if_borrow_expr(visitor, subexpr, blk_id); - visitor.scope_tree.record_rvalue_candidate( - subexpr.hir_id, - RvalueCandidateType::Borrow { - target: subexpr.hir_id.local_id, - lifetime: blk_id, - }, - ); - } - hir::ExprKind::Struct(_, fields, _) => { - for field in fields { - record_rvalue_scope_if_borrow_expr(visitor, &field.expr, blk_id); - } - } - hir::ExprKind::Array(subexprs) | hir::ExprKind::Tup(subexprs) => { - for subexpr in subexprs { - record_rvalue_scope_if_borrow_expr(visitor, &subexpr, blk_id); - } - } - hir::ExprKind::Cast(ref subexpr, _) => { - record_rvalue_scope_if_borrow_expr(visitor, &subexpr, blk_id) - } - hir::ExprKind::Block(ref block, _) => { - if let Some(ref subexpr) = block.expr { - record_rvalue_scope_if_borrow_expr(visitor, &subexpr, blk_id); - } - } - hir::ExprKind::Call(..) | hir::ExprKind::MethodCall(..) => { - // FIXME(@dingxiangfei2009): choose call arguments here - // for candidacy for extended parameter rule application - } - hir::ExprKind::Index(..) => { - // FIXME(@dingxiangfei2009): select the indices - // as candidate for rvalue scope rules - } - _ => {} - } - } -} - -impl<'tcx> RegionResolutionVisitor<'tcx> { - /// Records the current parent (if any) as the parent of `child_scope`. - /// Returns the depth of `child_scope`. - fn record_child_scope(&mut self, child_scope: Scope) -> ScopeDepth { - let parent = self.cx.parent; - self.scope_tree.record_scope_parent(child_scope, parent); - // If `child_scope` has no parent, it must be the root node, and so has - // a depth of 1. Otherwise, its depth is one more than its parent's. - parent.map_or(1, |(_p, d)| d + 1) - } - - /// Records the current parent (if any) as the parent of `child_scope`, - /// and sets `child_scope` as the new current parent. - fn enter_scope(&mut self, child_scope: Scope) { - let child_depth = self.record_child_scope(child_scope); - self.cx.parent = Some((child_scope, child_depth)); - } - - fn enter_node_scope_with_dtor(&mut self, id: hir::ItemLocalId) { - // If node was previously marked as a terminating scope during the - // recursive visit of its parent node in the AST, then we need to - // account for the destruction scope representing the scope of - // the destructors that run immediately after it completes. - if self.terminating_scopes.contains(&id) { - self.enter_scope(Scope { id, data: ScopeData::Destruction }); - } - self.enter_scope(Scope { id, data: ScopeData::Node }); - } -} - -impl<'tcx> Visitor<'tcx> for RegionResolutionVisitor<'tcx> { - fn visit_block(&mut self, b: &'tcx Block<'tcx>) { - resolve_block(self, b); - } - - fn visit_body(&mut self, body: &'tcx hir::Body<'tcx>) { - let body_id = body.id(); - let owner_id = self.tcx.hir().body_owner_def_id(body_id); - - debug!( - "visit_body(id={:?}, span={:?}, body.id={:?}, cx.parent={:?})", - owner_id, - self.tcx.sess.source_map().span_to_diagnostic_string(body.value.span), - body_id, - self.cx.parent - ); - - // Save all state that is specific to the outer function - // body. These will be restored once down below, once we've - // visited the body. - let outer_ec = mem::replace(&mut self.expr_and_pat_count, 0); - let outer_cx = self.cx; - let outer_ts = mem::take(&mut self.terminating_scopes); - // The 'pessimistic yield' flag is set to true when we are - // processing a `+=` statement and have to make pessimistic - // control flow assumptions. This doesn't apply to nested - // bodies within the `+=` statements. See #69307. - let outer_pessimistic_yield = mem::replace(&mut self.pessimistic_yield, false); - self.terminating_scopes.insert(body.value.hir_id.local_id); - - self.enter_scope(Scope { id: body.value.hir_id.local_id, data: ScopeData::CallSite }); - self.enter_scope(Scope { id: body.value.hir_id.local_id, data: ScopeData::Arguments }); - - // The arguments and `self` are parented to the fn. - self.cx.var_parent = self.cx.parent.take(); - for param in body.params { - self.visit_pat(¶m.pat); - } - - // The body of the every fn is a root scope. - self.cx.parent = self.cx.var_parent; - if self.tcx.hir().body_owner_kind(owner_id).is_fn_or_closure() { - self.visit_expr(&body.value) - } else { - // Only functions have an outer terminating (drop) scope, while - // temporaries in constant initializers may be 'static, but only - // according to rvalue lifetime semantics, using the same - // syntactical rules used for let initializers. - // - // e.g., in `let x = &f();`, the temporary holding the result from - // the `f()` call lives for the entirety of the surrounding block. - // - // Similarly, `const X: ... = &f();` would have the result of `f()` - // live for `'static`, implying (if Drop restrictions on constants - // ever get lifted) that the value *could* have a destructor, but - // it'd get leaked instead of the destructor running during the - // evaluation of `X` (if at all allowed by CTFE). - // - // However, `const Y: ... = g(&f());`, like `let y = g(&f());`, - // would *not* let the `f()` temporary escape into an outer scope - // (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)); - } - - if body.generator_kind.is_some() { - self.scope_tree.body_expr_count.insert(body_id, self.expr_and_pat_count); - } - - // Restore context we had at the start. - self.expr_and_pat_count = outer_ec; - self.cx = outer_cx; - self.terminating_scopes = outer_ts; - self.pessimistic_yield = outer_pessimistic_yield; - } - - fn visit_arm(&mut self, a: &'tcx Arm<'tcx>) { - resolve_arm(self, a); - } - fn visit_pat(&mut self, p: &'tcx Pat<'tcx>) { - resolve_pat(self, p); - } - fn visit_stmt(&mut self, s: &'tcx Stmt<'tcx>) { - resolve_stmt(self, s); - } - fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) { - resolve_expr(self, ex); - } - fn visit_local(&mut self, l: &'tcx Local<'tcx>) { - resolve_local(self, Some(&l.pat), l.init) - } -} - -/// Per-body `region::ScopeTree`. The `DefId` should be the owner `DefId` for the body; -/// in the case of closures, this will be redirected to the enclosing function. -/// -/// Performance: This is a query rather than a simple function to enable -/// re-use in incremental scenarios. We may sometimes need to rerun the -/// type checker even when the HIR hasn't changed, and in those cases -/// we can avoid reconstructing the region scope tree. -pub fn region_scope_tree(tcx: TyCtxt<'_>, def_id: DefId) -> &ScopeTree { - let typeck_root_def_id = tcx.typeck_root_def_id(def_id); - if typeck_root_def_id != def_id { - return tcx.region_scope_tree(typeck_root_def_id); - } - - let scope_tree = if let Some(body_id) = tcx.hir().maybe_body_owned_by(def_id.expect_local()) { - let mut visitor = RegionResolutionVisitor { - tcx, - scope_tree: ScopeTree::default(), - expr_and_pat_count: 0, - cx: Context { parent: None, var_parent: None }, - terminating_scopes: Default::default(), - pessimistic_yield: false, - fixup_scopes: vec![], - }; - - let body = tcx.hir().body(body_id); - visitor.scope_tree.root_body = Some(body.value.hir_id); - visitor.visit_body(body); - visitor.scope_tree - } else { - ScopeTree::default() - }; - - tcx.arena.alloc(scope_tree) -} diff --git a/compiler/rustc_typeck/src/check/rvalue_scopes.rs b/compiler/rustc_typeck/src/check/rvalue_scopes.rs deleted file mode 100644 index 22c9e7961..000000000 --- a/compiler/rustc_typeck/src/check/rvalue_scopes.rs +++ /dev/null @@ -1,83 +0,0 @@ -use super::FnCtxt; -use hir::def_id::DefId; -use hir::Node; -use rustc_hir as hir; -use rustc_middle::middle::region::{RvalueCandidateType, Scope, ScopeTree}; -use rustc_middle::ty::RvalueScopes; - -/// Applied to an expression `expr` if `expr` -- or something owned or partially owned by -/// `expr` -- is going to be indirectly referenced by a variable in a let statement. In that -/// case, the "temporary lifetime" or `expr` is extended to be the block enclosing the `let` -/// statement. -/// -/// More formally, if `expr` matches the grammar `ET`, record the rvalue scope of the matching -/// `<rvalue>` as `blk_id`: -/// -/// ```text -/// ET = *ET -/// | ET[...] -/// | ET.f -/// | (ET) -/// | <rvalue> -/// ``` -/// -/// Note: ET is intended to match "rvalues or places based on rvalues". -fn record_rvalue_scope_rec( - rvalue_scopes: &mut RvalueScopes, - mut expr: &hir::Expr<'_>, - lifetime: Option<Scope>, -) { - loop { - // Note: give all the expressions matching `ET` with the - // extended temporary lifetime, not just the innermost rvalue, - // because in codegen if we must compile e.g., `*rvalue()` - // into a temporary, we request the temporary scope of the - // outer expression. - - rvalue_scopes.record_rvalue_scope(expr.hir_id.local_id, lifetime); - - match expr.kind { - hir::ExprKind::AddrOf(_, _, subexpr) - | hir::ExprKind::Unary(hir::UnOp::Deref, subexpr) - | hir::ExprKind::Field(subexpr, _) - | hir::ExprKind::Index(subexpr, _) => { - expr = subexpr; - } - _ => { - return; - } - } - } -} -fn record_rvalue_scope( - rvalue_scopes: &mut RvalueScopes, - expr: &hir::Expr<'_>, - candidate: &RvalueCandidateType, -) { - debug!("resolve_rvalue_scope(expr={expr:?}, candidate={candidate:?})"); - match candidate { - RvalueCandidateType::Borrow { lifetime, .. } - | RvalueCandidateType::Pattern { lifetime, .. } => { - record_rvalue_scope_rec(rvalue_scopes, expr, *lifetime) - } // FIXME(@dingxiangfei2009): handle the candidates in the function call arguments - } -} - -pub fn resolve_rvalue_scopes<'a, 'tcx>( - fcx: &'a FnCtxt<'a, 'tcx>, - scope_tree: &'a ScopeTree, - def_id: DefId, -) -> RvalueScopes { - let tcx = &fcx.tcx; - let hir_map = tcx.hir(); - let mut rvalue_scopes = RvalueScopes::new(); - debug!("start resolving rvalue scopes, def_id={def_id:?}"); - debug!("rvalue_scope: rvalue_candidates={:?}", scope_tree.rvalue_candidates); - for (&hir_id, candidate) in &scope_tree.rvalue_candidates { - let Some(Node::Expr(expr)) = hir_map.find(hir_id) else { - bug!("hir node does not exist") - }; - record_rvalue_scope(&mut rvalue_scopes, expr, candidate); - } - rvalue_scopes -} diff --git a/compiler/rustc_typeck/src/check/upvar.rs b/compiler/rustc_typeck/src/check/upvar.rs deleted file mode 100644 index 0b207a6c0..000000000 --- a/compiler/rustc_typeck/src/check/upvar.rs +++ /dev/null @@ -1,2276 +0,0 @@ -//! ### Inferring borrow kinds for upvars -//! -//! Whenever there is a closure expression, we need to determine how each -//! upvar is used. We do this by initially assigning each upvar an -//! immutable "borrow kind" (see `ty::BorrowKind` for details) and then -//! "escalating" the kind as needed. The borrow kind proceeds according to -//! the following lattice: -//! ```ignore (not-rust) -//! ty::ImmBorrow -> ty::UniqueImmBorrow -> ty::MutBorrow -//! ``` -//! So, for example, if we see an assignment `x = 5` to an upvar `x`, we -//! will promote its borrow kind to mutable borrow. If we see an `&mut x` -//! we'll do the same. Naturally, this applies not just to the upvar, but -//! to everything owned by `x`, so the result is the same for something -//! like `x.f = 5` and so on (presuming `x` is not a borrowed pointer to a -//! struct). These adjustments are performed in -//! `adjust_upvar_borrow_kind()` (you can trace backwards through the code -//! from there). -//! -//! The fact that we are inferring borrow kinds as we go results in a -//! semi-hacky interaction with mem-categorization. In particular, -//! mem-categorization will query the current borrow kind as it -//! categorizes, and we'll return the *current* value, but this may get -//! adjusted later. Therefore, in this module, we generally ignore the -//! borrow kind (and derived mutabilities) that are returned from -//! mem-categorization, since they may be inaccurate. (Another option -//! would be to use a unification scheme, where instead of returning a -//! concrete borrow kind like `ty::ImmBorrow`, we return a -//! `ty::InferBorrow(upvar_id)` or something like that, but this would -//! then mean that all later passes would have to check for these figments -//! and report an error, and it just seems like more mess in the end.) - -use super::FnCtxt; - -use crate::expr_use_visitor as euv; -use rustc_errors::{Applicability, MultiSpan}; -use rustc_hir as hir; -use rustc_hir::def_id::LocalDefId; -use rustc_hir::intravisit::{self, Visitor}; -use rustc_infer::infer::UpvarRegion; -use rustc_middle::hir::place::{Place, PlaceBase, PlaceWithHirId, Projection, ProjectionKind}; -use rustc_middle::mir::FakeReadCause; -use rustc_middle::ty::{ - self, ClosureSizeProfileData, Ty, TyCtxt, TypeckResults, UpvarCapture, UpvarSubsts, -}; -use rustc_session::lint; -use rustc_span::sym; -use rustc_span::{BytePos, Pos, Span, Symbol}; -use rustc_trait_selection::infer::InferCtxtExt; - -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_index::vec::Idx; -use rustc_target::abi::VariantIdx; - -use std::iter; - -/// Describe the relationship between the paths of two places -/// eg: -/// - `foo` is ancestor of `foo.bar.baz` -/// - `foo.bar.baz` is an descendant of `foo.bar` -/// - `foo.bar` and `foo.baz` are divergent -enum PlaceAncestryRelation { - Ancestor, - Descendant, - SamePlace, - Divergent, -} - -/// Intermediate format to store a captured `Place` and associated `ty::CaptureInfo` -/// during capture analysis. Information in this map feeds into the minimum capture -/// analysis pass. -type InferredCaptureInformation<'tcx> = Vec<(Place<'tcx>, ty::CaptureInfo)>; - -impl<'a, 'tcx> FnCtxt<'a, 'tcx> { - pub fn closure_analyze(&self, body: &'tcx hir::Body<'tcx>) { - InferBorrowKindVisitor { fcx: self }.visit_body(body); - - // it's our job to process these. - assert!(self.deferred_call_resolutions.borrow().is_empty()); - } -} - -/// Intermediate format to store the hir_id pointing to the use that resulted in the -/// corresponding place being captured and a String which contains the captured value's -/// name (i.e: a.b.c) -#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] -enum UpvarMigrationInfo { - /// We previously captured all of `x`, but now we capture some sub-path. - CapturingPrecise { source_expr: Option<hir::HirId>, var_name: String }, - CapturingNothing { - // where the variable appears in the closure (but is not captured) - use_span: Span, - }, -} - -/// Reasons that we might issue a migration warning. -#[derive(Clone, Debug, Default, PartialEq, Eq, PartialOrd, Ord, Hash)] -struct MigrationWarningReason { - /// When we used to capture `x` in its entirety, we implemented the auto-trait(s) - /// in this vec, but now we don't. - auto_traits: Vec<&'static str>, - - /// When we used to capture `x` in its entirety, we would execute some destructors - /// at a different time. - drop_order: bool, -} - -impl MigrationWarningReason { - fn migration_message(&self) -> String { - let base = "changes to closure capture in Rust 2021 will affect"; - if !self.auto_traits.is_empty() && self.drop_order { - format!("{} drop order and which traits the closure implements", base) - } else if self.drop_order { - format!("{} drop order", base) - } else { - format!("{} which traits the closure implements", base) - } - } -} - -/// Intermediate format to store information needed to generate a note in the migration lint. -struct MigrationLintNote { - captures_info: UpvarMigrationInfo, - - /// reasons why migration is needed for this capture - reason: MigrationWarningReason, -} - -/// Intermediate format to store the hir id of the root variable and a HashSet containing -/// information on why the root variable should be fully captured -struct NeededMigration { - var_hir_id: hir::HirId, - diagnostics_info: Vec<MigrationLintNote>, -} - -struct InferBorrowKindVisitor<'a, 'tcx> { - fcx: &'a FnCtxt<'a, 'tcx>, -} - -impl<'a, 'tcx> Visitor<'tcx> for InferBorrowKindVisitor<'a, 'tcx> { - fn visit_expr(&mut self, expr: &'tcx hir::Expr<'tcx>) { - match expr.kind { - hir::ExprKind::Closure(&hir::Closure { capture_clause, body: body_id, .. }) => { - let body = self.fcx.tcx.hir().body(body_id); - self.visit_body(body); - self.fcx.analyze_closure(expr.hir_id, expr.span, body_id, body, capture_clause); - } - hir::ExprKind::ConstBlock(anon_const) => { - let body = self.fcx.tcx.hir().body(anon_const.body); - self.visit_body(body); - } - _ => {} - } - - intravisit::walk_expr(self, expr); - } -} - -impl<'a, 'tcx> FnCtxt<'a, 'tcx> { - /// Analysis starting point. - #[instrument(skip(self, body), level = "debug")] - fn analyze_closure( - &self, - closure_hir_id: hir::HirId, - span: Span, - body_id: hir::BodyId, - body: &'tcx hir::Body<'tcx>, - capture_clause: hir::CaptureBy, - ) { - // Extract the type of the closure. - let ty = self.node_ty(closure_hir_id); - let (closure_def_id, substs) = match *ty.kind() { - ty::Closure(def_id, substs) => (def_id, UpvarSubsts::Closure(substs)), - ty::Generator(def_id, substs, _) => (def_id, UpvarSubsts::Generator(substs)), - ty::Error(_) => { - // #51714: skip analysis when we have already encountered type errors - return; - } - _ => { - span_bug!( - span, - "type of closure expr {:?} is not a closure {:?}", - closure_hir_id, - ty - ); - } - }; - let closure_def_id = closure_def_id.expect_local(); - - let infer_kind = if let UpvarSubsts::Closure(closure_substs) = substs { - self.closure_kind(closure_substs).is_none().then_some(closure_substs) - } else { - None - }; - - assert_eq!(self.tcx.hir().body_owner_def_id(body.id()), closure_def_id); - let mut delegate = InferBorrowKind { - fcx: self, - closure_def_id, - capture_information: Default::default(), - fake_reads: Default::default(), - }; - euv::ExprUseVisitor::new( - &mut delegate, - &self.infcx, - closure_def_id, - self.param_env, - &self.typeck_results.borrow(), - ) - .consume_body(body); - - debug!( - "For closure={:?}, capture_information={:#?}", - closure_def_id, delegate.capture_information - ); - - self.log_capture_analysis_first_pass(closure_def_id, &delegate.capture_information, span); - - let (capture_information, closure_kind, origin) = self - .process_collected_capture_information(capture_clause, delegate.capture_information); - - self.compute_min_captures(closure_def_id, capture_information, span); - - let closure_hir_id = self.tcx.hir().local_def_id_to_hir_id(closure_def_id); - - if should_do_rust_2021_incompatible_closure_captures_analysis(self.tcx, closure_hir_id) { - self.perform_2229_migration_anaysis(closure_def_id, body_id, capture_clause, span); - } - - let after_feature_tys = self.final_upvar_tys(closure_def_id); - - // We now fake capture information for all variables that are mentioned within the closure - // We do this after handling migrations so that min_captures computes before - if !enable_precise_capture(self.tcx, span) { - let mut capture_information: InferredCaptureInformation<'tcx> = Default::default(); - - if let Some(upvars) = self.tcx.upvars_mentioned(closure_def_id) { - for var_hir_id in upvars.keys() { - let place = self.place_for_root_variable(closure_def_id, *var_hir_id); - - debug!("seed place {:?}", place); - - let capture_kind = self.init_capture_kind_for_place(&place, capture_clause); - let fake_info = ty::CaptureInfo { - capture_kind_expr_id: None, - path_expr_id: None, - capture_kind, - }; - - capture_information.push((place, fake_info)); - } - } - - // This will update the min captures based on this new fake information. - self.compute_min_captures(closure_def_id, capture_information, span); - } - - let before_feature_tys = self.final_upvar_tys(closure_def_id); - - if let Some(closure_substs) = infer_kind { - // Unify the (as yet unbound) type variable in the closure - // substs with the kind we inferred. - let closure_kind_ty = closure_substs.as_closure().kind_ty(); - self.demand_eqtype(span, closure_kind.to_ty(self.tcx), closure_kind_ty); - - // If we have an origin, store it. - if let Some(origin) = origin { - let origin = if enable_precise_capture(self.tcx, span) { - (origin.0, origin.1) - } else { - (origin.0, Place { projections: vec![], ..origin.1 }) - }; - - self.typeck_results - .borrow_mut() - .closure_kind_origins_mut() - .insert(closure_hir_id, origin); - } - } - - self.log_closure_min_capture_info(closure_def_id, span); - - // Now that we've analyzed the closure, we know how each - // variable is borrowed, and we know what traits the closure - // implements (Fn vs FnMut etc). We now have some updates to do - // with that information. - // - // Note that no closure type C may have an upvar of type C - // (though it may reference itself via a trait object). This - // results from the desugaring of closures to a struct like - // `Foo<..., UV0...UVn>`. If one of those upvars referenced - // C, then the type would have infinite size (and the - // inference algorithm will reject it). - - // Equate the type variables for the upvars with the actual types. - let final_upvar_tys = self.final_upvar_tys(closure_def_id); - debug!( - "analyze_closure: id={:?} substs={:?} final_upvar_tys={:?}", - closure_hir_id, substs, final_upvar_tys - ); - - // Build a tuple (U0..Un) of the final upvar types U0..Un - // and unify the upvar tuple type in the closure with it: - let final_tupled_upvars_type = self.tcx.mk_tup(final_upvar_tys.iter()); - self.demand_suptype(span, substs.tupled_upvars_ty(), final_tupled_upvars_type); - - let fake_reads = delegate - .fake_reads - .into_iter() - .map(|(place, cause, hir_id)| (place, cause, hir_id)) - .collect(); - self.typeck_results.borrow_mut().closure_fake_reads.insert(closure_def_id, fake_reads); - - if self.tcx.sess.opts.unstable_opts.profile_closures { - self.typeck_results.borrow_mut().closure_size_eval.insert( - closure_def_id, - ClosureSizeProfileData { - before_feature_tys: self.tcx.mk_tup(before_feature_tys.into_iter()), - after_feature_tys: self.tcx.mk_tup(after_feature_tys.into_iter()), - }, - ); - } - - // If we are also inferred the closure kind here, - // process any deferred resolutions. - let deferred_call_resolutions = self.remove_deferred_call_resolutions(closure_def_id); - for deferred_call_resolution in deferred_call_resolutions { - deferred_call_resolution.resolve(self); - } - } - - // Returns a list of `Ty`s for each upvar. - fn final_upvar_tys(&self, closure_id: LocalDefId) -> Vec<Ty<'tcx>> { - self.typeck_results - .borrow() - .closure_min_captures_flattened(closure_id) - .map(|captured_place| { - let upvar_ty = captured_place.place.ty(); - let capture = captured_place.info.capture_kind; - - debug!( - "final_upvar_tys: place={:?} upvar_ty={:?} capture={:?}, mutability={:?}", - captured_place.place, upvar_ty, capture, captured_place.mutability, - ); - - apply_capture_kind_on_capture_ty(self.tcx, upvar_ty, capture, captured_place.region) - }) - .collect() - } - - /// Adjusts the closure capture information to ensure that the operations aren't unsafe, - /// and that the path can be captured with required capture kind (depending on use in closure, - /// move closure etc.) - /// - /// Returns the set of of adjusted information along with the inferred closure kind and span - /// associated with the closure kind inference. - /// - /// Note that we *always* infer a minimal kind, even if - /// we don't always *use* that in the final result (i.e., sometimes - /// we've taken the closure kind from the expectations instead, and - /// for generators we don't even implement the closure traits - /// really). - /// - /// If we inferred that the closure needs to be FnMut/FnOnce, last element of the returned tuple - /// contains a `Some()` with the `Place` that caused us to do so. - fn process_collected_capture_information( - &self, - capture_clause: hir::CaptureBy, - capture_information: InferredCaptureInformation<'tcx>, - ) -> (InferredCaptureInformation<'tcx>, ty::ClosureKind, Option<(Span, Place<'tcx>)>) { - let mut closure_kind = ty::ClosureKind::LATTICE_BOTTOM; - let mut origin: Option<(Span, Place<'tcx>)> = None; - - let processed = capture_information - .into_iter() - .map(|(place, mut capture_info)| { - // Apply rules for safety before inferring closure kind - let (place, capture_kind) = - restrict_capture_precision(place, capture_info.capture_kind); - - let (place, capture_kind) = truncate_capture_for_optimization(place, capture_kind); - - let usage_span = if let Some(usage_expr) = capture_info.path_expr_id { - self.tcx.hir().span(usage_expr) - } else { - unreachable!() - }; - - let updated = match capture_kind { - ty::UpvarCapture::ByValue => match closure_kind { - ty::ClosureKind::Fn | ty::ClosureKind::FnMut => { - (ty::ClosureKind::FnOnce, Some((usage_span, place.clone()))) - } - // If closure is already FnOnce, don't update - ty::ClosureKind::FnOnce => (closure_kind, origin.take()), - }, - - ty::UpvarCapture::ByRef( - ty::BorrowKind::MutBorrow | ty::BorrowKind::UniqueImmBorrow, - ) => { - match closure_kind { - ty::ClosureKind::Fn => { - (ty::ClosureKind::FnMut, Some((usage_span, place.clone()))) - } - // Don't update the origin - ty::ClosureKind::FnMut | ty::ClosureKind::FnOnce => { - (closure_kind, origin.take()) - } - } - } - - _ => (closure_kind, origin.take()), - }; - - closure_kind = updated.0; - origin = updated.1; - - let (place, capture_kind) = match capture_clause { - hir::CaptureBy::Value => adjust_for_move_closure(place, capture_kind), - hir::CaptureBy::Ref => adjust_for_non_move_closure(place, capture_kind), - }; - - // This restriction needs to be applied after we have handled adjustments for `move` - // closures. We want to make sure any adjustment that might make us move the place into - // the closure gets handled. - let (place, capture_kind) = - restrict_precision_for_drop_types(self, place, capture_kind, usage_span); - - capture_info.capture_kind = capture_kind; - (place, capture_info) - }) - .collect(); - - (processed, closure_kind, origin) - } - - /// Analyzes the information collected by `InferBorrowKind` to compute the min number of - /// Places (and corresponding capture kind) that we need to keep track of to support all - /// the required captured paths. - /// - /// - /// Note: If this function is called multiple times for the same closure, it will update - /// the existing min_capture map that is stored in TypeckResults. - /// - /// Eg: - /// ``` - /// #[derive(Debug)] - /// struct Point { x: i32, y: i32 } - /// - /// let s = String::from("s"); // hir_id_s - /// let mut p = Point { x: 2, y: -2 }; // his_id_p - /// let c = || { - /// println!("{s:?}"); // L1 - /// p.x += 10; // L2 - /// println!("{}" , p.y); // L3 - /// println!("{p:?}"); // L4 - /// drop(s); // L5 - /// }; - /// ``` - /// and let hir_id_L1..5 be the expressions pointing to use of a captured variable on - /// the lines L1..5 respectively. - /// - /// InferBorrowKind results in a structure like this: - /// - /// ```ignore (illustrative) - /// { - /// Place(base: hir_id_s, projections: [], ....) -> { - /// capture_kind_expr: hir_id_L5, - /// path_expr_id: hir_id_L5, - /// capture_kind: ByValue - /// }, - /// Place(base: hir_id_p, projections: [Field(0, 0)], ...) -> { - /// capture_kind_expr: hir_id_L2, - /// path_expr_id: hir_id_L2, - /// capture_kind: ByValue - /// }, - /// Place(base: hir_id_p, projections: [Field(1, 0)], ...) -> { - /// capture_kind_expr: hir_id_L3, - /// path_expr_id: hir_id_L3, - /// capture_kind: ByValue - /// }, - /// Place(base: hir_id_p, projections: [], ...) -> { - /// capture_kind_expr: hir_id_L4, - /// path_expr_id: hir_id_L4, - /// capture_kind: ByValue - /// }, - /// } - /// ``` - /// - /// After the min capture analysis, we get: - /// ```ignore (illustrative) - /// { - /// hir_id_s -> [ - /// Place(base: hir_id_s, projections: [], ....) -> { - /// capture_kind_expr: hir_id_L5, - /// path_expr_id: hir_id_L5, - /// capture_kind: ByValue - /// }, - /// ], - /// hir_id_p -> [ - /// Place(base: hir_id_p, projections: [], ...) -> { - /// capture_kind_expr: hir_id_L2, - /// path_expr_id: hir_id_L4, - /// capture_kind: ByValue - /// }, - /// ], - /// } - /// ``` - fn compute_min_captures( - &self, - closure_def_id: LocalDefId, - capture_information: InferredCaptureInformation<'tcx>, - closure_span: Span, - ) { - if capture_information.is_empty() { - return; - } - - let mut typeck_results = self.typeck_results.borrow_mut(); - - let mut root_var_min_capture_list = - typeck_results.closure_min_captures.remove(&closure_def_id).unwrap_or_default(); - - for (mut place, capture_info) in capture_information.into_iter() { - let var_hir_id = match place.base { - PlaceBase::Upvar(upvar_id) => upvar_id.var_path.hir_id, - base => bug!("Expected upvar, found={:?}", base), - }; - - let Some(min_cap_list) = root_var_min_capture_list.get_mut(&var_hir_id) else { - let mutability = self.determine_capture_mutability(&typeck_results, &place); - let min_cap_list = vec![ty::CapturedPlace { - place, - info: capture_info, - mutability, - region: None, - }]; - root_var_min_capture_list.insert(var_hir_id, min_cap_list); - continue; - }; - - // Go through each entry in the current list of min_captures - // - if ancestor is found, update it's capture kind to account for current place's - // capture information. - // - // - if descendant is found, remove it from the list, and update the current place's - // capture information to account for the descendant's capture kind. - // - // We can never be in a case where the list contains both an ancestor and a descendant - // Also there can only be ancestor but in case of descendants there might be - // multiple. - - let mut descendant_found = false; - let mut updated_capture_info = capture_info; - min_cap_list.retain(|possible_descendant| { - match determine_place_ancestry_relation(&place, &possible_descendant.place) { - // current place is ancestor of possible_descendant - PlaceAncestryRelation::Ancestor => { - descendant_found = true; - - let mut possible_descendant = possible_descendant.clone(); - let backup_path_expr_id = updated_capture_info.path_expr_id; - - // Truncate the descendant (already in min_captures) to be same as the ancestor to handle any - // possible change in capture mode. - truncate_place_to_len_and_update_capture_kind( - &mut possible_descendant.place, - &mut possible_descendant.info.capture_kind, - place.projections.len(), - ); - - updated_capture_info = - determine_capture_info(updated_capture_info, possible_descendant.info); - - // we need to keep the ancestor's `path_expr_id` - updated_capture_info.path_expr_id = backup_path_expr_id; - false - } - - _ => true, - } - }); - - let mut ancestor_found = false; - if !descendant_found { - for possible_ancestor in min_cap_list.iter_mut() { - match determine_place_ancestry_relation(&place, &possible_ancestor.place) { - PlaceAncestryRelation::SamePlace => { - ancestor_found = true; - possible_ancestor.info = determine_capture_info( - possible_ancestor.info, - updated_capture_info, - ); - - // Only one related place will be in the list. - break; - } - // current place is descendant of possible_ancestor - PlaceAncestryRelation::Descendant => { - ancestor_found = true; - let backup_path_expr_id = possible_ancestor.info.path_expr_id; - - // Truncate the descendant (current place) to be same as the ancestor to handle any - // possible change in capture mode. - truncate_place_to_len_and_update_capture_kind( - &mut place, - &mut updated_capture_info.capture_kind, - possible_ancestor.place.projections.len(), - ); - - possible_ancestor.info = determine_capture_info( - possible_ancestor.info, - updated_capture_info, - ); - - // we need to keep the ancestor's `path_expr_id` - possible_ancestor.info.path_expr_id = backup_path_expr_id; - - // Only one related place will be in the list. - break; - } - _ => {} - } - } - } - - // Only need to insert when we don't have an ancestor in the existing min capture list - if !ancestor_found { - let mutability = self.determine_capture_mutability(&typeck_results, &place); - let captured_place = ty::CapturedPlace { - place, - info: updated_capture_info, - mutability, - region: None, - }; - min_cap_list.push(captured_place); - } - } - - // For each capture that is determined to be captured by ref, add region info. - for (_, captures) in &mut root_var_min_capture_list { - for capture in captures { - match capture.info.capture_kind { - ty::UpvarCapture::ByRef(_) => { - let PlaceBase::Upvar(upvar_id) = capture.place.base else { bug!("expected upvar") }; - let origin = UpvarRegion(upvar_id, closure_span); - let upvar_region = self.next_region_var(origin); - capture.region = Some(upvar_region); - } - _ => (), - } - } - } - - debug!( - "For closure={:?}, min_captures before sorting={:?}", - closure_def_id, root_var_min_capture_list - ); - - // Now that we have the minimized list of captures, sort the captures by field id. - // This causes the closure to capture the upvars in the same order as the fields are - // declared which is also the drop order. Thus, in situations where we capture all the - // fields of some type, the observable drop order will remain the same as it previously - // was even though we're dropping each capture individually. - // See https://github.com/rust-lang/project-rfc-2229/issues/42 and - // `src/test/ui/closures/2229_closure_analysis/preserve_field_drop_order.rs`. - for (_, captures) in &mut root_var_min_capture_list { - captures.sort_by(|capture1, capture2| { - for (p1, p2) in capture1.place.projections.iter().zip(&capture2.place.projections) { - // We do not need to look at the `Projection.ty` fields here because at each - // step of the iteration, the projections will either be the same and therefore - // the types must be as well or the current projection will be different and - // we will return the result of comparing the field indexes. - match (p1.kind, p2.kind) { - // Paths are the same, continue to next loop. - (ProjectionKind::Deref, ProjectionKind::Deref) => {} - (ProjectionKind::Field(i1, _), ProjectionKind::Field(i2, _)) - if i1 == i2 => {} - - // Fields are different, compare them. - (ProjectionKind::Field(i1, _), ProjectionKind::Field(i2, _)) => { - return i1.cmp(&i2); - } - - // We should have either a pair of `Deref`s or a pair of `Field`s. - // Anything else is a bug. - ( - l @ (ProjectionKind::Deref | ProjectionKind::Field(..)), - r @ (ProjectionKind::Deref | ProjectionKind::Field(..)), - ) => bug!( - "ProjectionKinds Deref and Field were mismatched: ({:?}, {:?})", - l, - r - ), - ( - l @ (ProjectionKind::Index - | ProjectionKind::Subslice - | ProjectionKind::Deref - | ProjectionKind::Field(..)), - r @ (ProjectionKind::Index - | ProjectionKind::Subslice - | ProjectionKind::Deref - | ProjectionKind::Field(..)), - ) => bug!( - "ProjectionKinds Index or Subslice were unexpected: ({:?}, {:?})", - l, - r - ), - } - } - - unreachable!( - "we captured two identical projections: capture1 = {:?}, capture2 = {:?}", - capture1, capture2 - ); - }); - } - - debug!( - "For closure={:?}, min_captures after sorting={:#?}", - closure_def_id, root_var_min_capture_list - ); - typeck_results.closure_min_captures.insert(closure_def_id, root_var_min_capture_list); - } - - /// Perform the migration analysis for RFC 2229, and emit lint - /// `disjoint_capture_drop_reorder` if needed. - fn perform_2229_migration_anaysis( - &self, - closure_def_id: LocalDefId, - body_id: hir::BodyId, - capture_clause: hir::CaptureBy, - span: Span, - ) { - let (need_migrations, reasons) = self.compute_2229_migrations( - closure_def_id, - span, - capture_clause, - self.typeck_results.borrow().closure_min_captures.get(&closure_def_id), - ); - - if !need_migrations.is_empty() { - let (migration_string, migrated_variables_concat) = - migration_suggestion_for_2229(self.tcx, &need_migrations); - - let closure_hir_id = self.tcx.hir().local_def_id_to_hir_id(closure_def_id); - let closure_head_span = self.tcx.def_span(closure_def_id); - self.tcx.struct_span_lint_hir( - lint::builtin::RUST_2021_INCOMPATIBLE_CLOSURE_CAPTURES, - closure_hir_id, - closure_head_span, - |lint| { - let mut diagnostics_builder = lint.build( - &reasons.migration_message(), - ); - for NeededMigration { var_hir_id, diagnostics_info } in &need_migrations { - // Labels all the usage of the captured variable and why they are responsible - // for migration being needed - for lint_note in diagnostics_info.iter() { - match &lint_note.captures_info { - UpvarMigrationInfo::CapturingPrecise { source_expr: Some(capture_expr_id), var_name: captured_name } => { - let cause_span = self.tcx.hir().span(*capture_expr_id); - diagnostics_builder.span_label(cause_span, format!("in Rust 2018, this closure captures all of `{}`, but in Rust 2021, it will only capture `{}`", - self.tcx.hir().name(*var_hir_id), - captured_name, - )); - } - UpvarMigrationInfo::CapturingNothing { use_span } => { - diagnostics_builder.span_label(*use_span, format!("in Rust 2018, this causes the closure to capture `{}`, but in Rust 2021, it has no effect", - self.tcx.hir().name(*var_hir_id), - )); - } - - _ => { } - } - - // Add a label pointing to where a captured variable affected by drop order - // is dropped - if lint_note.reason.drop_order { - let drop_location_span = drop_location_span(self.tcx, closure_hir_id); - - match &lint_note.captures_info { - UpvarMigrationInfo::CapturingPrecise { var_name: captured_name, .. } => { - diagnostics_builder.span_label(drop_location_span, format!("in Rust 2018, `{}` is dropped here, but in Rust 2021, only `{}` will be dropped here as part of the closure", - self.tcx.hir().name(*var_hir_id), - captured_name, - )); - } - UpvarMigrationInfo::CapturingNothing { use_span: _ } => { - diagnostics_builder.span_label(drop_location_span, format!("in Rust 2018, `{v}` is dropped here along with the closure, but in Rust 2021 `{v}` is not part of the closure", - v = self.tcx.hir().name(*var_hir_id), - )); - } - } - } - - // Add a label explaining why a closure no longer implements a trait - for &missing_trait in &lint_note.reason.auto_traits { - // not capturing something anymore cannot cause a trait to fail to be implemented: - match &lint_note.captures_info { - UpvarMigrationInfo::CapturingPrecise { var_name: captured_name, .. } => { - let var_name = self.tcx.hir().name(*var_hir_id); - diagnostics_builder.span_label(closure_head_span, format!("\ - in Rust 2018, this closure implements {missing_trait} \ - as `{var_name}` implements {missing_trait}, but in Rust 2021, \ - this closure will no longer implement {missing_trait} \ - because `{var_name}` is not fully captured \ - and `{captured_name}` does not implement {missing_trait}")); - } - - // Cannot happen: if we don't capture a variable, we impl strictly more traits - UpvarMigrationInfo::CapturingNothing { use_span } => span_bug!(*use_span, "missing trait from not capturing something"), - } - } - } - } - diagnostics_builder.note("for more information, see <https://doc.rust-lang.org/nightly/edition-guide/rust-2021/disjoint-capture-in-closures.html>"); - - let diagnostic_msg = format!( - "add a dummy let to cause {} to be fully captured", - migrated_variables_concat - ); - - let closure_span = self.tcx.hir().span_with_body(closure_hir_id); - let mut closure_body_span = { - // If the body was entirely expanded from a macro - // invocation, i.e. the body is not contained inside the - // closure span, then we walk up the expansion until we - // find the span before the expansion. - let s = self.tcx.hir().span_with_body(body_id.hir_id); - s.find_ancestor_inside(closure_span).unwrap_or(s) - }; - - if let Ok(mut s) = self.tcx.sess.source_map().span_to_snippet(closure_body_span) { - if s.starts_with('$') { - // Looks like a macro fragment. Try to find the real block. - if let Some(hir::Node::Expr(&hir::Expr { - kind: hir::ExprKind::Block(block, ..), .. - })) = self.tcx.hir().find(body_id.hir_id) { - // If the body is a block (with `{..}`), we use the span of that block. - // E.g. with a `|| $body` expanded from a `m!({ .. })`, we use `{ .. }`, and not `$body`. - // Since we know it's a block, we know we can insert the `let _ = ..` without - // breaking the macro syntax. - if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(block.span) { - closure_body_span = block.span; - s = snippet; - } - } - } - - let mut lines = s.lines(); - let line1 = lines.next().unwrap_or_default(); - - if line1.trim_end() == "{" { - // This is a multi-line closure with just a `{` on the first line, - // so we put the `let` on its own line. - // We take the indentation from the next non-empty line. - let line2 = lines.find(|line| !line.is_empty()).unwrap_or_default(); - let indent = line2.split_once(|c: char| !c.is_whitespace()).unwrap_or_default().0; - diagnostics_builder.span_suggestion( - closure_body_span.with_lo(closure_body_span.lo() + BytePos::from_usize(line1.len())).shrink_to_lo(), - &diagnostic_msg, - format!("\n{indent}{migration_string};"), - Applicability::MachineApplicable, - ); - } else if line1.starts_with('{') { - // This is a closure with its body wrapped in - // braces, but with more than just the opening - // brace on the first line. We put the `let` - // directly after the `{`. - diagnostics_builder.span_suggestion( - closure_body_span.with_lo(closure_body_span.lo() + BytePos(1)).shrink_to_lo(), - &diagnostic_msg, - format!(" {migration_string};"), - Applicability::MachineApplicable, - ); - } else { - // This is a closure without braces around the body. - // We add braces to add the `let` before the body. - diagnostics_builder.multipart_suggestion( - &diagnostic_msg, - vec![ - (closure_body_span.shrink_to_lo(), format!("{{ {migration_string}; ")), - (closure_body_span.shrink_to_hi(), " }".to_string()), - ], - Applicability::MachineApplicable - ); - } - } else { - diagnostics_builder.span_suggestion( - closure_span, - &diagnostic_msg, - migration_string, - Applicability::HasPlaceholders - ); - } - - diagnostics_builder.emit(); - }, - ); - } - } - - /// Combines all the reasons for 2229 migrations - fn compute_2229_migrations_reasons( - &self, - auto_trait_reasons: FxHashSet<&'static str>, - drop_order: bool, - ) -> MigrationWarningReason { - let mut reasons = MigrationWarningReason::default(); - - reasons.auto_traits.extend(auto_trait_reasons); - reasons.drop_order = drop_order; - - // `auto_trait_reasons` are in hashset order, so sort them to put the - // diagnostics we emit later in a cross-platform-consistent order. - reasons.auto_traits.sort_unstable(); - - reasons - } - - /// Figures out the list of root variables (and their types) that aren't completely - /// captured by the closure when `capture_disjoint_fields` is enabled and auto-traits - /// differ between the root variable and the captured paths. - /// - /// Returns a tuple containing a HashMap of CapturesInfo that maps to a HashSet of trait names - /// if migration is needed for traits for the provided var_hir_id, otherwise returns None - fn compute_2229_migrations_for_trait( - &self, - min_captures: Option<&ty::RootVariableMinCaptureList<'tcx>>, - var_hir_id: hir::HirId, - closure_clause: hir::CaptureBy, - ) -> Option<FxHashMap<UpvarMigrationInfo, FxHashSet<&'static str>>> { - let auto_traits_def_id = vec![ - self.tcx.lang_items().clone_trait(), - self.tcx.lang_items().sync_trait(), - self.tcx.get_diagnostic_item(sym::Send), - self.tcx.lang_items().unpin_trait(), - self.tcx.get_diagnostic_item(sym::unwind_safe_trait), - self.tcx.get_diagnostic_item(sym::ref_unwind_safe_trait), - ]; - const AUTO_TRAITS: [&str; 6] = - ["`Clone`", "`Sync`", "`Send`", "`Unpin`", "`UnwindSafe`", "`RefUnwindSafe`"]; - - let root_var_min_capture_list = min_captures.and_then(|m| m.get(&var_hir_id))?; - - let ty = self.resolve_vars_if_possible(self.node_ty(var_hir_id)); - - let ty = match closure_clause { - hir::CaptureBy::Value => ty, // For move closure the capture kind should be by value - hir::CaptureBy::Ref => { - // For non move closure the capture kind is the max capture kind of all captures - // according to the ordering ImmBorrow < UniqueImmBorrow < MutBorrow < ByValue - let mut max_capture_info = root_var_min_capture_list.first().unwrap().info; - for capture in root_var_min_capture_list.iter() { - max_capture_info = determine_capture_info(max_capture_info, capture.info); - } - - apply_capture_kind_on_capture_ty( - self.tcx, - ty, - max_capture_info.capture_kind, - Some(self.tcx.lifetimes.re_erased), - ) - } - }; - - let mut obligations_should_hold = Vec::new(); - // Checks if a root variable implements any of the auto traits - for check_trait in auto_traits_def_id.iter() { - obligations_should_hold.push( - check_trait - .map(|check_trait| { - self.infcx - .type_implements_trait( - check_trait, - ty, - self.tcx.mk_substs_trait(ty, &[]), - self.param_env, - ) - .must_apply_modulo_regions() - }) - .unwrap_or(false), - ); - } - - let mut problematic_captures = FxHashMap::default(); - // Check whether captured fields also implement the trait - for capture in root_var_min_capture_list.iter() { - let ty = apply_capture_kind_on_capture_ty( - self.tcx, - capture.place.ty(), - capture.info.capture_kind, - Some(self.tcx.lifetimes.re_erased), - ); - - // Checks if a capture implements any of the auto traits - let mut obligations_holds_for_capture = Vec::new(); - for check_trait in auto_traits_def_id.iter() { - obligations_holds_for_capture.push( - check_trait - .map(|check_trait| { - self.infcx - .type_implements_trait( - check_trait, - ty, - self.tcx.mk_substs_trait(ty, &[]), - self.param_env, - ) - .must_apply_modulo_regions() - }) - .unwrap_or(false), - ); - } - - let mut capture_problems = FxHashSet::default(); - - // Checks if for any of the auto traits, one or more trait is implemented - // by the root variable but not by the capture - for (idx, _) in obligations_should_hold.iter().enumerate() { - if !obligations_holds_for_capture[idx] && obligations_should_hold[idx] { - capture_problems.insert(AUTO_TRAITS[idx]); - } - } - - if !capture_problems.is_empty() { - problematic_captures.insert( - UpvarMigrationInfo::CapturingPrecise { - source_expr: capture.info.path_expr_id, - var_name: capture.to_string(self.tcx), - }, - capture_problems, - ); - } - } - if !problematic_captures.is_empty() { - return Some(problematic_captures); - } - None - } - - /// Figures out the list of root variables (and their types) that aren't completely - /// captured by the closure when `capture_disjoint_fields` is enabled and drop order of - /// some path starting at that root variable **might** be affected. - /// - /// The output list would include a root variable if: - /// - It would have been moved into the closure when `capture_disjoint_fields` wasn't - /// enabled, **and** - /// - It wasn't completely captured by the closure, **and** - /// - One of the paths starting at this root variable, that is not captured needs Drop. - /// - /// This function only returns a HashSet of CapturesInfo for significant drops. If there - /// are no significant drops than None is returned - #[instrument(level = "debug", skip(self))] - fn compute_2229_migrations_for_drop( - &self, - closure_def_id: LocalDefId, - closure_span: Span, - min_captures: Option<&ty::RootVariableMinCaptureList<'tcx>>, - closure_clause: hir::CaptureBy, - var_hir_id: hir::HirId, - ) -> Option<FxHashSet<UpvarMigrationInfo>> { - let ty = self.resolve_vars_if_possible(self.node_ty(var_hir_id)); - - if !ty.has_significant_drop(self.tcx, self.tcx.param_env(closure_def_id)) { - debug!("does not have significant drop"); - return None; - } - - let Some(root_var_min_capture_list) = min_captures.and_then(|m| m.get(&var_hir_id)) else { - // The upvar is mentioned within the closure but no path starting from it is - // used. This occurs when you have (e.g.) - // - // ``` - // let x = move || { - // let _ = y; - // }); - // ``` - debug!("no path starting from it is used"); - - - match closure_clause { - // Only migrate if closure is a move closure - hir::CaptureBy::Value => { - let mut diagnostics_info = FxHashSet::default(); - let upvars = self.tcx.upvars_mentioned(closure_def_id).expect("must be an upvar"); - let upvar = upvars[&var_hir_id]; - diagnostics_info.insert(UpvarMigrationInfo::CapturingNothing { use_span: upvar.span }); - return Some(diagnostics_info); - } - hir::CaptureBy::Ref => {} - } - - return None; - }; - debug!(?root_var_min_capture_list); - - let mut projections_list = Vec::new(); - let mut diagnostics_info = FxHashSet::default(); - - for captured_place in root_var_min_capture_list.iter() { - match captured_place.info.capture_kind { - // Only care about captures that are moved into the closure - ty::UpvarCapture::ByValue => { - projections_list.push(captured_place.place.projections.as_slice()); - diagnostics_info.insert(UpvarMigrationInfo::CapturingPrecise { - source_expr: captured_place.info.path_expr_id, - var_name: captured_place.to_string(self.tcx), - }); - } - ty::UpvarCapture::ByRef(..) => {} - } - } - - debug!(?projections_list); - debug!(?diagnostics_info); - - let is_moved = !projections_list.is_empty(); - debug!(?is_moved); - - let is_not_completely_captured = - root_var_min_capture_list.iter().any(|capture| !capture.place.projections.is_empty()); - debug!(?is_not_completely_captured); - - if is_moved - && is_not_completely_captured - && self.has_significant_drop_outside_of_captures( - closure_def_id, - closure_span, - ty, - projections_list, - ) - { - return Some(diagnostics_info); - } - - None - } - - /// Figures out the list of root variables (and their types) that aren't completely - /// captured by the closure when `capture_disjoint_fields` is enabled and either drop - /// order of some path starting at that root variable **might** be affected or auto-traits - /// differ between the root variable and the captured paths. - /// - /// The output list would include a root variable if: - /// - It would have been moved into the closure when `capture_disjoint_fields` wasn't - /// enabled, **and** - /// - It wasn't completely captured by the closure, **and** - /// - One of the paths starting at this root variable, that is not captured needs Drop **or** - /// - One of the paths captured does not implement all the auto-traits its root variable - /// implements. - /// - /// Returns a tuple containing a vector of MigrationDiagnosticInfo, as well as a String - /// containing the reason why root variables whose HirId is contained in the vector should - /// be captured - #[instrument(level = "debug", skip(self))] - fn compute_2229_migrations( - &self, - closure_def_id: LocalDefId, - closure_span: Span, - closure_clause: hir::CaptureBy, - min_captures: Option<&ty::RootVariableMinCaptureList<'tcx>>, - ) -> (Vec<NeededMigration>, MigrationWarningReason) { - let Some(upvars) = self.tcx.upvars_mentioned(closure_def_id) else { - return (Vec::new(), MigrationWarningReason::default()); - }; - - let mut need_migrations = Vec::new(); - let mut auto_trait_migration_reasons = FxHashSet::default(); - let mut drop_migration_needed = false; - - // Perform auto-trait analysis - for (&var_hir_id, _) in upvars.iter() { - let mut diagnostics_info = Vec::new(); - - let auto_trait_diagnostic = if let Some(diagnostics_info) = - self.compute_2229_migrations_for_trait(min_captures, var_hir_id, closure_clause) - { - diagnostics_info - } else { - FxHashMap::default() - }; - - let drop_reorder_diagnostic = if let Some(diagnostics_info) = self - .compute_2229_migrations_for_drop( - closure_def_id, - closure_span, - min_captures, - closure_clause, - var_hir_id, - ) { - drop_migration_needed = true; - diagnostics_info - } else { - FxHashSet::default() - }; - - // Combine all the captures responsible for needing migrations into one HashSet - let mut capture_diagnostic = drop_reorder_diagnostic.clone(); - for key in auto_trait_diagnostic.keys() { - capture_diagnostic.insert(key.clone()); - } - - let mut capture_diagnostic = capture_diagnostic.into_iter().collect::<Vec<_>>(); - capture_diagnostic.sort(); - for captures_info in capture_diagnostic { - // Get the auto trait reasons of why migration is needed because of that capture, if there are any - let capture_trait_reasons = - if let Some(reasons) = auto_trait_diagnostic.get(&captures_info) { - reasons.clone() - } else { - FxHashSet::default() - }; - - // Check if migration is needed because of drop reorder as a result of that capture - let capture_drop_reorder_reason = drop_reorder_diagnostic.contains(&captures_info); - - // Combine all the reasons of why the root variable should be captured as a result of - // auto trait implementation issues - auto_trait_migration_reasons.extend(capture_trait_reasons.iter().copied()); - - diagnostics_info.push(MigrationLintNote { - captures_info, - reason: self.compute_2229_migrations_reasons( - capture_trait_reasons, - capture_drop_reorder_reason, - ), - }); - } - - if !diagnostics_info.is_empty() { - need_migrations.push(NeededMigration { var_hir_id, diagnostics_info }); - } - } - ( - need_migrations, - self.compute_2229_migrations_reasons( - auto_trait_migration_reasons, - drop_migration_needed, - ), - ) - } - - /// This is a helper function to `compute_2229_migrations_precise_pass`. Provided the type - /// of a root variable and a list of captured paths starting at this root variable (expressed - /// using list of `Projection` slices), it returns true if there is a path that is not - /// captured starting at this root variable that implements Drop. - /// - /// The way this function works is at a given call it looks at type `base_path_ty` of some base - /// path say P and then list of projection slices which represent the different captures moved - /// into the closure starting off of P. - /// - /// This will make more sense with an example: - /// - /// ```rust - /// #![feature(capture_disjoint_fields)] - /// - /// struct FancyInteger(i32); // This implements Drop - /// - /// struct Point { x: FancyInteger, y: FancyInteger } - /// struct Color; - /// - /// struct Wrapper { p: Point, c: Color } - /// - /// fn f(w: Wrapper) { - /// let c = || { - /// // Closure captures w.p.x and w.c by move. - /// }; - /// - /// c(); - /// } - /// ``` - /// - /// If `capture_disjoint_fields` wasn't enabled the closure would've moved `w` instead of the - /// precise paths. If we look closely `w.p.y` isn't captured which implements Drop and - /// therefore Drop ordering would change and we want this function to return true. - /// - /// Call stack to figure out if we need to migrate for `w` would look as follows: - /// - /// Our initial base path is just `w`, and the paths captured from it are `w[p, x]` and - /// `w[c]`. - /// Notation: - /// - Ty(place): Type of place - /// - `(a, b)`: Represents the function parameters `base_path_ty` and `captured_by_move_projs` - /// respectively. - /// ```ignore (illustrative) - /// (Ty(w), [ &[p, x], &[c] ]) - /// // | - /// // ---------------------------- - /// // | | - /// // v v - /// (Ty(w.p), [ &[x] ]) (Ty(w.c), [ &[] ]) // I(1) - /// // | | - /// // v v - /// (Ty(w.p), [ &[x] ]) false - /// // | - /// // | - /// // ------------------------------- - /// // | | - /// // v v - /// (Ty((w.p).x), [ &[] ]) (Ty((w.p).y), []) // IMP 2 - /// // | | - /// // v v - /// false NeedsSignificantDrop(Ty(w.p.y)) - /// // | - /// // v - /// true - /// ``` - /// - /// IMP 1 `(Ty(w.c), [ &[] ])`: Notice the single empty slice inside `captured_projs`. - /// This implies that the `w.c` is completely captured by the closure. - /// Since drop for this path will be called when the closure is - /// dropped we don't need to migrate for it. - /// - /// IMP 2 `(Ty((w.p).y), [])`: Notice that `captured_projs` is empty. This implies that this - /// path wasn't captured by the closure. Also note that even - /// though we didn't capture this path, the function visits it, - /// which is kind of the point of this function. We then return - /// if the type of `w.p.y` implements Drop, which in this case is - /// true. - /// - /// Consider another example: - /// - /// ```ignore (pseudo-rust) - /// struct X; - /// impl Drop for X {} - /// - /// struct Y(X); - /// impl Drop for Y {} - /// - /// fn foo() { - /// let y = Y(X); - /// let c = || move(y.0); - /// } - /// ``` - /// - /// Note that `y.0` is captured by the closure. When this function is called for `y`, it will - /// return true, because even though all paths starting at `y` are captured, `y` itself - /// implements Drop which will be affected since `y` isn't completely captured. - fn has_significant_drop_outside_of_captures( - &self, - closure_def_id: LocalDefId, - closure_span: Span, - base_path_ty: Ty<'tcx>, - captured_by_move_projs: Vec<&[Projection<'tcx>]>, - ) -> bool { - let needs_drop = - |ty: Ty<'tcx>| ty.has_significant_drop(self.tcx, self.tcx.param_env(closure_def_id)); - - let is_drop_defined_for_ty = |ty: Ty<'tcx>| { - let drop_trait = self.tcx.require_lang_item(hir::LangItem::Drop, Some(closure_span)); - let ty_params = self.tcx.mk_substs_trait(base_path_ty, &[]); - self.infcx - .type_implements_trait( - drop_trait, - ty, - ty_params, - self.tcx.param_env(closure_def_id), - ) - .must_apply_modulo_regions() - }; - - let is_drop_defined_for_ty = is_drop_defined_for_ty(base_path_ty); - - // If there is a case where no projection is applied on top of current place - // then there must be exactly one capture corresponding to such a case. Note that this - // represents the case of the path being completely captured by the variable. - // - // eg. If `a.b` is captured and we are processing `a.b`, then we can't have the closure also - // capture `a.b.c`, because that violates min capture. - let is_completely_captured = captured_by_move_projs.iter().any(|projs| projs.is_empty()); - - assert!(!is_completely_captured || (captured_by_move_projs.len() == 1)); - - if is_completely_captured { - // The place is captured entirely, so doesn't matter if needs dtor, it will be drop - // when the closure is dropped. - return false; - } - - if captured_by_move_projs.is_empty() { - return needs_drop(base_path_ty); - } - - if is_drop_defined_for_ty { - // If drop is implemented for this type then we need it to be fully captured, - // and we know it is not completely captured because of the previous checks. - - // Note that this is a bug in the user code that will be reported by the - // borrow checker, since we can't move out of drop types. - - // The bug exists in the user's code pre-migration, and we don't migrate here. - return false; - } - - match base_path_ty.kind() { - // Observations: - // - `captured_by_move_projs` is not empty. Therefore we can call - // `captured_by_move_projs.first().unwrap()` safely. - // - All entries in `captured_by_move_projs` have at least one projection. - // Therefore we can call `captured_by_move_projs.first().unwrap().first().unwrap()` safely. - - // We don't capture derefs in case of move captures, which would have be applied to - // access any further paths. - ty::Adt(def, _) if def.is_box() => unreachable!(), - ty::Ref(..) => unreachable!(), - ty::RawPtr(..) => unreachable!(), - - ty::Adt(def, substs) => { - // Multi-variant enums are captured in entirety, - // which would've been handled in the case of single empty slice in `captured_by_move_projs`. - assert_eq!(def.variants().len(), 1); - - // Only Field projections can be applied to a non-box Adt. - assert!( - captured_by_move_projs.iter().all(|projs| matches!( - projs.first().unwrap().kind, - ProjectionKind::Field(..) - )) - ); - def.variants().get(VariantIdx::new(0)).unwrap().fields.iter().enumerate().any( - |(i, field)| { - let paths_using_field = captured_by_move_projs - .iter() - .filter_map(|projs| { - if let ProjectionKind::Field(field_idx, _) = - projs.first().unwrap().kind - { - if (field_idx as usize) == i { Some(&projs[1..]) } else { None } - } else { - unreachable!(); - } - }) - .collect(); - - let after_field_ty = field.ty(self.tcx, substs); - self.has_significant_drop_outside_of_captures( - closure_def_id, - closure_span, - after_field_ty, - paths_using_field, - ) - }, - ) - } - - ty::Tuple(fields) => { - // Only Field projections can be applied to a tuple. - assert!( - captured_by_move_projs.iter().all(|projs| matches!( - projs.first().unwrap().kind, - ProjectionKind::Field(..) - )) - ); - - fields.iter().enumerate().any(|(i, element_ty)| { - let paths_using_field = captured_by_move_projs - .iter() - .filter_map(|projs| { - if let ProjectionKind::Field(field_idx, _) = projs.first().unwrap().kind - { - if (field_idx as usize) == i { Some(&projs[1..]) } else { None } - } else { - unreachable!(); - } - }) - .collect(); - - self.has_significant_drop_outside_of_captures( - closure_def_id, - closure_span, - element_ty, - paths_using_field, - ) - }) - } - - // Anything else would be completely captured and therefore handled already. - _ => unreachable!(), - } - } - - fn init_capture_kind_for_place( - &self, - place: &Place<'tcx>, - capture_clause: hir::CaptureBy, - ) -> ty::UpvarCapture { - match capture_clause { - // In case of a move closure if the data is accessed through a reference we - // want to capture by ref to allow precise capture using reborrows. - // - // If the data will be moved out of this place, then the place will be truncated - // at the first Deref in `adjust_upvar_borrow_kind_for_consume` and then moved into - // the closure. - hir::CaptureBy::Value if !place.deref_tys().any(Ty::is_ref) => { - ty::UpvarCapture::ByValue - } - hir::CaptureBy::Value | hir::CaptureBy::Ref => ty::UpvarCapture::ByRef(ty::ImmBorrow), - } - } - - fn place_for_root_variable( - &self, - closure_def_id: LocalDefId, - var_hir_id: hir::HirId, - ) -> Place<'tcx> { - let upvar_id = ty::UpvarId::new(var_hir_id, closure_def_id); - - Place { - base_ty: self.node_ty(var_hir_id), - base: PlaceBase::Upvar(upvar_id), - projections: Default::default(), - } - } - - fn should_log_capture_analysis(&self, closure_def_id: LocalDefId) -> bool { - self.tcx.has_attr(closure_def_id.to_def_id(), sym::rustc_capture_analysis) - } - - fn log_capture_analysis_first_pass( - &self, - closure_def_id: LocalDefId, - capture_information: &InferredCaptureInformation<'tcx>, - closure_span: Span, - ) { - if self.should_log_capture_analysis(closure_def_id) { - let mut diag = - self.tcx.sess.struct_span_err(closure_span, "First Pass analysis includes:"); - for (place, capture_info) in capture_information { - let capture_str = construct_capture_info_string(self.tcx, place, capture_info); - let output_str = format!("Capturing {capture_str}"); - - let span = - capture_info.path_expr_id.map_or(closure_span, |e| self.tcx.hir().span(e)); - diag.span_note(span, &output_str); - } - diag.emit(); - } - } - - fn log_closure_min_capture_info(&self, closure_def_id: LocalDefId, closure_span: Span) { - if self.should_log_capture_analysis(closure_def_id) { - if let Some(min_captures) = - self.typeck_results.borrow().closure_min_captures.get(&closure_def_id) - { - let mut diag = - self.tcx.sess.struct_span_err(closure_span, "Min Capture analysis includes:"); - - for (_, min_captures_for_var) in min_captures { - for capture in min_captures_for_var { - let place = &capture.place; - let capture_info = &capture.info; - - let capture_str = - construct_capture_info_string(self.tcx, place, capture_info); - let output_str = format!("Min Capture {capture_str}"); - - if capture.info.path_expr_id != capture.info.capture_kind_expr_id { - let path_span = capture_info - .path_expr_id - .map_or(closure_span, |e| self.tcx.hir().span(e)); - let capture_kind_span = capture_info - .capture_kind_expr_id - .map_or(closure_span, |e| self.tcx.hir().span(e)); - - let mut multi_span: MultiSpan = - MultiSpan::from_spans(vec![path_span, capture_kind_span]); - - let capture_kind_label = - construct_capture_kind_reason_string(self.tcx, place, capture_info); - let path_label = construct_path_string(self.tcx, place); - - multi_span.push_span_label(path_span, path_label); - multi_span.push_span_label(capture_kind_span, capture_kind_label); - - diag.span_note(multi_span, &output_str); - } else { - let span = capture_info - .path_expr_id - .map_or(closure_span, |e| self.tcx.hir().span(e)); - - diag.span_note(span, &output_str); - }; - } - } - diag.emit(); - } - } - } - - /// A captured place is mutable if - /// 1. Projections don't include a Deref of an immut-borrow, **and** - /// 2. PlaceBase is mut or projections include a Deref of a mut-borrow. - fn determine_capture_mutability( - &self, - typeck_results: &'a TypeckResults<'tcx>, - place: &Place<'tcx>, - ) -> hir::Mutability { - let var_hir_id = match place.base { - PlaceBase::Upvar(upvar_id) => upvar_id.var_path.hir_id, - _ => unreachable!(), - }; - - let bm = *typeck_results.pat_binding_modes().get(var_hir_id).expect("missing binding mode"); - - let mut is_mutbl = match bm { - ty::BindByValue(mutability) => mutability, - ty::BindByReference(_) => hir::Mutability::Not, - }; - - for pointer_ty in place.deref_tys() { - match pointer_ty.kind() { - // We don't capture derefs of raw ptrs - ty::RawPtr(_) => unreachable!(), - - // Dereferencing a mut-ref allows us to mut the Place if we don't deref - // an immut-ref after on top of this. - ty::Ref(.., hir::Mutability::Mut) => is_mutbl = hir::Mutability::Mut, - - // The place isn't mutable once we dereference an immutable reference. - ty::Ref(.., hir::Mutability::Not) => return hir::Mutability::Not, - - // Dereferencing a box doesn't change mutability - ty::Adt(def, ..) if def.is_box() => {} - - unexpected_ty => bug!("deref of unexpected pointer type {:?}", unexpected_ty), - } - } - - is_mutbl - } -} - -/// Truncate the capture so that the place being borrowed is in accordance with RFC 1240, -/// which states that it's unsafe to take a reference into a struct marked `repr(packed)`. -fn restrict_repr_packed_field_ref_capture<'tcx>( - tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, - mut place: Place<'tcx>, - mut curr_borrow_kind: ty::UpvarCapture, -) -> (Place<'tcx>, ty::UpvarCapture) { - let pos = place.projections.iter().enumerate().position(|(i, p)| { - let ty = place.ty_before_projection(i); - - // Return true for fields of packed structs, unless those fields have alignment 1. - match p.kind { - ProjectionKind::Field(..) => match ty.kind() { - ty::Adt(def, _) if def.repr().packed() => { - // We erase regions here because they cannot be hashed - match tcx.layout_of(param_env.and(tcx.erase_regions(p.ty))) { - Ok(layout) if layout.align.abi.bytes() == 1 => { - // if the alignment is 1, the type can't be further - // disaligned. - debug!( - "restrict_repr_packed_field_ref_capture: ({:?}) - align = 1", - place - ); - false - } - _ => { - debug!("restrict_repr_packed_field_ref_capture: ({:?}) - true", place); - true - } - } - } - - _ => false, - }, - _ => false, - } - }); - - if let Some(pos) = pos { - truncate_place_to_len_and_update_capture_kind(&mut place, &mut curr_borrow_kind, pos); - } - - (place, curr_borrow_kind) -} - -/// Returns a Ty that applies the specified capture kind on the provided capture Ty -fn apply_capture_kind_on_capture_ty<'tcx>( - tcx: TyCtxt<'tcx>, - ty: Ty<'tcx>, - capture_kind: UpvarCapture, - region: Option<ty::Region<'tcx>>, -) -> Ty<'tcx> { - match capture_kind { - ty::UpvarCapture::ByValue => ty, - ty::UpvarCapture::ByRef(kind) => { - tcx.mk_ref(region.unwrap(), ty::TypeAndMut { ty: ty, mutbl: kind.to_mutbl_lossy() }) - } - } -} - -/// Returns the Span of where the value with the provided HirId would be dropped -fn drop_location_span<'tcx>(tcx: TyCtxt<'tcx>, hir_id: hir::HirId) -> Span { - let owner_id = tcx.hir().get_enclosing_scope(hir_id).unwrap(); - - let owner_node = tcx.hir().get(owner_id); - let owner_span = match owner_node { - hir::Node::Item(item) => match item.kind { - hir::ItemKind::Fn(_, _, owner_id) => tcx.hir().span(owner_id.hir_id), - _ => { - bug!("Drop location span error: need to handle more ItemKind '{:?}'", item.kind); - } - }, - hir::Node::Block(block) => tcx.hir().span(block.hir_id), - hir::Node::TraitItem(item) => tcx.hir().span(item.hir_id()), - hir::Node::ImplItem(item) => tcx.hir().span(item.hir_id()), - _ => { - bug!("Drop location span error: need to handle more Node '{:?}'", owner_node); - } - }; - tcx.sess.source_map().end_point(owner_span) -} - -struct InferBorrowKind<'a, 'tcx> { - fcx: &'a FnCtxt<'a, 'tcx>, - - // The def-id of the closure whose kind and upvar accesses are being inferred. - closure_def_id: LocalDefId, - - /// For each Place that is captured by the closure, we track the minimal kind of - /// access we need (ref, ref mut, move, etc) and the expression that resulted in such access. - /// - /// Consider closure where s.str1 is captured via an ImmutableBorrow and - /// s.str2 via a MutableBorrow - /// - /// ```rust,no_run - /// struct SomeStruct { str1: String, str2: String }; - /// - /// // Assume that the HirId for the variable definition is `V1` - /// let mut s = SomeStruct { str1: format!("s1"), str2: format!("s2") }; - /// - /// let fix_s = |new_s2| { - /// // Assume that the HirId for the expression `s.str1` is `E1` - /// println!("Updating SomeStruct with str1={0}", s.str1); - /// // Assume that the HirId for the expression `*s.str2` is `E2` - /// s.str2 = new_s2; - /// }; - /// ``` - /// - /// For closure `fix_s`, (at a high level) the map contains - /// - /// ```ignore (illustrative) - /// Place { V1, [ProjectionKind::Field(Index=0, Variant=0)] } : CaptureKind { E1, ImmutableBorrow } - /// Place { V1, [ProjectionKind::Field(Index=1, Variant=0)] } : CaptureKind { E2, MutableBorrow } - /// ``` - capture_information: InferredCaptureInformation<'tcx>, - fake_reads: Vec<(Place<'tcx>, FakeReadCause, hir::HirId)>, -} - -impl<'a, 'tcx> euv::Delegate<'tcx> for InferBorrowKind<'a, 'tcx> { - fn fake_read( - &mut self, - place: &PlaceWithHirId<'tcx>, - cause: FakeReadCause, - diag_expr_id: hir::HirId, - ) { - let PlaceBase::Upvar(_) = place.place.base else { return }; - - // We need to restrict Fake Read precision to avoid fake reading unsafe code, - // such as deref of a raw pointer. - let dummy_capture_kind = ty::UpvarCapture::ByRef(ty::BorrowKind::ImmBorrow); - - let (place, _) = restrict_capture_precision(place.place.clone(), dummy_capture_kind); - - let (place, _) = restrict_repr_packed_field_ref_capture( - self.fcx.tcx, - self.fcx.param_env, - place, - dummy_capture_kind, - ); - self.fake_reads.push((place, cause, diag_expr_id)); - } - - #[instrument(skip(self), level = "debug")] - fn consume(&mut self, place_with_id: &PlaceWithHirId<'tcx>, diag_expr_id: hir::HirId) { - let PlaceBase::Upvar(upvar_id) = place_with_id.place.base else { return }; - assert_eq!(self.closure_def_id, upvar_id.closure_expr_id); - - self.capture_information.push(( - place_with_id.place.clone(), - ty::CaptureInfo { - capture_kind_expr_id: Some(diag_expr_id), - path_expr_id: Some(diag_expr_id), - capture_kind: ty::UpvarCapture::ByValue, - }, - )); - } - - #[instrument(skip(self), level = "debug")] - fn borrow( - &mut self, - place_with_id: &PlaceWithHirId<'tcx>, - diag_expr_id: hir::HirId, - bk: ty::BorrowKind, - ) { - let PlaceBase::Upvar(upvar_id) = place_with_id.place.base else { return }; - assert_eq!(self.closure_def_id, upvar_id.closure_expr_id); - - // The region here will get discarded/ignored - let capture_kind = ty::UpvarCapture::ByRef(bk); - - // We only want repr packed restriction to be applied to reading references into a packed - // struct, and not when the data is being moved. Therefore we call this method here instead - // of in `restrict_capture_precision`. - let (place, mut capture_kind) = restrict_repr_packed_field_ref_capture( - self.fcx.tcx, - self.fcx.param_env, - place_with_id.place.clone(), - capture_kind, - ); - - // Raw pointers don't inherit mutability - if place_with_id.place.deref_tys().any(Ty::is_unsafe_ptr) { - capture_kind = ty::UpvarCapture::ByRef(ty::BorrowKind::ImmBorrow); - } - - self.capture_information.push(( - place, - ty::CaptureInfo { - capture_kind_expr_id: Some(diag_expr_id), - path_expr_id: Some(diag_expr_id), - capture_kind, - }, - )); - } - - #[instrument(skip(self), level = "debug")] - fn mutate(&mut self, assignee_place: &PlaceWithHirId<'tcx>, diag_expr_id: hir::HirId) { - self.borrow(assignee_place, diag_expr_id, ty::BorrowKind::MutBorrow); - } -} - -/// Rust doesn't permit moving fields out of a type that implements drop -fn restrict_precision_for_drop_types<'a, 'tcx>( - fcx: &'a FnCtxt<'a, 'tcx>, - mut place: Place<'tcx>, - mut curr_mode: ty::UpvarCapture, - span: Span, -) -> (Place<'tcx>, ty::UpvarCapture) { - let is_copy_type = fcx.infcx.type_is_copy_modulo_regions(fcx.param_env, place.ty(), span); - - if let (false, UpvarCapture::ByValue) = (is_copy_type, curr_mode) { - for i in 0..place.projections.len() { - match place.ty_before_projection(i).kind() { - ty::Adt(def, _) if def.destructor(fcx.tcx).is_some() => { - truncate_place_to_len_and_update_capture_kind(&mut place, &mut curr_mode, i); - break; - } - _ => {} - } - } - } - - (place, curr_mode) -} - -/// Truncate `place` so that an `unsafe` block isn't required to capture it. -/// - No projections are applied to raw pointers, since these require unsafe blocks. We capture -/// them completely. -/// - No projections are applied on top of Union ADTs, since these require unsafe blocks. -fn restrict_precision_for_unsafe<'tcx>( - mut place: Place<'tcx>, - mut curr_mode: ty::UpvarCapture, -) -> (Place<'tcx>, ty::UpvarCapture) { - if place.base_ty.is_unsafe_ptr() { - truncate_place_to_len_and_update_capture_kind(&mut place, &mut curr_mode, 0); - } - - if place.base_ty.is_union() { - truncate_place_to_len_and_update_capture_kind(&mut place, &mut curr_mode, 0); - } - - for (i, proj) in place.projections.iter().enumerate() { - if proj.ty.is_unsafe_ptr() { - // Don't apply any projections on top of an unsafe ptr. - truncate_place_to_len_and_update_capture_kind(&mut place, &mut curr_mode, i + 1); - break; - } - - if proj.ty.is_union() { - // Don't capture precise fields of a union. - truncate_place_to_len_and_update_capture_kind(&mut place, &mut curr_mode, i + 1); - break; - } - } - - (place, curr_mode) -} - -/// Truncate projections so that following rules are obeyed by the captured `place`: -/// - No Index projections are captured, since arrays are captured completely. -/// - No unsafe block is required to capture `place` -/// Returns the truncated place and updated capture mode. -fn restrict_capture_precision<'tcx>( - place: Place<'tcx>, - curr_mode: ty::UpvarCapture, -) -> (Place<'tcx>, ty::UpvarCapture) { - let (mut place, mut curr_mode) = restrict_precision_for_unsafe(place, curr_mode); - - if place.projections.is_empty() { - // Nothing to do here - return (place, curr_mode); - } - - for (i, proj) in place.projections.iter().enumerate() { - match proj.kind { - ProjectionKind::Index => { - // Arrays are completely captured, so we drop Index projections - truncate_place_to_len_and_update_capture_kind(&mut place, &mut curr_mode, i); - return (place, curr_mode); - } - ProjectionKind::Deref => {} - ProjectionKind::Field(..) => {} // ignore - ProjectionKind::Subslice => {} // We never capture this - } - } - - (place, curr_mode) -} - -/// Truncate deref of any reference. -fn adjust_for_move_closure<'tcx>( - mut place: Place<'tcx>, - mut kind: ty::UpvarCapture, -) -> (Place<'tcx>, ty::UpvarCapture) { - let first_deref = place.projections.iter().position(|proj| proj.kind == ProjectionKind::Deref); - - if let Some(idx) = first_deref { - truncate_place_to_len_and_update_capture_kind(&mut place, &mut kind, idx); - } - - (place, ty::UpvarCapture::ByValue) -} - -/// Adjust closure capture just that if taking ownership of data, only move data -/// from enclosing stack frame. -fn adjust_for_non_move_closure<'tcx>( - mut place: Place<'tcx>, - mut kind: ty::UpvarCapture, -) -> (Place<'tcx>, ty::UpvarCapture) { - let contains_deref = - place.projections.iter().position(|proj| proj.kind == ProjectionKind::Deref); - - match kind { - ty::UpvarCapture::ByValue => { - if let Some(idx) = contains_deref { - truncate_place_to_len_and_update_capture_kind(&mut place, &mut kind, idx); - } - } - - ty::UpvarCapture::ByRef(..) => {} - } - - (place, kind) -} - -fn construct_place_string<'tcx>(tcx: TyCtxt<'_>, place: &Place<'tcx>) -> String { - let variable_name = match place.base { - PlaceBase::Upvar(upvar_id) => var_name(tcx, upvar_id.var_path.hir_id).to_string(), - _ => bug!("Capture_information should only contain upvars"), - }; - - let mut projections_str = String::new(); - for (i, item) in place.projections.iter().enumerate() { - let proj = match item.kind { - ProjectionKind::Field(a, b) => format!("({:?}, {:?})", a, b), - ProjectionKind::Deref => String::from("Deref"), - ProjectionKind::Index => String::from("Index"), - ProjectionKind::Subslice => String::from("Subslice"), - }; - if i != 0 { - projections_str.push(','); - } - projections_str.push_str(proj.as_str()); - } - - format!("{variable_name}[{projections_str}]") -} - -fn construct_capture_kind_reason_string<'tcx>( - tcx: TyCtxt<'_>, - place: &Place<'tcx>, - capture_info: &ty::CaptureInfo, -) -> String { - let place_str = construct_place_string(tcx, place); - - let capture_kind_str = match capture_info.capture_kind { - ty::UpvarCapture::ByValue => "ByValue".into(), - ty::UpvarCapture::ByRef(kind) => format!("{:?}", kind), - }; - - format!("{place_str} captured as {capture_kind_str} here") -} - -fn construct_path_string<'tcx>(tcx: TyCtxt<'_>, place: &Place<'tcx>) -> String { - let place_str = construct_place_string(tcx, place); - - format!("{place_str} used here") -} - -fn construct_capture_info_string<'tcx>( - tcx: TyCtxt<'_>, - place: &Place<'tcx>, - capture_info: &ty::CaptureInfo, -) -> String { - let place_str = construct_place_string(tcx, place); - - let capture_kind_str = match capture_info.capture_kind { - ty::UpvarCapture::ByValue => "ByValue".into(), - ty::UpvarCapture::ByRef(kind) => format!("{:?}", kind), - }; - format!("{place_str} -> {capture_kind_str}") -} - -fn var_name(tcx: TyCtxt<'_>, var_hir_id: hir::HirId) -> Symbol { - tcx.hir().name(var_hir_id) -} - -#[instrument(level = "debug", skip(tcx))] -fn should_do_rust_2021_incompatible_closure_captures_analysis( - tcx: TyCtxt<'_>, - closure_id: hir::HirId, -) -> bool { - if tcx.sess.rust_2021() { - return false; - } - - let (level, _) = - tcx.lint_level_at_node(lint::builtin::RUST_2021_INCOMPATIBLE_CLOSURE_CAPTURES, closure_id); - - !matches!(level, lint::Level::Allow) -} - -/// Return a two string tuple (s1, s2) -/// - s1: Line of code that is needed for the migration: eg: `let _ = (&x, ...)`. -/// - s2: Comma separated names of the variables being migrated. -fn migration_suggestion_for_2229( - tcx: TyCtxt<'_>, - need_migrations: &[NeededMigration], -) -> (String, String) { - let need_migrations_variables = need_migrations - .iter() - .map(|NeededMigration { var_hir_id: v, .. }| var_name(tcx, *v)) - .collect::<Vec<_>>(); - - let migration_ref_concat = - need_migrations_variables.iter().map(|v| format!("&{v}")).collect::<Vec<_>>().join(", "); - - let migration_string = if 1 == need_migrations.len() { - format!("let _ = {migration_ref_concat}") - } else { - format!("let _ = ({migration_ref_concat})") - }; - - let migrated_variables_concat = - need_migrations_variables.iter().map(|v| format!("`{v}`")).collect::<Vec<_>>().join(", "); - - (migration_string, migrated_variables_concat) -} - -/// Helper function to determine if we need to escalate CaptureKind from -/// CaptureInfo A to B and returns the escalated CaptureInfo. -/// (Note: CaptureInfo contains CaptureKind and an expression that led to capture it in that way) -/// -/// If both `CaptureKind`s are considered equivalent, then the CaptureInfo is selected based -/// on the `CaptureInfo` containing an associated `capture_kind_expr_id`. -/// -/// It is the caller's duty to figure out which path_expr_id to use. -/// -/// If both the CaptureKind and Expression are considered to be equivalent, -/// then `CaptureInfo` A is preferred. This can be useful in cases where we want to prioritize -/// expressions reported back to the user as part of diagnostics based on which appears earlier -/// in the closure. This can be achieved simply by calling -/// `determine_capture_info(existing_info, current_info)`. This works out because the -/// expressions that occur earlier in the closure body than the current expression are processed before. -/// Consider the following example -/// ```rust,no_run -/// struct Point { x: i32, y: i32 } -/// let mut p = Point { x: 10, y: 10 }; -/// -/// let c = || { -/// p.x += 10; -/// // ^ E1 ^ -/// // ... -/// // More code -/// // ... -/// p.x += 10; // E2 -/// // ^ E2 ^ -/// }; -/// ``` -/// `CaptureKind` associated with both `E1` and `E2` will be ByRef(MutBorrow), -/// and both have an expression associated, however for diagnostics we prefer reporting -/// `E1` since it appears earlier in the closure body. When `E2` is being processed we -/// would've already handled `E1`, and have an existing capture_information for it. -/// Calling `determine_capture_info(existing_info_e1, current_info_e2)` will return -/// `existing_info_e1` in this case, allowing us to point to `E1` in case of diagnostics. -fn determine_capture_info( - capture_info_a: ty::CaptureInfo, - capture_info_b: ty::CaptureInfo, -) -> ty::CaptureInfo { - // If the capture kind is equivalent then, we don't need to escalate and can compare the - // expressions. - let eq_capture_kind = match (capture_info_a.capture_kind, capture_info_b.capture_kind) { - (ty::UpvarCapture::ByValue, ty::UpvarCapture::ByValue) => true, - (ty::UpvarCapture::ByRef(ref_a), ty::UpvarCapture::ByRef(ref_b)) => ref_a == ref_b, - (ty::UpvarCapture::ByValue, _) | (ty::UpvarCapture::ByRef(_), _) => false, - }; - - if eq_capture_kind { - match (capture_info_a.capture_kind_expr_id, capture_info_b.capture_kind_expr_id) { - (Some(_), _) | (None, None) => capture_info_a, - (None, Some(_)) => capture_info_b, - } - } else { - // We select the CaptureKind which ranks higher based the following priority order: - // ByValue > MutBorrow > UniqueImmBorrow > ImmBorrow - match (capture_info_a.capture_kind, capture_info_b.capture_kind) { - (ty::UpvarCapture::ByValue, _) => capture_info_a, - (_, ty::UpvarCapture::ByValue) => capture_info_b, - (ty::UpvarCapture::ByRef(ref_a), ty::UpvarCapture::ByRef(ref_b)) => { - match (ref_a, ref_b) { - // Take LHS: - (ty::UniqueImmBorrow | ty::MutBorrow, ty::ImmBorrow) - | (ty::MutBorrow, ty::UniqueImmBorrow) => capture_info_a, - - // Take RHS: - (ty::ImmBorrow, ty::UniqueImmBorrow | ty::MutBorrow) - | (ty::UniqueImmBorrow, ty::MutBorrow) => capture_info_b, - - (ty::ImmBorrow, ty::ImmBorrow) - | (ty::UniqueImmBorrow, ty::UniqueImmBorrow) - | (ty::MutBorrow, ty::MutBorrow) => { - bug!("Expected unequal capture kinds"); - } - } - } - } - } -} - -/// Truncates `place` to have up to `len` projections. -/// `curr_mode` is the current required capture kind for the place. -/// Returns the truncated `place` and the updated required capture kind. -/// -/// Note: Capture kind changes from `MutBorrow` to `UniqueImmBorrow` if the truncated part of the `place` -/// contained `Deref` of `&mut`. -fn truncate_place_to_len_and_update_capture_kind<'tcx>( - place: &mut Place<'tcx>, - curr_mode: &mut ty::UpvarCapture, - len: usize, -) { - let is_mut_ref = |ty: Ty<'_>| matches!(ty.kind(), ty::Ref(.., hir::Mutability::Mut)); - - // If the truncated part of the place contains `Deref` of a `&mut` then convert MutBorrow -> - // UniqueImmBorrow - // Note that if the place contained Deref of a raw pointer it would've not been MutBorrow, so - // we don't need to worry about that case here. - match curr_mode { - ty::UpvarCapture::ByRef(ty::BorrowKind::MutBorrow) => { - for i in len..place.projections.len() { - if place.projections[i].kind == ProjectionKind::Deref - && is_mut_ref(place.ty_before_projection(i)) - { - *curr_mode = ty::UpvarCapture::ByRef(ty::BorrowKind::UniqueImmBorrow); - break; - } - } - } - - ty::UpvarCapture::ByRef(..) => {} - ty::UpvarCapture::ByValue => {} - } - - place.projections.truncate(len); -} - -/// Determines the Ancestry relationship of Place A relative to Place B -/// -/// `PlaceAncestryRelation::Ancestor` implies Place A is ancestor of Place B -/// `PlaceAncestryRelation::Descendant` implies Place A is descendant of Place B -/// `PlaceAncestryRelation::Divergent` implies neither of them is the ancestor of the other. -fn determine_place_ancestry_relation<'tcx>( - place_a: &Place<'tcx>, - place_b: &Place<'tcx>, -) -> PlaceAncestryRelation { - // If Place A and Place B, don't start off from the same root variable, they are divergent. - if place_a.base != place_b.base { - return PlaceAncestryRelation::Divergent; - } - - // Assume of length of projections_a = n - let projections_a = &place_a.projections; - - // Assume of length of projections_b = m - let projections_b = &place_b.projections; - - let same_initial_projections = - iter::zip(projections_a, projections_b).all(|(proj_a, proj_b)| proj_a.kind == proj_b.kind); - - if same_initial_projections { - use std::cmp::Ordering; - - // First min(n, m) projections are the same - // Select Ancestor/Descendant - match projections_b.len().cmp(&projections_a.len()) { - Ordering::Greater => PlaceAncestryRelation::Ancestor, - Ordering::Equal => PlaceAncestryRelation::SamePlace, - Ordering::Less => PlaceAncestryRelation::Descendant, - } - } else { - PlaceAncestryRelation::Divergent - } -} - -/// Reduces the precision of the captured place when the precision doesn't yield any benefit from -/// borrow checking perspective, allowing us to save us on the size of the capture. -/// -/// -/// Fields that are read through a shared reference will always be read via a shared ref or a copy, -/// and therefore capturing precise paths yields no benefit. This optimization truncates the -/// rightmost deref of the capture if the deref is applied to a shared ref. -/// -/// Reason we only drop the last deref is because of the following edge case: -/// -/// ``` -/// # struct A { field_of_a: Box<i32> } -/// # struct B {} -/// # struct C<'a>(&'a i32); -/// struct MyStruct<'a> { -/// a: &'static A, -/// b: B, -/// c: C<'a>, -/// } -/// -/// fn foo<'a, 'b>(m: &'a MyStruct<'b>) -> impl FnMut() + 'static { -/// || drop(&*m.a.field_of_a) -/// // Here we really do want to capture `*m.a` because that outlives `'static` -/// -/// // If we capture `m`, then the closure no longer outlives `'static' -/// // it is constrained to `'a` -/// } -/// ``` -fn truncate_capture_for_optimization<'tcx>( - mut place: Place<'tcx>, - mut curr_mode: ty::UpvarCapture, -) -> (Place<'tcx>, ty::UpvarCapture) { - let is_shared_ref = |ty: Ty<'_>| matches!(ty.kind(), ty::Ref(.., hir::Mutability::Not)); - - // Find the right-most deref (if any). All the projections that come after this - // are fields or other "in-place pointer adjustments"; these refer therefore to - // data owned by whatever pointer is being dereferenced here. - let idx = place.projections.iter().rposition(|proj| ProjectionKind::Deref == proj.kind); - - match idx { - // If that pointer is a shared reference, then we don't need those fields. - Some(idx) if is_shared_ref(place.ty_before_projection(idx)) => { - truncate_place_to_len_and_update_capture_kind(&mut place, &mut curr_mode, idx + 1) - } - None | Some(_) => {} - } - - (place, curr_mode) -} - -/// Precise capture is enabled if the feature gate `capture_disjoint_fields` is enabled or if -/// user is using Rust Edition 2021 or higher. -/// -/// `span` is the span of the closure. -fn enable_precise_capture(tcx: TyCtxt<'_>, span: Span) -> bool { - // We use span here to ensure that if the closure was generated by a macro with a different - // edition. - tcx.features().capture_disjoint_fields || span.rust_2021() -} diff --git a/compiler/rustc_typeck/src/check/wfcheck.rs b/compiler/rustc_typeck/src/check/wfcheck.rs deleted file mode 100644 index 27b3da8ab..000000000 --- a/compiler/rustc_typeck/src/check/wfcheck.rs +++ /dev/null @@ -1,1989 +0,0 @@ -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}; -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::outlives::env::{OutlivesEnvironment, RegionBoundPairs}; -use rustc_infer::infer::outlives::obligations::TypeOutlives; -use rustc_infer::infer::{self, InferCtxt, TyCtxtInferExt}; -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_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::outlives_bounds::InferCtxtExt as _; -use rustc_trait_selection::traits::query::evaluate_obligation::InferCtxtExt as _; -use rustc_trait_selection::traits::{ - self, ObligationCause, ObligationCauseCode, ObligationCtxt, WellFormedLoc, -}; - -use std::cell::LazyCell; -use std::convert::TryInto; -use std::iter; -use std::ops::{ControlFlow, Deref}; - -pub(super) struct WfCheckingCtxt<'a, 'tcx> { - pub(super) ocx: ObligationCtxt<'a, 'tcx>, - span: Span, - body_id: hir::HirId, - param_env: ty::ParamEnv<'tcx>, -} -impl<'a, 'tcx> Deref for WfCheckingCtxt<'a, 'tcx> { - type Target = ObligationCtxt<'a, 'tcx>; - fn deref(&self) -> &Self::Target { - &self.ocx - } -} - -impl<'tcx> WfCheckingCtxt<'_, 'tcx> { - fn tcx(&self) -> TyCtxt<'tcx> { - self.ocx.infcx.tcx - } - - fn normalize<T>(&self, span: Span, loc: Option<WellFormedLoc>, value: T) -> T - where - T: TypeFoldable<'tcx>, - { - self.ocx.normalize( - ObligationCause::new(span, self.body_id, ObligationCauseCode::WellFormed(loc)), - self.param_env, - value, - ) - } - - fn register_wf_obligation( - &self, - span: Span, - loc: Option<WellFormedLoc>, - arg: ty::GenericArg<'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, - param_env, - ty::Binder::dummy(ty::PredicateKind::WellFormed(arg)).to_predicate(self.tcx()), - )); - } -} - -pub(super) fn enter_wf_checking_ctxt<'tcx, F>( - tcx: TyCtxt<'tcx>, - span: Span, - body_def_id: LocalDefId, - f: F, -) where - 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 assumed_wf_types = ocx.assumed_wf_types(param_env, span, body_def_id); - - 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.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) { - let node = tcx.hir().expect_owner(def_id); - match node { - hir::OwnerNode::Crate(_) => {} - hir::OwnerNode::Item(item) => check_item(tcx, item), - hir::OwnerNode::TraitItem(item) => check_trait_item(tcx, item), - hir::OwnerNode::ImplItem(item) => check_impl_item(tcx, item), - hir::OwnerNode::ForeignItem(item) => check_foreign_item(tcx, item), - } - - if let Some(generics) = node.generics() { - for param in generics.params { - check_param_wf(tcx, param) - } - } -} - -/// Checks that the field types (in a struct def'n) or argument types (in an enum def'n) are -/// well-formed, meaning that they do not require any constraints not declared in the struct -/// definition itself. For example, this definition would be illegal: -/// -/// ```rust -/// struct Ref<'a, T> { x: &'a T } -/// ``` -/// -/// because the type did not declare that `T:'a`. -/// -/// We do this check as a pre-pass before checking fn bodies because if these constraints are -/// not included it frequently leads to confusing errors in fn bodies. So it's better to check -/// 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; - - debug!( - ?item.def_id, - item.name = ? tcx.def_path_str(def_id.to_def_id()) - ); - - match item.kind { - // Right now we check that every default trait implementation - // has an implementation of itself. Basically, a case like: - // - // impl Trait for T {} - // - // has a requirement of `T: Trait` which was required for default - // method implementations. Although this could be improved now that - // there's a better infrastructure in place for this, it's being left - // for a follow-up work. - // - // Since there's such a requirement, we need to check *just* positive - // implementations, otherwise things like: - // - // impl !Send for T {} - // - // won't be allowed unless there's an *explicit* implementation of `Send` - // for `T` - hir::ItemKind::Impl(ref impl_) => { - let is_auto = tcx - .impl_trait_ref(item.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); - let mut err = - tcx.sess.struct_span_err(sp, "impls of auto traits cannot be default"); - err.span_labels(impl_.defaultness_span, "default because of this"); - err.span_label(sp, "auto trait"); - err.emit(); - } - // We match on both `ty::ImplPolarity` and `ast::ImplPolarity` just to get the `!` span. - match (tcx.impl_polarity(def_id), impl_.polarity) { - (ty::ImplPolarity::Positive, _) => { - check_impl(tcx, item, impl_.self_ty, &impl_.of_trait, impl_.constness); - } - (ty::ImplPolarity::Negative, ast::ImplPolarity::Negative(span)) => { - // FIXME(#27579): what amount of WF checking do we need for neg impls? - if let hir::Defaultness::Default { .. } = impl_.defaultness { - let mut spans = vec![span]; - spans.extend(impl_.defaultness_span); - struct_span_err!( - tcx.sess, - spans, - E0750, - "negative impls cannot be default impls" - ) - .emit(); - } - } - (ty::ImplPolarity::Reservation, _) => { - // FIXME: what amount of WF checking do we need for reservation impls? - } - _ => unreachable!(), - } - } - hir::ItemKind::Fn(ref sig, ..) => { - check_item_fn(tcx, item.def_id, item.ident, item.span, sig.decl); - } - hir::ItemKind::Static(ty, ..) => { - check_item_type(tcx, item.def_id, ty.span, false); - } - hir::ItemKind::Const(ty, ..) => { - check_item_type(tcx, item.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)]); - - check_variances_for_type_defn(tcx, item, ast_generics); - } - hir::ItemKind::Union(ref struct_def, ref ast_generics) => { - check_type_defn(tcx, item, true, |wfcx| vec![wfcx.non_enum_variant(struct_def)]); - - check_variances_for_type_defn(tcx, item, ast_generics); - } - hir::ItemKind::Enum(ref enum_def, ref ast_generics) => { - check_type_defn(tcx, item, true, |wfcx| wfcx.enum_variants(enum_def)); - - check_variances_for_type_defn(tcx, item, ast_generics); - } - hir::ItemKind::Trait(..) => { - check_trait(tcx, item); - } - hir::ItemKind::TraitAlias(..) => { - check_trait(tcx, item); - } - // `ForeignItem`s are handled separately. - hir::ItemKind::ForeignMod { .. } => {} - _ => {} - } -} - -fn check_foreign_item(tcx: TyCtxt<'_>, item: &hir::ForeignItem<'_>) { - let def_id = item.def_id; - - debug!( - ?item.def_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) - } - hir::ForeignItemKind::Static(ty, ..) => check_item_type(tcx, item.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 (method_sig, span) = match trait_item.kind { - hir::TraitItemKind::Fn(ref sig, _) => (Some(sig), trait_item.span), - hir::TraitItemKind::Type(_bounds, Some(ty)) => (None, ty.span), - _ => (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); - - 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 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() { - Some("fn_mut") - } else { - None - }; - - if let (Some(fn_lang_item_name), "call") = - (fn_lang_item_name, trait_item.ident.name.to_ident_string().as_str()) - { - // We are looking at the `call` function of the `fn` or `fn_mut` lang item. - // Do some rudimentary sanity checking to avoid an ICE later (issue #83471). - if let Some(hir::FnSig { decl, span, .. }) = method_sig { - if let [self_ty, _] = decl.inputs { - if !matches!(self_ty.kind, hir::TyKind::Rptr(_, _)) { - tcx.sess - .struct_span_err( - self_ty.span, - &format!( - "first argument of `call` in `{fn_lang_item_name}` lang item must be a reference", - ), - ) - .emit(); - } - } else { - tcx.sess - .struct_span_err( - *span, - &format!( - "`call` function in `{fn_lang_item_name}` lang item takes exactly two arguments", - ), - ) - .emit(); - } - } else { - tcx.sess - .struct_span_err( - trait_item.span, - &format!( - "`call` trait item in `{fn_lang_item_name}` lang item must be a function", - ), - ) - .emit(); - } - } -} - -/// Require that the user writes where clauses on GATs for the implicit -/// outlives bounds involving trait parameters in trait functions and -/// lifetimes passed as GAT substs. See `self-outlives-lint` test. -/// -/// We use the following trait as an example throughout this function: -/// ```rust,ignore (this code fails due to this lint) -/// trait IntoIter { -/// type Iter<'a>: Iterator<Item = Self::Item<'a>>; -/// type Item<'a>; -/// fn into_iter<'a>(&'a self) -> Self::Iter<'a>; -/// } -/// ``` -fn check_gat_where_clauses(tcx: TyCtxt<'_>, associated_items: &[hir::TraitItemRef]) { - // Associates every GAT's def_id to a list of possibly missing bounds detected by this lint. - let mut required_bounds_by_item = FxHashMap::default(); - - // Loop over all GATs together, because if this lint suggests adding a where-clause bound - // to one GAT, it might then require us to an additional bound on another GAT. - // In our `IntoIter` example, we discover a missing `Self: 'a` bound on `Iter<'a>`, which - // then in a second loop adds a `Self: 'a` bound to `Item` due to the relationship between - // those GATs. - loop { - let mut should_continue = false; - for gat_item in associated_items { - let gat_def_id = gat_item.id.def_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 { - continue; - } - let gat_generics = tcx.generics_of(gat_def_id); - // FIXME(jackh726): we can also warn in the more general case - if gat_generics.params.is_empty() { - continue; - } - - // Gather the bounds with which all other items inside of this trait constrain the GAT. - // This is calculated by taking the intersection of the bounds that each item - // 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; - // Skip our own GAT, since it does not constrain itself at all. - if item_def_id == gat_def_id { - continue; - } - - let item_hir_id = item.id.hir_id(); - let param_env = tcx.param_env(item_def_id); - - let item_required_bounds = match item.kind { - // In our example, this corresponds to `into_iter` method - hir::AssocItemKind::Fn { .. } => { - // For methods, we check the function signature's return type for any GATs - // to constrain. In the `into_iter` case, we see that the return type - // `Self::Iter<'a>` is a GAT we want to gather any potential missing bounds from. - let sig: ty::FnSig<'_> = tcx.liberate_late_bound_regions( - item_def_id.to_def_id(), - tcx.fn_sig(item_def_id), - ); - gather_gat_bounds( - tcx, - param_env, - item_hir_id, - 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_generics, - ) - } - // In our example, this corresponds to the `Iter` and `Item` associated types - hir::AssocItemKind::Type => { - // If our associated item is a GAT with missing bounds, add them to - // the param-env here. This allows this GAT to propagate missing bounds - // to other GATs. - let param_env = augment_param_env( - tcx, - param_env, - required_bounds_by_item.get(&item_def_id), - ); - gather_gat_bounds( - tcx, - param_env, - item_hir_id, - tcx.explicit_item_bounds(item_def_id) - .iter() - .copied() - .collect::<Vec<_>>(), - &FxHashSet::default(), - gat_def_id, - gat_generics, - ) - } - hir::AssocItemKind::Const => None, - }; - - if let Some(item_required_bounds) = item_required_bounds { - // Take the intersection of the required bounds for this GAT, and - // the item_required_bounds which are the ones implied by just - // this item alone. - // This is why we use an Option<_>, since we need to distinguish - // the empty set of bounds from the _uninitialized_ set of bounds. - if let Some(new_required_bounds) = &mut new_required_bounds { - new_required_bounds.retain(|b| item_required_bounds.contains(b)); - } else { - new_required_bounds = Some(item_required_bounds); - } - } - } - - if let Some(new_required_bounds) = new_required_bounds { - let required_bounds = required_bounds_by_item.entry(gat_def_id).or_default(); - if new_required_bounds.into_iter().any(|p| required_bounds.insert(p)) { - // Iterate until our required_bounds no longer change - // Since they changed here, we should continue the loop - should_continue = true; - } - } - } - // We know that this loop will eventually halt, since we only set `should_continue` if the - // `required_bounds` for this item grows. Since we are not creating any new region or type - // variables, the set of all region and type bounds that we could ever insert are limited - // by the number of unique types and regions we observe in a given item. - if !should_continue { - break; - } - } - - for (gat_def_id, required_bounds) in required_bounds_by_item { - let gat_item_hir = tcx.hir().expect_trait_item(gat_def_id); - debug!(?required_bounds); - let param_env = tcx.param_env(gat_def_id); - let gat_hir = gat_item_hir.hir_id(); - - let mut unsatisfied_bounds: Vec<_> = required_bounds - .into_iter() - .filter(|clause| match clause.kind().skip_binder() { - ty::PredicateKind::RegionOutlives(ty::OutlivesPredicate(a, b)) => { - !region_known_to_outlive(tcx, gat_hir, param_env, &FxHashSet::default(), a, b) - } - ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(a, b)) => { - !ty_known_to_outlive(tcx, gat_hir, param_env, &FxHashSet::default(), a, b) - } - _ => bug!("Unexpected PredicateKind"), - }) - .map(|clause| clause.to_string()) - .collect(); - - // We sort so that order is predictable - unsatisfied_bounds.sort(); - - if !unsatisfied_bounds.is_empty() { - let plural = pluralize!(unsatisfied_bounds.len()); - let mut err = tcx.sess.struct_span_err( - gat_item_hir.span, - &format!("missing required bound{} on `{}`", plural, gat_item_hir.ident), - ); - - let suggestion = format!( - "{} {}", - gat_item_hir.generics.add_where_or_trailing_comma(), - unsatisfied_bounds.join(", "), - ); - err.span_suggestion( - gat_item_hir.generics.tail_span_for_predicate_suggestion(), - &format!("add the required where clause{plural}"), - suggestion, - Applicability::MachineApplicable, - ); - - let bound = - if unsatisfied_bounds.len() > 1 { "these bounds are" } else { "this bound is" }; - err.note(&format!( - "{} currently required to ensure that impls have maximum flexibility", - bound - )); - err.note( - "we are soliciting feedback, see issue #87479 \ - <https://github.com/rust-lang/rust/issues/87479> \ - for more information", - ); - - err.emit(); - } - } -} - -/// Add a new set of predicates to the caller_bounds of an existing param_env. -fn augment_param_env<'tcx>( - tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, - new_predicates: Option<&FxHashSet<ty::Predicate<'tcx>>>, -) -> ty::ParamEnv<'tcx> { - let Some(new_predicates) = new_predicates else { - return param_env; - }; - - if new_predicates.is_empty() { - return param_env; - } - - let bounds = - tcx.mk_predicates(param_env.caller_bounds().iter().chain(new_predicates.iter().cloned())); - // FIXME(compiler-errors): Perhaps there is a case where we need to normalize this - // i.e. traits::normalize_param_env_or_error - ty::ParamEnv::new(bounds, param_env.reveal(), param_env.constness()) -} - -/// We use the following trait as an example throughout this function. -/// Specifically, let's assume that `to_check` here is the return type -/// of `into_iter`, and the GAT we are checking this for is `Iter`. -/// ```rust,ignore (this code fails due to this lint) -/// trait IntoIter { -/// type Iter<'a>: Iterator<Item = Self::Item<'a>>; -/// type Item<'a>; -/// fn into_iter<'a>(&'a self) -> Self::Iter<'a>; -/// } -/// ``` -fn gather_gat_bounds<'tcx, T: TypeFoldable<'tcx>>( - tcx: TyCtxt<'tcx>, - param_env: ty::ParamEnv<'tcx>, - item_hir: hir::HirId, - to_check: T, - wf_tys: &FxHashSet<Ty<'tcx>>, - gat_def_id: LocalDefId, - gat_generics: &'tcx ty::Generics, -) -> Option<FxHashSet<ty::Predicate<'tcx>>> { - // The bounds we that we would require from `to_check` - let mut bounds = FxHashSet::default(); - - let (regions, types) = GATSubstCollector::visit(gat_def_id.to_def_id(), to_check); - - // If both regions and types are empty, then this GAT isn't in the - // set of types we are checking, and we shouldn't try to do clause analysis - // (particularly, doing so would end up with an empty set of clauses, - // since the current method would require none, and we take the - // intersection of requirements of all methods) - if types.is_empty() && regions.is_empty() { - return None; - } - - for (region_a, region_a_idx) in ®ions { - // Ignore `'static` lifetimes for the purpose of this lint: it's - // because we know it outlives everything and so doesn't give meaningful - // clues - if let ty::ReStatic = **region_a { - continue; - } - // For each region argument (e.g., `'a` in our example), check for a - // relationship to the type arguments (e.g., `Self`). If there is an - // outlives relationship (`Self: 'a`), then we want to ensure that is - // reflected in a where clause on the GAT itself. - for (ty, ty_idx) in &types { - // In our example, requires that `Self: 'a` - if ty_known_to_outlive(tcx, item_hir, param_env, &wf_tys, *ty, *region_a) { - debug!(?ty_idx, ?region_a_idx); - debug!("required clause: {ty} must outlive {region_a}"); - // Translate into the generic parameters of the GAT. In - // our example, the type was `Self`, which will also be - // `Self` in the GAT. - let ty_param = gat_generics.param_at(*ty_idx, tcx); - let ty_param = tcx - .mk_ty(ty::Param(ty::ParamTy { index: ty_param.index, name: ty_param.name })); - // Same for the region. In our example, 'a corresponds - // to the 'me parameter. - let region_param = gat_generics.param_at(*region_a_idx, tcx); - let region_param = - tcx.mk_region(ty::RegionKind::ReEarlyBound(ty::EarlyBoundRegion { - def_id: region_param.def_id, - index: region_param.index, - name: region_param.name, - })); - // The predicate we expect to see. (In our example, - // `Self: 'me`.) - let clause = - ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(ty_param, region_param)); - let clause = tcx.mk_predicate(ty::Binder::dummy(clause)); - bounds.insert(clause); - } - } - - // For each region argument (e.g., `'a` in our example), also check for a - // relationship to the other region arguments. If there is an outlives - // relationship, then we want to ensure that is reflected in the where clause - // on the GAT itself. - for (region_b, region_b_idx) in ®ions { - // Again, skip `'static` because it outlives everything. Also, we trivially - // know that a region outlives itself. - if ty::ReStatic == **region_b || region_a == region_b { - continue; - } - if region_known_to_outlive(tcx, item_hir, param_env, &wf_tys, *region_a, *region_b) { - debug!(?region_a_idx, ?region_b_idx); - debug!("required clause: {region_a} must outlive {region_b}"); - // Translate into the generic parameters of the GAT. - let region_a_param = gat_generics.param_at(*region_a_idx, tcx); - let region_a_param = - tcx.mk_region(ty::RegionKind::ReEarlyBound(ty::EarlyBoundRegion { - def_id: region_a_param.def_id, - index: region_a_param.index, - name: region_a_param.name, - })); - // Same for the region. - let region_b_param = gat_generics.param_at(*region_b_idx, tcx); - let region_b_param = - tcx.mk_region(ty::RegionKind::ReEarlyBound(ty::EarlyBoundRegion { - def_id: region_b_param.def_id, - index: region_b_param.index, - name: region_b_param.name, - })); - // The predicate we expect to see. - let clause = ty::PredicateKind::RegionOutlives(ty::OutlivesPredicate( - region_a_param, - region_b_param, - )); - let clause = tcx.mk_predicate(ty::Binder::dummy(clause)); - bounds.insert(clause); - } - } - } - - Some(bounds) -} - -/// Given a known `param_env` and a set of well formed types, can we prove that -/// `ty` outlives `region`. -fn ty_known_to_outlive<'tcx>( - tcx: TyCtxt<'tcx>, - id: hir::HirId, - param_env: ty::ParamEnv<'tcx>, - wf_tys: &FxHashSet<Ty<'tcx>>, - ty: Ty<'tcx>, - region: ty::Region<'tcx>, -) -> bool { - 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, ConstraintCategory::BoringNoLocation); - }) -} - -/// Given a known `param_env` and a set of well formed types, can we prove that -/// `region_a` outlives `region_b` -fn region_known_to_outlive<'tcx>( - tcx: TyCtxt<'tcx>, - id: hir::HirId, - param_env: ty::ParamEnv<'tcx>, - wf_tys: &FxHashSet<Ty<'tcx>>, - region_a: ty::Region<'tcx>, - region_b: ty::Region<'tcx>, -) -> bool { - resolve_regions_with_wf_tys(tcx, id, param_env, &wf_tys, |mut infcx, _| { - 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, - ConstraintCategory::BoringNoLocation, - ); - }) -} - -/// Given a known `param_env` and a set of well formed types, set up an -/// `InferCtxt`, call the passed function (to e.g. set up region constraints -/// to be tested), then resolve region and return errors -fn resolve_regions_with_wf_tys<'tcx>( - tcx: TyCtxt<'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>), -) -> 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 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); - - let errors = infcx.resolve_regions(&outlives_environment); - - 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() - }) -} - -/// TypeVisitor that looks for uses of GATs like -/// `<P0 as Trait<P1..Pn>>::GAT<Pn..Pm>` and adds the arguments `P0..Pm` into -/// the two vectors, `regions` and `types` (depending on their kind). For each -/// parameter `Pi` also track the index `i`. -struct GATSubstCollector<'tcx> { - gat: DefId, - // Which region appears and which parameter index its substituted for - regions: FxHashSet<(ty::Region<'tcx>, usize)>, - // Which params appears and which parameter index its substituted for - types: FxHashSet<(Ty<'tcx>, usize)>, -} - -impl<'tcx> GATSubstCollector<'tcx> { - fn visit<T: TypeFoldable<'tcx>>( - gat: DefId, - t: T, - ) -> (FxHashSet<(ty::Region<'tcx>, usize)>, FxHashSet<(Ty<'tcx>, usize)>) { - let mut visitor = - GATSubstCollector { gat, regions: FxHashSet::default(), types: FxHashSet::default() }; - t.visit_with(&mut visitor); - (visitor.regions, visitor.types) - } -} - -impl<'tcx> TypeVisitor<'tcx> for GATSubstCollector<'tcx> { - type BreakTy = !; - - fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { - match t.kind() { - ty::Projection(p) if p.item_def_id == self.gat => { - for (idx, subst) in p.substs.iter().enumerate() { - match subst.unpack() { - GenericArgKind::Lifetime(lt) if !lt.is_late_bound() => { - self.regions.insert((lt, idx)); - } - GenericArgKind::Type(t) => { - self.types.insert((t, idx)); - } - _ => {} - } - } - } - _ => {} - } - t.super_visit_with(self) - } -} - -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.opt_def_id() == Some(trait_def_id.to_def_id()), - _ => false, - }, - _ => false, - } -} - -/// Detect when an object unsafe trait is referring to itself in one of its associated items. -/// 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())) { - hir::Node::Item(item) => match item.kind { - hir::ItemKind::Trait(..) => (item.ident, item.def_id), - _ => return, - }, - _ => return, - }; - 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) => - { - 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) { - trait_should_be_self.push(ty.span); - } - } - match sig.decl.output { - hir::FnRetTy::Return(ty) if could_be_self(trait_def_id, ty) => { - trait_should_be_self.push(ty.span); - } - _ => {} - } - } - _ => {} - } - if !trait_should_be_self.is_empty() { - if tcx.object_safety_violations(trait_def_id).is_empty() { - return; - } - let sugg = trait_should_be_self.iter().map(|span| (*span, "Self".to_string())).collect(); - tcx.sess - .struct_span_err( - trait_should_be_self, - "associated item referring to unboxed trait object for its own trait", - ) - .span_label(trait_name.span, "in this trait") - .multipart_suggestion( - "you might have meant to use `Self` to refer to the implementing type", - sugg, - Applicability::MachineApplicable, - ) - .emit(); - } -} - -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), - _ => (None, impl_item.span), - }; - - check_associated_item(tcx, def_id, span, method_sig); -} - -fn check_param_wf(tcx: TyCtxt<'_>, param: &hir::GenericParam<'_>) { - match param.kind { - // We currently only check wf of const params here. - hir::GenericParamKind::Lifetime { .. } | hir::GenericParamKind::Type { .. } => (), - - // Const parameters are well formed if their type is structural match. - hir::GenericParamKind::Const { ty: hir_ty, default: _ } => { - let ty = tcx.type_of(tcx.hir().local_def_id(param.hir_id)); - - if tcx.features().adt_const_params { - if let Some(non_structural_match_ty) = - traits::search_for_adt_const_param_violation(param.span, tcx, ty) - { - // We use the same error code in both branches, because this is really the same - // issue: we just special-case the message for type parameters to make it - // clearer. - match non_structural_match_ty.kind() { - ty::Param(_) => { - // Const parameters may not have type parameters as their types, - // because we cannot be sure that the type parameter derives `PartialEq` - // and `Eq` (just implementing them is not enough for `structural_match`). - struct_span_err!( - tcx.sess, - hir_ty.span, - E0741, - "`{ty}` is not guaranteed to `#[derive(PartialEq, Eq)]`, so may not be \ - used as the type of a const parameter", - ) - .span_label( - hir_ty.span, - format!("`{ty}` may not derive both `PartialEq` and `Eq`"), - ) - .note( - "it is not currently possible to use a type parameter as the type of a \ - const parameter", - ) - .emit(); - } - ty::Float(_) => { - struct_span_err!( - tcx.sess, - hir_ty.span, - E0741, - "`{ty}` is forbidden as the type of a const generic parameter", - ) - .note("floats do not derive `Eq` or `Ord`, which are required for const parameters") - .emit(); - } - ty::FnPtr(_) => { - struct_span_err!( - tcx.sess, - hir_ty.span, - E0741, - "using function pointers as const generic parameters is forbidden", - ) - .emit(); - } - ty::RawPtr(_) => { - struct_span_err!( - tcx.sess, - hir_ty.span, - E0741, - "using raw pointers as const generic parameters is forbidden", - ) - .emit(); - } - _ => { - let mut diag = struct_span_err!( - tcx.sess, - hir_ty.span, - E0741, - "`{}` must be annotated with `#[derive(PartialEq, Eq)]` to be used as \ - the type of a const parameter", - non_structural_match_ty, - ); - - if ty == non_structural_match_ty { - diag.span_label( - hir_ty.span, - format!("`{ty}` doesn't derive both `PartialEq` and `Eq`"), - ); - } - - diag.emit(); - } - } - } - } else { - let err_ty_str; - let mut is_ptr = true; - - let err = match ty.kind() { - ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Error(_) => None, - ty::FnPtr(_) => Some("function pointers"), - ty::RawPtr(_) => Some("raw pointers"), - _ => { - is_ptr = false; - err_ty_str = format!("`{ty}`"); - Some(err_ty_str.as_str()) - } - }; - - if let Some(unsupported_type) = err { - if is_ptr { - tcx.sess.span_err( - hir_ty.span, - &format!( - "using {unsupported_type} as const generic parameters is forbidden", - ), - ); - } else { - let mut err = tcx.sess.struct_span_err( - hir_ty.span, - &format!( - "{unsupported_type} is forbidden as the type of a const generic parameter", - ), - ); - err.note("the only supported types are integers, `bool` and `char`"); - if tcx.sess.is_nightly_build() { - err.help( - "more complex types are supported with `#![feature(adt_const_params)]`", - ); - } - err.emit(); - } - } - } - } - } -} - -#[instrument(level = "debug", skip(tcx, span, sig_if_method))] -fn check_associated_item( - tcx: TyCtxt<'_>, - item_id: LocalDefId, - span: Span, - sig_if_method: Option<&hir::FnSig<'_>>, -) { - let loc = Some(WellFormedLoc::Ty(item_id)); - enter_wf_checking_ctxt(tcx, span, item_id, |wfcx| { - let item = tcx.associated_item(item_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 { - ty::AssocKind::Const => { - let ty = tcx.type_of(item.def_id); - let ty = wfcx.normalize(span, Some(WellFormedLoc::Ty(item_id)), ty); - wfcx.register_wf_obligation(span, loc, ty.into()); - } - ty::AssocKind::Fn => { - let sig = tcx.fn_sig(item.def_id); - let hir_sig = sig_if_method.expect("bad signature for method"); - check_fn_or_method( - wfcx, - item.ident(tcx).span, - sig, - hir_sig.decl, - item.def_id.expect_local(), - ); - check_method_receiver(wfcx, hir_sig, item, self_ty); - } - ty::AssocKind::Type => { - if let ty::AssocItemContainer::TraitContainer = item.container { - check_associated_type_bounds(wfcx, item, span) - } - if item.defaultness(tcx).has_value() { - let ty = tcx.type_of(item.def_id); - let ty = wfcx.normalize(span, Some(WellFormedLoc::Ty(item_id)), ty); - wfcx.register_wf_obligation(span, loc, ty.into()); - } - } - } - }) -} - -fn item_adt_kind(kind: &ItemKind<'_>) -> Option<AdtKind> { - match kind { - ItemKind::Struct(..) => Some(AdtKind::Struct), - ItemKind::Union(..) => Some(AdtKind::Union), - ItemKind::Enum(..) => Some(AdtKind::Enum), - _ => None, - } -} - -/// In a type definition, we check that to ensure that the types of the fields are well-formed. -fn check_type_defn<'tcx, F>( - tcx: TyCtxt<'tcx>, - item: &hir::Item<'tcx>, - all_sized: bool, - mut lookup_fields: F, -) where - F: FnMut(&WfCheckingCtxt<'_, 'tcx>) -> Vec<AdtVariant<'tcx>>, -{ - enter_wf_checking_ctxt(tcx, item.span, item.def_id, |wfcx| { - let variants = lookup_fields(wfcx); - let packed = tcx.adt_def(item.def_id).repr().packed(); - - for variant in &variants { - // All field types must be well-formed. - for field in &variant.fields { - wfcx.register_wf_obligation( - field.span, - Some(WellFormedLoc::Ty(field.def_id)), - field.ty.into(), - ) - } - - // For DST, or when drop needs to copy things around, all - // intermediate types must be sized. - let needs_drop_copy = || { - packed && { - let ty = variant.fields.last().unwrap().ty; - let ty = tcx.erase_regions(ty); - if ty.needs_infer() { - tcx.sess - .delay_span_bug(item.span, &format!("inference variables in {:?}", ty)); - // Just treat unresolved type expression as if it needs drop. - true - } else { - ty.needs_drop(tcx, tcx.param_env(item.def_id)) - } - } - }; - // All fields (except for possibly the last) should be sized. - let all_sized = all_sized || variant.fields.is_empty() || needs_drop_copy(); - let unsized_len = if all_sized { 0 } else { 1 }; - for (idx, field) in - variant.fields[..variant.fields.len() - unsized_len].iter().enumerate() - { - let last = idx == variant.fields.len() - 1; - wfcx.register_bound( - traits::ObligationCause::new( - field.span, - wfcx.body_id, - traits::FieldSized { - adt_kind: match item_adt_kind(&item.kind) { - Some(i) => i, - None => bug!(), - }, - span: field.span, - last, - }, - ), - wfcx.param_env, - field.ty, - tcx.require_lang_item(LangItem::Sized, None), - ); - } - - // 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, - traits::MiscObligation, - ); - 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, - ))) - .to_predicate(tcx), - )); - } - } - - check_where_clauses(wfcx, item.span, item.def_id); - }); -} - -#[instrument(skip(tcx, item))] -fn check_trait(tcx: TyCtxt<'_>, item: &hir::Item<'_>) { - debug!(?item.def_id); - - let trait_def = tcx.trait_def(item.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) { - struct_span_err!( - tcx.sess, - tcx.def_span(*associated_def_id), - E0714, - "marker traits cannot have associated items", - ) - .emit(); - } - } - - enter_wf_checking_ctxt(tcx, item.span, item.def_id, |wfcx| { - check_where_clauses(wfcx, item.span, item.def_id) - }); - - // Only check traits, don't check trait aliases - if let hir::ItemKind::Trait(_, _, _, _, items) = item.kind { - check_gat_where_clauses(tcx, items); - } -} - -/// Checks all associated type defaults of trait `trait_def_id`. -/// -/// Assuming the defaults are used, check that all predicates (bounds on the -/// assoc type and where clauses on the trait) hold. -fn check_associated_type_bounds(wfcx: &WfCheckingCtxt<'_, '_>, item: &ty::AssocItem, span: Span) { - let bounds = wfcx.tcx().explicit_item_bounds(item.def_id); - - debug!("check_associated_type_bounds: bounds={:?}", bounds); - 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); -} - -fn check_item_fn( - tcx: TyCtxt<'_>, - def_id: LocalDefId, - ident: Ident, - span: Span, - decl: &hir::FnDecl<'_>, -) { - enter_wf_checking_ctxt(tcx, span, def_id, |wfcx| { - let sig = tcx.fn_sig(def_id); - check_fn_or_method(wfcx, ident.span, sig, decl, def_id); - }) -} - -fn check_item_type(tcx: TyCtxt<'_>, item_id: LocalDefId, ty_span: Span, allow_foreign_ty: bool) { - debug!("check_item_type: {:?}", item_id); - - enter_wf_checking_ctxt(tcx, ty_span, item_id, |wfcx| { - let ty = tcx.type_of(item_id); - let item_ty = wfcx.normalize(ty_span, Some(WellFormedLoc::Ty(item_id)), ty); - - let mut forbid_unsized = true; - if allow_foreign_ty { - let tail = tcx.struct_tail_erasing_lifetimes(item_ty, wfcx.param_env); - if let ty::Foreign(_) = tail.kind() { - forbid_unsized = false; - } - } - - wfcx.register_wf_obligation(ty_span, Some(WellFormedLoc::Ty(item_id)), item_ty.into()); - if forbid_unsized { - wfcx.register_bound( - traits::ObligationCause::new(ty_span, wfcx.body_id, traits::WellFormed(None)), - wfcx.param_env, - item_ty, - tcx.require_lang_item(LangItem::Sized, None), - ); - } - - // Ensure that the end result is `Sync` in a non-thread local `static`. - let should_check_for_sync = tcx.static_mutability(item_id.to_def_id()) - == Some(hir::Mutability::Not) - && !tcx.is_foreign_item(item_id.to_def_id()) - && !tcx.is_thread_local_static(item_id.to_def_id()); - - if should_check_for_sync { - wfcx.register_bound( - traits::ObligationCause::new(ty_span, wfcx.body_id, traits::SharedStatic), - wfcx.param_env, - item_ty, - tcx.require_lang_item(LangItem::Sync, Some(ty_span)), - ); - } - }); -} - -#[instrument(level = "debug", skip(tcx, ast_self_ty, ast_trait_ref))] -fn check_impl<'tcx>( - tcx: TyCtxt<'tcx>, - item: &'tcx hir::Item<'tcx>, - ast_self_ty: &hir::Ty<'_>, - ast_trait_ref: &Option<hir::TraitRef<'_>>, - constness: hir::Constness, -) { - enter_wf_checking_ctxt(tcx, item.span, item.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 = wfcx.normalize(ast_trait_ref.path.span, None, trait_ref); - let trait_pred = ty::TraitPredicate { - trait_ref, - constness: match constness { - hir::Constness::Const => ty::BoundConstness::ConstIfConst, - hir::Constness::NotConst => ty::BoundConstness::NotConst, - }, - polarity: ty::ImplPolarity::Positive, - }; - let obligations = traits::wf::trait_obligations( - wfcx.infcx, - wfcx.param_env, - wfcx.body_id, - &trait_pred, - ast_trait_ref.path.span, - item, - ); - debug!(?obligations); - wfcx.register_obligations(obligations); - } - None => { - let self_ty = tcx.type_of(item.def_id); - let self_ty = wfcx.normalize( - item.span, - Some(WellFormedLoc::Ty(item.hir_id().expect_owner())), - self_ty, - ); - wfcx.register_wf_obligation( - ast_self_ty.span, - Some(WellFormedLoc::Ty(item.hir_id().expect_owner())), - self_ty.into(), - ); - } - } - - check_where_clauses(wfcx, item.span, item.def_id); - }); -} - -/// Checks where-clauses and inline bounds that are declared on `def_id`. -#[instrument(level = "debug", skip(wfcx))] -fn check_where_clauses<'tcx>(wfcx: &WfCheckingCtxt<'_, 'tcx>, span: Span, def_id: LocalDefId) { - let infcx = wfcx.infcx; - let tcx = wfcx.tcx(); - - let predicates = tcx.bound_predicates_of(def_id.to_def_id()); - let generics = tcx.generics_of(def_id); - - let is_our_default = |def: &ty::GenericParamDef| match def.kind { - GenericParamDefKind::Type { has_default, .. } - | GenericParamDefKind::Const { has_default } => { - has_default && def.index >= generics.parent_count as u32 - } - GenericParamDefKind::Lifetime => unreachable!(), - }; - - // Check that concrete defaults are well-formed. See test `type-check-defaults.rs`. - // For example, this forbids the declaration: - // - // struct Foo<T = Vec<[u32]>> { .. } - // - // Here, the default `Vec<[u32]>` is not WF because `[u32]: Sized` does not hold. - for param in &generics.params { - match param.kind { - GenericParamDefKind::Type { .. } => { - if is_our_default(param) { - let ty = tcx.type_of(param.def_id); - // Ignore dependent defaults -- that is, where the default of one type - // 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), - Some(WellFormedLoc::Ty(param.def_id.expect_local())), - ty.into(), - ); - } - } - } - GenericParamDefKind::Const { .. } => { - if is_our_default(param) { - // FIXME(const_generics_defaults): This - // is incorrect when dealing with unused substs, for example - // for `struct Foo<const N: usize, const M: usize = { 1 - 2 }>` - // we should eagerly error. - let default_ct = tcx.const_param_default(param.def_id); - if !default_ct.needs_subst() { - wfcx.register_wf_obligation( - tcx.def_span(param.def_id), - None, - default_ct.into(), - ); - } - } - } - // Doesn't have defaults. - GenericParamDefKind::Lifetime => {} - } - } - - // Check that trait predicates are WF when params are substituted by their defaults. - // We don't want to overly constrain the predicates that may be written but we want to - // catch cases where a default my never be applied such as `struct Foo<T: Copy = String>`. - // Therefore we check if a predicate which contains a single type param - // with a concrete default is WF with that default substituted. - // For more examples see tests `defaults-well-formedness.rs` and `type-check-defaults.rs`. - // - // First we build the defaulted substitution. - let substs = InternalSubsts::for_item(tcx, def_id.to_def_id(), |param, _| { - match param.kind { - GenericParamDefKind::Lifetime => { - // All regions are identity. - tcx.mk_param_from_def(param) - } - - GenericParamDefKind::Type { .. } => { - // If the param has a default, ... - if is_our_default(param) { - let default_ty = tcx.type_of(param.def_id); - // ... and it's not a dependent default, ... - if !default_ty.needs_subst() { - // ... then substitute it with the default. - return default_ty.into(); - } - } - - tcx.mk_param_from_def(param) - } - GenericParamDefKind::Const { .. } => { - // If the param has a default, ... - if is_our_default(param) { - let default_ct = tcx.const_param_default(param.def_id); - // ... and it's not a dependent default, ... - if !default_ct.needs_subst() { - // ... then substitute it with the default. - return default_ct.into(); - } - } - - tcx.mk_param_from_def(param) - } - } - }); - - // Now we build the substituted predicates. - let default_obligations = predicates - .0 - .predicates - .iter() - .flat_map(|&(pred, sp)| { - #[derive(Default)] - struct CountParams { - params: FxHashSet<u32>, - } - impl<'tcx> ty::visit::TypeVisitor<'tcx> for CountParams { - type BreakTy = (); - - fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { - if let ty::Param(param) = t.kind() { - self.params.insert(param.index); - } - t.super_visit_with(self) - } - - fn visit_region(&mut self, _: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> { - ControlFlow::BREAK - } - - fn visit_const(&mut self, c: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> { - if let ty::ConstKind::Param(param) = c.kind() { - self.params.insert(param.index); - } - c.super_visit_with(self) - } - } - let mut param_count = CountParams::default(); - let has_region = pred.visit_with(&mut param_count).is_break(); - 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 - { - None - } else if predicates.0.predicates.iter().any(|&(p, _)| p == substituted_pred) { - // Avoid duplication of predicates that contain no parameters, for example. - None - } else { - Some((substituted_pred, sp)) - } - }) - .map(|(pred, sp)| { - // Convert each of those into an obligation. So if you have - // something like `struct Foo<T: Copy = String>`, we would - // take that predicate `T: Copy`, substitute to `String: Copy` - // (actually that happens in the previous `flat_map` call), - // and then try to prove it (in this case, we'll fail). - // - // Note the subtle difference from how we handle `predicates` - // below: there, we are not trying to prove those predicates - // to be *true* but merely *well-formed*. - let pred = wfcx.normalize(sp, None, pred); - let cause = traits::ObligationCause::new( - sp, - wfcx.body_id, - traits::ItemObligation(def_id.to_def_id()), - ); - traits::Obligation::new(cause, wfcx.param_env, pred) - }); - - let predicates = predicates.0.instantiate_identity(tcx); - - let predicates = wfcx.normalize(span, None, predicates); - - debug!(?predicates.predicates); - 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.without_const(), - wfcx.body_id, - p, - sp, - ) - }); - - let obligations: Vec<_> = wf_obligations.chain(default_obligations).collect(); - wfcx.register_obligations(obligations); -} - -#[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, -) { - let tcx = wfcx.tcx(); - let sig = tcx.liberate_late_bound_regions(def_id.to_def_id(), sig); - - // Normalize the input and output types one at a time, using a different - // `WellFormedLoc` for each. We cannot call `normalize_associated_types` - // on the entire `FnSig`, since this would use the same `WellFormedLoc` - // for each type, preventing the HIR wf check from generating - // a nice error message. - let ty::FnSig { mut inputs_and_output, c_variadic, unsafety, abi } = sig; - inputs_and_output = tcx.mk_type_list(inputs_and_output.iter().enumerate().map(|(i, ty)| { - wfcx.normalize( - span, - Some(WellFormedLoc::Param { - function: def_id, - // Note that the `param_idx` of the output type is - // one greater than the index of the last input type. - param_idx: i.try_into().unwrap(), - }), - ty, - ) - })); - // Manually call `normalize_associated_types_in` on the other types - // in `FnSig`. This ensures that if the types of these fields - // ever change to include projections, we will start normalizing - // them automatically. - let sig = ty::FnSig { - inputs_and_output, - c_variadic: wfcx.normalize(span, None, c_variadic), - unsafety: wfcx.normalize(span, None, unsafety), - abi: wfcx.normalize(span, None, abi), - }; - - for (i, (&input_ty, ty)) in iter::zip(sig.inputs(), hir_decl.inputs).enumerate() { - wfcx.register_wf_obligation( - ty.span, - Some(WellFormedLoc::Param { function: def_id, param_idx: i.try_into().unwrap() }), - input_ty.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(), - ); - - check_where_clauses(wfcx, span, def_id); - - check_return_position_impl_trait_in_trait_bounds( - tcx, - wfcx, - def_id, - sig.output(), - hir_decl.output.span(), - ); -} - -/// 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`)"; - -#[instrument(level = "debug", skip(wfcx))] -fn check_method_receiver<'tcx>( - wfcx: &WfCheckingCtxt<'_, 'tcx>, - fn_sig: &hir::FnSig<'_>, - method: &ty::AssocItem, - self_ty: Ty<'tcx>, -) { - let tcx = wfcx.tcx(); - - if !method.fn_has_self_parameter { - return; - } - - let span = fn_sig.decl.inputs[0].span; - - let sig = tcx.fn_sig(method.def_id); - let sig = tcx.liberate_late_bound_regions(method.def_id, sig); - let sig = wfcx.normalize(span, None, sig); - - debug!("check_method_receiver: sig={:?}", sig); - - let self_ty = wfcx.normalize(span, None, self_ty); - - let receiver_ty = sig.inputs()[0]; - let receiver_ty = wfcx.normalize(span, None, receiver_ty); - - if tcx.features().arbitrary_self_types { - if !receiver_is_valid(wfcx, span, receiver_ty, self_ty, true) { - // Report error; `arbitrary_self_types` was enabled. - e0307(tcx, span, receiver_ty); - } - } else { - if !receiver_is_valid(wfcx, span, receiver_ty, self_ty, false) { - if receiver_is_valid(wfcx, span, receiver_ty, self_ty, true) { - // Report error; would have worked with `arbitrary_self_types`. - feature_err( - &tcx.sess.parse_sess, - sym::arbitrary_self_types, - span, - &format!( - "`{receiver_ty}` cannot be used as the type of `self` without \ - the `arbitrary_self_types` feature", - ), - ) - .help(HELP_FOR_SELF_TYPE) - .emit(); - } else { - // Report error; would not have worked with `arbitrary_self_types`. - e0307(tcx, span, receiver_ty); - } - } - } -} - -fn e0307<'tcx>(tcx: TyCtxt<'tcx>, span: Span, receiver_ty: Ty<'_>) { - struct_span_err!( - tcx.sess.diagnostic(), - span, - E0307, - "invalid `self` parameter type: {receiver_ty}" - ) - .note("type of `self` must be `Self` or a type that dereferences to it") - .help(HELP_FOR_SELF_TYPE) - .emit(); -} - -/// Returns whether `receiver_ty` would be considered a valid receiver type for `self_ty`. If -/// `arbitrary_self_types` is enabled, `receiver_ty` must transitively deref to `self_ty`, possibly -/// through a `*const/mut T` raw pointer. If the feature is not enabled, the requirements are more -/// strict: `receiver_ty` must implement `Receiver` and directly implement -/// `Deref<Target = self_ty>`. -/// -/// N.B., there are cases this function returns `true` but causes an error to be emitted, -/// particularly when `receiver_ty` derefs to a type that is the same as `self_ty` but has the -/// wrong lifetime. Be careful of this if you are calling this function speculatively. -fn receiver_is_valid<'tcx>( - wfcx: &WfCheckingCtxt<'_, 'tcx>, - span: Span, - receiver_ty: Ty<'tcx>, - self_ty: Ty<'tcx>, - arbitrary_self_types_enabled: bool, -) -> bool { - let infcx = wfcx.infcx; - let tcx = wfcx.tcx(); - let cause = - ObligationCause::new(span, wfcx.body_id, traits::ObligationCauseCode::MethodReceiver); - - let can_eq_self = |ty| infcx.can_eq(wfcx.param_env, self_ty, ty).is_ok(); - - // `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(); - } - return true; - } - - let mut autoderef = - Autoderef::new(infcx, wfcx.param_env, wfcx.body_id, span, receiver_ty, span); - - // The `arbitrary_self_types` feature allows raw pointer receivers like `self: *const Self`. - if arbitrary_self_types_enabled { - autoderef = autoderef.include_raw_pointers(); - } - - // The first type is `receiver_ty`, which we know its not equal to `self_ty`; skip it. - autoderef.next(); - - let receiver_trait_def_id = tcx.require_lang_item(LangItem::Receiver, None); - - // Keep dereferencing `receiver_ty` until we get to `self_ty`. - loop { - if let Some((potential_self_ty, _)) = autoderef.next() { - debug!( - "receiver_is_valid: potential self type `{:?}` to match `{:?}`", - potential_self_ty, self_ty - ); - - if can_eq_self(potential_self_ty) { - wfcx.register_obligations(autoderef.into_obligations()); - - 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(); - } - - break; - } else { - // Without `feature(arbitrary_self_types)`, we require that each step in the - // deref chain implement `receiver` - if !arbitrary_self_types_enabled - && !receiver_is_implemented( - wfcx, - receiver_trait_def_id, - cause.clone(), - potential_self_ty, - ) - { - return false; - } - } - } else { - debug!("receiver_is_valid: type `{:?}` does not deref to `{:?}`", receiver_ty, self_ty); - // If the receiver already has errors reported due to it, consider it valid to avoid - // unnecessary errors (#58712). - return receiver_ty.references_error(); - } - } - - // Without `feature(arbitrary_self_types)`, we require that `receiver_ty` implements `Receiver`. - if !arbitrary_self_types_enabled - && !receiver_is_implemented(wfcx, receiver_trait_def_id, cause.clone(), receiver_ty) - { - return false; - } - - true -} - -fn receiver_is_implemented<'tcx>( - wfcx: &WfCheckingCtxt<'_, 'tcx>, - receiver_trait_def_id: DefId, - cause: ObligationCause<'tcx>, - receiver_ty: Ty<'tcx>, -) -> bool { - let tcx = wfcx.tcx(); - let trait_ref = ty::Binder::dummy(ty::TraitRef { - def_id: receiver_trait_def_id, - substs: tcx.mk_substs_trait(receiver_ty, &[]), - }); - - let obligation = - traits::Obligation::new(cause, wfcx.param_env, trait_ref.without_const().to_predicate(tcx)); - - if wfcx.infcx.predicate_must_hold_modulo_regions(&obligation) { - true - } else { - debug!( - "receiver_is_implemented: type `{:?}` does not implement `Receiver` trait", - receiver_ty - ); - false - } -} - -fn check_variances_for_type_defn<'tcx>( - tcx: TyCtxt<'tcx>, - item: &hir::Item<'tcx>, - hir_generics: &hir::Generics<'_>, -) { - let ty = tcx.type_of(item.def_id); - if tcx.has_error_field(ty) { - return; - } - - let ty_predicates = tcx.predicates_of(item.def_id); - assert_eq!(ty_predicates.parent, None); - let variances = tcx.variances_of(item.def_id); - - let mut constrained_parameters: FxHashSet<_> = variances - .iter() - .enumerate() - .filter(|&(_, &variance)| variance != ty::Bivariant) - .map(|(index, _)| Parameter(index as u32)) - .collect(); - - identify_constrained_generic_params(tcx, ty_predicates, None, &mut constrained_parameters); - - // 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()); - hir_generics - .predicates - .iter() - .filter_map(|predicate| match predicate { - hir::WherePredicate::BoundPredicate(predicate) => { - match icx.to_ty(predicate.bounded_ty).kind() { - ty::Param(data) => Some(Parameter(data.index)), - _ => None, - } - } - _ => None, - }) - .collect::<FxHashSet<_>>() - }); - - for (index, _) in variances.iter().enumerate() { - let parameter = Parameter(index as u32); - - if constrained_parameters.contains(¶meter) { - continue; - } - - let param = &hir_generics.params[index]; - - match param.name { - hir::ParamName::Error => {} - _ => { - let has_explicit_bounds = explicitly_bounded_params.contains(¶meter); - report_bivariance(tcx, param, has_explicit_bounds); - } - } - } -} - -fn report_bivariance( - tcx: TyCtxt<'_>, - param: &rustc_hir::GenericParam<'_>, - has_explicit_bounds: bool, -) -> ErrorGuaranteed { - let span = param.span; - let param_name = param.name.ident().name; - let mut err = error_392(tcx, span, param_name); - - let suggested_marker_id = tcx.lang_items().phantom_data(); - // Help is available only in presence of lang items. - let msg = if let Some(def_id) = suggested_marker_id { - format!( - "consider removing `{}`, referring to it in a field, or using a marker such as `{}`", - param_name, - tcx.def_path_str(def_id), - ) - } else { - format!("consider removing `{param_name}` or referring to it in a field") - }; - err.help(&msg); - - if matches!(param.kind, hir::GenericParamKind::Type { .. }) && !has_explicit_bounds { - err.help(&format!( - "if you intended `{0}` to be a const parameter, use `const {0}: usize` instead", - param_name - )); - } - err.emit() -} - -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; - let empty_env = ty::ParamEnv::empty(); - - let def_id = tcx.hir().local_def_id(self.body_id); - let predicates_with_span = tcx.predicates_of(def_id).predicates.iter().copied(); - // Check elaborated bounds. - let implied_obligations = traits::elaborate_predicates_with_span(tcx, predicates_with_span); - - for obligation in implied_obligations { - // We lower empty bounds like `Vec<dyn Copy>:` as - // `WellFormed(Vec<dyn Copy>)`, which will later get checked by - // regular WF checking - if let ty::PredicateKind::WellFormed(..) = obligation.predicate.kind().skip_binder() { - continue; - } - let pred = obligation.predicate; - // Match the existing behavior. - if pred.is_global() && !pred.has_late_bound_regions() { - let pred = self.normalize(span, None, pred); - let hir_node = tcx.hir().find(self.body_id); - - // only use the span of the predicate clause (#90869) - - if let Some(hir::Generics { predicates, .. }) = - hir_node.and_then(|node| node.generics()) - { - let obligation_span = obligation.cause.span(); - - span = predicates - .iter() - // There seems to be no better way to find out which predicate we are in - .find(|pred| pred.span().contains(obligation_span)) - .map(|pred| pred.span()) - .unwrap_or(obligation_span); - } - - let obligation = traits::Obligation::new( - traits::ObligationCause::new(span, self.body_id, traits::TrivialBound), - empty_env, - pred, - ); - self.ocx.register_obligation(obligation); - } - } - } -} - -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)); -} - -/////////////////////////////////////////////////////////////////////////// -// ADT - -// FIXME(eddyb) replace this with getting fields/discriminants through `ty::AdtDef`. -struct AdtVariant<'tcx> { - /// Types of fields in the variant, that must be well-formed. - fields: Vec<AdtField<'tcx>>, - - /// Explicit discriminant of this variant (e.g. `A = 123`), - /// that must evaluate to a constant value. - explicit_discr: Option<LocalDefId>, -} - -struct AdtField<'tcx> { - ty: Ty<'tcx>, - def_id: LocalDefId, - span: Span, -} - -impl<'a, 'tcx> WfCheckingCtxt<'a, 'tcx> { - // FIXME(eddyb) replace this with getting fields through `ty::AdtDef`. - fn non_enum_variant(&self, struct_def: &hir::VariantData<'_>) -> AdtVariant<'tcx> { - let fields = struct_def - .fields() - .iter() - .map(|field| { - let def_id = self.tcx().hir().local_def_id(field.hir_id); - let field_ty = self.tcx().type_of(def_id); - let field_ty = self.normalize(field.ty.span, None, field_ty); - debug!("non_enum_variant: type of field {:?} is {:?}", field, field_ty); - AdtField { ty: field_ty, span: field.ty.span, def_id } - }) - .collect(); - AdtVariant { fields, explicit_discr: None } - } - - fn enum_variants(&self, enum_def: &hir::EnumDef<'_>) -> Vec<AdtVariant<'tcx>> { - enum_def - .variants - .iter() - .map(|variant| AdtVariant { - fields: self.non_enum_variant(&variant.data).fields, - explicit_discr: variant - .disr_expr - .map(|explicit_discr| self.tcx().hir().local_def_id(explicit_discr.hir_id)), - }) - .collect() - } -} - -fn error_392( - tcx: TyCtxt<'_>, - span: Span, - param_name: Symbol, -) -> DiagnosticBuilder<'_, ErrorGuaranteed> { - let mut err = struct_span_err!(tcx.sess, span, E0392, "parameter `{param_name}` is never used"); - err.span_label(span, "unused parameter"); - err -} - -pub fn provide(providers: &mut Providers) { - *providers = Providers { check_mod_type_wf, check_well_formed, ..*providers }; -} diff --git a/compiler/rustc_typeck/src/check/writeback.rs b/compiler/rustc_typeck/src/check/writeback.rs deleted file mode 100644 index 9ecf34e9a..000000000 --- a/compiler/rustc_typeck/src/check/writeback.rs +++ /dev/null @@ -1,816 +0,0 @@ -// Type resolution: the phase that finds all the types in the AST with -// unresolved type variables and replaces "ty_var" types with their -// substitutions. - -use crate::check::FnCtxt; -use hir::def_id::LocalDefId; -use rustc_data_structures::fx::FxHashMap; -use rustc_errors::ErrorGuaranteed; -use rustc_hir as hir; -use rustc_hir::intravisit::{self, Visitor}; -use rustc_infer::infer::error_reporting::TypeAnnotationNeeded::E0282; -use rustc_infer::infer::InferCtxt; -use rustc_middle::hir::place::Place as HirPlace; -use rustc_middle::mir::FakeReadCause; -use rustc_middle::ty::adjustment::{Adjust, Adjustment, PointerCast}; -use rustc_middle::ty::fold::{TypeFoldable, TypeFolder, TypeSuperFoldable}; -use rustc_middle::ty::visit::{TypeSuperVisitable, TypeVisitable}; -use rustc_middle::ty::TypeckResults; -use rustc_middle::ty::{self, ClosureSizeProfileData, Ty, TyCtxt}; -use rustc_span::symbol::sym; -use rustc_span::Span; - -use std::mem; -use std::ops::ControlFlow; - -/////////////////////////////////////////////////////////////////////////// -// Entry point - -// During type inference, partially inferred types are -// represented using Type variables (ty::Infer). These don't appear in -// the final TypeckResults since all of the types should have been -// inferred once typeck is done. -// When type inference is running however, having to update the typeck -// typeck results every time a new type is inferred would be unreasonably slow, -// so instead all of the replacement happens at the end in -// resolve_type_vars_in_body, which creates a new TypeTables which -// doesn't contain any inference types. -impl<'a, 'tcx> FnCtxt<'a, 'tcx> { - pub fn resolve_type_vars_in_body( - &self, - body: &'tcx hir::Body<'tcx>, - ) -> &'tcx ty::TypeckResults<'tcx> { - let item_id = self.tcx.hir().body_owner(body.id()); - let item_def_id = self.tcx.hir().local_def_id(item_id); - - // This attribute causes us to dump some writeback information - // in the form of errors, which is used for unit tests. - let rustc_dump_user_substs = - self.tcx.has_attr(item_def_id.to_def_id(), sym::rustc_dump_user_substs); - - let mut wbcx = WritebackCx::new(self, body, rustc_dump_user_substs); - for param in body.params { - wbcx.visit_node_id(param.pat.span, param.hir_id); - } - // Type only exists for constants and statics, not functions. - match self.tcx.hir().body_owner_kind(item_def_id) { - hir::BodyOwnerKind::Const | hir::BodyOwnerKind::Static(_) => { - wbcx.visit_node_id(body.value.span, item_id); - } - hir::BodyOwnerKind::Closure | hir::BodyOwnerKind::Fn => (), - } - wbcx.visit_body(body); - wbcx.visit_min_capture_map(); - wbcx.eval_closure_size(); - wbcx.visit_fake_reads_map(); - wbcx.visit_closures(); - wbcx.visit_liberated_fn_sigs(); - wbcx.visit_fru_field_types(); - wbcx.visit_opaque_types(); - wbcx.visit_coercion_casts(); - wbcx.visit_user_provided_tys(); - wbcx.visit_user_provided_sigs(); - wbcx.visit_generator_interior_types(); - - wbcx.typeck_results.rvalue_scopes = - mem::take(&mut self.typeck_results.borrow_mut().rvalue_scopes); - - let used_trait_imports = - mem::take(&mut self.typeck_results.borrow_mut().used_trait_imports); - debug!("used_trait_imports({:?}) = {:?}", item_def_id, used_trait_imports); - wbcx.typeck_results.used_trait_imports = used_trait_imports; - - wbcx.typeck_results.treat_byte_string_as_slice = - mem::take(&mut self.typeck_results.borrow_mut().treat_byte_string_as_slice); - - if self.is_tainted_by_errors() { - // FIXME(eddyb) keep track of `ErrorGuaranteed` from where the error was emitted. - wbcx.typeck_results.tainted_by_errors = - Some(ErrorGuaranteed::unchecked_claim_error_was_emitted()); - } - - debug!("writeback: typeck results for {:?} are {:#?}", item_def_id, wbcx.typeck_results); - - self.tcx.arena.alloc(wbcx.typeck_results) - } -} - -/////////////////////////////////////////////////////////////////////////// -// The Writeback context. This visitor walks the HIR, checking the -// fn-specific typeck results to find references to types or regions. It -// resolves those regions to remove inference variables and writes the -// final result back into the master typeck results in the tcx. Here and -// there, it applies a few ad-hoc checks that were not convenient to -// do elsewhere. - -struct WritebackCx<'cx, 'tcx> { - fcx: &'cx FnCtxt<'cx, 'tcx>, - - typeck_results: ty::TypeckResults<'tcx>, - - body: &'tcx hir::Body<'tcx>, - - rustc_dump_user_substs: bool, -} - -impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { - fn new( - fcx: &'cx FnCtxt<'cx, 'tcx>, - body: &'tcx hir::Body<'tcx>, - rustc_dump_user_substs: bool, - ) -> WritebackCx<'cx, 'tcx> { - let owner = body.id().hir_id.owner; - - WritebackCx { - fcx, - typeck_results: ty::TypeckResults::new(owner), - body, - rustc_dump_user_substs, - } - } - - fn tcx(&self) -> TyCtxt<'tcx> { - self.fcx.tcx - } - - fn write_ty_to_typeck_results(&mut self, hir_id: hir::HirId, ty: Ty<'tcx>) { - debug!("write_ty_to_typeck_results({:?}, {:?})", hir_id, ty); - assert!(!ty.needs_infer() && !ty.has_placeholders() && !ty.has_free_regions()); - self.typeck_results.node_types_mut().insert(hir_id, ty); - } - - // Hacky hack: During type-checking, we treat *all* operators - // as potentially overloaded. But then, during writeback, if - // we observe that something like `a+b` is (known to be) - // operating on scalars, we clear the overload. - fn fix_scalar_builtin_expr(&mut self, e: &hir::Expr<'_>) { - match e.kind { - hir::ExprKind::Unary(hir::UnOp::Neg | hir::UnOp::Not, inner) => { - let inner_ty = self.fcx.node_ty(inner.hir_id); - let inner_ty = self.fcx.resolve_vars_if_possible(inner_ty); - - if inner_ty.is_scalar() { - let mut typeck_results = self.fcx.typeck_results.borrow_mut(); - typeck_results.type_dependent_defs_mut().remove(e.hir_id); - typeck_results.node_substs_mut().remove(e.hir_id); - } - } - hir::ExprKind::Binary(ref op, lhs, rhs) | hir::ExprKind::AssignOp(ref op, lhs, rhs) => { - let lhs_ty = self.fcx.node_ty(lhs.hir_id); - let lhs_ty = self.fcx.resolve_vars_if_possible(lhs_ty); - - let rhs_ty = self.fcx.node_ty(rhs.hir_id); - let rhs_ty = self.fcx.resolve_vars_if_possible(rhs_ty); - - if lhs_ty.is_scalar() && rhs_ty.is_scalar() { - let mut typeck_results = self.fcx.typeck_results.borrow_mut(); - typeck_results.type_dependent_defs_mut().remove(e.hir_id); - typeck_results.node_substs_mut().remove(e.hir_id); - - match e.kind { - hir::ExprKind::Binary(..) => { - if !op.node.is_by_value() { - let mut adjustments = typeck_results.adjustments_mut(); - if let Some(a) = adjustments.get_mut(lhs.hir_id) { - a.pop(); - } - if let Some(a) = adjustments.get_mut(rhs.hir_id) { - a.pop(); - } - } - } - hir::ExprKind::AssignOp(..) - if let Some(a) = typeck_results.adjustments_mut().get_mut(lhs.hir_id) => - { - a.pop(); - } - _ => {} - } - } - } - _ => {} - } - } - - // (ouz-a 1005988): Normally `[T] : std::ops::Index<usize>` should be normalized - // into [T] but currently `Where` clause stops the normalization process for it, - // here we compare types of expr and base in a code without `Where` clause they would be equal - // if they are not we don't modify the expr, hence we bypass the ICE - fn is_builtin_index( - &mut self, - typeck_results: &TypeckResults<'tcx>, - e: &hir::Expr<'_>, - base_ty: Ty<'tcx>, - index_ty: Ty<'tcx>, - ) -> bool { - if let Some(elem_ty) = base_ty.builtin_index() { - let Some(exp_ty) = typeck_results.expr_ty_opt(e) else {return false;}; - let resolved_exp_ty = self.resolve(exp_ty, &e.span); - - elem_ty == resolved_exp_ty && index_ty == self.fcx.tcx.types.usize - } else { - false - } - } - - // Similar to operators, indexing is always assumed to be overloaded - // Here, correct cases where an indexing expression can be simplified - // to use builtin indexing because the index type is known to be - // usize-ish - fn fix_index_builtin_expr(&mut self, e: &hir::Expr<'_>) { - if let hir::ExprKind::Index(ref base, ref index) = e.kind { - let mut typeck_results = self.fcx.typeck_results.borrow_mut(); - - // All valid indexing looks like this; might encounter non-valid indexes at this point. - let base_ty = typeck_results - .expr_ty_adjusted_opt(base) - .map(|t| self.fcx.resolve_vars_if_possible(t).kind()); - if base_ty.is_none() { - // When encountering `return [0][0]` outside of a `fn` body we can encounter a base - // that isn't in the type table. We assume more relevant errors have already been - // emitted, so we delay an ICE if none have. (#64638) - self.tcx().sess.delay_span_bug(e.span, &format!("bad base: `{:?}`", base)); - } - if let Some(ty::Ref(_, base_ty, _)) = base_ty { - let index_ty = typeck_results.expr_ty_adjusted_opt(index).unwrap_or_else(|| { - // When encountering `return [0][0]` outside of a `fn` body we would attempt - // to access an nonexistent index. We assume that more relevant errors will - // already have been emitted, so we only gate on this with an ICE if no - // error has been emitted. (#64638) - self.fcx.tcx.ty_error_with_message( - e.span, - &format!("bad index {:?} for base: `{:?}`", index, base), - ) - }); - let index_ty = self.fcx.resolve_vars_if_possible(index_ty); - let resolved_base_ty = self.resolve(*base_ty, &base.span); - - if self.is_builtin_index(&typeck_results, e, resolved_base_ty, index_ty) { - // Remove the method call record - typeck_results.type_dependent_defs_mut().remove(e.hir_id); - typeck_results.node_substs_mut().remove(e.hir_id); - - if let Some(a) = typeck_results.adjustments_mut().get_mut(base.hir_id) { - // Discard the need for a mutable borrow - - // Extra adjustment made when indexing causes a drop - // of size information - we need to get rid of it - // Since this is "after" the other adjustment to be - // discarded, we do an extra `pop()` - if let Some(Adjustment { - kind: Adjust::Pointer(PointerCast::Unsize), .. - }) = a.pop() - { - // So the borrow discard actually happens here - a.pop(); - } - } - } - } - } - } -} - -/////////////////////////////////////////////////////////////////////////// -// Impl of Visitor for Resolver -// -// This is the master code which walks the AST. It delegates most of -// the heavy lifting to the generic visit and resolve functions -// below. In general, a function is made into a `visitor` if it must -// traffic in node-ids or update typeck results in the type context etc. - -impl<'cx, 'tcx> Visitor<'tcx> for WritebackCx<'cx, 'tcx> { - fn visit_expr(&mut self, e: &'tcx hir::Expr<'tcx>) { - self.fix_scalar_builtin_expr(e); - self.fix_index_builtin_expr(e); - - match e.kind { - hir::ExprKind::Closure(&hir::Closure { body, .. }) => { - let body = self.fcx.tcx.hir().body(body); - for param in body.params { - self.visit_node_id(e.span, param.hir_id); - } - - self.visit_body(body); - } - hir::ExprKind::Struct(_, fields, _) => { - for field in fields { - self.visit_field_id(field.hir_id); - } - } - hir::ExprKind::Field(..) => { - self.visit_field_id(e.hir_id); - } - hir::ExprKind::ConstBlock(anon_const) => { - self.visit_node_id(e.span, anon_const.hir_id); - - let body = self.tcx().hir().body(anon_const.body); - self.visit_body(body); - } - _ => {} - } - - self.visit_node_id(e.span, e.hir_id); - intravisit::walk_expr(self, e); - } - - fn visit_generic_param(&mut self, p: &'tcx hir::GenericParam<'tcx>) { - match &p.kind { - hir::GenericParamKind::Lifetime { .. } => { - // Nothing to write back here - } - hir::GenericParamKind::Type { .. } | hir::GenericParamKind::Const { .. } => { - self.tcx().sess.delay_span_bug(p.span, format!("unexpected generic param: {p:?}")); - } - } - } - - fn visit_block(&mut self, b: &'tcx hir::Block<'tcx>) { - self.visit_node_id(b.span, b.hir_id); - intravisit::walk_block(self, b); - } - - fn visit_pat(&mut self, p: &'tcx hir::Pat<'tcx>) { - match p.kind { - hir::PatKind::Binding(..) => { - let typeck_results = self.fcx.typeck_results.borrow(); - if let Some(bm) = - typeck_results.extract_binding_mode(self.tcx().sess, p.hir_id, p.span) - { - self.typeck_results.pat_binding_modes_mut().insert(p.hir_id, bm); - } - } - hir::PatKind::Struct(_, fields, _) => { - for field in fields { - self.visit_field_id(field.hir_id); - } - } - _ => {} - }; - - self.visit_pat_adjustments(p.span, p.hir_id); - - self.visit_node_id(p.span, p.hir_id); - intravisit::walk_pat(self, p); - } - - fn visit_local(&mut self, l: &'tcx hir::Local<'tcx>) { - intravisit::walk_local(self, l); - let var_ty = self.fcx.local_ty(l.span, l.hir_id).decl_ty; - let var_ty = self.resolve(var_ty, &l.span); - self.write_ty_to_typeck_results(l.hir_id, var_ty); - } - - fn visit_ty(&mut self, hir_ty: &'tcx hir::Ty<'tcx>) { - intravisit::walk_ty(self, hir_ty); - let ty = self.fcx.node_ty(hir_ty.hir_id); - let ty = self.resolve(ty, &hir_ty.span); - self.write_ty_to_typeck_results(hir_ty.hir_id, ty); - } - - fn visit_infer(&mut self, inf: &'tcx hir::InferArg) { - intravisit::walk_inf(self, inf); - // Ignore cases where the inference is a const. - if let Some(ty) = self.fcx.node_ty_opt(inf.hir_id) { - let ty = self.resolve(ty, &inf.span); - self.write_ty_to_typeck_results(inf.hir_id, ty); - } - } -} - -impl<'cx, 'tcx> WritebackCx<'cx, 'tcx> { - fn eval_closure_size(&mut self) { - let mut res: FxHashMap<LocalDefId, ClosureSizeProfileData<'tcx>> = Default::default(); - for (&closure_def_id, data) in self.fcx.typeck_results.borrow().closure_size_eval.iter() { - let closure_hir_id = self.tcx().hir().local_def_id_to_hir_id(closure_def_id); - - let data = self.resolve(*data, &closure_hir_id); - - res.insert(closure_def_id, data); - } - - self.typeck_results.closure_size_eval = res; - } - fn visit_min_capture_map(&mut self) { - let mut min_captures_wb = ty::MinCaptureInformationMap::with_capacity_and_hasher( - self.fcx.typeck_results.borrow().closure_min_captures.len(), - Default::default(), - ); - for (&closure_def_id, root_min_captures) in - self.fcx.typeck_results.borrow().closure_min_captures.iter() - { - let mut root_var_map_wb = ty::RootVariableMinCaptureList::with_capacity_and_hasher( - root_min_captures.len(), - Default::default(), - ); - for (var_hir_id, min_list) in root_min_captures.iter() { - let min_list_wb = min_list - .iter() - .map(|captured_place| { - let locatable = captured_place.info.path_expr_id.unwrap_or_else(|| { - self.tcx().hir().local_def_id_to_hir_id(closure_def_id) - }); - - self.resolve(captured_place.clone(), &locatable) - }) - .collect(); - root_var_map_wb.insert(*var_hir_id, min_list_wb); - } - min_captures_wb.insert(closure_def_id, root_var_map_wb); - } - - self.typeck_results.closure_min_captures = min_captures_wb; - } - - fn visit_fake_reads_map(&mut self) { - let mut resolved_closure_fake_reads: FxHashMap< - LocalDefId, - Vec<(HirPlace<'tcx>, FakeReadCause, hir::HirId)>, - > = Default::default(); - for (&closure_def_id, fake_reads) in - self.fcx.typeck_results.borrow().closure_fake_reads.iter() - { - let mut resolved_fake_reads = Vec::<(HirPlace<'tcx>, FakeReadCause, hir::HirId)>::new(); - for (place, cause, hir_id) in fake_reads.iter() { - let locatable = self.tcx().hir().local_def_id_to_hir_id(closure_def_id); - - let resolved_fake_read = self.resolve(place.clone(), &locatable); - resolved_fake_reads.push((resolved_fake_read, *cause, *hir_id)); - } - resolved_closure_fake_reads.insert(closure_def_id, resolved_fake_reads); - } - self.typeck_results.closure_fake_reads = resolved_closure_fake_reads; - } - - fn visit_closures(&mut self) { - let fcx_typeck_results = self.fcx.typeck_results.borrow(); - assert_eq!(fcx_typeck_results.hir_owner, self.typeck_results.hir_owner); - let common_hir_owner = fcx_typeck_results.hir_owner; - - for (id, origin) in fcx_typeck_results.closure_kind_origins().iter() { - let hir_id = hir::HirId { owner: common_hir_owner, local_id: *id }; - let place_span = origin.0; - let place = self.resolve(origin.1.clone(), &place_span); - self.typeck_results.closure_kind_origins_mut().insert(hir_id, (place_span, place)); - } - } - - fn visit_coercion_casts(&mut self) { - let fcx_typeck_results = self.fcx.typeck_results.borrow(); - let fcx_coercion_casts = fcx_typeck_results.coercion_casts(); - assert_eq!(fcx_typeck_results.hir_owner, self.typeck_results.hir_owner); - - for local_id in fcx_coercion_casts { - self.typeck_results.set_coercion_cast(*local_id); - } - } - - fn visit_user_provided_tys(&mut self) { - let fcx_typeck_results = self.fcx.typeck_results.borrow(); - assert_eq!(fcx_typeck_results.hir_owner, self.typeck_results.hir_owner); - let common_hir_owner = fcx_typeck_results.hir_owner; - - let mut errors_buffer = Vec::new(); - for (&local_id, c_ty) in fcx_typeck_results.user_provided_types().iter() { - let hir_id = hir::HirId { owner: common_hir_owner, local_id }; - - if cfg!(debug_assertions) && c_ty.needs_infer() { - span_bug!( - hir_id.to_span(self.fcx.tcx), - "writeback: `{:?}` has inference variables", - c_ty - ); - }; - - self.typeck_results.user_provided_types_mut().insert(hir_id, *c_ty); - - if let ty::UserType::TypeOf(_, user_substs) = c_ty.value { - if self.rustc_dump_user_substs { - // This is a unit-testing mechanism. - let span = self.tcx().hir().span(hir_id); - // We need to buffer the errors in order to guarantee a consistent - // order when emitting them. - let err = self - .tcx() - .sess - .struct_span_err(span, &format!("user substs: {:?}", user_substs)); - err.buffer(&mut errors_buffer); - } - } - } - - if !errors_buffer.is_empty() { - errors_buffer.sort_by_key(|diag| diag.span.primary_span()); - for mut diag in errors_buffer { - self.tcx().sess.diagnostic().emit_diagnostic(&mut diag); - } - } - } - - fn visit_user_provided_sigs(&mut self) { - let fcx_typeck_results = self.fcx.typeck_results.borrow(); - assert_eq!(fcx_typeck_results.hir_owner, self.typeck_results.hir_owner); - - for (&def_id, c_sig) in fcx_typeck_results.user_provided_sigs.iter() { - if cfg!(debug_assertions) && c_sig.needs_infer() { - span_bug!( - self.fcx.tcx.hir().span_if_local(def_id).unwrap(), - "writeback: `{:?}` has inference variables", - c_sig - ); - }; - - self.typeck_results.user_provided_sigs.insert(def_id, *c_sig); - } - } - - fn visit_generator_interior_types(&mut self) { - let fcx_typeck_results = self.fcx.typeck_results.borrow(); - assert_eq!(fcx_typeck_results.hir_owner, self.typeck_results.hir_owner); - self.typeck_results.generator_interior_types = - fcx_typeck_results.generator_interior_types.clone(); - } - - #[instrument(skip(self), level = "debug")] - fn visit_opaque_types(&mut self) { - let opaque_types = - self.fcx.infcx.inner.borrow_mut().opaque_type_storage.take_opaque_types(); - for (opaque_type_key, decl) in opaque_types { - let hidden_type = match decl.origin { - hir::OpaqueTyOrigin::FnReturn(_) | hir::OpaqueTyOrigin::AsyncFn(_) => { - let ty = self.resolve(decl.hidden_type.ty, &decl.hidden_type.span); - struct RecursionChecker { - def_id: LocalDefId, - } - impl<'tcx> ty::TypeVisitor<'tcx> for RecursionChecker { - type BreakTy = (); - fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { - if let ty::Opaque(def_id, _) = *t.kind() { - if def_id == self.def_id.to_def_id() { - return ControlFlow::Break(()); - } - } - t.super_visit_with(self) - } - } - if ty - .visit_with(&mut RecursionChecker { def_id: opaque_type_key.def_id }) - .is_break() - { - return; - } - Some(ty) - } - hir::OpaqueTyOrigin::TyAlias => None, - }; - self.typeck_results.concrete_opaque_types.insert(opaque_type_key.def_id, hidden_type); - } - } - - fn visit_field_id(&mut self, hir_id: hir::HirId) { - if let Some(index) = self.fcx.typeck_results.borrow_mut().field_indices_mut().remove(hir_id) - { - self.typeck_results.field_indices_mut().insert(hir_id, index); - } - } - - #[instrument(skip(self, span), level = "debug")] - fn visit_node_id(&mut self, span: Span, hir_id: hir::HirId) { - // Export associated path extensions and method resolutions. - if let Some(def) = - self.fcx.typeck_results.borrow_mut().type_dependent_defs_mut().remove(hir_id) - { - self.typeck_results.type_dependent_defs_mut().insert(hir_id, def); - } - - // Resolve any borrowings for the node with id `node_id` - self.visit_adjustments(span, hir_id); - - // Resolve the type of the node with id `node_id` - let n_ty = self.fcx.node_ty(hir_id); - let n_ty = self.resolve(n_ty, &span); - self.write_ty_to_typeck_results(hir_id, n_ty); - debug!(?n_ty); - - // Resolve any substitutions - if let Some(substs) = self.fcx.typeck_results.borrow().node_substs_opt(hir_id) { - let substs = self.resolve(substs, &span); - debug!("write_substs_to_tcx({:?}, {:?})", hir_id, substs); - assert!(!substs.needs_infer() && !substs.has_placeholders()); - self.typeck_results.node_substs_mut().insert(hir_id, substs); - } - } - - #[instrument(skip(self, span), level = "debug")] - fn visit_adjustments(&mut self, span: Span, hir_id: hir::HirId) { - let adjustment = self.fcx.typeck_results.borrow_mut().adjustments_mut().remove(hir_id); - match adjustment { - None => { - debug!("no adjustments for node"); - } - - Some(adjustment) => { - let resolved_adjustment = self.resolve(adjustment, &span); - debug!(?resolved_adjustment); - self.typeck_results.adjustments_mut().insert(hir_id, resolved_adjustment); - } - } - } - - #[instrument(skip(self, span), level = "debug")] - fn visit_pat_adjustments(&mut self, span: Span, hir_id: hir::HirId) { - let adjustment = self.fcx.typeck_results.borrow_mut().pat_adjustments_mut().remove(hir_id); - match adjustment { - None => { - debug!("no pat_adjustments for node"); - } - - Some(adjustment) => { - let resolved_adjustment = self.resolve(adjustment, &span); - debug!(?resolved_adjustment); - self.typeck_results.pat_adjustments_mut().insert(hir_id, resolved_adjustment); - } - } - } - - fn visit_liberated_fn_sigs(&mut self) { - let fcx_typeck_results = self.fcx.typeck_results.borrow(); - assert_eq!(fcx_typeck_results.hir_owner, self.typeck_results.hir_owner); - let common_hir_owner = fcx_typeck_results.hir_owner; - - for (&local_id, &fn_sig) in fcx_typeck_results.liberated_fn_sigs().iter() { - let hir_id = hir::HirId { owner: common_hir_owner, local_id }; - let fn_sig = self.resolve(fn_sig, &hir_id); - self.typeck_results.liberated_fn_sigs_mut().insert(hir_id, fn_sig); - } - } - - fn visit_fru_field_types(&mut self) { - let fcx_typeck_results = self.fcx.typeck_results.borrow(); - assert_eq!(fcx_typeck_results.hir_owner, self.typeck_results.hir_owner); - let common_hir_owner = fcx_typeck_results.hir_owner; - - for (&local_id, ftys) in fcx_typeck_results.fru_field_types().iter() { - let hir_id = hir::HirId { owner: common_hir_owner, local_id }; - let ftys = self.resolve(ftys.clone(), &hir_id); - self.typeck_results.fru_field_types_mut().insert(hir_id, ftys); - } - } - - fn resolve<T>(&mut self, x: T, span: &dyn Locatable) -> T - where - T: TypeFoldable<'tcx>, - { - let mut resolver = Resolver::new(self.fcx, span, self.body); - let x = x.fold_with(&mut resolver); - if cfg!(debug_assertions) && x.needs_infer() { - span_bug!(span.to_span(self.fcx.tcx), "writeback: `{:?}` has inference variables", x); - } - - // We may have introduced e.g. `ty::Error`, if inference failed, make sure - // to mark the `TypeckResults` as tainted in that case, so that downstream - // users of the typeck results don't produce extra errors, or worse, ICEs. - if resolver.replaced_with_error { - // FIXME(eddyb) keep track of `ErrorGuaranteed` from where the error was emitted. - self.typeck_results.tainted_by_errors = - Some(ErrorGuaranteed::unchecked_claim_error_was_emitted()); - } - - x - } -} - -pub(crate) trait Locatable { - fn to_span(&self, tcx: TyCtxt<'_>) -> Span; -} - -impl Locatable for Span { - fn to_span(&self, _: TyCtxt<'_>) -> Span { - *self - } -} - -impl Locatable for hir::HirId { - fn to_span(&self, tcx: TyCtxt<'_>) -> Span { - tcx.hir().span(*self) - } -} - -/// The Resolver. This is the type folding engine that detects -/// unresolved types and so forth. -struct Resolver<'cx, 'tcx> { - tcx: TyCtxt<'tcx>, - infcx: &'cx InferCtxt<'cx, 'tcx>, - span: &'cx dyn Locatable, - body: &'tcx hir::Body<'tcx>, - - /// Set to `true` if any `Ty` or `ty::Const` had to be replaced with an `Error`. - replaced_with_error: bool, -} - -impl<'cx, 'tcx> Resolver<'cx, 'tcx> { - fn new( - fcx: &'cx FnCtxt<'cx, 'tcx>, - span: &'cx dyn Locatable, - body: &'tcx hir::Body<'tcx>, - ) -> Resolver<'cx, 'tcx> { - Resolver { tcx: fcx.tcx, infcx: fcx, span, body, replaced_with_error: false } - } - - fn report_type_error(&self, t: Ty<'tcx>) { - if !self.tcx.sess.has_errors().is_some() { - self.infcx - .emit_inference_failure_err( - Some(self.body.id()), - self.span.to_span(self.tcx), - t.into(), - E0282, - false, - ) - .emit(); - } - } - - fn report_const_error(&self, c: ty::Const<'tcx>) { - if self.tcx.sess.has_errors().is_none() { - self.infcx - .emit_inference_failure_err( - Some(self.body.id()), - self.span.to_span(self.tcx), - c.into(), - E0282, - false, - ) - .emit(); - } - } -} - -struct EraseEarlyRegions<'tcx> { - tcx: TyCtxt<'tcx>, -} - -impl<'tcx> TypeFolder<'tcx> for EraseEarlyRegions<'tcx> { - fn tcx<'b>(&'b self) -> TyCtxt<'tcx> { - self.tcx - } - fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { - if ty.has_type_flags(ty::TypeFlags::HAS_FREE_REGIONS) { - ty.super_fold_with(self) - } else { - ty - } - } - fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { - if r.is_late_bound() { r } else { self.tcx.lifetimes.re_erased } - } -} - -impl<'cx, 'tcx> TypeFolder<'tcx> for Resolver<'cx, 'tcx> { - fn tcx<'a>(&'a self) -> TyCtxt<'tcx> { - self.tcx - } - - fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> { - match self.infcx.fully_resolve(t) { - Ok(t) => { - // Do not anonymize late-bound regions - // (e.g. keep `for<'a>` named `for<'a>`). - // This allows NLL to generate error messages that - // refer to the higher-ranked lifetime names written by the user. - EraseEarlyRegions { tcx: self.tcx }.fold_ty(t) - } - Err(_) => { - debug!("Resolver::fold_ty: input type `{:?}` not fully resolvable", t); - self.report_type_error(t); - self.replaced_with_error = true; - self.tcx().ty_error() - } - } - } - - fn fold_region(&mut self, r: ty::Region<'tcx>) -> ty::Region<'tcx> { - debug_assert!(!r.is_late_bound(), "Should not be resolving bound region."); - self.tcx.lifetimes.re_erased - } - - fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> { - match self.infcx.fully_resolve(ct) { - Ok(ct) => self.tcx.erase_regions(ct), - Err(_) => { - debug!("Resolver::fold_const: input const `{:?}` not fully resolvable", ct); - self.report_const_error(ct); - self.replaced_with_error = true; - self.tcx().const_error(ct.ty()) - } - } - } -} - -/////////////////////////////////////////////////////////////////////////// -// During type check, we store promises with the result of trait -// lookup rather than the actual results (because the results are not -// necessarily available immediately). These routines unwind the -// promises. It is expected that we will have already reported any -// errors that may be encountered, so if the promises store an error, -// a dummy result is returned. diff --git a/compiler/rustc_typeck/src/check_unused.rs b/compiler/rustc_typeck/src/check_unused.rs deleted file mode 100644 index 1d23ed929..000000000 --- a/compiler/rustc_typeck/src/check_unused.rs +++ /dev/null @@ -1,187 +0,0 @@ -use crate::errors::{ExternCrateNotIdiomatic, UnusedExternCrate}; -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -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: FxHashSet<LocalDefId> = FxHashSet::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.iter()); - } - - 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!() }; - tcx.struct_span_lint_hir(lint::builtin::UNUSED_IMPORTS, item.hir_id(), path.span, |lint| { - let msg = if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(path.span) { - format!("unused import: `{}`", snippet) - } else { - "unused import".to_owned() - }; - lint.build(&msg).emit(); - }); - } - - 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.def_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.def_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_typeck/src/coherence/builtin.rs b/compiler/rustc_typeck/src/coherence/builtin.rs deleted file mode 100644 index d08c0d4db..000000000 --- a/compiler/rustc_typeck/src/coherence/builtin.rs +++ /dev/null @@ -1,594 +0,0 @@ -//! 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::InferCtxtExt; -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 span = tcx.hir().span(impl_hir_id); - 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 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 item = tcx.hir().expect_item(impl_did); - let span = if let ItemKind::Impl(hir::Impl { of_trait: Some(ref tr), .. }) = item.kind { - tr.path.span - } else { - span - }; - - 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. - tcx.infer_ctxt().ignoring_regions().enter(|infcx| { - 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) => { - let item = tcx.hir().expect_item(impl_did); - let span = - if let ItemKind::Impl(ref impl_) = item.kind { impl_.self_ty.span } else { span }; - - 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); - - tcx.infer_ctxt().enter(|infcx| { - 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.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); - - tcx.infer_ctxt().enter(|infcx| { - 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 - .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.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_typeck/src/coherence/inherent_impls.rs deleted file mode 100644 index 52aad636f..000000000 --- a/compiler/rustc_typeck/src/coherence/inherent_impls.rs +++ /dev/null @@ -1,249 +0,0 @@ -//! The code in this module gathers up all of the inherent impls in -//! the current crate and organizes them in a map. It winds up -//! touching the whole crate and thus must be recomputed completely -//! for any change, but it is very cheap to compute. In practice, most -//! code in the compiler never *directly* requests this map. Instead, -//! it requests the inherent impls specific to some type (via -//! `tcx.inherent_impls(def_id)`). That value, however, -//! is computed by selecting an idea from this table. - -use rustc_errors::struct_span_err; -use rustc_hir as hir; -use rustc_hir::def::DefKind; -use rustc_hir::def_id::{CrateNum, DefId, LocalDefId}; -use rustc_middle::ty::fast_reject::{simplify_type, SimplifiedType, TreatParams}; -use rustc_middle::ty::{self, CrateInherentImpls, Ty, TyCtxt}; -use rustc_span::symbol::sym; -use rustc_span::Span; - -/// On-demand query: yields a map containing all types mapped to their inherent impls. -pub fn crate_inherent_impls(tcx: TyCtxt<'_>, (): ()) -> CrateInherentImpls { - let mut collect = InherentCollect { tcx, impls_map: Default::default() }; - for id in tcx.hir().items() { - collect.check_item(id); - } - collect.impls_map -} - -pub fn crate_incoherent_impls(tcx: TyCtxt<'_>, (_, simp): (CrateNum, SimplifiedType)) -> &[DefId] { - let crate_map = tcx.crate_inherent_impls(()); - tcx.arena.alloc_from_iter( - crate_map.incoherent_impls.get(&simp).unwrap_or(&Vec::new()).iter().map(|d| d.to_def_id()), - ) -} - -/// On-demand query: yields a vector of the inherent impls for a specific type. -pub fn inherent_impls(tcx: TyCtxt<'_>, ty_def_id: DefId) -> &[DefId] { - let ty_def_id = ty_def_id.expect_local(); - - let crate_map = tcx.crate_inherent_impls(()); - match crate_map.inherent_impls.get(&ty_def_id) { - Some(v) => &v[..], - None => &[], - } -} - -struct InherentCollect<'tcx> { - tcx: TyCtxt<'tcx>, - impls_map: CrateInherentImpls, -} - -const INTO_CORE: &str = "consider moving this inherent impl into `core` if possible"; -const INTO_DEFINING_CRATE: &str = - "consider moving this inherent impl into the crate defining the type if possible"; -const ADD_ATTR_TO_TY: &str = "alternatively add `#[rustc_has_incoherent_inherent_impls]` to the type \ - and `#[rustc_allow_incoherent_impl]` to the relevant impl items"; -const ADD_ATTR: &str = - "alternatively add `#[rustc_allow_incoherent_impl]` to the relevant impl items"; - -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; - 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 - // the implementation does not have any associated traits. - let vec = self.impls_map.inherent_impls.entry(def_id).or_default(); - vec.push(impl_def_id.to_def_id()); - return; - } - - if self.tcx.features().rustc_attrs { - let hir::ItemKind::Impl(&hir::Impl { items, .. }) = item.kind else { - bug!("expected `impl` item: {:?}", item); - }; - - if !self.tcx.has_attr(def_id, sym::rustc_has_incoherent_inherent_impls) { - struct_span_err!( - self.tcx.sess, - item.span, - E0390, - "cannot define inherent `impl` for a type outside of the crate where the type is defined", - ) - .help(INTO_DEFINING_CRATE) - .span_help(item.span, ADD_ATTR_TO_TY) - .emit(); - return; - } - - for impl_item in items { - if !self - .tcx - .has_attr(impl_item.id.def_id.to_def_id(), sym::rustc_allow_incoherent_impl) - { - struct_span_err!( - self.tcx.sess, - item.span, - E0390, - "cannot define inherent `impl` for a type outside of the crate where the type is defined", - ) - .help(INTO_DEFINING_CRATE) - .span_help(impl_item.span, ADD_ATTR) - .emit(); - return; - } - } - - 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); - } else { - bug!("unexpected self type: {:?}", self_ty); - } - } else { - struct_span_err!( - self.tcx.sess, - item.span, - E0116, - "cannot define inherent `impl` for a type outside of the crate \ - where the type is defined" - ) - .span_label(item.span, "impl for type defined outside of crate.") - .note("define and implement a trait or new type instead") - .emit(); - } - } - - fn check_primitive_impl( - &mut self, - impl_def_id: LocalDefId, - ty: Ty<'tcx>, - items: &[hir::ImplItemRef], - span: Span, - ) { - if !self.tcx.hir().rustc_coherence_is_core() { - if self.tcx.features().rustc_attrs { - for item in items { - if !self - .tcx - .has_attr(item.id.def_id.to_def_id(), sym::rustc_allow_incoherent_impl) - { - struct_span_err!( - self.tcx.sess, - span, - E0390, - "cannot define inherent `impl` for primitive types outside of `core`", - ) - .help(INTO_CORE) - .span_help(item.span, ADD_ATTR) - .emit(); - return; - } - } - } else { - let mut err = struct_span_err!( - self.tcx.sess, - span, - E0390, - "cannot define inherent `impl` for primitive types", - ); - err.help("consider using an extension trait instead"); - if let ty::Ref(_, subty, _) = ty.kind() { - err.note(&format!( - "you could also try moving the reference to \ - uses of `{}` (such as `self`) within the implementation", - subty - )); - } - err.emit(); - return; - } - } - - if let Some(simp) = simplify_type(self.tcx, ty, TreatParams::AsInfer) { - self.impls_map.incoherent_impls.entry(simp).or_default().push(impl_def_id); - } else { - bug!("unexpected primitive type: {:?}", ty); - } - } - - fn check_item(&mut self, id: hir::ItemId) { - if !matches!(self.tcx.def_kind(id.def_id), DefKind::Impl) { - return; - } - - let item = self.tcx.hir().item(id); - let hir::ItemKind::Impl(hir::Impl { of_trait: None, self_ty: ty, ref items, .. }) = item.kind else { - return; - }; - - let self_ty = self.tcx.type_of(item.def_id); - match *self_ty.kind() { - ty::Adt(def, _) => { - self.check_def_id(item, self_ty, def.did()); - } - ty::Foreign(did) => { - self.check_def_id(item, self_ty, did); - } - ty::Dynamic(data, ..) if data.principal_def_id().is_some() => { - self.check_def_id(item, self_ty, data.principal_def_id().unwrap()); - } - ty::Dynamic(..) => { - struct_span_err!( - self.tcx.sess, - ty.span, - E0785, - "cannot define inherent `impl` for a dyn auto trait" - ) - .span_label(ty.span, "impl requires at least one non-auto trait") - .note("define and implement a new trait or type instead") - .emit(); - } - ty::Bool - | ty::Char - | ty::Int(_) - | ty::Uint(_) - | ty::Float(_) - | ty::Str - | ty::Array(..) - | ty::Slice(_) - | ty::RawPtr(_) - | ty::Ref(..) - | ty::Never - | ty::FnPtr(_) - | ty::Tuple(..) => self.check_primitive_impl(item.def_id, self_ty, items, ty.span), - ty::Projection(..) | ty::Opaque(..) | ty::Param(_) => { - let mut err = struct_span_err!( - self.tcx.sess, - ty.span, - E0118, - "no nominal type found for inherent implementation" - ); - - err.span_label(ty.span, "impl requires a nominal type") - .note("either implement a trait on it or create a newtype to wrap it instead"); - - err.emit(); - } - ty::FnDef(..) - | ty::Closure(..) - | ty::Generator(..) - | ty::GeneratorWitness(..) - | ty::Bound(..) - | ty::Placeholder(_) - | ty::Infer(_) => { - bug!("unexpected impl self type of impl: {:?} {:?}", item.def_id, self_ty); - } - ty::Error(_) => {} - } - } -} diff --git a/compiler/rustc_typeck/src/coherence/inherent_impls_overlap.rs b/compiler/rustc_typeck/src/coherence/inherent_impls_overlap.rs deleted file mode 100644 index 03e076bf5..000000000 --- a/compiler/rustc_typeck/src/coherence/inherent_impls_overlap.rs +++ /dev/null @@ -1,307 +0,0 @@ -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_errors::struct_span_err; -use rustc_hir as hir; -use rustc_hir::def::DefKind; -use rustc_hir::def_id::DefId; -use rustc_index::vec::IndexVec; -use rustc_middle::traits::specialization_graph::OverlapMode; -use rustc_middle::ty::{self, TyCtxt}; -use rustc_span::Symbol; -use rustc_trait_selection::traits::{self, SkipLeakCheck}; -use smallvec::SmallVec; -use std::collections::hash_map::Entry; - -pub fn crate_inherent_impls_overlap_check(tcx: TyCtxt<'_>, (): ()) { - let mut inherent_overlap_checker = InherentOverlapChecker { tcx }; - for id in tcx.hir().items() { - inherent_overlap_checker.check_item(id); - } -} - -struct InherentOverlapChecker<'tcx> { - tcx: TyCtxt<'tcx>, -} - -impl<'tcx> InherentOverlapChecker<'tcx> { - /// Checks whether any associated items in impls 1 and 2 share the same identifier and - /// namespace. - fn impls_have_common_items( - &self, - impl_items1: &ty::AssocItems<'_>, - impl_items2: &ty::AssocItems<'_>, - ) -> bool { - let mut impl_items1 = &impl_items1; - let mut impl_items2 = &impl_items2; - - // Performance optimization: iterate over the smaller list - if impl_items1.len() > impl_items2.len() { - std::mem::swap(&mut impl_items1, &mut impl_items2); - } - - for item1 in impl_items1.in_definition_order() { - let collision = impl_items2 - .filter_by_name_unhygienic(item1.name) - .any(|item2| self.compare_hygienically(item1, item2)); - - if collision { - return true; - } - } - - false - } - - fn compare_hygienically(&self, item1: &ty::AssocItem, item2: &ty::AssocItem) -> bool { - // Symbols and namespace match, compare hygienically. - item1.kind.namespace() == item2.kind.namespace() - && item1.ident(self.tcx).normalize_to_macros_2_0() - == item2.ident(self.tcx).normalize_to_macros_2_0() - } - - fn check_for_common_items_in_impls( - &self, - impl1: DefId, - impl2: DefId, - overlap: traits::OverlapResult<'_>, - ) { - let impl_items1 = self.tcx.associated_items(impl1); - let impl_items2 = self.tcx.associated_items(impl2); - - for item1 in impl_items1.in_definition_order() { - let collision = impl_items2 - .filter_by_name_unhygienic(item1.name) - .find(|item2| self.compare_hygienically(item1, item2)); - - if let Some(item2) = collision { - let name = item1.ident(self.tcx).normalize_to_macros_2_0(); - let mut err = struct_span_err!( - self.tcx.sess, - self.tcx.def_span(item1.def_id), - E0592, - "duplicate definitions with name `{}`", - name - ); - err.span_label( - self.tcx.def_span(item1.def_id), - format!("duplicate definitions for `{}`", name), - ); - err.span_label( - self.tcx.def_span(item2.def_id), - format!("other definition for `{}`", name), - ); - - for cause in &overlap.intercrate_ambiguity_causes { - cause.add_intercrate_ambiguity_hint(&mut err); - } - - if overlap.involves_placeholder { - traits::add_placeholder_note(&mut err); - } - - err.emit(); - } - } - } - - fn check_for_overlapping_inherent_impls( - &self, - overlap_mode: OverlapMode, - impl1_def_id: DefId, - impl2_def_id: DefId, - ) { - traits::overlapping_impls( - self.tcx, - impl1_def_id, - impl2_def_id, - // We go ahead and just skip the leak check for - // 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, - ); - } - - fn check_item(&mut self, id: hir::ItemId) { - let def_kind = self.tcx.def_kind(id.def_id); - if !matches!(def_kind, DefKind::Enum | DefKind::Struct | DefKind::Trait | DefKind::Union) { - return; - } - - let impls = self.tcx.inherent_impls(id.def_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 impls_items = impls - .iter() - .map(|impl_def_id| (impl_def_id, self.tcx.associated_items(*impl_def_id))) - .collect::<SmallVec<[_; 8]>>(); - - // Perform a O(n^2) algorithm for small n, - // otherwise switch to an allocating algorithm with - // faster asymptotic runtime. - const ALLOCATING_ALGO_THRESHOLD: usize = 500; - if impls.len() < ALLOCATING_ALGO_THRESHOLD { - for (i, &(&impl1_def_id, impl_items1)) in impls_items.iter().enumerate() { - 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( - overlap_mode, - impl1_def_id, - impl2_def_id, - ); - } - } - } - } else { - // Build a set of connected regions of impl blocks. - // Two impl blocks are regarded as connected if they share - // an item with the same unhygienic identifier. - // After we have assembled the connected regions, - // run the O(n^2) algorithm on each connected region. - // This is advantageous to running the algorithm over the - // entire graph when there are many connected regions. - - rustc_index::newtype_index! { - pub struct RegionId { - ENCODABLE = custom - } - } - struct ConnectedRegion { - idents: SmallVec<[Symbol; 8]>, - impl_blocks: FxHashSet<usize>, - } - let mut connected_regions: IndexVec<RegionId, _> = Default::default(); - // Reverse map from the Symbol to the connected region id. - let mut connected_region_ids = FxHashMap::default(); - - for (i, &(&_impl_def_id, impl_items)) in impls_items.iter().enumerate() { - if impl_items.len() == 0 { - continue; - } - // First obtain a list of existing connected region ids - let mut idents_to_add = SmallVec::<[Symbol; 8]>::new(); - let mut ids = impl_items - .in_definition_order() - .filter_map(|item| { - let entry = connected_region_ids.entry(item.name); - if let Entry::Occupied(e) = &entry { - Some(*e.get()) - } else { - idents_to_add.push(item.name); - None - } - }) - .collect::<SmallVec<[RegionId; 8]>>(); - // Sort the id list so that the algorithm is deterministic - ids.sort_unstable(); - ids.dedup(); - let ids = ids; - match &ids[..] { - // Create a new connected region - [] => { - let id_to_set = connected_regions.next_index(); - // Update the connected region ids - for ident in &idents_to_add { - connected_region_ids.insert(*ident, id_to_set); - } - connected_regions.insert( - id_to_set, - ConnectedRegion { - idents: idents_to_add, - impl_blocks: std::iter::once(i).collect(), - }, - ); - } - // Take the only id inside the list - &[id_to_set] => { - let region = connected_regions[id_to_set].as_mut().unwrap(); - region.impl_blocks.insert(i); - region.idents.extend_from_slice(&idents_to_add); - // Update the connected region ids - for ident in &idents_to_add { - connected_region_ids.insert(*ident, id_to_set); - } - } - // We have multiple connected regions to merge. - // In the worst case this might add impl blocks - // one by one and can thus be O(n^2) in the size - // of the resulting final connected region, but - // this is no issue as the final step to check - // for overlaps runs in O(n^2) as well. - &[id_to_set, ..] => { - let mut region = connected_regions.remove(id_to_set).unwrap(); - region.impl_blocks.insert(i); - region.idents.extend_from_slice(&idents_to_add); - // Update the connected region ids - for ident in &idents_to_add { - connected_region_ids.insert(*ident, id_to_set); - } - - // Remove other regions from ids. - for &id in ids.iter() { - if id == id_to_set { - continue; - } - let r = connected_regions.remove(id).unwrap(); - for ident in r.idents.iter() { - connected_region_ids.insert(*ident, id_to_set); - } - region.idents.extend_from_slice(&r.idents); - region.impl_blocks.extend(r.impl_blocks); - } - - connected_regions.insert(id_to_set, region); - } - } - } - - debug!( - "churning through {} components (sum={}, avg={}, var={}, max={})", - connected_regions.len(), - impls.len(), - impls.len() / connected_regions.len(), - { - let avg = impls.len() / connected_regions.len(); - let s = connected_regions - .iter() - .flatten() - .map(|r| r.impl_blocks.len() as isize - avg as isize) - .map(|v| v.abs() as usize) - .sum::<usize>(); - s / connected_regions.len() - }, - connected_regions.iter().flatten().map(|r| r.impl_blocks.len()).max().unwrap() - ); - // List of connected regions is built. Now, run the overlap check - // for each pair of impl blocks in the same connected region. - for region in connected_regions.into_iter().flatten() { - let mut impl_blocks = - region.impl_blocks.into_iter().collect::<SmallVec<[usize; 8]>>(); - 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]; - 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) { - self.check_for_overlapping_inherent_impls( - overlap_mode, - impl1_def_id, - impl2_def_id, - ); - } - } - } - } - } - } -} diff --git a/compiler/rustc_typeck/src/coherence/mod.rs b/compiler/rustc_typeck/src/coherence/mod.rs deleted file mode 100644 index ae9ebe590..000000000 --- a/compiler/rustc_typeck/src/coherence/mod.rs +++ /dev/null @@ -1,237 +0,0 @@ -// Coherence phase -// -// The job of the coherence phase of typechecking is to ensure that -// each trait has at most one implementation for each type. This is -// done by the orphan and overlap modules. Then we build up various -// mappings. That mapping code resides here. - -use rustc_errors::struct_span_err; -use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_middle::ty::query::Providers; -use rustc_middle::ty::{self, TyCtxt, TypeVisitable}; -use rustc_trait_selection::traits; - -mod builtin; -mod inherent_impls; -mod inherent_impls_overlap; -mod orphan; -mod unsafety; - -fn check_impl(tcx: TyCtxt<'_>, impl_def_id: LocalDefId, trait_ref: ty::TraitRef<'_>) { - debug!( - "(checking implementation) adding impl for trait '{:?}', item '{}'", - trait_ref, - tcx.def_path_str(impl_def_id.to_def_id()) - ); - - // Skip impls where one of the self type is an error type. - // This occurs with e.g., resolve failures (#30589). - if trait_ref.references_error() { - return; - } - - enforce_trait_manually_implementable(tcx, impl_def_id, trait_ref.def_id); - enforce_empty_impls_for_marker_traits(tcx, impl_def_id, trait_ref.def_id); -} - -fn enforce_trait_manually_implementable( - tcx: TyCtxt<'_>, - impl_def_id: LocalDefId, - trait_def_id: DefId, -) { - let did = Some(trait_def_id); - let li = tcx.lang_items(); - let impl_header_span = tcx.def_span(impl_def_id); - - // Disallow *all* explicit impls of `Pointee`, `DiscriminantKind`, `Sized` and `Unsize` for now. - if did == li.pointee_trait() { - struct_span_err!( - tcx.sess, - impl_header_span, - E0322, - "explicit impls for the `Pointee` trait are not permitted" - ) - .span_label(impl_header_span, "impl of `Pointee` not allowed") - .emit(); - return; - } - - if did == li.discriminant_kind_trait() { - struct_span_err!( - tcx.sess, - impl_header_span, - E0322, - "explicit impls for the `DiscriminantKind` trait are not permitted" - ) - .span_label(impl_header_span, "impl of `DiscriminantKind` not allowed") - .emit(); - return; - } - - if did == li.sized_trait() { - struct_span_err!( - tcx.sess, - impl_header_span, - E0322, - "explicit impls for the `Sized` trait are not permitted" - ) - .span_label(impl_header_span, "impl of `Sized` not allowed") - .emit(); - return; - } - - if did == li.unsize_trait() { - struct_span_err!( - tcx.sess, - impl_header_span, - E0328, - "explicit impls for the `Unsize` trait are not permitted" - ) - .span_label(impl_header_span, "impl of `Unsize` not allowed") - .emit(); - return; - } - - if tcx.features().unboxed_closures { - // the feature gate allows all Fn traits - return; - } - - if let ty::trait_def::TraitSpecializationKind::AlwaysApplicable = - tcx.trait_def(trait_def_id).specialization_kind - { - if !tcx.features().specialization && !tcx.features().min_specialization { - tcx.sess - .struct_span_err( - impl_header_span, - "implementing `rustc_specialization_trait` traits is unstable", - ) - .help("add `#![feature(min_specialization)]` to the crate attributes to enable") - .emit(); - return; - } - } -} - -/// We allow impls of marker traits to overlap, so they can't override impls -/// as that could make it ambiguous which associated item to use. -fn enforce_empty_impls_for_marker_traits( - tcx: TyCtxt<'_>, - impl_def_id: LocalDefId, - trait_def_id: DefId, -) { - if !tcx.trait_def(trait_def_id).is_marker { - return; - } - - if tcx.associated_item_def_ids(trait_def_id).is_empty() { - return; - } - - struct_span_err!( - tcx.sess, - tcx.def_span(impl_def_id), - E0715, - "impls for marker traits cannot contain items" - ) - .emit(); -} - -pub fn provide(providers: &mut Providers) { - use self::builtin::coerce_unsized_info; - use self::inherent_impls::{crate_incoherent_impls, crate_inherent_impls, inherent_impls}; - use self::inherent_impls_overlap::crate_inherent_impls_overlap_check; - use self::orphan::orphan_check_impl; - - *providers = Providers { - coherent_trait, - crate_inherent_impls, - crate_incoherent_impls, - inherent_impls, - crate_inherent_impls_overlap_check, - coerce_unsized_info, - orphan_check_impl, - ..*providers - }; -} - -fn coherent_trait(tcx: TyCtxt<'_>, def_id: DefId) { - // Trigger building the specialization graph for the trait. This will detect and report any - // overlap errors. - tcx.ensure().specialization_graph_of(def_id); - - let impls = tcx.hir().trait_impls(def_id); - for &impl_def_id in impls { - let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap(); - - check_impl(tcx, impl_def_id, trait_ref); - check_object_overlap(tcx, impl_def_id, trait_ref); - - tcx.sess.time("unsafety_checking", || unsafety::check_item(tcx, impl_def_id)); - tcx.sess.time("orphan_checking", || tcx.ensure().orphan_check_impl(impl_def_id)); - } - - builtin::check_trait(tcx, def_id); -} - -/// Checks whether an impl overlaps with the automatic `impl Trait for dyn Trait`. -fn check_object_overlap<'tcx>( - tcx: TyCtxt<'tcx>, - impl_def_id: LocalDefId, - trait_ref: ty::TraitRef<'tcx>, -) { - let trait_def_id = trait_ref.def_id; - - if trait_ref.references_error() { - debug!("coherence: skipping impl {:?} with error {:?}", impl_def_id, trait_ref); - return; - } - - // check for overlap with the automatic `impl Trait for dyn Trait` - if let ty::Dynamic(data, ..) = trait_ref.self_ty().kind() { - // This is something like impl Trait1 for Trait2. Illegal - // if Trait1 is a supertrait of Trait2 or Trait2 is not object safe. - - let component_def_ids = data.iter().flat_map(|predicate| { - match predicate.skip_binder() { - ty::ExistentialPredicate::Trait(tr) => Some(tr.def_id), - ty::ExistentialPredicate::AutoTrait(def_id) => Some(def_id), - // An associated type projection necessarily comes with - // an additional `Trait` requirement. - ty::ExistentialPredicate::Projection(..) => None, - } - }); - - for component_def_id in component_def_ids { - if !tcx.is_object_safe(component_def_id) { - // Without the 'object_safe_for_dispatch' feature this is an error - // which will be reported by wfcheck. Ignore it here. - // This is tested by `coherence-impl-trait-for-trait-object-safe.rs`. - // With the feature enabled, the trait is not implemented automatically, - // so this is valid. - } else { - let mut supertrait_def_ids = traits::supertrait_def_ids(tcx, component_def_id); - if supertrait_def_ids.any(|d| d == trait_def_id) { - let span = tcx.def_span(impl_def_id); - struct_span_err!( - tcx.sess, - span, - E0371, - "the object type `{}` automatically implements the trait `{}`", - trait_ref.self_ty(), - tcx.def_path_str(trait_def_id) - ) - .span_label( - span, - format!( - "`{}` automatically implements trait `{}`", - trait_ref.self_ty(), - tcx.def_path_str(trait_def_id) - ), - ) - .emit(); - } - } - } - } -} diff --git a/compiler/rustc_typeck/src/coherence/orphan.rs b/compiler/rustc_typeck/src/coherence/orphan.rs deleted file mode 100644 index 1608550aa..000000000 --- a/compiler/rustc_typeck/src/coherence/orphan.rs +++ /dev/null @@ -1,507 +0,0 @@ -//! Orphan checker: every impl either implements a trait defined in this -//! 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::{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; -use rustc_middle::ty::{ - self, ImplPolarity, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor, -}; -use rustc_session::lint; -use rustc_span::def_id::{DefId, LocalDefId}; -use rustc_span::Span; -use rustc_trait_selection::traits; -use std::ops::ControlFlow; - -#[instrument(skip(tcx), level = "debug")] -pub(crate) fn orphan_check_impl( - tcx: TyCtxt<'_>, - impl_def_id: LocalDefId, -) -> Result<(), ErrorGuaranteed> { - let trait_ref = tcx.impl_trait_ref(impl_def_id).unwrap(); - if let Some(err) = trait_ref.error_reported() { - return Err(err); - } - - let ret = do_orphan_check_impl(tcx, trait_ref, impl_def_id); - if tcx.trait_is_auto(trait_ref.def_id) { - lint_auto_trait_impl(tcx, trait_ref, impl_def_id); - } - - ret -} - -fn do_orphan_check_impl<'tcx>( - tcx: TyCtxt<'tcx>, - trait_ref: ty::TraitRef<'tcx>, - def_id: LocalDefId, -) -> Result<(), ErrorGuaranteed> { - let trait_def_id = trait_ref.def_id; - - let item = tcx.hir().item(hir::ItemId { def_id }); - let hir::ItemKind::Impl(ref impl_) = item.kind else { - bug!("{:?} is not an impl: {:?}", def_id, item); - }; - let sp = tcx.def_span(def_id); - let tr = impl_.of_trait.as_ref().unwrap(); - - // Ensure no opaque types are present in this impl header. See issues #76202 and #86411 for examples, - // and #84660 where it would otherwise allow unsoundness. - if trait_ref.has_opaque_types() { - trace!("{:#?}", item); - // First we find the opaque type in question. - for ty in trait_ref.substs { - for ty in ty.walk() { - let ty::subst::GenericArgKind::Type(ty) = ty.unpack() else { continue }; - let ty::Opaque(def_id, _) = *ty.kind() else { continue }; - trace!(?def_id); - - // Then we search for mentions of the opaque type's type alias in the HIR - struct SpanFinder<'tcx> { - sp: Span, - def_id: DefId, - tcx: TyCtxt<'tcx>, - } - impl<'v, 'tcx> hir::intravisit::Visitor<'v> for SpanFinder<'tcx> { - #[instrument(level = "trace", skip(self, _id))] - fn visit_path(&mut self, path: &'v hir::Path<'v>, _id: hir::HirId) { - // You can't mention an opaque type directly, so we look for type aliases - if let hir::def::Res::Def(hir::def::DefKind::TyAlias, def_id) = path.res { - // And check if that type alias's type contains the opaque type we're looking for - for arg in self.tcx.type_of(def_id).walk() { - if let GenericArgKind::Type(ty) = arg.unpack() { - if let ty::Opaque(def_id, _) = *ty.kind() { - if def_id == self.def_id { - // Finally we update the span to the mention of the type alias - self.sp = path.span; - return; - } - } - } - } - } - hir::intravisit::walk_path(self, path) - } - } - - let mut visitor = SpanFinder { sp, def_id, tcx }; - hir::intravisit::walk_item(&mut visitor, item); - let reported = tcx - .sess - .struct_span_err(visitor.sp, "cannot implement trait on type alias impl trait") - .span_note(tcx.def_span(def_id), "type alias impl trait defined here") - .emit(); - return Err(reported); - } - } - span_bug!(sp, "opaque type not found, but `has_opaque_types` is set") - } - - match traits::orphan_check(tcx, item.def_id.to_def_id()) { - Ok(()) => {} - Err(err) => emit_orphan_check_error( - tcx, - sp, - item.span, - tr.path.span, - trait_ref.self_ty(), - impl_.self_ty.span, - &impl_.generics, - err, - )?, - } - - // In addition to the above rules, we restrict impls of auto traits - // so that they can only be implemented on nominal types, such as structs, - // enums or foreign types. To see why this restriction exists, consider the - // following example (#22978). Imagine that crate A defines an auto trait - // `Foo` and a fn that operates on pairs of types: - // - // ``` - // // Crate A - // auto trait Foo { } - // fn two_foos<A:Foo,B:Foo>(..) { - // one_foo::<(A,B)>(..) - // } - // fn one_foo<T:Foo>(..) { .. } - // ``` - // - // This type-checks fine; in particular the fn - // `two_foos` is able to conclude that `(A,B):Foo` - // because `A:Foo` and `B:Foo`. - // - // Now imagine that crate B comes along and does the following: - // - // ``` - // struct A { } - // struct B { } - // impl Foo for A { } - // impl Foo for B { } - // impl !Send for (A, B) { } - // ``` - // - // This final impl is legal according to the orphan - // rules, but it invalidates the reasoning from - // `two_foos` above. - debug!( - "trait_ref={:?} trait_def_id={:?} trait_is_auto={}", - trait_ref, - trait_def_id, - tcx.trait_is_auto(trait_def_id) - ); - - if tcx.trait_is_auto(trait_def_id) && !trait_def_id.is_local() { - let self_ty = trait_ref.self_ty(); - let opt_self_def_id = match *self_ty.kind() { - ty::Adt(self_def, _) => Some(self_def.did()), - ty::Foreign(did) => Some(did), - _ => None, - }; - - let msg = match opt_self_def_id { - // We only want to permit nominal types, but not *all* nominal types. - // They must be local to the current crate, so that people - // can't do `unsafe impl Send for Rc<SomethingLocal>` or - // `impl !Send for Box<SomethingLocalAndSend>`. - Some(self_def_id) => { - if self_def_id.is_local() { - None - } else { - Some(( - format!( - "cross-crate traits with a default impl, like `{}`, \ - can only be implemented for a struct/enum type \ - defined in the current crate", - tcx.def_path_str(trait_def_id) - ), - "can't implement cross-crate trait for type in another crate", - )) - } - } - _ => Some(( - format!( - "cross-crate traits with a default impl, like `{}`, can \ - only be implemented for a struct/enum type, not `{}`", - tcx.def_path_str(trait_def_id), - self_ty - ), - "can't implement cross-crate trait with a default impl for \ - non-struct/enum type", - )), - }; - - if let Some((msg, label)) = msg { - let reported = - struct_span_err!(tcx.sess, sp, E0321, "{}", msg).span_label(sp, label).emit(); - return Err(reported); - } - } - - Ok(()) -} - -fn emit_orphan_check_error<'tcx>( - tcx: TyCtxt<'tcx>, - sp: Span, - full_impl_span: Span, - trait_span: Span, - self_ty: Ty<'tcx>, - self_ty_span: Span, - generics: &hir::Generics<'tcx>, - err: traits::OrphanCheckErr<'tcx>, -) -> Result<!, ErrorGuaranteed> { - Err(match err { - traits::OrphanCheckErr::NonLocalInputType(tys) => { - let msg = match self_ty.kind() { - ty::Adt(..) => "can be implemented for types defined outside of the crate", - _ if self_ty.is_primitive() => "can be implemented for primitive types", - _ => "can be implemented for arbitrary types", - }; - let mut err = struct_span_err!( - tcx.sess, - sp, - E0117, - "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); - }); - 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`. - // That way if we had `Vec<MyType>`, we will properly attribute the - // problem to `Vec<T>` and avoid confusing the user if they were to see - // `MyType` in the error. - ty::Adt(def, _) => tcx.mk_adt(*def, ty::List::empty()), - _ => ty, - }; - let this = "this".to_string(); - let (ty, postfix) = match &ty.kind() { - ty::Slice(_) => (this, " because slices are always foreign"), - ty::Array(..) => (this, " because arrays are always foreign"), - ty::Tuple(..) => (this, " because tuples are always foreign"), - ty::RawPtr(ptr_ty) => { - emit_newtype_suggestion_for_raw_ptr( - full_impl_span, - self_ty, - self_ty_span, - ptr_ty, - &mut err, - ); - - (format!("`{}`", ty), " because raw pointers are always foreign") - } - _ => (format!("`{}`", ty), ""), - }; - - let msg = format!("{} is not defined in the current crate{}", ty, postfix); - 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 { - // Point at `C<B>` in `impl<A, B> for C<B> in D<A>` - err.span_label(trait_span, &msg); - } - } - err.note("define and implement a trait or new type instead"); - err.emit() - } - traits::OrphanCheckErr::UncoveredTy(param_ty, local_type) => { - let mut sp = sp; - for param in generics.params { - if param.name.ident().to_string() == param_ty.to_string() { - sp = param.span; - } - } - - match local_type { - Some(local_type) => struct_span_err!( - tcx.sess, - sp, - E0210, - "type parameter `{}` must be covered by another type \ - when it appears before the first local type (`{}`)", - param_ty, - local_type - ) - .span_label( - sp, - format!( - "type parameter `{}` must be covered by another type \ - when it appears before the first local type (`{}`)", - param_ty, local_type - ), - ) - .note( - "implementing a foreign trait is only possible if at \ - least one of the types for which it is implemented is local, \ - and no uncovered type parameters appear before that first \ - local type", - ) - .note( - "in this case, 'before' refers to the following order: \ - `impl<..> ForeignTrait<T1, ..., Tn> for T0`, \ - where `T0` is the first and `Tn` is the last", - ) - .emit(), - None => struct_span_err!( - tcx.sess, - sp, - E0210, - "type parameter `{}` must be used as the type parameter for some \ - local type (e.g., `MyStruct<{}>`)", - param_ty, - param_ty - ) - .span_label( - sp, - format!( - "type parameter `{}` must be used as the type parameter for some \ - local type", - param_ty, - ), - ) - .note( - "implementing a foreign trait is only possible if at \ - least one of the types for which it is implemented is local", - ) - .note( - "only traits defined in the current crate can be \ - implemented for a type parameter", - ) - .emit(), - } - } - }) -} - -fn emit_newtype_suggestion_for_raw_ptr( - full_impl_span: Span, - self_ty: Ty<'_>, - self_ty_span: Span, - ptr_ty: &ty::TypeAndMut<'_>, - diag: &mut Diagnostic, -) { - if !self_ty.needs_subst() { - let mut_key = if ptr_ty.mutbl == rustc_middle::mir::Mutability::Mut { "mut " } else { "" }; - let msg_sugg = "consider introducing a new wrapper type".to_owned(); - let sugg = vec![ - ( - full_impl_span.shrink_to_lo(), - format!("struct WrapperType(*{}{});\n\n", mut_key, ptr_ty.ty), - ), - (self_ty_span, "WrapperType".to_owned()), - ]; - diag.multipart_suggestion(msg_sugg, sugg, rustc_errors::Applicability::MaybeIncorrect); - } -} - -/// Lint impls of auto traits if they are likely to have -/// unsound or surprising effects on auto impls. -fn lint_auto_trait_impl<'tcx>( - tcx: TyCtxt<'tcx>, - trait_ref: ty::TraitRef<'tcx>, - impl_def_id: LocalDefId, -) { - if tcx.impl_polarity(impl_def_id) != ImplPolarity::Positive { - return; - } - - assert_eq!(trait_ref.substs.len(), 1); - let self_ty = trait_ref.self_ty(); - let (self_type_did, substs) = match self_ty.kind() { - ty::Adt(def, substs) => (def.did(), substs), - _ => { - // FIXME: should also lint for stuff like `&i32` but - // considering that auto traits are unstable, that - // isn't too important for now as this only affects - // crates using `nightly`, and std. - return; - } - }; - - // Impls which completely cover a given root type are fine as they - // disable auto impls entirely. So only lint if the substs - // are not a permutation of the identity substs. - let Err(arg) = tcx.uses_unique_generic_params(substs, IgnoreRegions::Yes) else { - // ok - return; - }; - - // Ideally: - // - // - compute the requirements for the auto impl candidate - // - check whether these are implied by the non covering impls - // - if not, emit the lint - // - // What we do here is a bit simpler: - // - // - badly check if an auto impl candidate definitely does not apply - // for the given simplified type - // - if so, do not lint - if fast_reject_auto_impl(tcx, trait_ref.def_id, self_ty) { - // ok - return; - } - - tcx.struct_span_lint_hir( - 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!( - "cross-crate traits with a default impl, like `{}`, \ - should not be specialized", - tcx.def_path_str(trait_ref.def_id), - )); - match arg { - ty::util::NotUniqueParam::DuplicateParam(arg) => { - err.note(&format!("`{}` is mentioned multiple times", arg)); - } - ty::util::NotUniqueParam::NotParam(arg) => { - err.note(&format!("`{}` is not a generic parameter", arg)); - } - } - err.span_note( - item_span, - &format!( - "try using the same sequence of generic parameters as the {} definition", - self_descr, - ), - ); - err.emit(); - }, - ); -} - -fn fast_reject_auto_impl<'tcx>(tcx: TyCtxt<'tcx>, trait_def_id: DefId, self_ty: Ty<'tcx>) -> bool { - struct DisableAutoTraitVisitor<'tcx> { - tcx: TyCtxt<'tcx>, - trait_def_id: DefId, - self_ty_root: Ty<'tcx>, - seen: FxHashSet<DefId>, - } - - impl<'tcx> TypeVisitor<'tcx> for DisableAutoTraitVisitor<'tcx> { - type BreakTy = (); - fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { - let tcx = self.tcx; - if t != self.self_ty_root { - for impl_def_id in tcx.non_blanket_impls_for_ty(self.trait_def_id, t) { - match tcx.impl_polarity(impl_def_id) { - ImplPolarity::Negative => return ControlFlow::BREAK, - ImplPolarity::Reservation => {} - // FIXME(@lcnr): That's probably not good enough, idk - // - // We might just want to take the rustdoc code and somehow avoid - // explicit impls for `Self`. - ImplPolarity::Positive => return ControlFlow::CONTINUE, - } - } - } - - match t.kind() { - ty::Adt(def, substs) if def.is_phantom_data() => substs.visit_with(self), - ty::Adt(def, substs) => { - // @lcnr: This is the only place where cycles can happen. We avoid this - // by only visiting each `DefId` once. - // - // This will be is incorrect in subtle cases, but I don't care :) - if self.seen.insert(def.did()) { - for ty in def.all_fields().map(|field| field.ty(tcx, substs)) { - ty.visit_with(self)?; - } - } - - ControlFlow::CONTINUE - } - _ => t.super_visit_with(self), - } - } - } - - let self_ty_root = match self_ty.kind() { - ty::Adt(def, _) => tcx.mk_adt(*def, InternalSubsts::identity_for_item(tcx, def.did())), - _ => unimplemented!("unexpected self ty {:?}", self_ty), - }; - - self_ty_root - .visit_with(&mut DisableAutoTraitVisitor { - tcx, - self_ty_root, - trait_def_id, - seen: FxHashSet::default(), - }) - .is_break() -} diff --git a/compiler/rustc_typeck/src/coherence/unsafety.rs b/compiler/rustc_typeck/src/coherence/unsafety.rs deleted file mode 100644 index e45fb5fe4..000000000 --- a/compiler/rustc_typeck/src/coherence/unsafety.rs +++ /dev/null @@ -1,66 +0,0 @@ -//! 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.def_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() - ) - .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() - ) - .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 - ) - .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_typeck/src/collect.rs b/compiler/rustc_typeck/src/collect.rs deleted file mode 100644 index 45a5eca70..000000000 --- a/compiler/rustc_typeck/src/collect.rs +++ /dev/null @@ -1,3401 +0,0 @@ -//! "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::bounds::Bounds; -use crate::check::intrinsic::intrinsic_operation_unsafety; -use crate::constrained_generic_params as cgp; -use crate::errors; -use crate::middle::resolve_lifetime as rl; -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, FxIndexSet}; -use rustc_errors::{struct_span_err, Applicability, DiagnosticBuilder, ErrorGuaranteed, StashKey}; -use rustc_hir as hir; -use rustc_hir::def::{CtorKind, DefKind}; -use rustc_hir::def_id::{DefId, LocalDefId, CRATE_DEF_ID, LOCAL_CRATE}; -use rustc_hir::intravisit::{self, Visitor}; -use rustc_hir::weak_lang_items; -use rustc_hir::{GenericParamKind, HirId, 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::subst::InternalSubsts; -use rustc_middle::ty::util::Discr; -use rustc_middle::ty::util::IntTypeExt; -use rustc_middle::ty::{self, AdtKind, Const, DefIdTree, IsSuggestable, Ty, TyCtxt}; -use rustc_middle::ty::{ReprOptions, ToPredicate}; -use rustc_session::lint; -use rustc_session::parse::feature_err; -use rustc_span::symbol::{kw, sym, Ident, Symbol}; -use rustc_span::{Span, DUMMY_SP}; -use rustc_target::spec::{abi, SanitizerSet}; -use rustc_trait_selection::traits::error_reporting::suggestions::NextTypeParamName; -use std::iter; - -mod item_bounds; -mod type_of; - -#[derive(Debug)] -struct OnlySelfBounds(bool); - -/////////////////////////////////////////////////////////////////////////// -// 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) { - *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, - predicates_of, - predicates_defined_on, - explicit_predicates_of, - super_predicates_of, - super_predicates_that_define_assoc_type, - trait_explicit_predicates_and_bounds, - 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. -/// -/// [`FnCtxt`]: crate::check::FnCtxt -/// [`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, - self.tcx, - 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())); - 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(<_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 { .. } = ¶m.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() -} - -/// 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))] -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.bounded_ty.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, - } - } -} - -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.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.def_id); - tcx.ensure().type_of(item.def_id); - tcx.ensure().predicates_of(item.def_id); - match item.kind { - hir::ForeignItemKind::Fn(..) => tcx.ensure().fn_sig(item.def_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); - tcx.ensure().generics_of(trait_item_id.def_id); - - match trait_item.kind { - hir::TraitItemKind::Fn(..) => { - tcx.ensure().type_of(trait_item_id.def_id); - tcx.ensure().fn_sig(trait_item_id.def_id); - } - - hir::TraitItemKind::Const(.., Some(_)) => { - tcx.ensure().type_of(trait_item_id.def_id); - } - - hir::TraitItemKind::Const(hir_ty, _) => { - tcx.ensure().type_of(trait_item_id.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(trait_item_id.def_id); - tcx.ensure().type_of(trait_item_id.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(trait_item_id.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(trait_item_id.def_id); -} - -fn convert_impl_item(tcx: TyCtxt<'_>, impl_item_id: hir::ImplItemId) { - let def_id = impl_item_id.def_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::TyAlias(_) => { - // 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) -} - -/// 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. -fn super_predicates_of(tcx: TyCtxt<'_>, trait_def_id: DefId) -> ty::GenericPredicates<'_> { - debug!("super_predicates(trait_def_id={:?})", trait_def_id); - 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. -fn super_predicates_that_define_assoc_type( - tcx: TyCtxt<'_>, - (trait_def_id, assoc_name): (DefId, Option<Ident>), -) -> ty::GenericPredicates<'_> { - debug!( - "super_predicates_that_define_assoc_type(trait_def_id={:?}, assoc_name={:?})", - trait_def_id, assoc_name - ); - if trait_def_id.is_local() { - debug!("super_predicates_that_define_assoc_type: local trait_def_id={:?}", trait_def_id); - 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) - } -} - -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.def_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 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) - } - } -} - -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[¶m_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, CRATE_DEF_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::TyAlias(..)) => { - (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 = 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; - - 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, - |lint| { - lint.build(TYPE_DEFAULT_NOT_ALLOWED).emit(); - }, - ); - } - Defaults::Deny => { - tcx.sess.span_err(param.span, TYPE_DEFAULT_NOT_ALLOWED); - } - } - } - - let kind = ty::GenericParamDefKind::Type { has_default: default.is_some(), synthetic }; - - let param_def = ty::GenericParamDef { - index: type_start + i as u32, - 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, - }; - i += 1; - Some(param_def) - } - 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", - ); - } - - let param_def = ty::GenericParamDef { - index: type_start + i as u32, - 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() }, - }; - i += 1; - Some(param_def) - } - })); - - // 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().enumerate().map(|(i, &arg)| ty::GenericParamDef { - index: type_start + i as u32, - 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: type_start, - 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 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 inference 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); - match tcx.hir().expect_item(def_id.expect_local()).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) - }), - _ => bug!(), - } -} - -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 -} - -/// 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. -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 - + 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.bounded_ty.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, ®ion_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(uv) = ct.kind() { - assert_eq!(uv.promoted, ()); - let span = self.tcx.hir().span(c.hir_id); - self.preds.insert(( - ty::Binder::dummy(ty::PredicateKind::ConstEvaluatable(uv)) - .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 -} - -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()) -} - -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) - } -} - -/// 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() -} - -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.item_name(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; - } - - // The panic_no_unwind function called by TerminatorKind::Abort will never - // unwind. If the panic handler that it invokes unwind then it will simply - // call the panic handler again. - if Some(did.to_def_id()) == tcx.lang_items().panic_no_unwind() { - codegen_fn_attrs.flags |= CodegenFnAttrFlags::NEVER_UNWIND; - } - - 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_allocator_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" - ) - .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, - |lint| { - lint.build("`no_sanitize` will have no effect after inlining") - .span_note(inline_span, "inlining requested here") - .emit(); - }, - ) - } - } - } - - // 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); - 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_typeck/src/collect/item_bounds.rs b/compiler/rustc_typeck/src/collect/item_bounds.rs deleted file mode 100644 index 0d34a8bfe..000000000 --- a/compiler/rustc_typeck/src/collect/item_bounds.rs +++ /dev/null @@ -1,110 +0,0 @@ -use super::ItemCtxt; -use crate::astconv::AstConv; -use rustc_hir as hir; -use rustc_infer::traits::util; -use rustc_middle::ty::subst::InternalSubsts; -use rustc_middle::ty::{self, DefIdTree, TyCtxt}; -use rustc_span::def_id::DefId; -use rustc_span::Span; - -/// For associated types we include both bounds written on the type -/// (`type X: Trait`) and predicates from the trait: `where Self::X: Trait`. -/// -/// Note that this filtering is done with the items identity substs to -/// simplify checking that these bounds are met in impls. This means that -/// a bound such as `for<'b> <Self as X<'b>>::U: Clone` can't be used, as in -/// `hr-associated-type-bound-1.rs`. -fn associated_type_bounds<'tcx>( - tcx: TyCtxt<'tcx>, - assoc_item_def_id: DefId, - ast_bounds: &'tcx [hir::GenericBound<'tcx>], - span: Span, -) -> &'tcx [(ty::Predicate<'tcx>, Span)] { - let item_ty = tcx.mk_projection( - assoc_item_def_id, - InternalSubsts::identity_for_item(tcx, assoc_item_def_id), - ); - - let icx = ItemCtxt::new(tcx, assoc_item_def_id); - let mut bounds = <dyn AstConv<'_>>::compute_bounds(&icx, item_ty, ast_bounds); - // Associated types are implicitly sized unless a `?Sized` bound is found - <dyn AstConv<'_>>::add_implicitly_sized(&icx, &mut bounds, ast_bounds, None, span); - - let trait_def_id = tcx.parent(assoc_item_def_id); - let trait_predicates = tcx.trait_explicit_predicates_and_bounds(trait_def_id.expect_local()); - - let bounds_from_parent = trait_predicates.predicates.iter().copied().filter(|(pred, _)| { - match pred.kind().skip_binder() { - ty::PredicateKind::Trait(tr) => tr.self_ty() == item_ty, - ty::PredicateKind::Projection(proj) => proj.projection_ty.self_ty() == item_ty, - ty::PredicateKind::TypeOutlives(outlives) => outlives.0 == item_ty, - _ => false, - } - }); - - let all_bounds = tcx - .arena - .alloc_from_iter(bounds.predicates(tcx, item_ty).into_iter().chain(bounds_from_parent)); - debug!("associated_type_bounds({}) = {:?}", tcx.def_path_str(assoc_item_def_id), all_bounds); - all_bounds -} - -/// Opaque types don't inherit bounds from their parent: for return position -/// 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 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)) - }) -} - -pub(super) fn explicit_item_bounds( - tcx: TyCtxt<'_>, - def_id: DefId, -) -> &'_ [(ty::Predicate<'_>, Span)] { - let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local()); - match tcx.hir().get(hir_id) { - hir::Node::TraitItem(hir::TraitItem { - kind: hir::TraitItemKind::Type(bounds, _), - span, - .. - }) => associated_type_bounds(tcx, def_id, bounds, *span), - hir::Node::Item(hir::Item { - kind: hir::ItemKind::OpaqueTy(hir::OpaqueTy { bounds, in_trait, .. }), - span, - .. - }) => opaque_type_bounds(tcx, def_id, bounds, *span, *in_trait), - _ => bug!("item_bounds called on {:?}", def_id), - } -} - -pub(super) fn item_bounds(tcx: TyCtxt<'_>, def_id: DefId) -> &'_ ty::List<ty::Predicate<'_>> { - tcx.mk_predicates( - util::elaborate_predicates( - tcx, - tcx.explicit_item_bounds(def_id).iter().map(|&(bound, _span)| bound), - ) - .map(|obligation| obligation.predicate), - ) -} diff --git a/compiler/rustc_typeck/src/collect/type_of.rs b/compiler/rustc_typeck/src/collect/type_of.rs deleted file mode 100644 index a1d1f125f..000000000 --- a/compiler/rustc_typeck/src/collect/type_of.rs +++ /dev/null @@ -1,936 +0,0 @@ -use rustc_errors::{Applicability, StashKey}; -use rustc_hir as hir; -use rustc_hir::def_id::{DefId, LocalDefId}; -use rustc_hir::intravisit; -use rustc_hir::intravisit::Visitor; -use rustc_hir::{HirId, Node}; -use rustc_middle::hir::nested_filter; -use rustc_middle::ty::subst::InternalSubsts; -use rustc_middle::ty::util::IntTypeExt; -use rustc_middle::ty::{self, DefIdTree, Ty, TyCtxt, TypeFolder, TypeSuperFoldable, TypeVisitable}; -use rustc_span::symbol::Ident; -use rustc_span::{Span, DUMMY_SP}; - -use super::ItemCtxt; -use super::{bad_placeholder, is_suggestable_infer_ty}; -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`. -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); - - match tcx.hir().get(hir_id) { - Node::AnonConst(_) => (), - _ => return None, - }; - - let parent_node_id = tcx.hir().get_parent_node(hir_id); - let parent_node = tcx.hir().get(parent_node_id); - - let (generics, arg_idx) = match parent_node { - // This match arm is for when the def_id appears in a GAT whose - // path can't be resolved without typechecking e.g. - // - // trait Foo { - // type Assoc<const N: usize>; - // fn foo() -> Self::Assoc<3>; - // } - // - // In the above code we would call this query with the def_id of 3 and - // the parent_node we match on would be the hir node for Self::Assoc<3> - // - // `Self::Assoc<3>` cant be resolved without typechecking here as we - // didnt write <Self as Foo>::Assoc<3>. If we did then another match - // arm would handle this. - // - // I believe this match arm is only needed for GAT but I am not 100% sure - BoxyUwU - Node::Ty(hir_ty @ Ty { kind: TyKind::Path(QPath::TypeRelative(_, segment)), .. }) => { - // Find the Item containing the associated type so we can create an ItemCtxt. - // Using the ItemCtxt convert the HIR for the unresolved assoc type into a - // ty which is a fully resolved projection. - // For the code example above, this would mean converting Self::Assoc<3> - // into a ty::Projection(<Self as Foo>::Assoc<3>) - let item_hir_id = tcx - .hir() - .parent_iter(hir_id) - .filter(|(_, node)| matches!(node, Node::Item(_))) - .map(|(id, _)| id) - .next() - .unwrap(); - let item_did = tcx.hir().local_def_id(item_hir_id).to_def_id(); - let item_ctxt = &ItemCtxt::new(tcx, item_did) as &dyn crate::astconv::AstConv<'_>; - 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 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); - - let arg_index = segment - .args - .and_then(|args| { - args.args - .iter() - .filter(|arg| arg.is_ty_or_const()) - .position(|arg| arg.hir_id() == hir_id) - }) - .unwrap_or_else(|| { - bug!("no arg matching AnonConst in segment"); - }); - - (generics, arg_index) - } else { - // I dont think it's possible to reach this but I'm not 100% sure - BoxyUwU - tcx.sess.delay_span_bug( - tcx.def_span(def_id), - "unexpected non-GAT usage of an anon const", - ); - return None; - } - } - Node::Expr(&Expr { - kind: - ExprKind::MethodCall(segment, ..) | ExprKind::Path(QPath::TypeRelative(_, segment)), - .. - }) => { - let body_owner = tcx.hir().enclosing_body_owner(hir_id); - let tables = tcx.typeck(body_owner); - // This may fail in case the method/path does not actually exist. - // As there is no relevant param for `def_id`, we simply return - // `None` here. - let type_dependent_def = tables.type_dependent_def_id(parent_node_id)?; - let idx = segment - .args - .and_then(|args| { - args.args - .iter() - .filter(|arg| arg.is_ty_or_const()) - .position(|arg| arg.hir_id() == hir_id) - }) - .unwrap_or_else(|| { - bug!("no arg matching AnonConst in segment"); - }); - - (tcx.generics_of(type_dependent_def), idx) - } - - Node::Ty(&Ty { kind: TyKind::Path(_), .. }) - | Node::Expr(&Expr { kind: ExprKind::Path(_) | ExprKind::Struct(..), .. }) - | Node::TraitRef(..) - | Node::Pat(_) => { - let path = match parent_node { - Node::Ty(&Ty { kind: TyKind::Path(QPath::Resolved(_, path)), .. }) - | Node::TraitRef(&TraitRef { path, .. }) => &*path, - Node::Expr(&Expr { - kind: - ExprKind::Path(QPath::Resolved(_, path)) - | ExprKind::Struct(&QPath::Resolved(_, path), ..), - .. - }) => { - let body_owner = tcx.hir().enclosing_body_owner(hir_id); - let _tables = tcx.typeck(body_owner); - &*path - } - Node::Pat(pat) => { - if let Some(path) = get_path_containing_arg_in_pat(pat, hir_id) { - path - } else { - tcx.sess.delay_span_bug( - tcx.def_span(def_id), - &format!("unable to find const parent for {} in pat {:?}", hir_id, pat), - ); - return None; - } - } - _ => { - tcx.sess.delay_span_bug( - tcx.def_span(def_id), - &format!("unexpected const parent path {:?}", parent_node), - ); - return None; - } - }; - - // We've encountered an `AnonConst` in some path, so we need to - // figure out which generic parameter it corresponds to and return - // the relevant type. - let Some((arg_index, segment)) = path.segments.iter().find_map(|seg| { - let args = seg.args?; - args.args - .iter() - .filter(|arg| arg.is_ty_or_const()) - .position(|arg| arg.hir_id() == hir_id) - .map(|index| (index, seg)).or_else(|| args.bindings - .iter() - .filter_map(TypeBinding::opt_const) - .position(|ct| ct.hir_id == hir_id) - .map(|idx| (idx, seg))) - }) else { - tcx.sess.delay_span_bug( - tcx.def_span(def_id), - "no arg matching AnonConst in path", - ); - return None; - }; - - 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: {:?}", segment.res, path), - ); - return None; - } - }; - - (generics, arg_index) - } - _ => return None, - }; - - debug!(?parent_node); - debug!(?generics, ?arg_idx); - generics - .params - .iter() - .filter(|param| param.kind.is_ty_or_const()) - .nth(match generics.has_self && generics.parent.is_none() { - true => arg_idx + 1, - false => arg_idx, - }) - .and_then(|param| match param.kind { - ty::GenericParamDefKind::Const { .. } => { - debug!(?param); - Some(param.def_id) - } - _ => None, - }) -} - -fn get_path_containing_arg_in_pat<'hir>( - pat: &'hir hir::Pat<'hir>, - arg_id: HirId, -) -> Option<&'hir hir::Path<'hir>> { - use hir::*; - - let is_arg_in_path = |p: &hir::Path<'_>| { - p.segments - .iter() - .filter_map(|seg| seg.args) - .flat_map(|args| args.args) - .any(|arg| arg.hir_id() == arg_id) - }; - let mut arg_path = None; - pat.walk(|pat| match pat.kind { - PatKind::Struct(QPath::Resolved(_, path), _, _) - | PatKind::TupleStruct(QPath::Resolved(_, path), _, _) - | PatKind::Path(QPath::Resolved(_, path)) - if is_arg_in_path(path) => - { - arg_path = Some(path); - false - } - _ => true, - }); - arg_path -} - -pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: DefId) -> Ty<'_> { - let def_id = def_id.expect_local(); - use rustc_hir::*; - - 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) { - Node::TraitItem(item) => match item.kind { - TraitItemKind::Fn(..) => { - let substs = InternalSubsts::identity_for_item(tcx, def_id.to_def_id()); - tcx.mk_fn_def(def_id.to_def_id(), substs) - } - TraitItemKind::Const(ty, body_id) => body_id - .and_then(|body_id| { - if is_suggestable_infer_ty(ty) { - Some(infer_placeholder_type( - tcx, def_id, body_id, ty.span, item.ident, "constant", - )) - } else { - None - } - }) - .unwrap_or_else(|| icx.to_ty(ty)), - TraitItemKind::Type(_, Some(ty)) => icx.to_ty(ty), - TraitItemKind::Type(_, None) => { - span_bug!(item.span, "associated type missing default"); - } - }, - - Node::ImplItem(item) => match item.kind { - ImplItemKind::Fn(..) => { - let substs = InternalSubsts::identity_for_item(tcx, def_id.to_def_id()); - tcx.mk_fn_def(def_id.to_def_id(), substs) - } - ImplItemKind::Const(ty, body_id) => { - if is_suggestable_infer_ty(ty) { - infer_placeholder_type(tcx, def_id, body_id, ty.span, item.ident, "constant") - } else { - icx.to_ty(ty) - } - } - ImplItemKind::TyAlias(ty) => { - if tcx.impl_trait_ref(tcx.hir().get_parent_item(hir_id)).is_none() { - check_feature_inherent_assoc_ty(tcx, item.span); - } - - icx.to_ty(ty) - } - }, - - Node::Item(item) => { - match item.kind { - ItemKind::Static(ty, .., body_id) => { - if is_suggestable_infer_ty(ty) { - infer_placeholder_type( - tcx, - def_id, - body_id, - ty.span, - item.ident, - "static variable", - ) - } else { - icx.to_ty(ty) - } - } - ItemKind::Const(ty, body_id) => { - if is_suggestable_infer_ty(ty) { - infer_placeholder_type( - tcx, def_id, body_id, ty.span, item.ident, "constant", - ) - } else { - icx.to_ty(ty) - } - } - ItemKind::TyAlias(self_ty, _) => icx.to_ty(self_ty), - ItemKind::Impl(hir::Impl { self_ty, .. }) => 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) - } - ItemKind::Enum(..) | ItemKind::Struct(..) | ItemKind::Union(..) => { - let def = tcx.adt_def(def_id); - let substs = InternalSubsts::identity_for_item(tcx, def_id.to_def_id()); - tcx.mk_adt(def, substs) - } - ItemKind::OpaqueTy(OpaqueTy { origin: hir::OpaqueTyOrigin::TyAlias, .. }) => { - 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), - in_trait, - .. - }) => { - if in_trait { - span_bug!(item.span, "impl-trait in trait has no default") - } else { - find_opaque_ty_constraints_for_rpit(tcx, def_id, owner) - } - } - ItemKind::Trait(..) - | ItemKind::TraitAlias(..) - | ItemKind::Macro(..) - | ItemKind::Mod(..) - | ItemKind::ForeignMod { .. } - | ItemKind::GlobalAsm(..) - | ItemKind::ExternCrate(..) - | ItemKind::Use(..) => { - span_bug!( - item.span, - "compute_type_of_item: unexpected item type: {:?}", - item.kind - ); - } - } - } - - Node::ForeignItem(foreign_item) => match foreign_item.kind { - ForeignItemKind::Fn(..) => { - let substs = InternalSubsts::identity_for_item(tcx, def_id.to_def_id()); - tcx.mk_fn_def(def_id.to_def_id(), substs) - } - ForeignItemKind::Static(t, _) => icx.to_ty(t), - ForeignItemKind::Type => tcx.mk_foreign(def_id.to_def_id()), - }, - - Node::Ctor(&ref def) | Node::Variant(Variant { data: ref def, .. }) => match *def { - VariantData::Unit(..) | VariantData::Struct(..) => { - tcx.type_of(tcx.hir().get_parent_item(hir_id)) - } - VariantData::Tuple(..) => { - let substs = InternalSubsts::identity_for_item(tcx, def_id.to_def_id()); - tcx.mk_fn_def(def_id.to_def_id(), substs) - } - }, - - Node::Field(field) => icx.to_ty(field.ty), - - 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 - // for generic arguments. - tcx.type_of(param) - } - - Node::AnonConst(_) => { - let parent_node = tcx.hir().get(tcx.hir().get_parent_node(hir_id)); - match parent_node { - Node::Ty(&Ty { kind: TyKind::Array(_, ref constant), .. }) - | Node::Expr(&Expr { kind: ExprKind::Repeat(_, ref constant), .. }) - if constant.hir_id() == hir_id => - { - tcx.types.usize - } - Node::Ty(&Ty { kind: TyKind::Typeof(ref e), .. }) if e.hir_id == hir_id => { - tcx.typeck(def_id).node_type(e.hir_id) - } - - Node::Expr(&Expr { kind: ExprKind::ConstBlock(ref anon_const), .. }) - if anon_const.hir_id == hir_id => - { - let substs = InternalSubsts::identity_for_item(tcx, def_id.to_def_id()); - substs.as_inline_const().ty() - } - - Node::Expr(&Expr { kind: ExprKind::InlineAsm(asm), .. }) - | 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 - } - _ => 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::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", - ) - } - } - - 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(assoc_item) = assoc_item { - tcx.type_of(tcx.generics_of(assoc_item.def_id).params[idx].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 { - hir_id: param_hir_id, - kind: GenericParamKind::Const { default: Some(ct), .. }, - .. - }) if ct.hir_id == hir_id => tcx.type_of(tcx.hir().local_def_id(param_hir_id)), - - x => tcx.ty_error_with_message( - DUMMY_SP, - &format!("unexpected const parent in type_of(): {x:?}"), - ), - } - } - - Node::GenericParam(param) => match ¶m.kind { - GenericParamKind::Type { default: Some(ty), .. } - | GenericParamKind::Const { ty, .. } => icx.to_ty(ty), - x => bug!("unexpected non-type Node::GenericParam: {:?}", x), - }, - - x => { - bug!("unexpected sort of node in type_of(): {:?}", x); - } - } -} - -#[instrument(skip(tcx), level = "debug")] -/// Checks "defining uses" of opaque `impl Trait` types to ensure that they meet the restrictions -/// laid for "higher-order pattern unification". -/// This ensures that inference is tractable. -/// In particular, definitions of opaque types can only use other generics as arguments, -/// and they cannot repeat an argument. Example: -/// -/// ```ignore (illustrative) -/// type Foo<A, B> = impl Bar<A, B>; -/// -/// // Okay -- `Foo` is applied to two distinct, generic types. -/// fn a<T, U>() -> Foo<T, U> { .. } -/// -/// // Not okay -- `Foo` is applied to `T` twice. -/// fn b<T>() -> Foo<T, T> { .. } -/// -/// // Not okay -- `Foo` is applied to a non-generic type. -/// fn b<T>() -> Foo<T, u32> { .. } -/// ``` -/// -fn find_opaque_ty_constraints_for_tait(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Ty<'_> { - use rustc_hir::{Expr, ImplItem, Item, TraitItem}; - - struct ConstraintLocator<'tcx> { - tcx: TyCtxt<'tcx>, - - /// def_id of the opaque type whose defining uses are being checked - def_id: LocalDefId, - - /// as we walk the defining uses, we are checking that all of them - /// define the same hidden type. This variable is set to `Some` - /// with the first type that we find, and then later types are - /// checked against it (we also carry the span of that first - /// type). - found: Option<ty::OpaqueHiddenType<'tcx>>, - } - - impl ConstraintLocator<'_> { - #[instrument(skip(self), level = "debug")] - fn check(&mut self, item_def_id: LocalDefId) { - // Don't try to check items that cannot possibly constrain the type. - if !self.tcx.has_typeck_results(item_def_id) { - debug!("no constraint: no typeck results"); - return; - } - // Calling `mir_borrowck` can lead to cycle errors through - // const-checking, avoid calling it if we don't have to. - // ```rust - // type Foo = impl Fn() -> usize; // when computing type for this - // const fn bar() -> Foo { - // || 0usize - // } - // const BAZR: Foo = bar(); // we would mir-borrowck this, causing cycles - // // because we again need to reveal `Foo` so we can check whether the - // // constant does not contain interior mutability. - // ``` - let tables = self.tcx.typeck(item_def_id); - if let Some(_) = tables.tainted_by_errors { - self.found = Some(ty::OpaqueHiddenType { span: DUMMY_SP, ty: self.tcx.ty_error() }); - return; - } - if !tables.concrete_opaque_types.contains_key(&self.def_id) { - debug!("no constraints in typeck results"); - return; - } - // 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() { - prev.report_mismatch(&concrete_type, self.tcx); - } - } else { - self.found = Some(concrete_type); - } - } - } - } - - impl<'tcx> intravisit::Visitor<'tcx> for ConstraintLocator<'tcx> { - type NestedFilter = nested_filter::All; - - fn nested_visit_map(&mut self) -> Self::Map { - self.tcx.hir() - } - fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) { - if let hir::ExprKind::Closure { .. } = ex.kind { - let def_id = self.tcx.hir().local_def_id(ex.hir_id); - self.check(def_id); - } - intravisit::walk_expr(self, ex); - } - fn visit_item(&mut self, it: &'tcx Item<'tcx>) { - trace!(?it.def_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); - intravisit::walk_item(self, it); - } - } - fn visit_impl_item(&mut self, it: &'tcx ImplItem<'tcx>) { - trace!(?it.def_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); - 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); - 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 }; - - debug!(?scope); - - if scope == hir::CRATE_HIR_ID { - tcx.hir().walk_toplevel_module(&mut locator); - } else { - trace!("scope={:#?}", tcx.hir().get(scope)); - match tcx.hir().get(scope) { - // We explicitly call `visit_*` methods, instead of using `intravisit::walk_*` methods - // This allows our visitor to process the defining item itself, causing - // it to pick up any 'sibling' defining uses. - // - // For example, this code: - // ``` - // fn foo() { - // type Blah = impl Debug; - // let my_closure = || -> Blah { true }; - // } - // ``` - // - // requires us to explicitly process `foo()` in order - // to notice the defining usage of `Blah`. - Node::Item(it) => locator.visit_item(it), - Node::ImplItem(it) => locator.visit_impl_item(it), - Node::TraitItem(it) => locator.visit_trait_item(it), - other => bug!("{:?} is not a valid scope for an opaque type item", other), - } - } - - 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() - } - } -} - -fn find_opaque_ty_constraints_for_rpit( - tcx: TyCtxt<'_>, - def_id: LocalDefId, - owner_def_id: LocalDefId, -) -> Ty<'_> { - use rustc_hir::{Expr, ImplItem, Item, TraitItem}; - - struct ConstraintChecker<'tcx> { - tcx: TyCtxt<'tcx>, - - /// def_id of the opaque type whose defining uses are being checked - def_id: LocalDefId, - - found: ty::OpaqueHiddenType<'tcx>, - } - - impl ConstraintChecker<'_> { - #[instrument(skip(self), level = "debug")] - fn check(&self, def_id: LocalDefId) { - // Use borrowck to get the type with unerased regions. - let concrete_opaque_types = &self.tcx.mir_borrowck(def_id).concrete_opaque_types; - debug!(?concrete_opaque_types); - for &(def_id, concrete_type) in concrete_opaque_types { - if def_id != self.def_id { - // Ignore constraints for other opaque types. - continue; - } - - debug!(?concrete_type, "found constraint"); - - if concrete_type.ty != self.found.ty - && !(concrete_type, self.found).references_error() - { - self.found.report_mismatch(&concrete_type, self.tcx); - } - } - } - } - - impl<'tcx> intravisit::Visitor<'tcx> for ConstraintChecker<'tcx> { - type NestedFilter = nested_filter::OnlyBodies; - - fn nested_visit_map(&mut self) -> Self::Map { - self.tcx.hir() - } - fn visit_expr(&mut self, ex: &'tcx Expr<'tcx>) { - if let hir::ExprKind::Closure { .. } = ex.kind { - let def_id = self.tcx.hir().local_def_id(ex.hir_id); - self.check(def_id); - } - intravisit::walk_expr(self, ex); - } - fn visit_item(&mut self, it: &'tcx Item<'tcx>) { - trace!(?it.def_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); - intravisit::walk_item(self, it); - } - } - fn visit_impl_item(&mut self, it: &'tcx ImplItem<'tcx>) { - trace!(?it.def_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); - 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); - intravisit::walk_trait_item(self, it); - } - } - - let concrete = tcx.mir_borrowck(owner_def_id).concrete_opaque_types.get(&def_id).copied(); - - if let Some(concrete) = concrete { - let scope = tcx.hir().local_def_id_to_hir_id(owner_def_id); - debug!(?scope); - let mut locator = ConstraintChecker { def_id: def_id, tcx, found: concrete }; - - match tcx.hir().get(scope) { - Node::Item(it) => intravisit::walk_item(&mut locator, it), - Node::ImplItem(it) => intravisit::walk_impl_item(&mut locator, it), - Node::TraitItem(it) => intravisit::walk_trait_item(&mut locator, it), - other => bug!("{:?} is not a valid scope for an opaque type item", other), - } - } - - concrete.map(|concrete| concrete.ty).unwrap_or_else(|| { - let table = tcx.typeck(owner_def_id); - if let Some(_) = table.tainted_by_errors { - // Some error in the - // owner fn prevented us from populating - // 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") - } - }) -} - -fn infer_placeholder_type<'a>( - tcx: TyCtxt<'a>, - def_id: LocalDefId, - body_id: hir::BodyId, - span: Span, - item_ident: Ident, - kind: &'static str, -) -> Ty<'a> { - // Attempts to make the type nameable by turning FnDefs into FnPtrs. - struct MakeNameable<'tcx> { - success: bool, - tcx: TyCtxt<'tcx>, - } - - impl<'tcx> MakeNameable<'tcx> { - fn new(tcx: TyCtxt<'tcx>) -> Self { - MakeNameable { success: true, tcx } - } - } - - impl<'tcx> TypeFolder<'tcx> for MakeNameable<'tcx> { - fn tcx(&self) -> TyCtxt<'tcx> { - self.tcx - } - - fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> { - if !self.success { - return ty; - } - - match ty.kind() { - ty::FnDef(def_id, _) => self.tcx.mk_fn_ptr(self.tcx.fn_sig(*def_id)), - // FIXME: non-capturing closures should also suggest a function pointer - ty::Closure(..) | ty::Generator(..) => { - self.success = false; - ty - } - _ => ty.super_fold_with(self), - } - } - } - - let ty = tcx.diagnostic_only_typeck(def_id).node_type(body_id.hir_id); - - // If this came from a free `const` or `static mut?` item, - // then the user may have written e.g. `const A = 42;`. - // In this case, the parser has stashed a diagnostic for - // us to improve in typeck so we do that now. - match tcx.sess.diagnostic().steal_diagnostic(span, StashKey::ItemNoType) { - Some(mut err) => { - 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`. - if let Ok(suggestions) = &mut err.suggestions { - suggestions.clear(); - } - - // Suggesting unnameable types won't help. - let mut mk_nameable = MakeNameable::new(tcx); - let ty = mk_nameable.fold_ty(ty); - let sugg_ty = if mk_nameable.success { Some(ty) } else { None }; - if let Some(sugg_ty) = sugg_ty { - err.span_suggestion( - span, - &format!("provide a type for the {item}", item = kind), - format!("{colon} {sugg_ty}"), - Applicability::MachineApplicable, - ); - } else { - err.span_note( - tcx.hir().body(body_id).value.span, - &format!("however, the inferred type `{}` cannot be named", ty), - ); - } - } - - err.emit(); - } - None => { - let mut diag = bad_placeholder(tcx, vec![span], kind); - - if !ty.references_error() { - let mut mk_nameable = MakeNameable::new(tcx); - let ty = mk_nameable.fold_ty(ty); - let sugg_ty = if mk_nameable.success { Some(ty) } else { None }; - if let Some(sugg_ty) = sugg_ty { - diag.span_suggestion( - span, - "replace with the correct type", - sugg_ty, - Applicability::MaybeIncorrect, - ); - } else { - diag.span_note( - tcx.hir().body(body_id).value.span, - &format!("however, the inferred type `{}` cannot be named", ty), - ); - } - } - - diag.emit(); - } - } - - // Typeck doesn't expect erased regions to be returned from `type_of`. - tcx.fold_regions(ty, |r, _| match *r { - ty::ReErased => tcx.lifetimes.re_static, - _ => r, - }) -} - -fn check_feature_inherent_assoc_ty(tcx: TyCtxt<'_>, span: Span) { - if !tcx.features().inherent_associated_types { - use rustc_session::parse::feature_err; - use rustc_span::symbol::sym; - feature_err( - &tcx.sess.parse_sess, - sym::inherent_associated_types, - span, - "inherent associated types are unstable", - ) - .emit(); - } -} diff --git a/compiler/rustc_typeck/src/constrained_generic_params.rs b/compiler/rustc_typeck/src/constrained_generic_params.rs deleted file mode 100644 index 8428e4664..000000000 --- a/compiler/rustc_typeck/src/constrained_generic_params.rs +++ /dev/null @@ -1,221 +0,0 @@ -use rustc_data_structures::fx::FxHashSet; -use rustc_middle::ty::visit::{TypeSuperVisitable, TypeVisitable, TypeVisitor}; -use rustc_middle::ty::{self, Ty, TyCtxt}; -use rustc_span::source_map::Span; -use std::ops::ControlFlow; - -#[derive(Clone, PartialEq, Eq, Hash, Debug)] -pub struct Parameter(pub u32); - -impl From<ty::ParamTy> for Parameter { - fn from(param: ty::ParamTy) -> Self { - Parameter(param.index) - } -} - -impl From<ty::EarlyBoundRegion> for Parameter { - fn from(param: ty::EarlyBoundRegion) -> Self { - Parameter(param.index) - } -} - -impl From<ty::ParamConst> for Parameter { - fn from(param: ty::ParamConst) -> Self { - Parameter(param.index) - } -} - -/// Returns the set of parameters constrained by the impl header. -pub fn parameters_for_impl<'tcx>( - impl_self_ty: Ty<'tcx>, - impl_trait_ref: Option<ty::TraitRef<'tcx>>, -) -> FxHashSet<Parameter> { - let vec = match impl_trait_ref { - Some(tr) => parameters_for(&tr, false), - None => parameters_for(&impl_self_ty, false), - }; - vec.into_iter().collect() -} - -/// If `include_nonconstraining` is false, returns the list of parameters that are -/// constrained by `t` - i.e., the value of each parameter in the list is -/// uniquely determined by `t` (see RFC 447). If it is true, return the list -/// of parameters whose values are needed in order to constrain `ty` - these -/// differ, with the latter being a superset, in the presence of projections. -pub fn parameters_for<'tcx>( - t: &impl TypeVisitable<'tcx>, - include_nonconstraining: bool, -) -> Vec<Parameter> { - let mut collector = ParameterCollector { parameters: vec![], include_nonconstraining }; - t.visit_with(&mut collector); - collector.parameters -} - -struct ParameterCollector { - parameters: Vec<Parameter>, - include_nonconstraining: bool, -} - -impl<'tcx> TypeVisitor<'tcx> for ParameterCollector { - fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> { - match *t.kind() { - ty::Projection(..) if !self.include_nonconstraining => { - // projections are not injective - return ControlFlow::CONTINUE; - } - ty::Param(data) => { - self.parameters.push(Parameter::from(data)); - } - _ => {} - } - - t.super_visit_with(self) - } - - fn visit_region(&mut self, r: ty::Region<'tcx>) -> ControlFlow<Self::BreakTy> { - if let ty::ReEarlyBound(data) = *r { - self.parameters.push(Parameter::from(data)); - } - ControlFlow::CONTINUE - } - - fn visit_const(&mut self, c: ty::Const<'tcx>) -> ControlFlow<Self::BreakTy> { - match c.kind() { - ty::ConstKind::Unevaluated(..) if !self.include_nonconstraining => { - // Constant expressions are not injective - return c.ty().visit_with(self); - } - ty::ConstKind::Param(data) => { - self.parameters.push(Parameter::from(data)); - } - _ => {} - } - - c.super_visit_with(self) - } -} - -pub fn identify_constrained_generic_params<'tcx>( - tcx: TyCtxt<'tcx>, - predicates: ty::GenericPredicates<'tcx>, - impl_trait_ref: Option<ty::TraitRef<'tcx>>, - input_parameters: &mut FxHashSet<Parameter>, -) { - let mut predicates = predicates.predicates.to_vec(); - setup_constraining_predicates(tcx, &mut predicates, impl_trait_ref, input_parameters); -} - -/// Order the predicates in `predicates` such that each parameter is -/// constrained before it is used, if that is possible, and add the -/// parameters so constrained to `input_parameters`. For example, -/// imagine the following impl: -/// ```ignore (illustrative) -/// impl<T: Debug, U: Iterator<Item = T>> Trait for U -/// ``` -/// 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 -/// -/// When we, for example, try to go over the trait-reference -/// `IntoIter<u32> as Trait`, we substitute the impl parameters with fresh -/// variables and match them with the impl trait-ref, so we know that -/// `$U = IntoIter<u32>`. -/// -/// However, in order to process the `$T: Debug` predicate, we must first -/// know the value of `$T` - which is only given by processing the -/// projection. As we occasionally want to process predicates in a single -/// pass, we want the projection to come first. In fact, as projections -/// can (acyclically) depend on one another - see RFC447 for details - we -/// need to topologically sort them. -/// -/// We *do* have to be somewhat careful when projection targets contain -/// projections themselves, for example in -/// 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*, -/// as it is only contained within a projection), but that requires `W` -/// which is determined by 1, which requires `U`, that is determined -/// by 0. I should probably pick a less tangled example, but I can't -/// think of any. -pub fn setup_constraining_predicates<'tcx>( - tcx: TyCtxt<'tcx>, - predicates: &mut [(ty::Predicate<'tcx>, Span)], - impl_trait_ref: Option<ty::TraitRef<'tcx>>, - input_parameters: &mut FxHashSet<Parameter>, -) { - // The canonical way of doing the needed topological sort - // would be a DFS, but getting the graph and its ownership - // right is annoying, so I am using an in-place fixed-point iteration, - // which is `O(nt)` where `t` is the depth of type-parameter constraints, - // remembering that `t` should be less than 7 in practice. - // - // Basically, I iterate over all projections and swap every - // "ready" projection to the start of the list, such that - // all of the projections before `i` are topologically sorted - // and constrain all the parameters in `input_parameters`. - // - // In the example, `input_parameters` starts by containing `U` - which - // is constrained by the trait-ref - and so on the first pass we - // observe that `<U as Iterator>::Item = T` is a "ready" projection that - // constrains `T` and swap it to front. As it is the sole projection, - // no more swaps can take place afterwards, with the result being - // * <U as Iterator>::Item = T - // * T: Debug - // * U: Iterator - debug!( - "setup_constraining_predicates: predicates={:?} \ - impl_trait_ref={:?} input_parameters={:?}", - predicates, impl_trait_ref, input_parameters - ); - let mut i = 0; - let mut changed = true; - while changed { - changed = false; - - for j in i..predicates.len() { - // Note that we don't have to care about binders here, - // as the impl trait ref never contains any late-bound regions. - if let ty::PredicateKind::Projection(projection) = predicates[j].0.kind().skip_binder() - { - // Special case: watch out for some kind of sneaky attempt - // to project out an associated type defined by this very - // trait. - let unbound_trait_ref = projection.projection_ty.trait_ref(tcx); - if Some(unbound_trait_ref) == impl_trait_ref { - continue; - } - - // A projection depends on its input types and determines its output - // type. For example, if we have - // `<<T as Bar>::Baz as Iterator>::Output = <U as Iterator>::Output` - // Then the projection only applies if `T` is known, but it still - // does not determine `U`. - let inputs = parameters_for(&projection.projection_ty, true); - let relies_only_on_inputs = inputs.iter().all(|p| input_parameters.contains(p)); - if !relies_only_on_inputs { - continue; - } - input_parameters.extend(parameters_for(&projection.term, false)); - } else { - continue; - } - // fancy control flow to bypass borrow checker - predicates.swap(i, j); - i += 1; - changed = true; - } - debug!( - "setup_constraining_predicates: predicates={:?} \ - i={} impl_trait_ref={:?} input_parameters={:?}", - predicates, i, impl_trait_ref, input_parameters - ); - } -} diff --git a/compiler/rustc_typeck/src/errors.rs b/compiler/rustc_typeck/src/errors.rs deleted file mode 100644 index 0d2e66745..000000000 --- a/compiler/rustc_typeck/src/errors.rs +++ /dev/null @@ -1,347 +0,0 @@ -//! Errors emitted by typeck. -use rustc_errors::{error_code, Applicability, DiagnosticBuilder, ErrorGuaranteed, Handler}; -use rustc_macros::{LintDiagnostic, SessionDiagnostic, SessionSubdiagnostic}; -use rustc_middle::ty::Ty; -use rustc_session::SessionDiagnostic; -use rustc_span::{symbol::Ident, Span, Symbol}; - -#[derive(SessionDiagnostic)] -#[diag(typeck::field_multiply_specified_in_initializer, code = "E0062")] -pub struct FieldMultiplySpecifiedInInitializer { - #[primary_span] - #[label] - pub span: Span, - #[label(typeck::previous_use_label)] - pub prev_span: Span, - pub ident: Ident, -} - -#[derive(SessionDiagnostic)] -#[diag(typeck::unrecognized_atomic_operation, code = "E0092")] -pub struct UnrecognizedAtomicOperation<'a> { - #[primary_span] - #[label] - pub span: Span, - pub op: &'a str, -} - -#[derive(SessionDiagnostic)] -#[diag(typeck::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(SessionDiagnostic)] -#[diag(typeck::unrecognized_intrinsic_function, code = "E0093")] -pub struct UnrecognizedIntrinsicFunction { - #[primary_span] - #[label] - pub span: Span, - pub name: Symbol, -} - -#[derive(SessionDiagnostic)] -#[diag(typeck::lifetimes_or_bounds_mismatch_on_trait, code = "E0195")] -pub struct LifetimesOrBoundsMismatchOnTrait { - #[primary_span] - #[label] - pub span: Span, - #[label(typeck::generics_label)] - pub generics_span: Option<Span>, - pub item_kind: &'static str, - pub ident: Ident, -} - -#[derive(SessionDiagnostic)] -#[diag(typeck::drop_impl_on_wrong_item, code = "E0120")] -pub struct DropImplOnWrongItem { - #[primary_span] - #[label] - pub span: Span, -} - -#[derive(SessionDiagnostic)] -#[diag(typeck::field_already_declared, code = "E0124")] -pub struct FieldAlreadyDeclared { - pub field_name: Ident, - #[primary_span] - #[label] - pub span: Span, - #[label(typeck::previous_decl_label)] - pub prev_span: Span, -} - -#[derive(SessionDiagnostic)] -#[diag(typeck::copy_impl_on_type_with_dtor, code = "E0184")] -pub struct CopyImplOnTypeWithDtor { - #[primary_span] - #[label] - pub span: Span, -} - -#[derive(SessionDiagnostic)] -#[diag(typeck::multiple_relaxed_default_bounds, code = "E0203")] -pub struct MultipleRelaxedDefaultBounds { - #[primary_span] - pub span: Span, -} - -#[derive(SessionDiagnostic)] -#[diag(typeck::copy_impl_on_non_adt, code = "E0206")] -pub struct CopyImplOnNonAdt { - #[primary_span] - #[label] - pub span: Span, -} - -#[derive(SessionDiagnostic)] -#[diag(typeck::trait_object_declared_with_no_traits, code = "E0224")] -pub struct TraitObjectDeclaredWithNoTraits { - #[primary_span] - pub span: Span, - #[label(typeck::alias_span)] - pub trait_alias_span: Option<Span>, -} - -#[derive(SessionDiagnostic)] -#[diag(typeck::ambiguous_lifetime_bound, code = "E0227")] -pub struct AmbiguousLifetimeBound { - #[primary_span] - pub span: Span, -} - -#[derive(SessionDiagnostic)] -#[diag(typeck::assoc_type_binding_not_allowed, code = "E0229")] -pub struct AssocTypeBindingNotAllowed { - #[primary_span] - #[label] - pub span: Span, -} - -#[derive(SessionDiagnostic)] -#[diag(typeck::functional_record_update_on_non_struct, code = "E0436")] -pub struct FunctionalRecordUpdateOnNonStruct { - #[primary_span] - pub span: Span, -} - -#[derive(SessionDiagnostic)] -#[diag(typeck::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(SessionDiagnostic)] -#[diag(typeck::return_stmt_outside_of_fn_body, code = "E0572")] -pub struct ReturnStmtOutsideOfFnBody { - #[primary_span] - pub span: Span, - #[label(typeck::encl_body_label)] - pub encl_body_span: Option<Span>, - #[label(typeck::encl_fn_label)] - pub encl_fn_span: Option<Span>, -} - -#[derive(SessionDiagnostic)] -#[diag(typeck::yield_expr_outside_of_generator, code = "E0627")] -pub struct YieldExprOutsideOfGenerator { - #[primary_span] - pub span: Span, -} - -#[derive(SessionDiagnostic)] -#[diag(typeck::struct_expr_non_exhaustive, code = "E0639")] -pub struct StructExprNonExhaustive { - #[primary_span] - pub span: Span, - pub what: &'static str, -} - -#[derive(SessionDiagnostic)] -#[diag(typeck::method_call_on_unknown_type, code = "E0699")] -pub struct MethodCallOnUnknownType { - #[primary_span] - pub span: Span, -} - -#[derive(SessionDiagnostic)] -#[diag(typeck::value_of_associated_struct_already_specified, code = "E0719")] -pub struct ValueOfAssociatedStructAlreadySpecified { - #[primary_span] - #[label] - pub span: Span, - #[label(typeck::previous_bound_label)] - pub prev_span: Span, - pub item_name: Ident, - pub def_path: String, -} - -#[derive(SessionDiagnostic)] -#[diag(typeck::address_of_temporary_taken, code = "E0745")] -pub struct AddressOfTemporaryTaken { - #[primary_span] - #[label] - pub span: Span, -} - -#[derive(SessionSubdiagnostic)] -pub enum AddReturnTypeSuggestion { - #[suggestion( - typeck::add_return_type_add, - code = "-> {found} ", - applicability = "machine-applicable" - )] - Add { - #[primary_span] - span: Span, - found: String, - }, - #[suggestion( - typeck::add_return_type_missing_here, - code = "-> _ ", - applicability = "has-placeholders" - )] - MissingHere { - #[primary_span] - span: Span, - }, -} - -#[derive(SessionSubdiagnostic)] -pub enum ExpectedReturnTypeLabel<'tcx> { - #[label(typeck::expected_default_return_type)] - Unit { - #[primary_span] - span: Span, - }, - #[label(typeck::expected_return_type)] - Other { - #[primary_span] - span: Span, - expected: Ty<'tcx>, - }, -} - -#[derive(SessionDiagnostic)] -#[diag(typeck::unconstrained_opaque_type)] -#[note] -pub struct UnconstrainedOpaqueType { - #[primary_span] - pub span: Span, - pub name: Symbol, -} - -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 `SessionDiagnostic` to be able to call `span_to_snippet`. -impl<'a> SessionDiagnostic<'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::typeck::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::typeck::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::typeck::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::typeck::no_suggestion_label); - } - - err.note(rustc_errors::fluent::typeck::note); - err - } -} - -#[derive(SessionDiagnostic)] -#[diag(typeck::manual_implementation, code = "E0183")] -#[help] -pub struct ManualImplementation { - #[primary_span] - #[label] - pub span: Span, - pub trait_name: String, -} - -#[derive(SessionDiagnostic)] -#[diag(typeck::substs_on_overridden_impl)] -pub struct SubstsOnOverriddenImpl { - #[primary_span] - pub span: Span, -} - -#[derive(LintDiagnostic)] -#[diag(typeck::unused_extern_crate)] -pub struct UnusedExternCrate { - #[suggestion(applicability = "machine-applicable", code = "")] - pub span: Span, -} - -#[derive(LintDiagnostic)] -#[diag(typeck::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(SessionDiagnostic)] -#[diag(typeck::expected_used_symbol)] -pub struct ExpectedUsedSymbol { - #[primary_span] - pub span: Span, -} diff --git a/compiler/rustc_typeck/src/expr_use_visitor.rs b/compiler/rustc_typeck/src/expr_use_visitor.rs deleted file mode 100644 index f483342b4..000000000 --- a/compiler/rustc_typeck/src/expr_use_visitor.rs +++ /dev/null @@ -1,915 +0,0 @@ -//! A different sort of visitor for walking fn bodies. Unlike the -//! normal visitor, which just walks the entire body in one shot, the -//! `ExprUseVisitor` determines how expressions are being used. - -use std::slice::from_ref; - -use hir::def::DefKind; -use hir::Expr; -// Export these here so that Clippy can use them. -pub use rustc_middle::hir::place::{Place, PlaceBase, PlaceWithHirId, Projection}; - -use rustc_data_structures::fx::FxIndexMap; -use rustc_hir as hir; -use rustc_hir::def::Res; -use rustc_hir::def_id::LocalDefId; -use rustc_hir::PatKind; -use rustc_index::vec::Idx; -use rustc_infer::infer::InferCtxt; -use rustc_middle::hir::place::ProjectionKind; -use rustc_middle::mir::FakeReadCause; -use rustc_middle::ty::{self, adjustment, AdtKind, Ty, TyCtxt}; -use rustc_target::abi::VariantIdx; -use ty::BorrowKind::ImmBorrow; - -use crate::mem_categorization as mc; - -/// This trait defines the callbacks you can expect to receive when -/// employing the ExprUseVisitor. -pub trait Delegate<'tcx> { - /// The value found at `place` is moved, depending - /// on `mode`. Where `diag_expr_id` is the id used for diagnostics for `place`. - /// - /// Use of a `Copy` type in a ByValue context is considered a use - /// by `ImmBorrow` and `borrow` is called instead. This is because - /// a shared borrow is the "minimum access" that would be needed - /// to perform a copy. - /// - /// - /// The parameter `diag_expr_id` indicates the HIR id that ought to be used for - /// diagnostics. Around pattern matching such as `let pat = expr`, the diagnostic - /// id will be the id of the expression `expr` but the place itself will have - /// the id of the binding in the pattern `pat`. - fn consume(&mut self, place_with_id: &PlaceWithHirId<'tcx>, diag_expr_id: hir::HirId); - - /// The value found at `place` is being borrowed with kind `bk`. - /// `diag_expr_id` is the id used for diagnostics (see `consume` for more details). - fn borrow( - &mut self, - place_with_id: &PlaceWithHirId<'tcx>, - diag_expr_id: hir::HirId, - bk: ty::BorrowKind, - ); - - /// The value found at `place` is being copied. - /// `diag_expr_id` is the id used for diagnostics (see `consume` for more details). - fn copy(&mut self, place_with_id: &PlaceWithHirId<'tcx>, diag_expr_id: hir::HirId) { - // In most cases, copying data from `x` is equivalent to doing `*&x`, so by default - // we treat a copy of `x` as a borrow of `x`. - self.borrow(place_with_id, diag_expr_id, ty::BorrowKind::ImmBorrow) - } - - /// The path at `assignee_place` is being assigned to. - /// `diag_expr_id` is the id used for diagnostics (see `consume` for more details). - fn mutate(&mut self, assignee_place: &PlaceWithHirId<'tcx>, diag_expr_id: hir::HirId); - - /// The path at `binding_place` is a binding that is being initialized. - /// - /// This covers cases such as `let x = 42;` - fn bind(&mut self, binding_place: &PlaceWithHirId<'tcx>, diag_expr_id: hir::HirId) { - // Bindings can normally be treated as a regular assignment, so by default we - // forward this to the mutate callback. - self.mutate(binding_place, diag_expr_id) - } - - /// The `place` should be a fake read because of specified `cause`. - fn fake_read( - &mut self, - place_with_id: &PlaceWithHirId<'tcx>, - cause: FakeReadCause, - diag_expr_id: hir::HirId, - ); -} - -#[derive(Copy, Clone, PartialEq, Debug)] -enum ConsumeMode { - /// reference to x where x has a type that copies - Copy, - /// reference to x where x has a type that moves - Move, -} - -#[derive(Copy, Clone, PartialEq, Debug)] -pub enum MutateMode { - Init, - /// Example: `x = y` - JustWrite, - /// Example: `x += y` - WriteAndRead, -} - -/// The ExprUseVisitor type -/// -/// This is the code that actually walks the tree. -pub struct ExprUseVisitor<'a, 'tcx> { - mc: mc::MemCategorizationContext<'a, 'tcx>, - body_owner: LocalDefId, - delegate: &'a mut dyn Delegate<'tcx>, -} - -/// If the MC results in an error, it's because the type check -/// failed (or will fail, when the error is uncovered and reported -/// during writeback). In this case, we just ignore this part of the -/// code. -/// -/// Note that this macro appears similar to try!(), but, unlike try!(), -/// it does not propagate the error. -macro_rules! return_if_err { - ($inp: expr) => { - match $inp { - Ok(v) => v, - Err(()) => { - debug!("mc reported err"); - return; - } - } - }; -} - -impl<'a, 'tcx> ExprUseVisitor<'a, 'tcx> { - /// Creates the ExprUseVisitor, configuring it with the various options provided: - /// - /// - `delegate` -- who receives the callbacks - /// - `param_env` --- parameter environment for trait lookups (esp. pertaining to `Copy`) - /// - `typeck_results` --- typeck results for the code being analyzed - pub fn new( - delegate: &'a mut (dyn Delegate<'tcx> + 'a), - infcx: &'a InferCtxt<'a, 'tcx>, - body_owner: LocalDefId, - param_env: ty::ParamEnv<'tcx>, - typeck_results: &'a ty::TypeckResults<'tcx>, - ) -> Self { - ExprUseVisitor { - mc: mc::MemCategorizationContext::new(infcx, param_env, body_owner, typeck_results), - body_owner, - delegate, - } - } - - #[instrument(skip(self), level = "debug")] - pub fn consume_body(&mut self, body: &hir::Body<'_>) { - for param in body.params { - let param_ty = return_if_err!(self.mc.pat_ty_adjusted(param.pat)); - debug!("consume_body: param_ty = {:?}", param_ty); - - let param_place = self.mc.cat_rvalue(param.hir_id, param.pat.span, param_ty); - - self.walk_irrefutable_pat(¶m_place, param.pat); - } - - self.consume_expr(&body.value); - } - - fn tcx(&self) -> TyCtxt<'tcx> { - self.mc.tcx() - } - - fn delegate_consume(&mut self, place_with_id: &PlaceWithHirId<'tcx>, diag_expr_id: hir::HirId) { - delegate_consume(&self.mc, self.delegate, place_with_id, diag_expr_id) - } - - fn consume_exprs(&mut self, exprs: &[hir::Expr<'_>]) { - for expr in exprs { - self.consume_expr(expr); - } - } - - pub fn consume_expr(&mut self, expr: &hir::Expr<'_>) { - debug!("consume_expr(expr={:?})", expr); - - let place_with_id = return_if_err!(self.mc.cat_expr(expr)); - self.delegate_consume(&place_with_id, place_with_id.hir_id); - self.walk_expr(expr); - } - - fn mutate_expr(&mut self, expr: &hir::Expr<'_>) { - let place_with_id = return_if_err!(self.mc.cat_expr(expr)); - self.delegate.mutate(&place_with_id, place_with_id.hir_id); - self.walk_expr(expr); - } - - fn borrow_expr(&mut self, expr: &hir::Expr<'_>, bk: ty::BorrowKind) { - debug!("borrow_expr(expr={:?}, bk={:?})", expr, bk); - - let place_with_id = return_if_err!(self.mc.cat_expr(expr)); - self.delegate.borrow(&place_with_id, place_with_id.hir_id, bk); - - self.walk_expr(expr) - } - - fn select_from_expr(&mut self, expr: &hir::Expr<'_>) { - self.walk_expr(expr) - } - - pub fn walk_expr(&mut self, expr: &hir::Expr<'_>) { - debug!("walk_expr(expr={:?})", expr); - - self.walk_adjustment(expr); - - match expr.kind { - hir::ExprKind::Path(_) => {} - - hir::ExprKind::Type(subexpr, _) => self.walk_expr(subexpr), - - hir::ExprKind::Unary(hir::UnOp::Deref, base) => { - // *base - self.select_from_expr(base); - } - - hir::ExprKind::Field(base, _) => { - // base.f - self.select_from_expr(base); - } - - hir::ExprKind::Index(lhs, rhs) => { - // lhs[rhs] - self.select_from_expr(lhs); - self.consume_expr(rhs); - } - - hir::ExprKind::Call(callee, args) => { - // callee(args) - self.consume_expr(callee); - self.consume_exprs(args); - } - - hir::ExprKind::MethodCall(.., receiver, args, _) => { - // callee.m(args) - self.consume_expr(receiver); - self.consume_exprs(args); - } - - hir::ExprKind::Struct(_, fields, ref opt_with) => { - self.walk_struct_expr(fields, opt_with); - } - - hir::ExprKind::Tup(exprs) => { - self.consume_exprs(exprs); - } - - hir::ExprKind::If(ref cond_expr, ref then_expr, ref opt_else_expr) => { - self.consume_expr(cond_expr); - self.consume_expr(then_expr); - if let Some(ref else_expr) = *opt_else_expr { - self.consume_expr(else_expr); - } - } - - hir::ExprKind::Let(hir::Let { pat, init, .. }) => { - self.walk_local(init, pat, None, |t| t.borrow_expr(init, ty::ImmBorrow)) - } - - hir::ExprKind::Match(ref discr, arms, _) => { - let discr_place = return_if_err!(self.mc.cat_expr(discr)); - self.maybe_read_scrutinee( - discr, - discr_place.clone(), - arms.iter().map(|arm| arm.pat), - ); - - // treatment of the discriminant is handled while walking the arms. - for arm in arms { - self.walk_arm(&discr_place, arm); - } - } - - hir::ExprKind::Array(exprs) => { - self.consume_exprs(exprs); - } - - hir::ExprKind::AddrOf(_, m, ref base) => { - // &base - // make sure that the thing we are pointing out stays valid - // for the lifetime `scope_r` of the resulting ptr: - let bk = ty::BorrowKind::from_mutbl(m); - self.borrow_expr(base, bk); - } - - hir::ExprKind::InlineAsm(asm) => { - for (op, _op_sp) in asm.operands { - match op { - hir::InlineAsmOperand::In { expr, .. } => self.consume_expr(expr), - hir::InlineAsmOperand::Out { expr: Some(expr), .. } - | hir::InlineAsmOperand::InOut { expr, .. } => { - self.mutate_expr(expr); - } - hir::InlineAsmOperand::SplitInOut { in_expr, out_expr, .. } => { - self.consume_expr(in_expr); - if let Some(out_expr) = out_expr { - self.mutate_expr(out_expr); - } - } - hir::InlineAsmOperand::Out { expr: None, .. } - | hir::InlineAsmOperand::Const { .. } - | hir::InlineAsmOperand::SymFn { .. } - | hir::InlineAsmOperand::SymStatic { .. } => {} - } - } - } - - hir::ExprKind::Continue(..) - | hir::ExprKind::Lit(..) - | hir::ExprKind::ConstBlock(..) - | hir::ExprKind::Err => {} - - hir::ExprKind::Loop(blk, ..) => { - self.walk_block(blk); - } - - hir::ExprKind::Unary(_, lhs) => { - self.consume_expr(lhs); - } - - hir::ExprKind::Binary(_, lhs, rhs) => { - self.consume_expr(lhs); - self.consume_expr(rhs); - } - - hir::ExprKind::Block(blk, _) => { - self.walk_block(blk); - } - - hir::ExprKind::Break(_, ref opt_expr) | hir::ExprKind::Ret(ref opt_expr) => { - if let Some(expr) = *opt_expr { - self.consume_expr(expr); - } - } - - hir::ExprKind::Assign(lhs, rhs, _) => { - self.mutate_expr(lhs); - self.consume_expr(rhs); - } - - hir::ExprKind::Cast(base, _) => { - self.consume_expr(base); - } - - hir::ExprKind::DropTemps(expr) => { - self.consume_expr(expr); - } - - hir::ExprKind::AssignOp(_, lhs, rhs) => { - if self.mc.typeck_results.is_method_call(expr) { - self.consume_expr(lhs); - } else { - self.mutate_expr(lhs); - } - self.consume_expr(rhs); - } - - hir::ExprKind::Repeat(base, _) => { - self.consume_expr(base); - } - - hir::ExprKind::Closure { .. } => { - self.walk_captures(expr); - } - - hir::ExprKind::Box(ref base) => { - self.consume_expr(base); - } - - hir::ExprKind::Yield(value, _) => { - self.consume_expr(value); - } - } - } - - fn walk_stmt(&mut self, stmt: &hir::Stmt<'_>) { - match stmt.kind { - hir::StmtKind::Local(hir::Local { pat, init: Some(expr), els, .. }) => { - self.walk_local(expr, pat, *els, |_| {}) - } - - hir::StmtKind::Local(_) => {} - - hir::StmtKind::Item(_) => { - // We don't visit nested items in this visitor, - // only the fn body we were given. - } - - hir::StmtKind::Expr(ref expr) | hir::StmtKind::Semi(ref expr) => { - self.consume_expr(expr); - } - } - } - - fn maybe_read_scrutinee<'t>( - &mut self, - discr: &Expr<'_>, - discr_place: PlaceWithHirId<'tcx>, - pats: impl Iterator<Item = &'t hir::Pat<'t>>, - ) { - // Matching should not always be considered a use of the place, hence - // discr does not necessarily need to be borrowed. - // We only want to borrow discr if the pattern contain something other - // than wildcards. - let ExprUseVisitor { ref mc, body_owner: _, delegate: _ } = *self; - let mut needs_to_be_read = false; - for pat in pats { - return_if_err!(mc.cat_pattern(discr_place.clone(), pat, |place, pat| { - match &pat.kind { - PatKind::Binding(.., opt_sub_pat) => { - // If the opt_sub_pat is None, than the binding does not count as - // a wildcard for the purpose of borrowing discr. - if opt_sub_pat.is_none() { - needs_to_be_read = true; - } - } - PatKind::Path(qpath) => { - // A `Path` pattern is just a name like `Foo`. This is either a - // named constant or else it refers to an ADT variant - - let res = self.mc.typeck_results.qpath_res(qpath, pat.hir_id); - match res { - Res::Def(DefKind::Const, _) | Res::Def(DefKind::AssocConst, _) => { - // Named constants have to be equated with the value - // being matched, so that's a read of the value being matched. - // - // FIXME: We don't actually reads for ZSTs. - needs_to_be_read = true; - } - _ => { - // Otherwise, this is a struct/enum variant, and so it's - // only a read if we need to read the discriminant. - needs_to_be_read |= is_multivariant_adt(place.place.ty()); - } - } - } - PatKind::TupleStruct(..) | PatKind::Struct(..) | PatKind::Tuple(..) => { - // For `Foo(..)`, `Foo { ... }` and `(...)` patterns, check if we are matching - // against a multivariant enum or struct. In that case, we have to read - // the discriminant. Otherwise this kind of pattern doesn't actually - // read anything (we'll get invoked for the `...`, which may indeed - // perform some reads). - - let place_ty = place.place.ty(); - needs_to_be_read |= is_multivariant_adt(place_ty); - } - PatKind::Lit(_) | PatKind::Range(..) => { - // If the PatKind is a Lit or a Range then we want - // to borrow discr. - needs_to_be_read = true; - } - PatKind::Or(_) - | PatKind::Box(_) - | PatKind::Slice(..) - | PatKind::Ref(..) - | PatKind::Wild => { - // If the PatKind is Or, Box, Slice or Ref, the decision is made later - // as these patterns contains subpatterns - // If the PatKind is Wild, the decision is made based on the other patterns being - // examined - } - } - })); - } - - if needs_to_be_read { - self.borrow_expr(discr, ty::ImmBorrow); - } else { - let closure_def_id = match discr_place.place.base { - PlaceBase::Upvar(upvar_id) => Some(upvar_id.closure_expr_id), - _ => None, - }; - - self.delegate.fake_read( - &discr_place, - FakeReadCause::ForMatchedPlace(closure_def_id), - discr_place.hir_id, - ); - - // We always want to walk the discriminant. We want to make sure, for instance, - // that the discriminant has been initialized. - self.walk_expr(discr); - } - } - - fn walk_local<F>( - &mut self, - expr: &hir::Expr<'_>, - pat: &hir::Pat<'_>, - els: Option<&hir::Block<'_>>, - mut f: F, - ) where - F: FnMut(&mut Self), - { - self.walk_expr(expr); - let expr_place = return_if_err!(self.mc.cat_expr(expr)); - f(self); - if let Some(els) = els { - // borrowing because we need to test the discriminant - self.maybe_read_scrutinee(expr, expr_place.clone(), from_ref(pat).iter()); - self.walk_block(els) - } - self.walk_irrefutable_pat(&expr_place, &pat); - } - - /// Indicates that the value of `blk` will be consumed, meaning either copied or moved - /// depending on its type. - fn walk_block(&mut self, blk: &hir::Block<'_>) { - debug!("walk_block(blk.hir_id={})", blk.hir_id); - - for stmt in blk.stmts { - self.walk_stmt(stmt); - } - - if let Some(ref tail_expr) = blk.expr { - self.consume_expr(tail_expr); - } - } - - fn walk_struct_expr<'hir>( - &mut self, - fields: &[hir::ExprField<'_>], - opt_with: &Option<&'hir hir::Expr<'_>>, - ) { - // Consume the expressions supplying values for each field. - for field in fields { - self.consume_expr(field.expr); - } - - let with_expr = match *opt_with { - Some(w) => &*w, - None => { - return; - } - }; - - let with_place = return_if_err!(self.mc.cat_expr(with_expr)); - - // Select just those fields of the `with` - // expression that will actually be used - match with_place.place.ty().kind() { - ty::Adt(adt, substs) if adt.is_struct() => { - // Consume those fields of the with expression that are needed. - for (f_index, with_field) in adt.non_enum_variant().fields.iter().enumerate() { - let is_mentioned = fields.iter().any(|f| { - self.tcx().field_index(f.hir_id, self.mc.typeck_results) == f_index - }); - if !is_mentioned { - let field_place = self.mc.cat_projection( - &*with_expr, - with_place.clone(), - with_field.ty(self.tcx(), substs), - ProjectionKind::Field(f_index as u32, VariantIdx::new(0)), - ); - self.delegate_consume(&field_place, field_place.hir_id); - } - } - } - _ => { - // the base expression should always evaluate to a - // struct; however, when EUV is run during typeck, it - // may not. This will generate an error earlier in typeck, - // so we can just ignore it. - if !self.tcx().sess.has_errors().is_some() { - span_bug!(with_expr.span, "with expression doesn't evaluate to a struct"); - } - } - } - - // walk the with expression so that complex expressions - // are properly handled. - self.walk_expr(with_expr); - } - - /// Invoke the appropriate delegate calls for anything that gets - /// consumed or borrowed as part of the automatic adjustment - /// process. - fn walk_adjustment(&mut self, expr: &hir::Expr<'_>) { - let adjustments = self.mc.typeck_results.expr_adjustments(expr); - let mut place_with_id = return_if_err!(self.mc.cat_expr_unadjusted(expr)); - for adjustment in adjustments { - debug!("walk_adjustment expr={:?} adj={:?}", expr, adjustment); - match adjustment.kind { - adjustment::Adjust::NeverToAny | adjustment::Adjust::Pointer(_) => { - // Creating a closure/fn-pointer or unsizing consumes - // the input and stores it into the resulting rvalue. - self.delegate_consume(&place_with_id, place_with_id.hir_id); - } - - adjustment::Adjust::Deref(None) => {} - - // Autoderefs for overloaded Deref calls in fact reference - // their receiver. That is, if we have `(*x)` where `x` - // is of type `Rc<T>`, then this in fact is equivalent to - // `x.deref()`. Since `deref()` is declared with `&self`, - // this is an autoref of `x`. - adjustment::Adjust::Deref(Some(ref deref)) => { - let bk = ty::BorrowKind::from_mutbl(deref.mutbl); - self.delegate.borrow(&place_with_id, place_with_id.hir_id, bk); - } - - adjustment::Adjust::Borrow(ref autoref) => { - self.walk_autoref(expr, &place_with_id, autoref); - } - } - place_with_id = - return_if_err!(self.mc.cat_expr_adjusted(expr, place_with_id, adjustment)); - } - } - - /// Walks the autoref `autoref` applied to the autoderef'd - /// `expr`. `base_place` is the mem-categorized form of `expr` - /// after all relevant autoderefs have occurred. - fn walk_autoref( - &mut self, - expr: &hir::Expr<'_>, - base_place: &PlaceWithHirId<'tcx>, - autoref: &adjustment::AutoBorrow<'tcx>, - ) { - debug!( - "walk_autoref(expr.hir_id={} base_place={:?} autoref={:?})", - expr.hir_id, base_place, autoref - ); - - match *autoref { - adjustment::AutoBorrow::Ref(_, m) => { - self.delegate.borrow( - base_place, - base_place.hir_id, - ty::BorrowKind::from_mutbl(m.into()), - ); - } - - adjustment::AutoBorrow::RawPtr(m) => { - debug!("walk_autoref: expr.hir_id={} base_place={:?}", expr.hir_id, base_place); - - self.delegate.borrow(base_place, base_place.hir_id, ty::BorrowKind::from_mutbl(m)); - } - } - } - - fn walk_arm(&mut self, discr_place: &PlaceWithHirId<'tcx>, arm: &hir::Arm<'_>) { - let closure_def_id = match discr_place.place.base { - PlaceBase::Upvar(upvar_id) => Some(upvar_id.closure_expr_id), - _ => None, - }; - - self.delegate.fake_read( - discr_place, - FakeReadCause::ForMatchedPlace(closure_def_id), - discr_place.hir_id, - ); - self.walk_pat(discr_place, arm.pat, arm.guard.is_some()); - - if let Some(hir::Guard::If(e)) = arm.guard { - self.consume_expr(e) - } else if let Some(hir::Guard::IfLet(ref l)) = arm.guard { - self.consume_expr(l.init) - } - - self.consume_expr(arm.body); - } - - /// Walks a pat that occurs in isolation (i.e., top-level of fn argument or - /// let binding, and *not* a match arm or nested pat.) - fn walk_irrefutable_pat(&mut self, discr_place: &PlaceWithHirId<'tcx>, pat: &hir::Pat<'_>) { - let closure_def_id = match discr_place.place.base { - PlaceBase::Upvar(upvar_id) => Some(upvar_id.closure_expr_id), - _ => None, - }; - - self.delegate.fake_read( - discr_place, - FakeReadCause::ForLet(closure_def_id), - discr_place.hir_id, - ); - self.walk_pat(discr_place, pat, false); - } - - /// The core driver for walking a pattern - fn walk_pat( - &mut self, - discr_place: &PlaceWithHirId<'tcx>, - pat: &hir::Pat<'_>, - has_guard: bool, - ) { - debug!("walk_pat(discr_place={:?}, pat={:?}, has_guard={:?})", discr_place, pat, has_guard); - - let tcx = self.tcx(); - let ExprUseVisitor { ref mc, body_owner: _, ref mut delegate } = *self; - return_if_err!(mc.cat_pattern(discr_place.clone(), pat, |place, pat| { - if let PatKind::Binding(_, canonical_id, ..) = pat.kind { - debug!("walk_pat: binding place={:?} pat={:?}", place, pat); - if let Some(bm) = - mc.typeck_results.extract_binding_mode(tcx.sess, pat.hir_id, pat.span) - { - debug!("walk_pat: pat.hir_id={:?} bm={:?}", pat.hir_id, bm); - - // pat_ty: the type of the binding being produced. - let pat_ty = return_if_err!(mc.node_ty(pat.hir_id)); - debug!("walk_pat: pat_ty={:?}", pat_ty); - - let def = Res::Local(canonical_id); - if let Ok(ref binding_place) = mc.cat_res(pat.hir_id, pat.span, pat_ty, def) { - delegate.bind(binding_place, binding_place.hir_id); - } - - // Subtle: MIR desugaring introduces immutable borrows for each pattern - // binding when lowering pattern guards to ensure that the guard does not - // modify the scrutinee. - if has_guard { - delegate.borrow(place, discr_place.hir_id, ImmBorrow); - } - - // It is also a borrow or copy/move of the value being matched. - // In a cases of pattern like `let pat = upvar`, don't use the span - // of the pattern, as this just looks confusing, instead use the span - // of the discriminant. - match bm { - ty::BindByReference(m) => { - let bk = ty::BorrowKind::from_mutbl(m); - delegate.borrow(place, discr_place.hir_id, bk); - } - ty::BindByValue(..) => { - debug!("walk_pat binding consuming pat"); - delegate_consume(mc, *delegate, place, discr_place.hir_id); - } - } - } - } - })); - } - - /// Handle the case where the current body contains a closure. - /// - /// When the current body being handled is a closure, then we must make sure that - /// - The parent closure only captures Places from the nested closure that are not local to it. - /// - /// In the following example the closures `c` only captures `p.x` even though `incr` - /// is a capture of the nested closure - /// - /// ``` - /// struct P { x: i32 } - /// let mut p = P { x: 4 }; - /// let c = || { - /// let incr = 10; - /// let nested = || p.x += incr; - /// }; - /// ``` - /// - /// - When reporting the Place back to the Delegate, ensure that the UpvarId uses the enclosing - /// closure as the DefId. - fn walk_captures(&mut self, closure_expr: &hir::Expr<'_>) { - fn upvar_is_local_variable<'tcx>( - upvars: Option<&'tcx FxIndexMap<hir::HirId, hir::Upvar>>, - upvar_id: hir::HirId, - body_owner_is_closure: bool, - ) -> bool { - upvars.map(|upvars| !upvars.contains_key(&upvar_id)).unwrap_or(body_owner_is_closure) - } - - debug!("walk_captures({:?})", closure_expr); - - let tcx = self.tcx(); - let closure_def_id = tcx.hir().local_def_id(closure_expr.hir_id); - let upvars = tcx.upvars_mentioned(self.body_owner); - - // For purposes of this function, generator and closures are equivalent. - let body_owner_is_closure = - matches!(tcx.hir().body_owner_kind(self.body_owner), hir::BodyOwnerKind::Closure,); - - // If we have a nested closure, we want to include the fake reads present in the nested closure. - if let Some(fake_reads) = self.mc.typeck_results.closure_fake_reads.get(&closure_def_id) { - for (fake_read, cause, hir_id) in fake_reads.iter() { - match fake_read.base { - PlaceBase::Upvar(upvar_id) => { - if upvar_is_local_variable( - upvars, - upvar_id.var_path.hir_id, - body_owner_is_closure, - ) { - // The nested closure might be fake reading the current (enclosing) closure's local variables. - // The only places we want to fake read before creating the parent closure are the ones that - // are not local to it/ defined by it. - // - // ```rust,ignore(cannot-test-this-because-pseudo-code) - // let v1 = (0, 1); - // let c = || { // fake reads: v1 - // let v2 = (0, 1); - // let e = || { // fake reads: v1, v2 - // let (_, t1) = v1; - // let (_, t2) = v2; - // } - // } - // ``` - // This check is performed when visiting the body of the outermost closure (`c`) and ensures - // that we don't add a fake read of v2 in c. - continue; - } - } - _ => { - bug!( - "Do not know how to get HirId out of Rvalue and StaticItem {:?}", - fake_read.base - ); - } - }; - self.delegate.fake_read( - &PlaceWithHirId { place: fake_read.clone(), hir_id: *hir_id }, - *cause, - *hir_id, - ); - } - } - - if let Some(min_captures) = self.mc.typeck_results.closure_min_captures.get(&closure_def_id) - { - for (var_hir_id, min_list) in min_captures.iter() { - if upvars.map_or(body_owner_is_closure, |upvars| !upvars.contains_key(var_hir_id)) { - // The nested closure might be capturing the current (enclosing) closure's local variables. - // We check if the root variable is ever mentioned within the enclosing closure, if not - // then for the current body (if it's a closure) these aren't captures, we will ignore them. - continue; - } - for captured_place in min_list { - let place = &captured_place.place; - let capture_info = captured_place.info; - - let place_base = if body_owner_is_closure { - // Mark the place to be captured by the enclosing closure - PlaceBase::Upvar(ty::UpvarId::new(*var_hir_id, self.body_owner)) - } else { - // If the body owner isn't a closure then the variable must - // be a local variable - PlaceBase::Local(*var_hir_id) - }; - let place_with_id = PlaceWithHirId::new( - capture_info.path_expr_id.unwrap_or( - capture_info.capture_kind_expr_id.unwrap_or(closure_expr.hir_id), - ), - place.base_ty, - place_base, - place.projections.clone(), - ); - - match capture_info.capture_kind { - ty::UpvarCapture::ByValue => { - self.delegate_consume(&place_with_id, place_with_id.hir_id); - } - ty::UpvarCapture::ByRef(upvar_borrow) => { - self.delegate.borrow( - &place_with_id, - place_with_id.hir_id, - upvar_borrow, - ); - } - } - } - } - } - } -} - -fn copy_or_move<'a, 'tcx>( - mc: &mc::MemCategorizationContext<'a, 'tcx>, - place_with_id: &PlaceWithHirId<'tcx>, -) -> ConsumeMode { - if !mc.type_is_copy_modulo_regions( - place_with_id.place.ty(), - mc.tcx().hir().span(place_with_id.hir_id), - ) { - ConsumeMode::Move - } else { - ConsumeMode::Copy - } -} - -// - If a place is used in a `ByValue` context then move it if it's not a `Copy` type. -// - If the place that is a `Copy` type consider it an `ImmBorrow`. -fn delegate_consume<'a, 'tcx>( - mc: &mc::MemCategorizationContext<'a, 'tcx>, - delegate: &mut (dyn Delegate<'tcx> + 'a), - place_with_id: &PlaceWithHirId<'tcx>, - diag_expr_id: hir::HirId, -) { - debug!("delegate_consume(place_with_id={:?})", place_with_id); - - let mode = copy_or_move(mc, place_with_id); - - match mode { - ConsumeMode::Move => delegate.consume(place_with_id, diag_expr_id), - ConsumeMode::Copy => delegate.copy(place_with_id, diag_expr_id), - } -} - -fn is_multivariant_adt(ty: Ty<'_>) -> bool { - if let ty::Adt(def, _) = ty.kind() { - // Note that if a non-exhaustive SingleVariant is defined in another crate, we need - // to assume that more cases will be added to the variant in the future. This mean - // that we should handle non-exhaustive SingleVariant the same way we would handle - // a MultiVariant. - // If the variant is not local it must be defined in another crate. - let is_non_exhaustive = match def.adt_kind() { - AdtKind::Struct | AdtKind::Union => { - def.non_enum_variant().is_field_list_non_exhaustive() - } - AdtKind::Enum => def.is_variant_list_non_exhaustive(), - }; - def.variants().len() > 1 || (!def.did().is_local() && is_non_exhaustive) - } else { - false - } -} diff --git a/compiler/rustc_typeck/src/hir_wf_check.rs b/compiler/rustc_typeck/src/hir_wf_check.rs deleted file mode 100644 index 7b080dc29..000000000 --- a/compiler/rustc_typeck/src/hir_wf_check.rs +++ /dev/null @@ -1,188 +0,0 @@ -use crate::collect::ItemCtxt; -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::{ObligationCause, WellFormedLoc}; -use rustc_middle::ty::query::Providers; -use rustc_middle::ty::{self, Region, ToPredicate, TyCtxt, TypeFoldable, TypeFolder}; -use rustc_trait_selection::traits; - -pub fn provide(providers: &mut Providers) { - *providers = Providers { diagnostic_hir_wf_check, ..*providers }; -} - -// Ideally, this would be in `rustc_trait_selection`, but we -// need access to `ItemCtxt` -fn diagnostic_hir_wf_check<'tcx>( - tcx: TyCtxt<'tcx>, - (predicate, loc): (ty::Predicate<'tcx>, WellFormedLoc), -) -> Option<ObligationCause<'tcx>> { - let hir = tcx.hir(); - - let def_id = match loc { - WellFormedLoc::Ty(def_id) => def_id, - WellFormedLoc::Param { function, param_idx: _ } => function, - }; - let hir_id = hir.local_def_id_to_hir_id(def_id); - - // HIR wfcheck should only ever happen as part of improving an existing error - tcx.sess - .delay_span_bug(tcx.def_span(def_id), "Performed HIR wfcheck without an existing error!"); - - let icx = ItemCtxt::new(tcx, def_id.to_def_id()); - - // To perform HIR-based WF checking, we iterate over all HIR types - // that occur 'inside' the item we're checking. For example, - // given the type `Option<MyStruct<u8>>`, we will check - // `Option<MyStruct<u8>>`, `MyStruct<u8>`, and `u8`. - // For each type, we perform a well-formed check, and see if we get - // an error that matches our expected predicate. We save - // the `ObligationCause` corresponding to the *innermost* type, - // which is the most specific type that we can point to. - // In general, the different components of an `hir::Ty` may have - // completely different spans due to macro invocations. Pointing - // to the most accurate part of the type can be the difference - // between a useless span (e.g. the macro invocation site) - // and a useful span (e.g. a user-provided type passed into the macro). - // - // This approach is quite inefficient - we redo a lot of work done - // by the normal WF checker. However, this code is run at most once - // per reported error - it will have no impact when compilation succeeds, - // and should only have an impact if a very large number of errors is - // displayed to the user. - struct HirWfCheck<'tcx> { - tcx: TyCtxt<'tcx>, - predicate: ty::Predicate<'tcx>, - cause: Option<ObligationCause<'tcx>>, - cause_depth: usize, - icx: ItemCtxt<'tcx>, - hir_id: HirId, - param_env: ty::ParamEnv<'tcx>, - depth: usize, - } - - impl<'tcx> Visitor<'tcx> for HirWfCheck<'tcx> { - fn visit_ty(&mut self, ty: &'tcx hir::Ty<'tcx>) { - self.tcx.infer_ctxt().enter(|infcx| { - 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; - } - } - - let mut visitor = HirWfCheck { - tcx, - predicate, - cause: None, - cause_depth: 0, - icx, - hir_id, - param_env: tcx.param_env(def_id.to_def_id()), - depth: 0, - }; - - // Get the starting `hir::Ty` using our `WellFormedLoc`. - // We will walk 'into' this type to try to find - // a more precise span for our predicate. - 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::Const(ty, _) => Some(ty), - ref item => bug!("Unexpected ImplItem {:?}", item), - }, - hir::Node::TraitItem(item) => match item.kind { - hir::TraitItemKind::Type(_, ty) => ty, - hir::TraitItemKind::Const(ty, _) => Some(ty), - ref item => bug!("Unexpected TraitItem {:?}", item), - }, - hir::Node::Item(item) => match item.kind { - hir::ItemKind::Static(ty, _, _) | hir::ItemKind::Const(ty, _) => Some(ty), - hir::ItemKind::Impl(ref impl_) => { - assert!(impl_.of_trait.is_none(), "Unexpected trait impl: {:?}", impl_); - Some(impl_.self_ty) - } - ref item => bug!("Unexpected item {:?}", item), - }, - hir::Node::Field(field) => Some(field.ty), - 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 } => { - let fn_decl = hir.fn_decl_by_hir_id(hir_id).unwrap(); - // Get return type - if param_idx as usize == fn_decl.inputs.len() { - match fn_decl.output { - hir::FnRetTy::Return(ty) => Some(ty), - // The unit type `()` is always well-formed - hir::FnRetTy::DefaultReturn(_span) => None, - } - } else { - Some(&fn_decl.inputs[param_idx as usize]) - } - } - }; - if let Some(ty) = ty { - visitor.visit_ty(ty); - } - visitor.cause -} - -struct EraseAllBoundRegions<'tcx> { - tcx: TyCtxt<'tcx>, -} - -// Higher ranked regions are complicated. -// To make matters worse, the HIR WF check can instantiate them -// outside of a `Binder`, due to the way we (ab)use -// `ItemCtxt::to_ty`. To make things simpler, we just erase all -// of them, regardless of depth. At worse, this will give -// us an inaccurate span for an error message, but cannot -// lead to unsoundness (we call `delay_span_bug` at the start -// of `diagnostic_hir_wf_check`). -impl<'tcx> TypeFolder<'tcx> for EraseAllBoundRegions<'tcx> { - fn tcx<'a>(&'a self) -> TyCtxt<'tcx> { - self.tcx - } - fn fold_region(&mut self, r: Region<'tcx>) -> Region<'tcx> { - if r.is_late_bound() { self.tcx.lifetimes.re_erased } else { r } - } -} diff --git a/compiler/rustc_typeck/src/impl_wf_check.rs b/compiler/rustc_typeck/src/impl_wf_check.rs deleted file mode 100644 index 9fee1eaae..000000000 --- a/compiler/rustc_typeck/src/impl_wf_check.rs +++ /dev/null @@ -1,228 +0,0 @@ -//! This pass enforces various "well-formedness constraints" on impls. -//! Logically, it is part of wfcheck -- but we do it early so that we -//! can stop compilation afterwards, since part of the trait matching -//! infrastructure gets very grumpy if these conditions don't hold. In -//! particular, if there are type parameters that are not part of the -//! impl, then coherence will report strange inference ambiguity -//! errors; if impls have duplicate items, we get misleading -//! specialization errors. These things can (and probably should) be -//! fixed, but for the moment it's easier to do these checks early. - -use crate::constrained_generic_params as cgp; -use min_specialization::check_min_specialization; - -use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_errors::struct_span_err; -use rustc_hir::def::DefKind; -use rustc_hir::def_id::LocalDefId; -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 -/// appear in the trait ref or self type (or are constrained by a -/// where-clause). These rules are needed to ensure that, given a -/// trait ref like `<T as Trait<U>>`, we can derive the values of all -/// parameters on the impl (which is needed to make specialization -/// possible). -/// -/// However, in the case of lifetimes, we only enforce these rules if -/// the lifetime parameter is used in an associated type. This is a -/// concession to backwards compatibility; see comment at the end of -/// the fn for details. -/// -/// Example: -/// -/// ```rust,ignore (pseudo-Rust) -/// impl<T> Trait<Foo> for Bar { ... } -/// // ^ T does not appear in `Foo` or `Bar`, error! -/// -/// impl<T> Trait<Foo<T>> for Bar { ... } -/// // ^ T appears in `Foo<T>`, ok. -/// -/// impl<T> Trait<Foo> for Bar where Bar: Iterator<Item = T> { ... } -/// // ^ T is bound to `<Bar as Iterator>::Item`, ok. -/// -/// impl<'a> Trait<Foo> for Bar { } -/// // ^ 'a is unused, but for back-compat we allow it -/// -/// impl<'a> Trait<Foo> for Bar { type X = &'a i32; } -/// // ^ 'a is unused and appears in assoc type, error -/// ``` -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 min_specialization { - check_min_specialization(tcx, id.def_id); - } - } - } -} - -pub fn provide(providers: &mut Providers) { - *providers = Providers { check_mod_impl_wf, ..*providers }; -} - -fn enforce_impl_params_are_constrained(tcx: TyCtxt<'_>, impl_def_id: LocalDefId) { - // Every lifetime used in an associated type must be constrained. - let impl_self_ty = tcx.type_of(impl_def_id); - if impl_self_ty.references_error() { - // Don't complain about unconstrained type params when self ty isn't known due to errors. - // (#36836) - tcx.sess.delay_span_bug( - tcx.def_span(impl_def_id), - &format!( - "potentially unconstrained type parameters weren't evaluated: {:?}", - impl_self_ty, - ), - ); - return; - } - let impl_generics = tcx.generics_of(impl_def_id); - let impl_predicates = tcx.predicates_of(impl_def_id); - let impl_trait_ref = tcx.impl_trait_ref(impl_def_id); - - let mut input_parameters = cgp::parameters_for_impl(impl_self_ty, impl_trait_ref); - cgp::identify_constrained_generic_params( - tcx, - impl_predicates, - impl_trait_ref, - &mut input_parameters, - ); - - // Disallow unconstrained lifetimes, but only if they appear in assoc types. - let lifetimes_in_associated_types: FxHashSet<_> = tcx - .associated_item_def_ids(impl_def_id) - .iter() - .flat_map(|def_id| { - let item = tcx.associated_item(def_id); - match item.kind { - ty::AssocKind::Type => { - if item.defaultness(tcx).has_value() { - cgp::parameters_for(&tcx.type_of(def_id), true) - } else { - Vec::new() - } - } - ty::AssocKind::Fn | ty::AssocKind::Const => Vec::new(), - } - }) - .collect(); - - for param in &impl_generics.params { - match param.kind { - // Disallow ANY unconstrained type parameters. - ty::GenericParamDefKind::Type { .. } => { - let param_ty = ty::ParamTy::for_def(param); - if !input_parameters.contains(&cgp::Parameter::from(param_ty)) { - report_unused_parameter(tcx, tcx.def_span(param.def_id), "type", param_ty.name); - } - } - ty::GenericParamDefKind::Lifetime => { - let param_lt = cgp::Parameter::from(param.to_early_bound_region_data()); - if lifetimes_in_associated_types.contains(¶m_lt) && // (*) - !input_parameters.contains(¶m_lt) - { - report_unused_parameter( - tcx, - tcx.def_span(param.def_id), - "lifetime", - param.name, - ); - } - } - ty::GenericParamDefKind::Const { .. } => { - let param_ct = ty::ParamConst::for_def(param); - if !input_parameters.contains(&cgp::Parameter::from(param_ct)) { - report_unused_parameter( - tcx, - tcx.def_span(param.def_id), - "const", - param_ct.name, - ); - } - } - } - } - - // (*) This is a horrible concession to reality. I think it'd be - // better to just ban unconstrained lifetimes outright, but in - // practice people do non-hygienic macros like: - // - // ``` - // macro_rules! __impl_slice_eq1 { - // ($Lhs: ty, $Rhs: ty, $Bound: ident) => { - // impl<'a, 'b, A: $Bound, B> PartialEq<$Rhs> for $Lhs where A: PartialEq<B> { - // .... - // } - // } - // } - // ``` - // - // In a concession to backwards compatibility, we continue to - // permit those, so long as the lifetimes aren't used in - // associated types. I believe this is sound, because lifetimes - // used elsewhere are not projected back out. -} - -fn report_unused_parameter(tcx: TyCtxt<'_>, span: Span, kind: &str, name: Symbol) { - let mut err = struct_span_err!( - tcx.sess, - span, - E0207, - "the {} parameter `{}` is not constrained by the \ - impl trait, self type, or predicates", - kind, - name - ); - err.span_label(span, format!("unconstrained {} parameter", kind)); - if kind == "const" { - err.note( - "expressions using a const parameter must map each value to a distinct output value", - ); - err.note( - "proving the result of expressions other than the parameter are unique is not supported", - ); - } - 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_typeck/src/impl_wf_check/min_specialization.rs deleted file mode 100644 index 2741d9f77..000000000 --- a/compiler/rustc_typeck/src/impl_wf_check/min_specialization.rs +++ /dev/null @@ -1,444 +0,0 @@ -//! # Minimal Specialization -//! -//! This module contains the checks for sound specialization used when the -//! `min_specialization` feature is enabled. This requires that the impl is -//! *always applicable*. -//! -//! If `impl1` specializes `impl2` then `impl1` is always applicable if we know -//! that all the bounds of `impl2` are satisfied, and all of the bounds of -//! `impl1` are satisfied for some choice of lifetimes then we know that -//! `impl1` applies for any choice of lifetimes. -//! -//! ## Basic approach -//! -//! To enforce this requirement on specializations we take the following -//! approach: -//! -//! 1. Match up the substs for `impl2` so that the implemented trait and -//! self-type match those for `impl1`. -//! 2. Check for any direct use of `'static` in the substs of `impl2`. -//! 3. Check that all of the generic parameters of `impl1` occur at most once -//! in the *unconstrained* substs for `impl2`. A parameter is constrained if -//! its value is completely determined by an associated type projection -//! predicate. -//! 4. Check that all predicates on `impl1` either exist on `impl2` (after -//! matching substs), or are well-formed predicates for the trait's type -//! arguments. -//! -//! ## Example -//! -//! Suppose we have the following always applicable impl: -//! -//! ```ignore (illustrative) -//! impl<T> SpecExtend<T> for std::vec::IntoIter<T> { /* specialized impl */ } -//! impl<T, I: Iterator<Item=T>> SpecExtend<T> for I { /* default impl */ } -//! ``` -//! -//! We get that the subst for `impl2` are `[T, std::vec::IntoIter<T>]`. `T` is -//! constrained to be `<I as Iterator>::Item`, so we check only -//! `std::vec::IntoIter<T>` for repeated parameters, which it doesn't have. The -//! predicates of `impl1` are only `T: Sized`, which is also a predicate of -//! `impl2`. So this specialization is sound. -//! -//! ## Extensions -//! -//! Unfortunately not all specializations in the standard library are allowed -//! by this. So there are two extensions to these rules that allow specializing -//! on some traits: that is, using them as bounds on the specializing impl, -//! even when they don't occur in the base impl. -//! -//! ### rustc_specialization_trait -//! -//! If a trait is always applicable, then it's sound to specialize on it. We -//! check trait is always applicable in the same way as impls, except that step -//! 4 is now "all predicates on `impl1` are always applicable". We require that -//! `specialization` or `min_specialization` is enabled to implement these -//! traits. -//! -//! ### rustc_unsafe_specialization_marker -//! -//! There are also some specialization on traits with no methods, including the -//! stable `FusedIterator` trait. We allow marking marker traits with an -//! unstable attribute that means we ignore them in point 3 of the checks -//! above. This is unsound, in the sense that the specialized impl may be used -//! when it doesn't apply, but we allow it in the short term since it can't -//! cause use after frees with purely safe code in the same way as specializing -//! on traits with methods can. - -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::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::error_reporting::InferCtxtExt; -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) { - check_always_applicable(tcx, impl_def_id, node); - } -} - -fn parent_specialization_node(tcx: TyCtxt<'_>, impl1_def_id: LocalDefId) -> Option<Node> { - let trait_ref = tcx.impl_trait_ref(impl1_def_id)?; - let trait_def = tcx.trait_def(trait_ref.def_id); - - let impl2_node = trait_def.ancestors(tcx, impl1_def_id.to_def_id()).ok()?.nth(1)?; - - let always_applicable_trait = - matches!(trait_def.specialization_kind, TraitSpecializationKind::AlwaysApplicable); - if impl2_node.is_from_trait() && !always_applicable_trait { - // Implementing a normal trait isn't a specialization. - return None; - } - Some(impl2_node) -} - -/// Check that `impl1` is a sound specialization -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 parent_substs = if impl2_node.is_from_trait() { - impl2_substs.to_vec() - } else { - unconstrained_parent_impl_substs(tcx, impl2_def_id, impl2_substs) - }; - - 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(tcx, impl1_def_id, impl1_substs, impl2_node, impl2_substs, span); - } -} - -/// Given a specializing impl `impl1`, and the base impl `impl2`, returns two -/// substitutions `(S1, S2)` that equate their trait references. The returned -/// types are expressed in terms of the generics of `impl1`. -/// -/// Example -/// -/// 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>( - tcx: TyCtxt<'tcx>, - impl1_def_id: LocalDefId, - impl2_node: Node, -) -> Option<(SubstsRef<'tcx>, SubstsRef<'tcx>)> { - tcx.infer_ctxt().enter(|ref infcx| { - 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 errors = ocx.select_all_or_error(); - if !errors.is_empty() { - ocx.infcx.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); - tcx.sess.emit_err(SubstsOnOverriddenImpl { span }); - return None; - }; - Some((impl1_substs, impl2_substs)) - }) -} - -/// Returns a list of all of the unconstrained subst of the given impl. -/// -/// For example given the impl: -/// -/// impl<'a, T, I> ... where &'a I: IntoIterator<Item=&'a T> -/// -/// This would return the substs corresponding to `['a, I]`, because knowing -/// `'a` and `I` determines the value of `T`. -fn unconstrained_parent_impl_substs<'tcx>( - tcx: TyCtxt<'tcx>, - impl_def_id: DefId, - impl_substs: SubstsRef<'tcx>, -) -> Vec<GenericArg<'tcx>> { - let impl_generic_predicates = tcx.predicates_of(impl_def_id); - let mut unconstrained_parameters = FxHashSet::default(); - let mut constrained_params = FxHashSet::default(); - let impl_trait_ref = tcx.impl_trait_ref(impl_def_id); - - // Unfortunately the functions in `constrained_generic_parameters` don't do - // what we want here. We want only a list of constrained parameters while - // the functions in `cgp` add the constrained parameters to a list of - // unconstrained parameters. - for (predicate, _) in impl_generic_predicates.predicates.iter() { - if let ty::PredicateKind::Projection(proj) = predicate.kind().skip_binder() { - let projection_ty = proj.projection_ty; - let projected_ty = proj.term; - - let unbound_trait_ref = projection_ty.trait_ref(tcx); - if Some(unbound_trait_ref) == impl_trait_ref { - continue; - } - - unconstrained_parameters.extend(cgp::parameters_for(&projection_ty, true)); - - for param in cgp::parameters_for(&projected_ty, false) { - if !unconstrained_parameters.contains(¶m) { - constrained_params.insert(param.0); - } - } - - unconstrained_parameters.extend(cgp::parameters_for(&projected_ty, true)); - } - } - - impl_substs - .iter() - .enumerate() - .filter(|&(idx, _)| !constrained_params.contains(&(idx as u32))) - .map(|(_, arg)| arg) - .collect() -} - -/// Check that parameters of the derived impl don't occur more than once in the -/// equated substs of the base impl. -/// -/// For example forbid the following: -/// -/// impl<A> Tr for A { } -/// impl<B> Tr for (B, B) { } -/// -/// Note that only consider the unconstrained parameters of the base impl: -/// -/// 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 -/// `[Vec<T>]`. This means we allow this impl. -fn check_duplicate_params<'tcx>( - tcx: TyCtxt<'tcx>, - impl1_substs: SubstsRef<'tcx>, - parent_substs: &Vec<GenericArg<'tcx>>, - span: Span, -) { - let mut base_params = cgp::parameters_for(parent_substs, true); - base_params.sort_by_key(|param| param.0); - if let (_, [duplicate, ..]) = base_params.partition_dedup() { - let param = impl1_substs[duplicate.0 as usize]; - tcx.sess - .struct_span_err(span, &format!("specializing impl repeats parameter `{}`", param)) - .emit(); - } -} - -/// Check that `'static` lifetimes are not introduced by the specializing impl. -/// -/// For example forbid the following: -/// -/// impl<A> Tr for A { } -/// impl Tr for &'static i32 { } -fn check_static_lifetimes<'tcx>( - tcx: TyCtxt<'tcx>, - parent_substs: &Vec<GenericArg<'tcx>>, - span: Span, -) { - if tcx.any_free_region_meets(parent_substs, |r| r.is_static()) { - tcx.sess.struct_span_err(span, "cannot specialize on `'static` lifetime").emit(); - } -} - -/// Check whether predicates on the specializing impl (`impl1`) are allowed. -/// -/// Each predicate `P` must be: -/// -/// * global (not reference any parameters) -/// * `T: Tr` predicate where `Tr` is an always-applicable trait -/// * on the base `impl impl2` -/// * Currently this check is done using syntactic equality, which is -/// conservative but generally sufficient. -/// * a well-formed predicate of a type argument of the trait being implemented, -/// including the `Self`-type. -fn check_predicates<'tcx>( - tcx: TyCtxt<'tcx>, - impl1_def_id: LocalDefId, - impl1_substs: SubstsRef<'tcx>, - impl2_node: Node, - impl2_substs: SubstsRef<'tcx>, - span: Span, -) { - let instantiated = tcx.predicates_of(impl1_def_id).instantiate(tcx, impl1_substs); - let impl1_predicates: Vec<_> = traits::elaborate_predicates_with_span( - tcx, - std::iter::zip( - instantiated.predicates, - // Don't drop predicates (unsound!) because `spans` is too short - instantiated.spans.into_iter().chain(std::iter::repeat(span)), - ), - ) - .map(|obligation| (obligation.predicate, obligation.cause.span)) - .collect(); - - let mut impl2_predicates = if impl2_node.is_from_trait() { - // Always applicable traits have to be always applicable without any - // assumptions. - Vec::new() - } else { - traits::elaborate_predicates( - tcx, - tcx.predicates_of(impl2_node.def_id()) - .instantiate(tcx, impl2_substs) - .predicates - .into_iter(), - ) - .map(|obligation| obligation.predicate) - .collect() - }; - debug!( - "check_always_applicable(\nimpl1_predicates={:?},\nimpl2_predicates={:?}\n)", - impl1_predicates, impl2_predicates, - ); - - // Since impls of always applicable traits don't get to assume anything, we - // can also assume their supertraits apply. - // - // For example, we allow: - // - // #[rustc_specialization_trait] - // trait AlwaysApplicable: Debug { } - // - // impl<T> Tr for T { } - // impl<T: AlwaysApplicable> Tr for T { } - // - // Specializing on `AlwaysApplicable` allows also specializing on `Debug` - // which is sound because we forbid impls like the following - // - // impl<D: Debug> AlwaysApplicable for D { } - let always_applicable_traits = impl1_predicates.iter().copied().filter(|&(predicate, _)| { - matches!( - trait_predicate_kind(tcx, predicate), - Some(TraitSpecializationKind::AlwaysApplicable) - ) - }); - - // Include the well-formed predicates of the type parameters of the impl. - for arg in tcx.impl_trait_ref(impl1_def_id).unwrap().substs { - tcx.infer_ctxt().enter(|ref infcx| { - 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, - ) - .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) - .map(|obligation| obligation.predicate), - ); - - for (predicate, span) in impl1_predicates { - if !impl2_predicates.contains(&predicate) { - check_specialization_on(tcx, predicate, span) - } - } -} - -fn check_specialization_on<'tcx>(tcx: TyCtxt<'tcx>, predicate: ty::Predicate<'tcx>, span: Span) { - debug!("can_specialize_on(predicate = {:?})", predicate); - match predicate.kind().skip_binder() { - // Global predicates are either always true or always false, so we - // are fine to specialize on. - _ if predicate.is_global() => (), - // We allow specializing on explicitly marked traits with no associated - // items. - ty::PredicateKind::Trait(ty::TraitPredicate { - trait_ref, - constness: ty::BoundConstness::NotConst, - polarity: _, - }) => { - if !matches!( - trait_predicate_kind(tcx, predicate), - Some(TraitSpecializationKind::Marker) - ) { - tcx.sess - .struct_span_err( - span, - &format!( - "cannot specialize on trait `{}`", - tcx.def_path_str(trait_ref.def_id), - ), - ) - .emit(); - } - } - ty::PredicateKind::Projection(ty::ProjectionPredicate { projection_ty, term }) => { - tcx.sess - .struct_span_err( - span, - &format!("cannot specialize on associated type `{projection_ty} == {term}`",), - ) - .emit(); - } - _ => { - tcx.sess - .struct_span_err(span, &format!("cannot specialize on predicate `{}`", predicate)) - .emit(); - } - } -} - -fn trait_predicate_kind<'tcx>( - tcx: TyCtxt<'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::TypeOutlives(_) - | ty::PredicateKind::Projection(_) - | ty::PredicateKind::WellFormed(_) - | ty::PredicateKind::Subtype(_) - | ty::PredicateKind::Coerce(_) - | ty::PredicateKind::ObjectSafe(_) - | ty::PredicateKind::ClosureKind(..) - | ty::PredicateKind::ConstEvaluatable(..) - | ty::PredicateKind::ConstEquate(..) - | ty::PredicateKind::TypeWellFormedFromEnv(..) => None, - } -} diff --git a/compiler/rustc_typeck/src/lib.rs b/compiler/rustc_typeck/src/lib.rs deleted file mode 100644 index b1ce972e1..000000000 --- a/compiler/rustc_typeck/src/lib.rs +++ /dev/null @@ -1,557 +0,0 @@ -/*! - -# 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)] -#![cfg_attr(bootstrap, feature(label_break_value))] -#![feature(let_chains)] -#![cfg_attr(bootstrap, feature(let_else))] -#![feature(min_specialization)] -#![feature(never_type)] -#![feature(once_cell)] -#![feature(slice_partition_dedup)] -#![feature(try_blocks)] -#![feature(is_some_with)] -#![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 expr_use_visitor; - -mod astconv; -mod bounds; -mod check_unused; -mod coherence; -mod collect; -mod constrained_generic_params; -mod errors; -pub mod hir_wf_check; -mod impl_wf_check; -mod mem_categorization; -mod outlives; -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::InferCtxtExt 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 { - tcx.infer_ctxt().enter(|ref infcx| { - 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.report_mismatched_types(cause, expected, actual, err).emit(); - return false; - } - }; - - match &errors[..] { - [] => true, - errors => { - infcx.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(); - tcx.infer_ctxt().enter(|infcx| { - // 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.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.def_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/mem_categorization.rs b/compiler/rustc_typeck/src/mem_categorization.rs deleted file mode 100644 index ced919f66..000000000 --- a/compiler/rustc_typeck/src/mem_categorization.rs +++ /dev/null @@ -1,786 +0,0 @@ -//! # Categorization -//! -//! The job of the categorization module is to analyze an expression to -//! determine what kind of memory is used in evaluating it (for example, -//! where dereferences occur and what kind of pointer is dereferenced; -//! whether the memory is mutable, etc.). -//! -//! Categorization effectively transforms all of our expressions into -//! expressions of the following forms (the actual enum has many more -//! possibilities, naturally, but they are all variants of these base -//! forms): -//! ```ignore (not-rust) -//! E = rvalue // some computed rvalue -//! | x // address of a local variable or argument -//! | *E // deref of a ptr -//! | E.comp // access to an interior component -//! ``` -//! Imagine a routine ToAddr(Expr) that evaluates an expression and returns an -//! address where the result is to be found. If Expr is a place, then this -//! is the address of the place. If `Expr` is an rvalue, this is the address of -//! some temporary spot in memory where the result is stored. -//! -//! Now, `cat_expr()` classifies the expression `Expr` and the address `A = ToAddr(Expr)` -//! as follows: -//! -//! - `cat`: what kind of expression was this? This is a subset of the -//! full expression forms which only includes those that we care about -//! for the purpose of the analysis. -//! - `mutbl`: mutability of the address `A`. -//! - `ty`: the type of data found at the address `A`. -//! -//! The resulting categorization tree differs somewhat from the expressions -//! themselves. For example, auto-derefs are explicit. Also, an index `a[b]` is -//! decomposed into two operations: a dereference to reach the array data and -//! then an index to jump forward to the relevant item. -//! -//! ## By-reference upvars -//! -//! One part of the codegen which may be non-obvious is that we translate -//! closure upvars into the dereference of a borrowed pointer; this more closely -//! resembles the runtime codegen. So, for example, if we had: -//! -//! let mut x = 3; -//! let y = 5; -//! let inc = || x += y; -//! -//! Then when we categorize `x` (*within* the closure) we would yield a -//! result of `*x'`, effectively, where `x'` is a `Categorization::Upvar` reference -//! tied to `x`. The type of `x'` will be a borrowed pointer. - -use rustc_middle::hir::place::*; -use rustc_middle::ty::adjustment; -use rustc_middle::ty::fold::TypeFoldable; -use rustc_middle::ty::visit::TypeVisitable; -use rustc_middle::ty::{self, Ty, TyCtxt}; - -use rustc_data_structures::fx::FxIndexMap; -use rustc_hir as hir; -use rustc_hir::def::{CtorOf, DefKind, Res}; -use rustc_hir::def_id::LocalDefId; -use rustc_hir::pat_util::EnumerateAndAdjustIterator; -use rustc_hir::PatKind; -use rustc_index::vec::Idx; -use rustc_infer::infer::InferCtxt; -use rustc_span::Span; -use rustc_target::abi::VariantIdx; -use rustc_trait_selection::infer::InferCtxtExt; - -pub(crate) trait HirNode { - fn hir_id(&self) -> hir::HirId; - fn span(&self) -> Span; -} - -impl HirNode for hir::Expr<'_> { - fn hir_id(&self) -> hir::HirId { - self.hir_id - } - fn span(&self) -> Span { - self.span - } -} - -impl HirNode for hir::Pat<'_> { - fn hir_id(&self) -> hir::HirId { - self.hir_id - } - fn span(&self) -> Span { - self.span - } -} - -#[derive(Clone)] -pub(crate) struct MemCategorizationContext<'a, 'tcx> { - pub(crate) typeck_results: &'a ty::TypeckResults<'tcx>, - infcx: &'a InferCtxt<'a, 'tcx>, - param_env: ty::ParamEnv<'tcx>, - body_owner: LocalDefId, - upvars: Option<&'tcx FxIndexMap<hir::HirId, hir::Upvar>>, -} - -pub(crate) type McResult<T> = Result<T, ()>; - -impl<'a, 'tcx> MemCategorizationContext<'a, 'tcx> { - /// Creates a `MemCategorizationContext`. - pub(crate) fn new( - infcx: &'a InferCtxt<'a, 'tcx>, - param_env: ty::ParamEnv<'tcx>, - body_owner: LocalDefId, - typeck_results: &'a ty::TypeckResults<'tcx>, - ) -> MemCategorizationContext<'a, 'tcx> { - MemCategorizationContext { - typeck_results, - infcx, - param_env, - body_owner, - upvars: infcx.tcx.upvars_mentioned(body_owner), - } - } - - pub(crate) fn tcx(&self) -> TyCtxt<'tcx> { - self.infcx.tcx - } - - pub(crate) fn type_is_copy_modulo_regions(&self, ty: Ty<'tcx>, span: Span) -> bool { - self.infcx.type_is_copy_modulo_regions(self.param_env, ty, span) - } - - fn resolve_vars_if_possible<T>(&self, value: T) -> T - where - T: TypeFoldable<'tcx>, - { - self.infcx.resolve_vars_if_possible(value) - } - - fn is_tainted_by_errors(&self) -> bool { - self.infcx.is_tainted_by_errors() - } - - fn resolve_type_vars_or_error( - &self, - id: hir::HirId, - ty: Option<Ty<'tcx>>, - ) -> McResult<Ty<'tcx>> { - match ty { - Some(ty) => { - let ty = self.resolve_vars_if_possible(ty); - if ty.references_error() || ty.is_ty_var() { - debug!("resolve_type_vars_or_error: error from {:?}", ty); - Err(()) - } else { - Ok(ty) - } - } - // FIXME - None if self.is_tainted_by_errors() => Err(()), - None => { - bug!( - "no type for node {}: {} in mem_categorization", - id, - self.tcx().hir().node_to_string(id) - ); - } - } - } - - pub(crate) fn node_ty(&self, hir_id: hir::HirId) -> McResult<Ty<'tcx>> { - self.resolve_type_vars_or_error(hir_id, self.typeck_results.node_type_opt(hir_id)) - } - - fn expr_ty(&self, expr: &hir::Expr<'_>) -> McResult<Ty<'tcx>> { - self.resolve_type_vars_or_error(expr.hir_id, self.typeck_results.expr_ty_opt(expr)) - } - - pub(crate) fn expr_ty_adjusted(&self, expr: &hir::Expr<'_>) -> McResult<Ty<'tcx>> { - self.resolve_type_vars_or_error(expr.hir_id, self.typeck_results.expr_ty_adjusted_opt(expr)) - } - - /// Returns the type of value that this pattern matches against. - /// Some non-obvious cases: - /// - /// - a `ref x` binding matches against a value of type `T` and gives - /// `x` the type `&T`; we return `T`. - /// - a pattern with implicit derefs (thanks to default binding - /// modes #42640) may look like `Some(x)` but in fact have - /// implicit deref patterns attached (e.g., it is really - /// `&Some(x)`). In that case, we return the "outermost" type - /// (e.g., `&Option<T>). - pub(crate) fn pat_ty_adjusted(&self, pat: &hir::Pat<'_>) -> McResult<Ty<'tcx>> { - // Check for implicit `&` types wrapping the pattern; note - // that these are never attached to binding patterns, so - // actually this is somewhat "disjoint" from the code below - // that aims to account for `ref x`. - if let Some(vec) = self.typeck_results.pat_adjustments().get(pat.hir_id) { - if let Some(first_ty) = vec.first() { - debug!("pat_ty(pat={:?}) found adjusted ty `{:?}`", pat, first_ty); - return Ok(*first_ty); - } - } - - self.pat_ty_unadjusted(pat) - } - - /// Like `pat_ty`, but ignores implicit `&` patterns. - fn pat_ty_unadjusted(&self, pat: &hir::Pat<'_>) -> McResult<Ty<'tcx>> { - let base_ty = self.node_ty(pat.hir_id)?; - debug!("pat_ty(pat={:?}) base_ty={:?}", pat, base_ty); - - // This code detects whether we are looking at a `ref x`, - // and if so, figures out what the type *being borrowed* is. - let ret_ty = match pat.kind { - PatKind::Binding(..) => { - let bm = *self - .typeck_results - .pat_binding_modes() - .get(pat.hir_id) - .expect("missing binding mode"); - - if let ty::BindByReference(_) = bm { - // a bind-by-ref means that the base_ty will be the type of the ident itself, - // but what we want here is the type of the underlying value being borrowed. - // So peel off one-level, turning the &T into T. - match base_ty.builtin_deref(false) { - Some(t) => t.ty, - None => { - debug!("By-ref binding of non-derefable type {:?}", base_ty); - return Err(()); - } - } - } else { - base_ty - } - } - _ => base_ty, - }; - debug!("pat_ty(pat={:?}) ret_ty={:?}", pat, ret_ty); - - Ok(ret_ty) - } - - pub(crate) fn cat_expr(&self, expr: &hir::Expr<'_>) -> McResult<PlaceWithHirId<'tcx>> { - // This recursion helper avoids going through *too many* - // adjustments, since *only* non-overloaded deref recurses. - fn helper<'a, 'tcx>( - mc: &MemCategorizationContext<'a, 'tcx>, - expr: &hir::Expr<'_>, - adjustments: &[adjustment::Adjustment<'tcx>], - ) -> McResult<PlaceWithHirId<'tcx>> { - match adjustments.split_last() { - None => mc.cat_expr_unadjusted(expr), - Some((adjustment, previous)) => { - mc.cat_expr_adjusted_with(expr, || helper(mc, expr, previous), adjustment) - } - } - } - - helper(self, expr, self.typeck_results.expr_adjustments(expr)) - } - - pub(crate) fn cat_expr_adjusted( - &self, - expr: &hir::Expr<'_>, - previous: PlaceWithHirId<'tcx>, - adjustment: &adjustment::Adjustment<'tcx>, - ) -> McResult<PlaceWithHirId<'tcx>> { - self.cat_expr_adjusted_with(expr, || Ok(previous), adjustment) - } - - fn cat_expr_adjusted_with<F>( - &self, - expr: &hir::Expr<'_>, - previous: F, - adjustment: &adjustment::Adjustment<'tcx>, - ) -> McResult<PlaceWithHirId<'tcx>> - where - F: FnOnce() -> McResult<PlaceWithHirId<'tcx>>, - { - debug!("cat_expr_adjusted_with({:?}): {:?}", adjustment, expr); - let target = self.resolve_vars_if_possible(adjustment.target); - match adjustment.kind { - adjustment::Adjust::Deref(overloaded) => { - // Equivalent to *expr or something similar. - let base = if let Some(deref) = overloaded { - let ref_ty = self - .tcx() - .mk_ref(deref.region, ty::TypeAndMut { ty: target, mutbl: deref.mutbl }); - self.cat_rvalue(expr.hir_id, expr.span, ref_ty) - } else { - previous()? - }; - self.cat_deref(expr, base) - } - - adjustment::Adjust::NeverToAny - | adjustment::Adjust::Pointer(_) - | adjustment::Adjust::Borrow(_) => { - // Result is an rvalue. - Ok(self.cat_rvalue(expr.hir_id, expr.span, target)) - } - } - } - - pub(crate) fn cat_expr_unadjusted( - &self, - expr: &hir::Expr<'_>, - ) -> McResult<PlaceWithHirId<'tcx>> { - debug!("cat_expr: id={} expr={:?}", expr.hir_id, expr); - - let expr_ty = self.expr_ty(expr)?; - match expr.kind { - hir::ExprKind::Unary(hir::UnOp::Deref, ref e_base) => { - if self.typeck_results.is_method_call(expr) { - self.cat_overloaded_place(expr, e_base) - } else { - let base = self.cat_expr(e_base)?; - self.cat_deref(expr, base) - } - } - - hir::ExprKind::Field(ref base, _) => { - let base = self.cat_expr(base)?; - debug!("cat_expr(cat_field): id={} expr={:?} base={:?}", expr.hir_id, expr, base); - - let field_idx = self - .typeck_results - .field_indices() - .get(expr.hir_id) - .cloned() - .expect("Field index not found"); - - Ok(self.cat_projection( - expr, - base, - expr_ty, - ProjectionKind::Field(field_idx as u32, VariantIdx::new(0)), - )) - } - - hir::ExprKind::Index(ref base, _) => { - if self.typeck_results.is_method_call(expr) { - // If this is an index implemented by a method call, then it - // will include an implicit deref of the result. - // The call to index() returns a `&T` value, which - // is an rvalue. That is what we will be - // dereferencing. - self.cat_overloaded_place(expr, base) - } else { - let base = self.cat_expr(base)?; - Ok(self.cat_projection(expr, base, expr_ty, ProjectionKind::Index)) - } - } - - hir::ExprKind::Path(ref qpath) => { - let res = self.typeck_results.qpath_res(qpath, expr.hir_id); - self.cat_res(expr.hir_id, expr.span, expr_ty, res) - } - - hir::ExprKind::Type(ref e, _) => self.cat_expr(e), - - hir::ExprKind::AddrOf(..) - | hir::ExprKind::Call(..) - | hir::ExprKind::Assign(..) - | hir::ExprKind::AssignOp(..) - | hir::ExprKind::Closure { .. } - | hir::ExprKind::Ret(..) - | hir::ExprKind::Unary(..) - | hir::ExprKind::Yield(..) - | hir::ExprKind::MethodCall(..) - | hir::ExprKind::Cast(..) - | hir::ExprKind::DropTemps(..) - | hir::ExprKind::Array(..) - | hir::ExprKind::If(..) - | hir::ExprKind::Tup(..) - | hir::ExprKind::Binary(..) - | hir::ExprKind::Block(..) - | hir::ExprKind::Let(..) - | hir::ExprKind::Loop(..) - | hir::ExprKind::Match(..) - | hir::ExprKind::Lit(..) - | hir::ExprKind::ConstBlock(..) - | hir::ExprKind::Break(..) - | hir::ExprKind::Continue(..) - | hir::ExprKind::Struct(..) - | hir::ExprKind::Repeat(..) - | hir::ExprKind::InlineAsm(..) - | hir::ExprKind::Box(..) - | hir::ExprKind::Err => Ok(self.cat_rvalue(expr.hir_id, expr.span, expr_ty)), - } - } - - pub(crate) fn cat_res( - &self, - hir_id: hir::HirId, - span: Span, - expr_ty: Ty<'tcx>, - res: Res, - ) -> McResult<PlaceWithHirId<'tcx>> { - debug!("cat_res: id={:?} expr={:?} def={:?}", hir_id, expr_ty, res); - - match res { - Res::Def( - DefKind::Ctor(..) - | DefKind::Const - | DefKind::ConstParam - | DefKind::AssocConst - | DefKind::Fn - | DefKind::AssocFn, - _, - ) - | Res::SelfCtor(..) => Ok(self.cat_rvalue(hir_id, span, expr_ty)), - - Res::Def(DefKind::Static(_), _) => { - Ok(PlaceWithHirId::new(hir_id, expr_ty, PlaceBase::StaticItem, Vec::new())) - } - - Res::Local(var_id) => { - if self.upvars.map_or(false, |upvars| upvars.contains_key(&var_id)) { - self.cat_upvar(hir_id, var_id) - } else { - Ok(PlaceWithHirId::new(hir_id, expr_ty, PlaceBase::Local(var_id), Vec::new())) - } - } - - def => span_bug!(span, "unexpected definition in memory categorization: {:?}", def), - } - } - - /// Categorize an upvar. - /// - /// Note: the actual upvar access contains invisible derefs of closure - /// environment and upvar reference as appropriate. Only regionck cares - /// about these dereferences, so we let it compute them as needed. - fn cat_upvar(&self, hir_id: hir::HirId, var_id: hir::HirId) -> McResult<PlaceWithHirId<'tcx>> { - let closure_expr_def_id = self.body_owner; - - let upvar_id = ty::UpvarId { - var_path: ty::UpvarPath { hir_id: var_id }, - closure_expr_id: closure_expr_def_id, - }; - let var_ty = self.node_ty(var_id)?; - - let ret = PlaceWithHirId::new(hir_id, var_ty, PlaceBase::Upvar(upvar_id), Vec::new()); - - debug!("cat_upvar ret={:?}", ret); - Ok(ret) - } - - pub(crate) fn cat_rvalue( - &self, - hir_id: hir::HirId, - span: Span, - expr_ty: Ty<'tcx>, - ) -> PlaceWithHirId<'tcx> { - debug!("cat_rvalue hir_id={:?}, expr_ty={:?}, span={:?}", hir_id, expr_ty, span); - let ret = PlaceWithHirId::new(hir_id, expr_ty, PlaceBase::Rvalue, Vec::new()); - debug!("cat_rvalue ret={:?}", ret); - ret - } - - pub(crate) fn cat_projection<N: HirNode>( - &self, - node: &N, - base_place: PlaceWithHirId<'tcx>, - ty: Ty<'tcx>, - kind: ProjectionKind, - ) -> PlaceWithHirId<'tcx> { - let mut projections = base_place.place.projections; - projections.push(Projection { kind, ty }); - let ret = PlaceWithHirId::new( - node.hir_id(), - base_place.place.base_ty, - base_place.place.base, - projections, - ); - debug!("cat_field ret {:?}", ret); - ret - } - - fn cat_overloaded_place( - &self, - expr: &hir::Expr<'_>, - base: &hir::Expr<'_>, - ) -> McResult<PlaceWithHirId<'tcx>> { - debug!("cat_overloaded_place(expr={:?}, base={:?})", expr, base); - - // Reconstruct the output assuming it's a reference with the - // same region and mutability as the receiver. This holds for - // `Deref(Mut)::Deref(_mut)` and `Index(Mut)::index(_mut)`. - let place_ty = self.expr_ty(expr)?; - let base_ty = self.expr_ty_adjusted(base)?; - - let ty::Ref(region, _, mutbl) = *base_ty.kind() else { - span_bug!(expr.span, "cat_overloaded_place: base is not a reference"); - }; - let ref_ty = self.tcx().mk_ref(region, ty::TypeAndMut { ty: place_ty, mutbl }); - - let base = self.cat_rvalue(expr.hir_id, expr.span, ref_ty); - self.cat_deref(expr, base) - } - - fn cat_deref( - &self, - node: &impl HirNode, - base_place: PlaceWithHirId<'tcx>, - ) -> McResult<PlaceWithHirId<'tcx>> { - debug!("cat_deref: base_place={:?}", base_place); - - let base_curr_ty = base_place.place.ty(); - let deref_ty = match base_curr_ty.builtin_deref(true) { - Some(mt) => mt.ty, - None => { - debug!("explicit deref of non-derefable type: {:?}", base_curr_ty); - return Err(()); - } - }; - let mut projections = base_place.place.projections; - projections.push(Projection { kind: ProjectionKind::Deref, ty: deref_ty }); - - let ret = PlaceWithHirId::new( - node.hir_id(), - base_place.place.base_ty, - base_place.place.base, - projections, - ); - debug!("cat_deref ret {:?}", ret); - Ok(ret) - } - - pub(crate) fn cat_pattern<F>( - &self, - place: PlaceWithHirId<'tcx>, - pat: &hir::Pat<'_>, - mut op: F, - ) -> McResult<()> - where - F: FnMut(&PlaceWithHirId<'tcx>, &hir::Pat<'_>), - { - self.cat_pattern_(place, pat, &mut op) - } - - /// Returns the variant index for an ADT used within a Struct or TupleStruct pattern - /// Here `pat_hir_id` is the HirId of the pattern itself. - fn variant_index_for_adt( - &self, - qpath: &hir::QPath<'_>, - pat_hir_id: hir::HirId, - span: Span, - ) -> McResult<VariantIdx> { - let res = self.typeck_results.qpath_res(qpath, pat_hir_id); - let ty = self.typeck_results.node_type(pat_hir_id); - let ty::Adt(adt_def, _) = ty.kind() else { - self.tcx() - .sess - .delay_span_bug(span, "struct or tuple struct pattern not applied to an ADT"); - return Err(()); - }; - - match res { - Res::Def(DefKind::Variant, variant_id) => Ok(adt_def.variant_index_with_id(variant_id)), - Res::Def(DefKind::Ctor(CtorOf::Variant, ..), variant_ctor_id) => { - Ok(adt_def.variant_index_with_ctor_id(variant_ctor_id)) - } - Res::Def(DefKind::Ctor(CtorOf::Struct, ..), _) - | Res::Def(DefKind::Struct | DefKind::Union | DefKind::TyAlias | DefKind::AssocTy, _) - | Res::SelfCtor(..) - | Res::SelfTy { .. } => { - // Structs and Unions have only have one variant. - Ok(VariantIdx::new(0)) - } - _ => bug!("expected ADT path, found={:?}", res), - } - } - - /// Returns the total number of fields in an ADT variant used within a pattern. - /// Here `pat_hir_id` is the HirId of the pattern itself. - fn total_fields_in_adt_variant( - &self, - pat_hir_id: hir::HirId, - variant_index: VariantIdx, - span: Span, - ) -> McResult<usize> { - let ty = self.typeck_results.node_type(pat_hir_id); - match ty.kind() { - ty::Adt(adt_def, _) => Ok(adt_def.variant(variant_index).fields.len()), - _ => { - self.tcx() - .sess - .delay_span_bug(span, "struct or tuple struct pattern not applied to an ADT"); - Err(()) - } - } - } - - /// Returns the total number of fields in a tuple used within a Tuple pattern. - /// Here `pat_hir_id` is the HirId of the pattern itself. - fn total_fields_in_tuple(&self, pat_hir_id: hir::HirId, span: Span) -> McResult<usize> { - let ty = self.typeck_results.node_type(pat_hir_id); - match ty.kind() { - ty::Tuple(substs) => Ok(substs.len()), - _ => { - self.tcx().sess.delay_span_bug(span, "tuple pattern not applied to a tuple"); - Err(()) - } - } - } - - // FIXME(#19596) This is a workaround, but there should be a better way to do this - fn cat_pattern_<F>( - &self, - mut place_with_id: PlaceWithHirId<'tcx>, - pat: &hir::Pat<'_>, - op: &mut F, - ) -> McResult<()> - where - F: FnMut(&PlaceWithHirId<'tcx>, &hir::Pat<'_>), - { - // Here, `place` is the `PlaceWithHirId` being matched and pat is the pattern it - // is being matched against. - // - // In general, the way that this works is that we walk down the pattern, - // constructing a `PlaceWithHirId` that represents the path that will be taken - // to reach the value being matched. - - debug!("cat_pattern(pat={:?}, place_with_id={:?})", pat, place_with_id); - - // If (pattern) adjustments are active for this pattern, adjust the `PlaceWithHirId` correspondingly. - // `PlaceWithHirId`s are constructed differently from patterns. For example, in - // - // ``` - // match foo { - // &&Some(x, ) => { ... }, - // _ => { ... }, - // } - // ``` - // - // the pattern `&&Some(x,)` is represented as `Ref { Ref { TupleStruct }}`. To build the - // corresponding `PlaceWithHirId` we start with the `PlaceWithHirId` for `foo`, and then, by traversing the - // pattern, try to answer the question: given the address of `foo`, how is `x` reached? - // - // `&&Some(x,)` `place_foo` - // `&Some(x,)` `deref { place_foo}` - // `Some(x,)` `deref { deref { place_foo }}` - // (x,)` `field0 { deref { deref { place_foo }}}` <- resulting place - // - // The above example has no adjustments. If the code were instead the (after adjustments, - // equivalent) version - // - // ``` - // match foo { - // Some(x, ) => { ... }, - // _ => { ... }, - // } - // ``` - // - // Then we see that to get the same result, we must start with - // `deref { deref { place_foo }}` instead of `place_foo` since the pattern is now `Some(x,)` - // and not `&&Some(x,)`, even though its assigned type is that of `&&Some(x,)`. - for _ in 0..self.typeck_results.pat_adjustments().get(pat.hir_id).map_or(0, |v| v.len()) { - debug!("cat_pattern: applying adjustment to place_with_id={:?}", place_with_id); - place_with_id = self.cat_deref(pat, place_with_id)?; - } - let place_with_id = place_with_id; // lose mutability - debug!("cat_pattern: applied adjustment derefs to get place_with_id={:?}", place_with_id); - - // Invoke the callback, but only now, after the `place_with_id` has adjusted. - // - // To see that this makes sense, consider `match &Some(3) { Some(x) => { ... }}`. In that - // case, the initial `place_with_id` will be that for `&Some(3)` and the pattern is `Some(x)`. We - // don't want to call `op` with these incompatible values. As written, what happens instead - // is that `op` is called with the adjusted place (that for `*&Some(3)`) and the pattern - // `Some(x)` (which matches). Recursing once more, `*&Some(3)` and the pattern `Some(x)` - // result in the place `Downcast<Some>(*&Some(3)).0` associated to `x` and invoke `op` with - // that (where the `ref` on `x` is implied). - op(&place_with_id, pat); - - match pat.kind { - PatKind::Tuple(subpats, dots_pos) => { - // (p1, ..., pN) - let total_fields = self.total_fields_in_tuple(pat.hir_id, pat.span)?; - - for (i, subpat) in subpats.iter().enumerate_and_adjust(total_fields, dots_pos) { - let subpat_ty = self.pat_ty_adjusted(subpat)?; - let projection_kind = ProjectionKind::Field(i as u32, VariantIdx::new(0)); - let sub_place = - self.cat_projection(pat, place_with_id.clone(), subpat_ty, projection_kind); - self.cat_pattern_(sub_place, subpat, op)?; - } - } - - PatKind::TupleStruct(ref qpath, subpats, dots_pos) => { - // S(p1, ..., pN) - let variant_index = self.variant_index_for_adt(qpath, pat.hir_id, pat.span)?; - let total_fields = - self.total_fields_in_adt_variant(pat.hir_id, variant_index, pat.span)?; - - for (i, subpat) in subpats.iter().enumerate_and_adjust(total_fields, dots_pos) { - let subpat_ty = self.pat_ty_adjusted(subpat)?; - let projection_kind = ProjectionKind::Field(i as u32, variant_index); - let sub_place = - self.cat_projection(pat, place_with_id.clone(), subpat_ty, projection_kind); - self.cat_pattern_(sub_place, subpat, op)?; - } - } - - PatKind::Struct(ref qpath, field_pats, _) => { - // S { f1: p1, ..., fN: pN } - - let variant_index = self.variant_index_for_adt(qpath, pat.hir_id, pat.span)?; - - for fp in field_pats { - let field_ty = self.pat_ty_adjusted(fp.pat)?; - let field_index = self - .typeck_results - .field_indices() - .get(fp.hir_id) - .cloned() - .expect("no index for a field"); - - let field_place = self.cat_projection( - pat, - place_with_id.clone(), - field_ty, - ProjectionKind::Field(field_index as u32, variant_index), - ); - self.cat_pattern_(field_place, fp.pat, op)?; - } - } - - PatKind::Or(pats) => { - for pat in pats { - self.cat_pattern_(place_with_id.clone(), pat, op)?; - } - } - - PatKind::Binding(.., Some(ref subpat)) => { - self.cat_pattern_(place_with_id, subpat, op)?; - } - - PatKind::Box(ref subpat) | PatKind::Ref(ref subpat, _) => { - // box p1, &p1, &mut p1. we can ignore the mutability of - // PatKind::Ref since that information is already contained - // in the type. - let subplace = self.cat_deref(pat, place_with_id)?; - self.cat_pattern_(subplace, subpat, op)?; - } - - PatKind::Slice(before, ref slice, after) => { - let Some(element_ty) = place_with_id.place.ty().builtin_index() else { - debug!("explicit index of non-indexable type {:?}", place_with_id); - return Err(()); - }; - let elt_place = self.cat_projection( - pat, - place_with_id.clone(), - element_ty, - ProjectionKind::Index, - ); - for before_pat in before { - self.cat_pattern_(elt_place.clone(), before_pat, op)?; - } - if let Some(ref slice_pat) = *slice { - let slice_pat_ty = self.pat_ty_adjusted(slice_pat)?; - let slice_place = self.cat_projection( - pat, - place_with_id, - slice_pat_ty, - ProjectionKind::Subslice, - ); - self.cat_pattern_(slice_place, slice_pat, op)?; - } - for after_pat in after { - self.cat_pattern_(elt_place.clone(), after_pat, op)?; - } - } - - PatKind::Path(_) - | PatKind::Binding(.., None) - | PatKind::Lit(..) - | PatKind::Range(..) - | PatKind::Wild => { - // always ok - } - } - - Ok(()) - } -} diff --git a/compiler/rustc_typeck/src/outlives/explicit.rs b/compiler/rustc_typeck/src/outlives/explicit.rs deleted file mode 100644 index 7534482cc..000000000 --- a/compiler/rustc_typeck/src/outlives/explicit.rs +++ /dev/null @@ -1,69 +0,0 @@ -use rustc_data_structures::fx::FxHashMap; -use rustc_hir::def_id::DefId; -use rustc_middle::ty::{self, OutlivesPredicate, TyCtxt}; - -use super::utils::*; - -#[derive(Debug)] -pub struct ExplicitPredicatesMap<'tcx> { - map: FxHashMap<DefId, ty::EarlyBinder<RequiredPredicates<'tcx>>>, -} - -impl<'tcx> ExplicitPredicatesMap<'tcx> { - pub fn new() -> ExplicitPredicatesMap<'tcx> { - ExplicitPredicatesMap { map: FxHashMap::default() } - } - - pub(crate) fn explicit_predicates_of( - &mut self, - tcx: TyCtxt<'tcx>, - def_id: DefId, - ) -> &ty::EarlyBinder<RequiredPredicates<'tcx>> { - self.map.entry(def_id).or_insert_with(|| { - let predicates = if def_id.is_local() { - tcx.explicit_predicates_of(def_id) - } else { - tcx.predicates_of(def_id) - }; - let mut required_predicates = RequiredPredicates::default(); - - // process predicates and convert to `RequiredPredicates` entry, see below - for &(predicate, span) in predicates.predicates { - match predicate.kind().skip_binder() { - ty::PredicateKind::TypeOutlives(OutlivesPredicate(ty, reg)) => { - insert_outlives_predicate( - tcx, - ty.into(), - reg, - span, - &mut required_predicates, - ) - } - - ty::PredicateKind::RegionOutlives(OutlivesPredicate(reg1, reg2)) => { - insert_outlives_predicate( - tcx, - reg1.into(), - reg2, - span, - &mut required_predicates, - ) - } - - ty::PredicateKind::Trait(..) - | ty::PredicateKind::Projection(..) - | ty::PredicateKind::WellFormed(..) - | ty::PredicateKind::ObjectSafe(..) - | ty::PredicateKind::ClosureKind(..) - | ty::PredicateKind::Subtype(..) - | ty::PredicateKind::Coerce(..) - | ty::PredicateKind::ConstEvaluatable(..) - | ty::PredicateKind::ConstEquate(..) - | ty::PredicateKind::TypeWellFormedFromEnv(..) => (), - } - } - - ty::EarlyBinder(required_predicates) - }) - } -} diff --git a/compiler/rustc_typeck/src/outlives/implicit_infer.rs b/compiler/rustc_typeck/src/outlives/implicit_infer.rs deleted file mode 100644 index 3b779280e..000000000 --- a/compiler/rustc_typeck/src/outlives/implicit_infer.rs +++ /dev/null @@ -1,300 +0,0 @@ -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_span::Span; - -use super::explicit::ExplicitPredicatesMap; -use super::utils::*; - -/// Infer predicates for the items in the crate. -/// -/// `global_inferred_outlives`: this is initially the empty map that -/// was generated by walking the items in the crate. This will -/// now be filled with inferred predicates. -pub(super) fn infer_predicates<'tcx>( - tcx: TyCtxt<'tcx>, -) -> FxHashMap<DefId, ty::EarlyBinder<RequiredPredicates<'tcx>>> { - debug!("infer_predicates"); - - let mut explicit_map = ExplicitPredicatesMap::new(); - - let mut global_inferred_outlives = FxHashMap::default(); - - // If new predicates were added then we need to re-calculate - // all crates since there could be new implied predicates. - 'outer: loop { - let mut predicates_added = false; - - // Visit all the crates and infer predicates - for id in tcx.hir().items() { - let item_did = id.def_id; - - debug!("InferVisitor::visit_item(item={:?})", item_did); - - let mut item_required_predicates = RequiredPredicates::default(); - match tcx.def_kind(item_did) { - DefKind::Union | DefKind::Enum | DefKind::Struct => { - let adt_def = tcx.adt_def(item_did.to_def_id()); - - // Iterate over all fields in item_did - for field_def in adt_def.all_fields() { - // Calculating the predicate requirements necessary - // for item_did. - // - // For field of type &'a T (reference) or Adt - // (struct/enum/union) there will be outlive - // requirements for adt_def. - let field_ty = tcx.type_of(field_def.did); - let field_span = tcx.def_span(field_def.did); - insert_required_predicates_to_be_wf( - tcx, - field_ty, - field_span, - &global_inferred_outlives, - &mut item_required_predicates, - &mut explicit_map, - ); - } - } - - _ => {} - }; - - // If new predicates were added (`local_predicate_map` has more - // predicates than the `global_inferred_outlives`), the new predicates - // might result in implied predicates for their parent types. - // Therefore mark `predicates_added` as true and which will ensure - // we walk the crates again and re-calculate predicates for all - // items. - let item_predicates_len: usize = - global_inferred_outlives.get(&item_did.to_def_id()).map_or(0, |p| p.0.len()); - if item_required_predicates.len() > item_predicates_len { - predicates_added = true; - global_inferred_outlives - .insert(item_did.to_def_id(), ty::EarlyBinder(item_required_predicates)); - } - } - - if !predicates_added { - break 'outer; - } - } - - global_inferred_outlives -} - -fn insert_required_predicates_to_be_wf<'tcx>( - tcx: TyCtxt<'tcx>, - field_ty: Ty<'tcx>, - field_span: Span, - global_inferred_outlives: &FxHashMap<DefId, ty::EarlyBinder<RequiredPredicates<'tcx>>>, - required_predicates: &mut RequiredPredicates<'tcx>, - explicit_map: &mut ExplicitPredicatesMap<'tcx>, -) { - for arg in field_ty.walk() { - let ty = match arg.unpack() { - GenericArgKind::Type(ty) => ty, - - // No predicates from lifetimes or constants, except potentially - // constants' types, but `walk` will get to them as well. - GenericArgKind::Lifetime(_) | GenericArgKind::Const(_) => continue, - }; - - match *ty.kind() { - // The field is of type &'a T which means that we will have - // a predicate requirement of T: 'a (T outlives 'a). - // - // We also want to calculate potential predicates for the T - ty::Ref(region, rty, _) => { - debug!("Ref"); - insert_outlives_predicate(tcx, rty.into(), region, field_span, required_predicates); - } - - // For each Adt (struct/enum/union) type `Foo<'a, T>`, we - // can load the current set of inferred and explicit - // predicates from `global_inferred_outlives` and filter the - // ones that are TypeOutlives. - ty::Adt(def, substs) => { - // First check the inferred predicates - // - // Example 1: - // - // struct Foo<'a, T> { - // field1: Bar<'a, T> - // } - // - // struct Bar<'b, U> { - // field2: &'b U - // } - // - // Here, when processing the type of `field1`, we would - // request the set of implicit predicates computed for `Bar` - // thus far. This will initially come back empty, but in next - // round we will get `U: 'b`. We then apply the substitution - // `['b => 'a, U => T]` and thus get the requirement that `T: - // 'a` holds for `Foo`. - debug!("Adt"); - if let Some(unsubstituted_predicates) = global_inferred_outlives.get(&def.did()) { - for (unsubstituted_predicate, &span) in &unsubstituted_predicates.0 { - // `unsubstituted_predicate` is `U: 'b` in the - // example above. So apply the substitution to - // get `T: 'a` (or `predicate`): - let predicate = unsubstituted_predicates - .rebind(*unsubstituted_predicate) - .subst(tcx, substs); - insert_outlives_predicate( - tcx, - predicate.0, - predicate.1, - span, - required_predicates, - ); - } - } - - // Check if the type has any explicit predicates that need - // to be added to `required_predicates` - // let _: () = substs.region_at(0); - check_explicit_predicates( - tcx, - def.did(), - substs, - required_predicates, - explicit_map, - None, - ); - } - - ty::Dynamic(obj, ..) => { - // This corresponds to `dyn Trait<..>`. In this case, we should - // use the explicit predicates as well. - - debug!("Dynamic"); - debug!("field_ty = {}", &field_ty); - debug!("ty in field = {}", &ty); - if let Some(ex_trait_ref) = obj.principal() { - // Here, we are passing the type `usize` as a - // placeholder value with the function - // `with_self_ty`, since there is no concrete type - // `Self` for a `dyn Trait` at this - // stage. Therefore when checking explicit - // predicates in `check_explicit_predicates` we - // need to ignore checking the explicit_map for - // Self type. - let substs = - ex_trait_ref.with_self_ty(tcx, tcx.types.usize).skip_binder().substs; - check_explicit_predicates( - tcx, - ex_trait_ref.skip_binder().def_id, - substs, - required_predicates, - explicit_map, - Some(tcx.types.self_param), - ); - } - } - - ty::Projection(obj) => { - // This corresponds to `<T as Foo<'a>>::Bar`. In this case, we should use the - // explicit predicates as well. - debug!("Projection"); - check_explicit_predicates( - tcx, - tcx.parent(obj.item_def_id), - obj.substs, - required_predicates, - explicit_map, - None, - ); - } - - _ => {} - } - } -} - -/// We also have to check the explicit predicates -/// declared on the type. -/// ```ignore (illustrative) -/// struct Foo<'a, T> { -/// field1: Bar<T> -/// } -/// -/// struct Bar<U> where U: 'static, U: Foo { -/// ... -/// } -/// ``` -/// Here, we should fetch the explicit predicates, which -/// will give us `U: 'static` and `U: Foo`. The latter we -/// can ignore, but we will want to process `U: 'static`, -/// applying the substitution as above. -fn check_explicit_predicates<'tcx>( - tcx: TyCtxt<'tcx>, - def_id: DefId, - substs: &[GenericArg<'tcx>], - required_predicates: &mut RequiredPredicates<'tcx>, - explicit_map: &mut ExplicitPredicatesMap<'tcx>, - ignored_self_ty: Option<Ty<'tcx>>, -) { - debug!( - "check_explicit_predicates(def_id={:?}, \ - substs={:?}, \ - explicit_map={:?}, \ - required_predicates={:?}, \ - ignored_self_ty={:?})", - def_id, substs, explicit_map, required_predicates, ignored_self_ty, - ); - let explicit_predicates = explicit_map.explicit_predicates_of(tcx, def_id); - - for (outlives_predicate, &span) in &explicit_predicates.0 { - debug!("outlives_predicate = {:?}", &outlives_predicate); - - // Careful: If we are inferring the effects of a `dyn Trait<..>` - // type, then when we look up the predicates for `Trait`, - // we may find some that reference `Self`. e.g., perhaps the - // definition of `Trait` was: - // - // ``` - // trait Trait<'a, T> where Self: 'a { .. } - // ``` - // - // we want to ignore such predicates here, because - // there is no type parameter for them to affect. Consider - // a struct containing `dyn Trait`: - // - // ``` - // struct MyStruct<'x, X> { field: Box<dyn Trait<'x, X>> } - // ``` - // - // The `where Self: 'a` predicate refers to the *existential, hidden type* - // that is represented by the `dyn Trait`, not to the `X` type parameter - // (or any other generic parameter) declared on `MyStruct`. - // - // Note that we do this check for self **before** applying `substs`. In the - // case that `substs` come from a `dyn Trait` type, our caller will have - // included `Self = usize` as the value for `Self`. If we were - // to apply the substs, and not filter this predicate, we might then falsely - // conclude that e.g., `X: 'x` was a reasonable inferred requirement. - // - // Another similar case is where we have an inferred - // requirement like `<Self as Trait>::Foo: 'b`. We presently - // ignore such requirements as well (cc #54467)-- though - // conceivably it might be better if we could extract the `Foo - // = X` binding from the object type (there must be such a - // binding) and thus infer an outlives requirement that `X: - // 'b`. - if let Some(self_ty) = ignored_self_ty - && let GenericArgKind::Type(ty) = outlives_predicate.0.unpack() - && ty.walk().any(|arg| arg == self_ty.into()) - { - debug!("skipping self ty = {:?}", &ty); - continue; - } - - let predicate = explicit_predicates.rebind(*outlives_predicate).subst(tcx, substs); - debug!("predicate = {:?}", &predicate); - insert_outlives_predicate(tcx, predicate.0, predicate.1, span, required_predicates); - } -} diff --git a/compiler/rustc_typeck/src/outlives/mod.rs b/compiler/rustc_typeck/src/outlives/mod.rs deleted file mode 100644 index e50c26765..000000000 --- a/compiler/rustc_typeck/src/outlives/mod.rs +++ /dev/null @@ -1,129 +0,0 @@ -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_typeck/src/outlives/test.rs b/compiler/rustc_typeck/src/outlives/test.rs deleted file mode 100644 index eb0e12034..000000000 --- a/compiler/rustc_typeck/src/outlives/test.rs +++ /dev/null @@ -1,21 +0,0 @@ -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.def_id.to_def_id(), sym::rustc_outlives) { - let inferred_outlives_of = tcx.inferred_outlives_of(id.def_id); - struct_span_err!( - tcx.sess, - tcx.def_span(id.def_id), - E0640, - "{:?}", - inferred_outlives_of - ) - .emit(); - } - } -} diff --git a/compiler/rustc_typeck/src/outlives/utils.rs b/compiler/rustc_typeck/src/outlives/utils.rs deleted file mode 100644 index 3e8d023fb..000000000 --- a/compiler/rustc_typeck/src/outlives/utils.rs +++ /dev/null @@ -1,169 +0,0 @@ -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::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_typeck/src/structured_errors.rs deleted file mode 100644 index 0b46fce17..000000000 --- a/compiler/rustc_typeck/src/structured_errors.rs +++ /dev/null @@ -1,42 +0,0 @@ -mod missing_cast_for_variadic_arg; -mod sized_unsized_cast; -mod wrong_number_of_generic_args; - -pub use self::{ - missing_cast_for_variadic_arg::*, sized_unsized_cast::*, wrong_number_of_generic_args::*, -}; - -use rustc_errors::{DiagnosticBuilder, DiagnosticId, ErrorGuaranteed}; -use rustc_session::Session; - -pub trait StructuredDiagnostic<'tcx> { - fn session(&self) -> &Session; - - fn code(&self) -> DiagnosticId; - - fn diagnostic(&self) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { - let err = self.diagnostic_common(); - - if self.session().teach(&self.code()) { - self.diagnostic_extended(err) - } else { - self.diagnostic_regular(err) - } - } - - fn diagnostic_common(&self) -> DiagnosticBuilder<'tcx, ErrorGuaranteed>; - - fn diagnostic_regular( - &self, - err: DiagnosticBuilder<'tcx, ErrorGuaranteed>, - ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { - err - } - - fn diagnostic_extended( - &self, - err: DiagnosticBuilder<'tcx, ErrorGuaranteed>, - ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { - err - } -} diff --git a/compiler/rustc_typeck/src/structured_errors/missing_cast_for_variadic_arg.rs b/compiler/rustc_typeck/src/structured_errors/missing_cast_for_variadic_arg.rs deleted file mode 100644 index 324df313e..000000000 --- a/compiler/rustc_typeck/src/structured_errors/missing_cast_for_variadic_arg.rs +++ /dev/null @@ -1,61 +0,0 @@ -use crate::structured_errors::StructuredDiagnostic; -use rustc_errors::{Applicability, DiagnosticBuilder, DiagnosticId, ErrorGuaranteed}; -use rustc_middle::ty::{Ty, TypeVisitable}; -use rustc_session::Session; -use rustc_span::Span; - -pub struct MissingCastForVariadicArg<'tcx, 's> { - pub sess: &'tcx Session, - pub span: Span, - pub ty: Ty<'tcx>, - pub cast_ty: &'s str, -} - -impl<'tcx> StructuredDiagnostic<'tcx> for MissingCastForVariadicArg<'tcx, '_> { - fn session(&self) -> &Session { - self.sess - } - - fn code(&self) -> DiagnosticId { - rustc_errors::error_code!(E0617) - } - - fn diagnostic_common(&self) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { - let mut err = self.sess.struct_span_err_with_code( - self.span, - &format!("can't pass `{}` to variadic function", self.ty), - self.code(), - ); - - if self.ty.references_error() { - err.downgrade_to_delayed_bug(); - } - - if let Ok(snippet) = self.sess.source_map().span_to_snippet(self.span) { - err.span_suggestion( - self.span, - &format!("cast the value to `{}`", self.cast_ty), - format!("{} as {}", snippet, self.cast_ty), - Applicability::MachineApplicable, - ); - } else { - err.help(&format!("cast the value to `{}`", self.cast_ty)); - } - - err - } - - fn diagnostic_extended( - &self, - mut err: DiagnosticBuilder<'tcx, ErrorGuaranteed>, - ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { - err.note(&format!( - "certain types, like `{}`, must be casted before passing them to a \ - variadic function, because of arcane ABI rules dictated by the C \ - standard", - self.ty - )); - - err - } -} diff --git a/compiler/rustc_typeck/src/structured_errors/sized_unsized_cast.rs b/compiler/rustc_typeck/src/structured_errors/sized_unsized_cast.rs deleted file mode 100644 index bb6088054..000000000 --- a/compiler/rustc_typeck/src/structured_errors/sized_unsized_cast.rs +++ /dev/null @@ -1,62 +0,0 @@ -use crate::structured_errors::StructuredDiagnostic; -use rustc_errors::{DiagnosticBuilder, DiagnosticId, ErrorGuaranteed}; -use rustc_middle::ty::{Ty, TypeVisitable}; -use rustc_session::Session; -use rustc_span::Span; - -pub struct SizedUnsizedCast<'tcx> { - pub sess: &'tcx Session, - pub span: Span, - pub expr_ty: Ty<'tcx>, - pub cast_ty: String, -} - -impl<'tcx> StructuredDiagnostic<'tcx> for SizedUnsizedCast<'tcx> { - fn session(&self) -> &Session { - self.sess - } - - fn code(&self) -> DiagnosticId { - rustc_errors::error_code!(E0607) - } - - fn diagnostic_common(&self) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { - let mut err = self.sess.struct_span_err_with_code( - self.span, - &format!( - "cannot cast thin pointer `{}` to fat pointer `{}`", - self.expr_ty, self.cast_ty - ), - self.code(), - ); - - if self.expr_ty.references_error() { - err.downgrade_to_delayed_bug(); - } - - err - } - - fn diagnostic_extended( - &self, - mut err: DiagnosticBuilder<'tcx, ErrorGuaranteed>, - ) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { - err.help( - "Thin pointers are \"simple\" pointers: they are purely a reference to a -memory address. - -Fat pointers are pointers referencing \"Dynamically Sized Types\" (also -called DST). DST don't have a statically known size, therefore they can -only exist behind some kind of pointers that contain additional -information. Slices and trait objects are DSTs. In the case of slices, -the additional information the fat pointer holds is their size. - -To fix this error, don't try to cast directly between thin and fat -pointers. - -For more information about casts, take a look at The Book: -https://doc.rust-lang.org/reference/expressions/operator-expr.html#type-cast-expressions", - ); - err - } -} diff --git a/compiler/rustc_typeck/src/structured_errors/wrong_number_of_generic_args.rs b/compiler/rustc_typeck/src/structured_errors/wrong_number_of_generic_args.rs deleted file mode 100644 index 435912464..000000000 --- a/compiler/rustc_typeck/src/structured_errors/wrong_number_of_generic_args.rs +++ /dev/null @@ -1,1023 +0,0 @@ -use crate::structured_errors::StructuredDiagnostic; -use rustc_errors::{ - pluralize, Applicability, Diagnostic, DiagnosticBuilder, DiagnosticId, ErrorGuaranteed, - MultiSpan, -}; -use rustc_hir as hir; -use rustc_middle::ty::{self as ty, AssocItems, AssocKind, TyCtxt}; -use rustc_session::Session; -use rustc_span::def_id::DefId; -use std::iter; - -use GenericArgsInfo::*; - -/// Handles the `wrong number of type / lifetime / ... arguments` family of error messages. -pub struct WrongNumberOfGenericArgs<'a, 'tcx> { - pub(crate) tcx: TyCtxt<'tcx>, - - pub(crate) angle_brackets: AngleBrackets, - - pub(crate) gen_args_info: GenericArgsInfo, - - /// Offending path segment - pub(crate) path_segment: &'a hir::PathSegment<'a>, - - /// Generic parameters as expected by type or trait - pub(crate) gen_params: &'a ty::Generics, - - /// Index offset into parameters. Depends on whether `Self` is included and on - /// number of lifetime parameters in case we're processing missing or redundant - /// type or constant arguments. - pub(crate) params_offset: usize, - - /// Generic arguments as provided by user - pub(crate) gen_args: &'a hir::GenericArgs<'a>, - - /// DefId of the generic type - pub(crate) def_id: DefId, -} - -// Provides information about the kind of arguments that were provided for -// the PathSegment, for which missing generic arguments were detected -#[derive(Debug)] -pub(crate) enum AngleBrackets { - // No angle brackets were provided, but generic arguments exist in elided form - Implied, - - // No angle brackets were provided - Missing, - - // Angle brackets are available, but missing some generic arguments - Available, -} - -// Information about the kind of arguments that are either missing or are unexpected -#[derive(Debug)] -pub enum GenericArgsInfo { - MissingLifetimes { - num_missing_args: usize, - }, - ExcessLifetimes { - num_redundant_args: usize, - }, - MissingTypesOrConsts { - num_missing_args: usize, - - // type or const generic arguments can have default values - num_default_params: usize, - - // lifetime arguments precede type and const parameters, this - // field gives the number of generic lifetime arguments to let - // us infer the position of type and const generic arguments - // in the angle brackets - args_offset: usize, - }, - - ExcessTypesOrConsts { - num_redundant_args: usize, - - // type or const generic arguments can have default values - num_default_params: usize, - - // lifetime arguments precede type and const parameters, this - // field gives the number of generic lifetime arguments to let - // us infer the position of type and const generic arguments - // in the angle brackets - args_offset: usize, - - // if synthetic type arguments (e.g. `impl Trait`) are specified - synth_provided: bool, - }, -} - -impl<'a, 'tcx> WrongNumberOfGenericArgs<'a, 'tcx> { - pub fn new( - tcx: TyCtxt<'tcx>, - gen_args_info: GenericArgsInfo, - path_segment: &'a hir::PathSegment<'_>, - gen_params: &'a ty::Generics, - params_offset: usize, - gen_args: &'a hir::GenericArgs<'a>, - def_id: DefId, - ) -> Self { - let angle_brackets = if gen_args.span_ext().is_none() { - if gen_args.is_empty() { AngleBrackets::Missing } else { AngleBrackets::Implied } - } else { - AngleBrackets::Available - }; - - Self { - tcx, - angle_brackets, - gen_args_info, - path_segment, - gen_params, - params_offset, - gen_args, - def_id, - } - } - - fn missing_lifetimes(&self) -> bool { - match self.gen_args_info { - MissingLifetimes { .. } | ExcessLifetimes { .. } => true, - MissingTypesOrConsts { .. } | ExcessTypesOrConsts { .. } => false, - } - } - - fn kind(&self) -> &str { - if self.missing_lifetimes() { "lifetime" } else { "generic" } - } - - fn num_provided_args(&self) -> usize { - if self.missing_lifetimes() { - self.num_provided_lifetime_args() - } else { - self.num_provided_type_or_const_args() - } - } - - fn num_provided_lifetime_args(&self) -> usize { - match self.angle_brackets { - AngleBrackets::Missing => 0, - // Only lifetime arguments can be implied - AngleBrackets::Implied => self.gen_args.args.len(), - AngleBrackets::Available => self.gen_args.num_lifetime_params(), - } - } - - fn num_provided_type_or_const_args(&self) -> usize { - match self.angle_brackets { - AngleBrackets::Missing => 0, - // Only lifetime arguments can be implied - AngleBrackets::Implied => 0, - AngleBrackets::Available => self.gen_args.num_generic_params(), - } - } - - fn num_expected_lifetime_args(&self) -> usize { - let num_provided_args = self.num_provided_lifetime_args(); - match self.gen_args_info { - MissingLifetimes { num_missing_args } => num_provided_args + num_missing_args, - ExcessLifetimes { num_redundant_args } => num_provided_args - num_redundant_args, - _ => 0, - } - } - - fn num_expected_type_or_const_args(&self) -> usize { - let num_provided_args = self.num_provided_type_or_const_args(); - match self.gen_args_info { - MissingTypesOrConsts { num_missing_args, .. } => num_provided_args + num_missing_args, - ExcessTypesOrConsts { num_redundant_args, .. } => { - num_provided_args - num_redundant_args - } - _ => 0, - } - } - - // Gives the number of expected arguments taking into account default arguments - fn num_expected_type_or_const_args_including_defaults(&self) -> usize { - let provided_args = self.num_provided_type_or_const_args(); - match self.gen_args_info { - MissingTypesOrConsts { num_missing_args, num_default_params, .. } => { - provided_args + num_missing_args - num_default_params - } - ExcessTypesOrConsts { num_redundant_args, num_default_params, .. } => { - provided_args - num_redundant_args - num_default_params - } - _ => 0, - } - } - - fn num_missing_lifetime_args(&self) -> usize { - let missing_args = self.num_expected_lifetime_args() - self.num_provided_lifetime_args(); - assert!(missing_args > 0); - missing_args - } - - fn num_missing_type_or_const_args(&self) -> usize { - let missing_args = self.num_expected_type_or_const_args_including_defaults() - - self.num_provided_type_or_const_args(); - assert!(missing_args > 0); - missing_args - } - - fn num_excess_lifetime_args(&self) -> usize { - match self.gen_args_info { - ExcessLifetimes { num_redundant_args } => num_redundant_args, - _ => 0, - } - } - - fn num_excess_type_or_const_args(&self) -> usize { - match self.gen_args_info { - ExcessTypesOrConsts { num_redundant_args, .. } => num_redundant_args, - _ => 0, - } - } - - fn too_many_args_provided(&self) -> bool { - match self.gen_args_info { - MissingLifetimes { .. } | MissingTypesOrConsts { .. } => false, - ExcessLifetimes { num_redundant_args } - | ExcessTypesOrConsts { num_redundant_args, .. } => { - assert!(num_redundant_args > 0); - true - } - } - } - - fn not_enough_args_provided(&self) -> bool { - match self.gen_args_info { - MissingLifetimes { num_missing_args } - | MissingTypesOrConsts { num_missing_args, .. } => { - assert!(num_missing_args > 0); - true - } - ExcessLifetimes { .. } | ExcessTypesOrConsts { .. } => false, - } - } - - // Helper method to get the index offset in angle brackets, at which type or const arguments - // start appearing - fn get_lifetime_args_offset(&self) -> usize { - match self.gen_args_info { - MissingLifetimes { .. } | ExcessLifetimes { .. } => 0, - MissingTypesOrConsts { args_offset, .. } | ExcessTypesOrConsts { args_offset, .. } => { - args_offset - } - } - } - - fn get_num_default_params(&self) -> usize { - match self.gen_args_info { - MissingTypesOrConsts { num_default_params, .. } - | ExcessTypesOrConsts { num_default_params, .. } => num_default_params, - _ => 0, - } - } - - fn is_synth_provided(&self) -> bool { - match self.gen_args_info { - ExcessTypesOrConsts { synth_provided, .. } => synth_provided, - _ => false, - } - } - - // Helper function to choose a quantifier word for the number of expected arguments - // and to give a bound for the number of expected arguments - fn get_quantifier_and_bound(&self) -> (&'static str, usize) { - if self.get_num_default_params() == 0 { - match self.gen_args_info { - MissingLifetimes { .. } | ExcessLifetimes { .. } => { - ("", self.num_expected_lifetime_args()) - } - MissingTypesOrConsts { .. } | ExcessTypesOrConsts { .. } => { - ("", self.num_expected_type_or_const_args()) - } - } - } else { - match self.gen_args_info { - MissingLifetimes { .. } => ("at least ", self.num_expected_lifetime_args()), - MissingTypesOrConsts { .. } => { - ("at least ", self.num_expected_type_or_const_args_including_defaults()) - } - ExcessLifetimes { .. } => ("at most ", self.num_expected_lifetime_args()), - ExcessTypesOrConsts { .. } => ("at most ", self.num_expected_type_or_const_args()), - } - } - } - - // Creates lifetime name suggestions from the lifetime parameter names - fn get_lifetime_args_suggestions_from_param_names( - &self, - path_hir_id: hir::HirId, - num_params_to_take: usize, - ) -> String { - debug!(?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; - } - } - - // We could not gather enough lifetime parameters in the scope. - // We use the parameter names from the target type's definition instead. - self.gen_params - .params - .iter() - .skip(self.params_offset + self.num_provided_lifetime_args()) - .take(num_params_to_take) - .map(|param| param.name.to_string()) - .collect::<Vec<_>>() - .join(", ") - } - - // Creates type or constant name suggestions from the provided parameter names - fn get_type_or_const_args_suggestions_from_param_names( - &self, - num_params_to_take: usize, - ) -> String { - 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 { - hir::TyKind::Path(hir::QPath::Resolved( - None, - hir::Path { res: hir::def::Res::Def(_, id), .. }, - )) => *id == def_id, - _ => false, - }) - }) - }; - self.gen_params - .params - .iter() - .skip(self.params_offset + self.num_provided_type_or_const_args()) - .take(num_params_to_take) - .map(|param| match param.kind { - // This is being inferred from the item's inputs, no need to set it. - ty::GenericParamDefKind::Type { .. } if is_used_in_input(param.def_id) => { - "_".to_string() - } - _ => param.name.to_string(), - }) - .collect::<Vec<_>>() - .join(", ") - } - - fn get_unbound_associated_types(&self) -> Vec<String> { - if self.tcx.is_trait(self.def_id) { - let items: &AssocItems<'_> = self.tcx.associated_items(self.def_id); - items - .in_definition_order() - .filter(|item| item.kind == AssocKind::Type) - .filter(|item| { - !self.gen_args.bindings.iter().any(|binding| binding.ident.name == item.name) - }) - .map(|item| item.name.to_ident_string()) - .collect() - } else { - Vec::default() - } - } - - fn create_error_message(&self) -> String { - let def_path = self.tcx.def_path_str(self.def_id); - let def_kind = self.tcx.def_kind(self.def_id).descr(self.def_id); - let (quantifier, bound) = self.get_quantifier_and_bound(); - let kind = self.kind(); - let provided_lt_args = self.num_provided_lifetime_args(); - let provided_type_or_const_args = self.num_provided_type_or_const_args(); - - let (provided_args_str, verb) = match self.gen_args_info { - MissingLifetimes { .. } | ExcessLifetimes { .. } => ( - format!("{} lifetime argument{}", provided_lt_args, pluralize!(provided_lt_args)), - pluralize!("was", provided_lt_args), - ), - MissingTypesOrConsts { .. } | ExcessTypesOrConsts { .. } => ( - format!( - "{} generic argument{}", - provided_type_or_const_args, - pluralize!(provided_type_or_const_args) - ), - pluralize!("was", provided_type_or_const_args), - ), - }; - - if self.gen_args.span_ext().is_some() { - format!( - "this {} takes {}{} {} argument{} but {} {} supplied", - def_kind, - quantifier, - bound, - kind, - pluralize!(bound), - provided_args_str.as_str(), - verb - ) - } else { - format!("missing generics for {} `{}`", def_kind, def_path) - } - } - - fn start_diagnostics(&self) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { - let span = self.path_segment.ident.span; - let msg = self.create_error_message(); - - self.tcx.sess.struct_span_err_with_code(span, &msg, self.code()) - } - - /// Builds the `expected 1 type argument / supplied 2 type arguments` message. - fn notify(&self, err: &mut Diagnostic) { - let (quantifier, bound) = self.get_quantifier_and_bound(); - let provided_args = self.num_provided_args(); - - err.span_label( - self.path_segment.ident.span, - format!( - "expected {}{} {} argument{}", - quantifier, - bound, - self.kind(), - pluralize!(bound), - ), - ); - - // When too many arguments were provided, we don't highlight each of them, because it - // would overlap with the suggestion to remove them: - // - // ``` - // type Foo = Bar<usize, usize>; - // ----- ----- supplied 2 type arguments - // ^^^^^^^ remove this type argument - // ``` - if self.too_many_args_provided() { - return; - } - - let args = self - .gen_args - .args - .iter() - .skip(self.get_lifetime_args_offset()) - .take(provided_args) - .enumerate(); - - for (i, arg) in args { - err.span_label( - arg.span(), - if i + 1 == provided_args { - format!( - "supplied {} {} argument{}", - provided_args, - self.kind(), - pluralize!(provided_args) - ) - } else { - String::new() - }, - ); - } - } - - fn suggest(&self, err: &mut Diagnostic) { - debug!( - "suggest(self.provided {:?}, self.gen_args.span(): {:?})", - self.num_provided_args(), - self.gen_args.span(), - ); - - match self.angle_brackets { - AngleBrackets::Missing | AngleBrackets::Implied => self.suggest_adding_args(err), - AngleBrackets::Available => { - 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!(); - } - } - } - } - - /// Suggests to add missing argument(s) when current invocation site already contains some - /// generics: - /// - /// ```text - /// type Map = HashMap<String>; - /// ``` - fn suggest_adding_args(&self, err: &mut Diagnostic) { - if self.gen_args.parenthesized { - return; - } - - match self.gen_args_info { - MissingLifetimes { .. } => { - self.suggest_adding_lifetime_args(err); - } - MissingTypesOrConsts { .. } => { - self.suggest_adding_type_and_const_args(err); - } - _ => unreachable!(), - } - } - - fn suggest_adding_lifetime_args(&self, err: &mut Diagnostic) { - debug!("suggest_adding_lifetime_args(path_segment: {:?})", self.path_segment); - let num_missing_args = self.num_missing_lifetime_args(); - let num_params_to_take = num_missing_args; - let msg = format!("add missing {} argument{}", self.kind(), pluralize!(num_missing_args)); - - let suggested_args = self.get_lifetime_args_suggestions_from_param_names( - self.path_segment.hir_id, - num_params_to_take, - ); - debug!("suggested_args: {:?}", &suggested_args); - - match self.angle_brackets { - AngleBrackets::Missing => { - let span = self.path_segment.ident.span; - - // insert a suggestion of the form "Y<'a, 'b>" - let ident = self.path_segment.ident.name.to_ident_string(); - let sugg = format!("{}<{}>", ident, suggested_args); - debug!("sugg: {:?}", sugg); - - err.span_suggestion_verbose(span, &msg, sugg, Applicability::HasPlaceholders); - } - - AngleBrackets::Available => { - let (sugg_span, is_first) = if self.num_provided_lifetime_args() == 0 { - (self.gen_args.span().unwrap().shrink_to_lo(), true) - } else { - let last_lt = &self.gen_args.args[self.num_provided_lifetime_args() - 1]; - (last_lt.span().shrink_to_hi(), false) - }; - let has_non_lt_args = self.num_provided_type_or_const_args() != 0; - let has_bindings = !self.gen_args.bindings.is_empty(); - - let sugg_prefix = if is_first { "" } else { ", " }; - let sugg_suffix = - if is_first && (has_non_lt_args || has_bindings) { ", " } else { "" }; - - let sugg = format!("{}{}{}", sugg_prefix, suggested_args, sugg_suffix); - debug!("sugg: {:?}", sugg); - - err.span_suggestion_verbose(sugg_span, &msg, sugg, Applicability::HasPlaceholders); - } - AngleBrackets::Implied => { - // We never encounter missing lifetimes in situations in which lifetimes are elided - unreachable!(); - } - } - } - - fn suggest_adding_type_and_const_args(&self, err: &mut Diagnostic) { - let num_missing_args = self.num_missing_type_or_const_args(); - let msg = format!("add missing {} argument{}", self.kind(), pluralize!(num_missing_args)); - - let suggested_args = - self.get_type_or_const_args_suggestions_from_param_names(num_missing_args); - debug!("suggested_args: {:?}", suggested_args); - - match self.angle_brackets { - AngleBrackets::Missing | AngleBrackets::Implied => { - let span = self.path_segment.ident.span; - - // insert a suggestion of the form "Y<T, U>" - let ident = self.path_segment.ident.name.to_ident_string(); - let sugg = format!("{}<{}>", ident, suggested_args); - debug!("sugg: {:?}", sugg); - - err.span_suggestion_verbose(span, &msg, sugg, Applicability::HasPlaceholders); - } - AngleBrackets::Available => { - let gen_args_span = self.gen_args.span().unwrap(); - let sugg_offset = - self.get_lifetime_args_offset() + self.num_provided_type_or_const_args(); - - let (sugg_span, is_first) = if sugg_offset == 0 { - (gen_args_span.shrink_to_lo(), true) - } else { - let arg_span = self.gen_args.args[sugg_offset - 1].span(); - // If we came here then inferred lifetime's spans can only point - // to either the opening bracket or to the space right after. - // Both of these spans have an `hi` lower than or equal to the span - // of the generics excluding the brackets. - // This allows us to check if `arg_span` is the artificial span of - // an inferred lifetime, in which case the generic we're suggesting to - // add will be the first visible, even if it isn't the actual first generic. - (arg_span.shrink_to_hi(), arg_span.hi() <= gen_args_span.lo()) - }; - - let sugg_prefix = if is_first { "" } else { ", " }; - let sugg_suffix = - if is_first && !self.gen_args.bindings.is_empty() { ", " } else { "" }; - - let sugg = format!("{}{}{}", sugg_prefix, suggested_args, sugg_suffix); - debug!("sugg: {:?}", sugg); - - err.span_suggestion_verbose(sugg_span, &msg, sugg, Applicability::HasPlaceholders); - } - } - } - - /// 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 - /// type Map = HashMap<String, String, String, String>; - /// ``` - fn suggest_removing_args_or_generics(&self, err: &mut Diagnostic) { - let num_provided_lt_args = self.num_provided_lifetime_args(); - let num_provided_type_const_args = self.num_provided_type_or_const_args(); - let unbound_types = self.get_unbound_associated_types(); - let num_provided_args = num_provided_lt_args + num_provided_type_const_args; - assert!(num_provided_args > 0); - - let num_redundant_lt_args = self.num_excess_lifetime_args(); - let num_redundant_type_or_const_args = self.num_excess_type_or_const_args(); - let num_redundant_args = num_redundant_lt_args + num_redundant_type_or_const_args; - - let redundant_lifetime_args = num_redundant_lt_args > 0; - let redundant_type_or_const_args = num_redundant_type_or_const_args > 0; - - let remove_entire_generics = num_redundant_args >= self.gen_args.args.len(); - let provided_args_matches_unbound_traits = - unbound_types.len() == num_redundant_type_or_const_args; - - let remove_lifetime_args = |err: &mut Diagnostic| { - let mut lt_arg_spans = Vec::new(); - let mut found_redundant = false; - for arg in self.gen_args.args { - if let hir::GenericArg::Lifetime(_) = arg { - lt_arg_spans.push(arg.span()); - if lt_arg_spans.len() > self.num_expected_lifetime_args() { - found_redundant = true; - } - } else if found_redundant { - // Argument which is redundant and separated like this `'c` - // is not included to avoid including `Bar` in span. - // ``` - // type Foo<'a, T> = &'a T; - // let _: Foo<'a, 'b, Bar, 'c>; - // ``` - break; - } - } - - let span_lo_redundant_lt_args = lt_arg_spans[self.num_expected_lifetime_args()]; - let span_hi_redundant_lt_args = lt_arg_spans[lt_arg_spans.len() - 1]; - - let span_redundant_lt_args = span_lo_redundant_lt_args.to(span_hi_redundant_lt_args); - debug!("span_redundant_lt_args: {:?}", span_redundant_lt_args); - - let num_redundant_lt_args = lt_arg_spans.len() - self.num_expected_lifetime_args(); - let msg_lifetimes = format!( - "remove {these} lifetime argument{s}", - these = pluralize!("this", num_redundant_lt_args), - s = pluralize!(num_redundant_lt_args), - ); - - err.span_suggestion( - span_redundant_lt_args, - &msg_lifetimes, - "", - Applicability::MaybeIncorrect, - ); - }; - - let remove_type_or_const_args = |err: &mut Diagnostic| { - let mut gen_arg_spans = Vec::new(); - let mut found_redundant = false; - for arg in self.gen_args.args { - match arg { - hir::GenericArg::Type(_) - | hir::GenericArg::Const(_) - | hir::GenericArg::Infer(_) => { - gen_arg_spans.push(arg.span()); - if gen_arg_spans.len() > self.num_expected_type_or_const_args() { - found_redundant = true; - } - } - _ if found_redundant => break, - _ => {} - } - } - - let span_lo_redundant_type_or_const_args = - gen_arg_spans[self.num_expected_type_or_const_args()]; - let span_hi_redundant_type_or_const_args = gen_arg_spans[gen_arg_spans.len() - 1]; - - let span_redundant_type_or_const_args = - span_lo_redundant_type_or_const_args.to(span_hi_redundant_type_or_const_args); - debug!("span_redundant_type_or_const_args: {:?}", span_redundant_type_or_const_args); - - let num_redundant_gen_args = - gen_arg_spans.len() - self.num_expected_type_or_const_args(); - let msg_types_or_consts = format!( - "remove {these} generic argument{s}", - these = pluralize!("this", num_redundant_gen_args), - s = pluralize!(num_redundant_gen_args), - ); - - err.span_suggestion( - span_redundant_type_or_const_args, - &msg_types_or_consts, - "", - Applicability::MaybeIncorrect, - ); - }; - - // 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 unused_generics = &self.gen_args.args[self.num_expected_type_or_const_args()..]; - 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_verbose( - &format!( - "replace the generic bound{s} with the associated type{s}", - s = pluralize!(unbound_types.len()) - ), - suggestions, - Applicability::MaybeIncorrect, - ); - } - } else if remove_entire_generics { - let span = self - .path_segment - .args - .unwrap() - .span_ext() - .unwrap() - .with_lo(self.path_segment.ident.span.hi()); - - let msg = format!( - "remove these {}generics", - if self.gen_args.parenthesized { "parenthetical " } else { "" }, - ); - - err.span_suggestion(span, &msg, "", Applicability::MaybeIncorrect); - } else if redundant_lifetime_args && redundant_type_or_const_args { - remove_lifetime_args(err); - remove_type_or_const_args(err); - } else if redundant_lifetime_args { - remove_lifetime_args(err); - } else { - assert!(redundant_type_or_const_args); - remove_type_or_const_args(err); - } - } - - /// Builds the `type defined here` message. - fn show_definition(&self, err: &mut Diagnostic) { - let mut spans: MultiSpan = if let Some(def_span) = self.tcx.def_ident_span(self.def_id) { - if self.tcx.sess.source_map().is_span_accessible(def_span) { - def_span.into() - } else { - return; - } - } else { - return; - }; - - let msg = { - let def_kind = self.tcx.def_kind(self.def_id).descr(self.def_id); - let (quantifier, bound) = self.get_quantifier_and_bound(); - - let params = if bound == 0 { - String::new() - } else { - let params = self - .gen_params - .params - .iter() - .skip(self.params_offset) - .take(bound) - .map(|param| { - let span = self.tcx.def_span(param.def_id); - spans.push_span_label(span, ""); - param - }) - .map(|param| format!("`{}`", param.name)) - .collect::<Vec<_>>() - .join(", "); - - format!(": {}", params) - }; - - format!( - "{} defined here, with {}{} {} parameter{}{}", - def_kind, - quantifier, - bound, - self.kind(), - pluralize!(bound), - params, - ) - }; - - err.span_note(spans, &msg); - } - - /// Add note if `impl Trait` is explicitly specified. - fn note_synth_provided(&self, err: &mut Diagnostic) { - if !self.is_synth_provided() { - return; - } - - err.note("`impl Trait` cannot be explicitly specified as a generic argument"); - } -} - -impl<'tcx> StructuredDiagnostic<'tcx> for WrongNumberOfGenericArgs<'_, 'tcx> { - fn session(&self) -> &Session { - self.tcx.sess - } - - fn code(&self) -> DiagnosticId { - rustc_errors::error_code!(E0107) - } - - fn diagnostic_common(&self) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> { - let mut err = self.start_diagnostics(); - - self.notify(&mut err); - self.suggest(&mut err); - self.show_definition(&mut err); - self.note_synth_provided(&mut err); - - err - } -} diff --git a/compiler/rustc_typeck/src/variance/constraints.rs b/compiler/rustc_typeck/src/variance/constraints.rs deleted file mode 100644 index eaf0310d5..000000000 --- a/compiler/rustc_typeck/src/variance/constraints.rs +++ /dev/null @@ -1,445 +0,0 @@ -//! Constraint construction and representation -//! -//! The second pass over the AST determines the set of constraints. -//! We walk the set of items and, for each member, generate new constraints. - -use hir::def_id::{DefId, LocalDefId}; -use rustc_hir as hir; -use rustc_hir::def::DefKind; -use rustc_middle::ty::subst::{GenericArgKind, SubstsRef}; -use rustc_middle::ty::{self, Ty, TyCtxt}; - -use super::terms::VarianceTerm::*; -use super::terms::*; - -pub struct ConstraintContext<'a, 'tcx> { - pub terms_cx: TermsContext<'a, 'tcx>, - - // These are pointers to common `ConstantTerm` instances - covariant: VarianceTermPtr<'a>, - contravariant: VarianceTermPtr<'a>, - invariant: VarianceTermPtr<'a>, - bivariant: VarianceTermPtr<'a>, - - pub constraints: Vec<Constraint<'a>>, -} - -/// Declares that the variable `decl_id` appears in a location with -/// variance `variance`. -#[derive(Copy, Clone)] -pub struct Constraint<'a> { - pub inferred: InferredIndex, - pub variance: &'a VarianceTerm<'a>, -} - -/// To build constraints, we visit one item (type, trait) at a time -/// and look at its contents. So e.g., if we have -/// ```ignore (illustrative) -/// struct Foo<T> { -/// b: Bar<T> -/// } -/// ``` -/// then while we are visiting `Bar<T>`, the `CurrentItem` would have -/// the `DefId` and the start of `Foo`'s inferreds. -pub struct CurrentItem { - inferred_start: InferredIndex, -} - -pub fn add_constraints_from_crate<'a, 'tcx>( - terms_cx: TermsContext<'a, 'tcx>, -) -> ConstraintContext<'a, 'tcx> { - let tcx = terms_cx.tcx; - let covariant = terms_cx.arena.alloc(ConstantTerm(ty::Covariant)); - let contravariant = terms_cx.arena.alloc(ConstantTerm(ty::Contravariant)); - let invariant = terms_cx.arena.alloc(ConstantTerm(ty::Invariant)); - let bivariant = terms_cx.arena.alloc(ConstantTerm(ty::Bivariant)); - let mut constraint_cx = ConstraintContext { - terms_cx, - covariant, - contravariant, - invariant, - bivariant, - constraints: Vec::new(), - }; - - let crate_items = tcx.hir_crate_items(()); - - for def_id in crate_items.definitions() { - let def_kind = tcx.def_kind(def_id); - match def_kind { - DefKind::Struct | DefKind::Union | DefKind::Enum => { - constraint_cx.build_constraints_for_item(def_id); - - let adt = tcx.adt_def(def_id); - for variant in adt.variants() { - if let Some(ctor) = variant.ctor_def_id { - constraint_cx.build_constraints_for_item(ctor.expect_local()); - } - } - } - DefKind::Fn | DefKind::AssocFn => constraint_cx.build_constraints_for_item(def_id), - _ => {} - } - } - - constraint_cx -} - -impl<'a, 'tcx> ConstraintContext<'a, 'tcx> { - fn tcx(&self) -> TyCtxt<'tcx> { - self.terms_cx.tcx - } - - fn build_constraints_for_item(&mut self, def_id: LocalDefId) { - let tcx = self.tcx(); - debug!("build_constraints_for_item({})", tcx.def_path_str(def_id.to_def_id())); - - // Skip items with no generics - there's nothing to infer in them. - if tcx.generics_of(def_id).count() == 0 { - return; - } - - let inferred_start = self.terms_cx.inferred_starts[&def_id]; - let current_item = &CurrentItem { inferred_start }; - match tcx.type_of(def_id).kind() { - ty::Adt(def, _) => { - // Not entirely obvious: constraints on structs/enums do not - // affect the variance of their type parameters. See discussion - // in comment at top of module. - // - // self.add_constraints_from_generics(generics); - - for field in def.all_fields() { - self.add_constraints_from_ty( - current_item, - tcx.type_of(field.did), - self.covariant, - ); - } - } - - ty::FnDef(..) => { - self.add_constraints_from_sig(current_item, tcx.fn_sig(def_id), self.covariant); - } - - ty::Error(_) => {} - _ => { - span_bug!( - tcx.def_span(def_id), - "`build_constraints_for_item` unsupported for this item" - ); - } - } - } - - fn add_constraint(&mut self, current: &CurrentItem, index: u32, variance: VarianceTermPtr<'a>) { - debug!("add_constraint(index={}, variance={:?})", index, variance); - self.constraints.push(Constraint { - inferred: InferredIndex(current.inferred_start.0 + index as usize), - variance, - }); - } - - fn contravariant(&mut self, variance: VarianceTermPtr<'a>) -> VarianceTermPtr<'a> { - self.xform(variance, self.contravariant) - } - - fn invariant(&mut self, variance: VarianceTermPtr<'a>) -> VarianceTermPtr<'a> { - self.xform(variance, self.invariant) - } - - fn constant_term(&self, v: ty::Variance) -> VarianceTermPtr<'a> { - match v { - ty::Covariant => self.covariant, - ty::Invariant => self.invariant, - ty::Contravariant => self.contravariant, - ty::Bivariant => self.bivariant, - } - } - - fn xform(&mut self, v1: VarianceTermPtr<'a>, v2: VarianceTermPtr<'a>) -> VarianceTermPtr<'a> { - match (*v1, *v2) { - (_, ConstantTerm(ty::Covariant)) => { - // Applying a "covariant" transform is always a no-op - v1 - } - - (ConstantTerm(c1), ConstantTerm(c2)) => self.constant_term(c1.xform(c2)), - - _ => &*self.terms_cx.arena.alloc(TransformTerm(v1, v2)), - } - } - - #[instrument(level = "debug", skip(self, current))] - fn add_constraints_from_invariant_substs( - &mut self, - current: &CurrentItem, - substs: SubstsRef<'tcx>, - variance: VarianceTermPtr<'a>, - ) { - // Trait are always invariant so we can take advantage of that. - let variance_i = self.invariant(variance); - - for k in substs { - match k.unpack() { - GenericArgKind::Lifetime(lt) => { - self.add_constraints_from_region(current, lt, variance_i) - } - GenericArgKind::Type(ty) => self.add_constraints_from_ty(current, ty, variance_i), - GenericArgKind::Const(val) => { - self.add_constraints_from_const(current, val, variance_i) - } - } - } - } - - /// Adds constraints appropriate for an instance of `ty` appearing - /// in a context with the generics defined in `generics` and - /// ambient variance `variance` - fn add_constraints_from_ty( - &mut self, - current: &CurrentItem, - ty: Ty<'tcx>, - variance: VarianceTermPtr<'a>, - ) { - debug!("add_constraints_from_ty(ty={:?}, variance={:?})", ty, variance); - - match *ty.kind() { - ty::Bool - | ty::Char - | ty::Int(_) - | ty::Uint(_) - | ty::Float(_) - | ty::Str - | ty::Never - | ty::Foreign(..) => { - // leaf type -- noop - } - - ty::FnDef(..) | ty::Generator(..) | ty::Closure(..) => { - bug!("Unexpected closure type in variance computation"); - } - - ty::Ref(region, ty, mutbl) => { - let contra = self.contravariant(variance); - self.add_constraints_from_region(current, region, contra); - self.add_constraints_from_mt(current, &ty::TypeAndMut { ty, mutbl }, variance); - } - - ty::Array(typ, len) => { - self.add_constraints_from_const(current, len, variance); - self.add_constraints_from_ty(current, typ, variance); - } - - ty::Slice(typ) => { - self.add_constraints_from_ty(current, typ, variance); - } - - ty::RawPtr(ref mt) => { - self.add_constraints_from_mt(current, mt, variance); - } - - ty::Tuple(subtys) => { - for subty in subtys { - self.add_constraints_from_ty(current, subty, variance); - } - } - - ty::Adt(def, substs) => { - self.add_constraints_from_substs(current, def.did(), substs, variance); - } - - ty::Projection(ref data) => { - self.add_constraints_from_invariant_substs(current, data.substs, variance); - } - - ty::Opaque(_, substs) => { - self.add_constraints_from_invariant_substs(current, substs, variance); - } - - 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); - - if let Some(poly_trait_ref) = data.principal() { - self.add_constraints_from_invariant_substs( - current, - poly_trait_ref.skip_binder().substs, - variance, - ); - } - - for projection in data.projection_bounds() { - match projection.skip_binder().term.unpack() { - ty::TermKind::Ty(ty) => { - self.add_constraints_from_ty(current, ty, self.invariant); - } - ty::TermKind::Const(c) => { - self.add_constraints_from_const(current, c, self.invariant) - } - } - } - } - - ty::Param(ref data) => { - self.add_constraint(current, data.index, variance); - } - - ty::FnPtr(sig) => { - self.add_constraints_from_sig(current, sig, variance); - } - - ty::Error(_) => { - // we encounter this when walking the trait references for object - // types, where we use Error as the Self type - } - - ty::Placeholder(..) | ty::GeneratorWitness(..) | ty::Bound(..) | ty::Infer(..) => { - bug!( - "unexpected type encountered in \ - variance inference: {}", - ty - ); - } - } - } - - /// Adds constraints appropriate for a nominal type (enum, struct, - /// object, etc) appearing in a context with ambient variance `variance` - fn add_constraints_from_substs( - &mut self, - current: &CurrentItem, - def_id: DefId, - substs: SubstsRef<'tcx>, - variance: VarianceTermPtr<'a>, - ) { - debug!( - "add_constraints_from_substs(def_id={:?}, substs={:?}, variance={:?})", - def_id, substs, variance - ); - - // We don't record `inferred_starts` entries for empty generics. - if substs.is_empty() { - return; - } - - let (local, remote) = if let Some(def_id) = def_id.as_local() { - (Some(self.terms_cx.inferred_starts[&def_id]), None) - } else { - (None, Some(self.tcx().variances_of(def_id))) - }; - - for (i, k) in substs.iter().enumerate() { - let variance_decl = if let Some(InferredIndex(start)) = local { - // Parameter on an item defined within current crate: - // variance not yet inferred, so return a symbolic - // variance. - self.terms_cx.inferred_terms[start + i] - } else { - // Parameter on an item defined within another crate: - // variance already inferred, just look it up. - self.constant_term(remote.as_ref().unwrap()[i]) - }; - let variance_i = self.xform(variance, variance_decl); - debug!( - "add_constraints_from_substs: variance_decl={:?} variance_i={:?}", - variance_decl, variance_i - ); - match k.unpack() { - GenericArgKind::Lifetime(lt) => { - self.add_constraints_from_region(current, lt, variance_i) - } - GenericArgKind::Type(ty) => self.add_constraints_from_ty(current, ty, variance_i), - GenericArgKind::Const(val) => { - self.add_constraints_from_const(current, val, variance) - } - } - } - } - - /// Adds constraints appropriate for a const expression `val` - /// in a context with ambient variance `variance` - fn add_constraints_from_const( - &mut self, - current: &CurrentItem, - c: ty::Const<'tcx>, - variance: VarianceTermPtr<'a>, - ) { - debug!("add_constraints_from_const(c={:?}, variance={:?})", c, variance); - - match &c.kind() { - ty::ConstKind::Unevaluated(uv) => { - self.add_constraints_from_invariant_substs(current, uv.substs, variance); - } - _ => {} - } - } - - /// Adds constraints appropriate for a function with signature - /// `sig` appearing in a context with ambient variance `variance` - fn add_constraints_from_sig( - &mut self, - current: &CurrentItem, - sig: ty::PolyFnSig<'tcx>, - variance: VarianceTermPtr<'a>, - ) { - let contra = self.contravariant(variance); - for &input in sig.skip_binder().inputs() { - self.add_constraints_from_ty(current, input, contra); - } - self.add_constraints_from_ty(current, sig.skip_binder().output(), variance); - } - - /// Adds constraints appropriate for a region appearing in a - /// context with ambient variance `variance` - fn add_constraints_from_region( - &mut self, - current: &CurrentItem, - region: ty::Region<'tcx>, - variance: VarianceTermPtr<'a>, - ) { - match *region { - ty::ReEarlyBound(ref data) => { - self.add_constraint(current, data.index, variance); - } - - ty::ReStatic => {} - - ty::ReLateBound(..) => { - // Late-bound regions do not get substituted the same - // way early-bound regions do, so we skip them here. - } - - 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!( - "unexpected region encountered in variance \ - inference: {:?}", - region - ); - } - } - } - - /// Adds constraints appropriate for a mutability-type pair - /// appearing in a context with ambient variance `variance` - fn add_constraints_from_mt( - &mut self, - current: &CurrentItem, - mt: &ty::TypeAndMut<'tcx>, - variance: VarianceTermPtr<'a>, - ) { - match mt.mutbl { - hir::Mutability::Mut => { - let invar = self.invariant(variance); - self.add_constraints_from_ty(current, mt.ty, invar); - } - - hir::Mutability::Not => { - self.add_constraints_from_ty(current, mt.ty, variance); - } - } - } -} diff --git a/compiler/rustc_typeck/src/variance/mod.rs b/compiler/rustc_typeck/src/variance/mod.rs deleted file mode 100644 index 82103c5a0..000000000 --- a/compiler/rustc_typeck/src/variance/mod.rs +++ /dev/null @@ -1,63 +0,0 @@ -//! Module for inferring the variance of type and lifetime parameters. See the [rustc dev guide] -//! chapter for more info. -//! -//! [rustc dev guide]: https://rustc-dev-guide.rust-lang.org/variance.html - -use rustc_arena::DroplessArena; -use rustc_hir::def::DefKind; -use rustc_hir::def_id::DefId; -use rustc_middle::ty::query::Providers; -use rustc_middle::ty::{self, CrateVariancesMap, TyCtxt}; - -/// Defines the `TermsContext` basically houses an arena where we can -/// allocate terms. -mod terms; - -/// Code to gather up constraints. -mod constraints; - -/// Code to solve constraints and write out the results. -mod solve; - -/// Code to write unit tests of variance. -pub mod test; - -/// Code for transforming variances. -mod xform; - -pub fn provide(providers: &mut Providers) { - *providers = Providers { variances_of, crate_variances, ..*providers }; -} - -fn crate_variances(tcx: TyCtxt<'_>, (): ()) -> CrateVariancesMap<'_> { - let arena = DroplessArena::default(); - let terms_cx = terms::determine_parameters_to_be_inferred(tcx, &arena); - let constraints_cx = constraints::add_constraints_from_crate(terms_cx); - solve::solve_constraints(constraints_cx) -} - -fn variances_of(tcx: TyCtxt<'_>, item_def_id: DefId) -> &[ty::Variance] { - // Skip items with no generics - there's nothing to infer in them. - if tcx.generics_of(item_def_id).count() == 0 { - return &[]; - } - - match tcx.def_kind(item_def_id) { - DefKind::Fn - | DefKind::AssocFn - | DefKind::Enum - | DefKind::Struct - | DefKind::Union - | DefKind::Variant - | DefKind::Ctor(..) => {} - _ => { - // Variance not relevant. - span_bug!(tcx.def_span(item_def_id), "asked to compute variance for wrong kind of item") - } - } - - // Everything else must be inferred. - - let crate_map = tcx.crate_variances(()); - crate_map.variances.get(&item_def_id).copied().unwrap_or(&[]) -} diff --git a/compiler/rustc_typeck/src/variance/solve.rs b/compiler/rustc_typeck/src/variance/solve.rs deleted file mode 100644 index 97aca621a..000000000 --- a/compiler/rustc_typeck/src/variance/solve.rs +++ /dev/null @@ -1,135 +0,0 @@ -//! Constraint solving -//! -//! The final phase iterates over the constraints, refining the variance -//! for each inferred until a fixed point is reached. This will be the -//! optimal solution to the constraints. The final variance for each -//! inferred is then written into the `variance_map` in the tcx. - -use rustc_data_structures::fx::FxHashMap; -use rustc_hir::def_id::DefId; -use rustc_middle::ty; - -use super::constraints::*; -use super::terms::VarianceTerm::*; -use super::terms::*; -use super::xform::*; - -struct SolveContext<'a, 'tcx> { - terms_cx: TermsContext<'a, 'tcx>, - constraints: Vec<Constraint<'a>>, - - // Maps from an InferredIndex to the inferred value for that variable. - solutions: Vec<ty::Variance>, -} - -pub fn solve_constraints<'tcx>( - constraints_cx: ConstraintContext<'_, 'tcx>, -) -> ty::CrateVariancesMap<'tcx> { - let ConstraintContext { terms_cx, constraints, .. } = constraints_cx; - - let mut solutions = vec![ty::Bivariant; terms_cx.inferred_terms.len()]; - for &(id, ref variances) in &terms_cx.lang_items { - let InferredIndex(start) = terms_cx.inferred_starts[&id]; - for (i, &variance) in variances.iter().enumerate() { - solutions[start + i] = variance; - } - } - - let mut solutions_cx = SolveContext { terms_cx, constraints, solutions }; - solutions_cx.solve(); - let variances = solutions_cx.create_map(); - - ty::CrateVariancesMap { variances } -} - -impl<'a, 'tcx> SolveContext<'a, 'tcx> { - fn solve(&mut self) { - // Propagate constraints until a fixed point is reached. Note - // that the maximum number of iterations is 2C where C is the - // number of constraints (each variable can change values at most - // twice). Since number of constraints is linear in size of the - // input, so is the inference process. - let mut changed = true; - while changed { - changed = false; - - for constraint in &self.constraints { - let Constraint { inferred, variance: term } = *constraint; - let InferredIndex(inferred) = inferred; - let variance = self.evaluate(term); - let old_value = self.solutions[inferred]; - let new_value = glb(variance, old_value); - if old_value != new_value { - debug!( - "updating inferred {} \ - from {:?} to {:?} due to {:?}", - inferred, old_value, new_value, term - ); - - self.solutions[inferred] = new_value; - changed = true; - } - } - } - } - - fn enforce_const_invariance(&self, generics: &ty::Generics, variances: &mut [ty::Variance]) { - let tcx = self.terms_cx.tcx; - - // Make all const parameters invariant. - for param in generics.params.iter() { - if let ty::GenericParamDefKind::Const { .. } = param.kind { - variances[param.index as usize] = ty::Invariant; - } - } - - // Make all the const parameters in the parent invariant (recursively). - if let Some(def_id) = generics.parent { - self.enforce_const_invariance(tcx.generics_of(def_id), variances); - } - } - - fn create_map(&self) -> FxHashMap<DefId, &'tcx [ty::Variance]> { - let tcx = self.terms_cx.tcx; - - let solutions = &self.solutions; - self.terms_cx - .inferred_starts - .iter() - .map(|(&def_id, &InferredIndex(start))| { - let generics = tcx.generics_of(def_id); - let count = generics.count(); - - let variances = tcx.arena.alloc_slice(&solutions[start..(start + count)]); - - // Const parameters are always invariant. - self.enforce_const_invariance(generics, variances); - - // Functions are permitted to have unused generic parameters: make those invariant. - if let ty::FnDef(..) = tcx.type_of(def_id).kind() { - for variance in variances.iter_mut() { - if *variance == ty::Bivariant { - *variance = ty::Invariant; - } - } - } - - (def_id.to_def_id(), &*variances) - }) - .collect() - } - - fn evaluate(&self, term: VarianceTermPtr<'a>) -> ty::Variance { - match *term { - ConstantTerm(v) => v, - - TransformTerm(t1, t2) => { - let v1 = self.evaluate(t1); - let v2 = self.evaluate(t2); - v1.xform(v2) - } - - InferredTerm(InferredIndex(index)) => self.solutions[index], - } - } -} diff --git a/compiler/rustc_typeck/src/variance/terms.rs b/compiler/rustc_typeck/src/variance/terms.rs deleted file mode 100644 index 1f763011e..000000000 --- a/compiler/rustc_typeck/src/variance/terms.rs +++ /dev/null @@ -1,145 +0,0 @@ -// Representing terms -// -// Terms are structured as a straightforward tree. Rather than rely on -// GC, we allocate terms out of a bounded arena (the lifetime of this -// arena is the lifetime 'a that is threaded around). -// -// We assign a unique index to each type/region parameter whose variance -// is to be inferred. We refer to such variables as "inferreds". An -// `InferredIndex` is a newtype'd int representing the index of such -// a variable. - -use rustc_arena::DroplessArena; -use rustc_hir::def::DefKind; -use rustc_hir::def_id::{LocalDefId, LocalDefIdMap}; -use rustc_middle::ty::{self, TyCtxt}; -use std::fmt; - -use self::VarianceTerm::*; - -pub type VarianceTermPtr<'a> = &'a VarianceTerm<'a>; - -#[derive(Copy, Clone, Debug)] -pub struct InferredIndex(pub usize); - -#[derive(Copy, Clone)] -pub enum VarianceTerm<'a> { - ConstantTerm(ty::Variance), - TransformTerm(VarianceTermPtr<'a>, VarianceTermPtr<'a>), - InferredTerm(InferredIndex), -} - -impl<'a> fmt::Debug for VarianceTerm<'a> { - fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { - match *self { - ConstantTerm(c1) => write!(f, "{:?}", c1), - TransformTerm(v1, v2) => write!(f, "({:?} \u{00D7} {:?})", v1, v2), - InferredTerm(id) => write!(f, "[{}]", { - let InferredIndex(i) = id; - i - }), - } - } -} - -// The first pass over the crate simply builds up the set of inferreds. - -pub struct TermsContext<'a, 'tcx> { - pub tcx: TyCtxt<'tcx>, - pub arena: &'a DroplessArena, - - // For marker types, UnsafeCell, and other lang items where - // variance is hardcoded, records the item-id and the hardcoded - // variance. - pub lang_items: Vec<(LocalDefId, Vec<ty::Variance>)>, - - // Maps from the node id of an item to the first inferred index - // used for its type & region parameters. - pub inferred_starts: LocalDefIdMap<InferredIndex>, - - // Maps from an InferredIndex to the term for that variable. - pub inferred_terms: Vec<VarianceTermPtr<'a>>, -} - -pub fn determine_parameters_to_be_inferred<'a, 'tcx>( - tcx: TyCtxt<'tcx>, - arena: &'a DroplessArena, -) -> TermsContext<'a, 'tcx> { - let mut terms_cx = TermsContext { - tcx, - arena, - inferred_starts: Default::default(), - inferred_terms: vec![], - - lang_items: lang_items(tcx), - }; - - // See the following for a discussion on dep-graph management. - // - // - https://rustc-dev-guide.rust-lang.org/query.html - // - https://rustc-dev-guide.rust-lang.org/variance.html - let crate_items = tcx.hir_crate_items(()); - - for def_id in crate_items.definitions() { - debug!("add_inferreds for item {:?}", def_id); - - let def_kind = tcx.def_kind(def_id); - - match def_kind { - DefKind::Struct | DefKind::Union | DefKind::Enum => { - terms_cx.add_inferreds_for_item(def_id); - - let adt = tcx.adt_def(def_id); - for variant in adt.variants() { - if let Some(ctor) = variant.ctor_def_id { - terms_cx.add_inferreds_for_item(ctor.expect_local()); - } - } - } - DefKind::Fn | DefKind::AssocFn => terms_cx.add_inferreds_for_item(def_id), - _ => {} - } - } - - terms_cx -} - -fn lang_items(tcx: TyCtxt<'_>) -> Vec<(LocalDefId, Vec<ty::Variance>)> { - let lang_items = tcx.lang_items(); - let all = [ - (lang_items.phantom_data(), vec![ty::Covariant]), - (lang_items.unsafe_cell_type(), vec![ty::Invariant]), - ]; - - all.into_iter() // iterating over (Option<DefId>, Variance) - .filter_map(|(d, v)| { - let def_id = d?.as_local()?; // LocalDefId - Some((def_id, v)) - }) - .collect() -} - -impl<'a, 'tcx> TermsContext<'a, 'tcx> { - fn add_inferreds_for_item(&mut self, def_id: LocalDefId) { - let tcx = self.tcx; - let count = tcx.generics_of(def_id).count(); - - if count == 0 { - return; - } - - // Record the start of this item's inferreds. - let start = self.inferred_terms.len(); - let newly_added = self.inferred_starts.insert(def_id, InferredIndex(start)).is_none(); - assert!(newly_added); - - // N.B., in the code below for writing the results back into the - // `CrateVariancesMap`, we rely on the fact that all inferreds - // for a particular item are assigned continuous indices. - - let arena = self.arena; - self.inferred_terms.extend( - (start..(start + count)).map(|i| &*arena.alloc(InferredTerm(InferredIndex(i)))), - ); - } -} diff --git a/compiler/rustc_typeck/src/variance/test.rs b/compiler/rustc_typeck/src/variance/test.rs deleted file mode 100644 index 2ba87db88..000000000 --- a/compiler/rustc_typeck/src/variance/test.rs +++ /dev/null @@ -1,14 +0,0 @@ -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.def_id.to_def_id(), sym::rustc_variance) { - let variances_of = tcx.variances_of(id.def_id); - struct_span_err!(tcx.sess, tcx.def_span(id.def_id), E0208, "{:?}", variances_of).emit(); - } - } -} diff --git a/compiler/rustc_typeck/src/variance/xform.rs b/compiler/rustc_typeck/src/variance/xform.rs deleted file mode 100644 index 027f0859f..000000000 --- a/compiler/rustc_typeck/src/variance/xform.rs +++ /dev/null @@ -1,22 +0,0 @@ -use rustc_middle::ty; - -pub fn glb(v1: ty::Variance, v2: ty::Variance) -> ty::Variance { - // Greatest lower bound of the variance lattice as - // defined in The Paper: - // - // * - // - + - // o - match (v1, v2) { - (ty::Invariant, _) | (_, ty::Invariant) => ty::Invariant, - - (ty::Covariant, ty::Contravariant) => ty::Invariant, - (ty::Contravariant, ty::Covariant) => ty::Invariant, - - (ty::Covariant, ty::Covariant) => ty::Covariant, - - (ty::Contravariant, ty::Contravariant) => ty::Contravariant, - - (x, ty::Bivariant) | (ty::Bivariant, x) => x, - } -} |