From 94a0819fe3a0d679c3042a77bfe6a2afc505daea Mon Sep 17 00:00:00 2001 From: Daniel Baumann Date: Wed, 17 Apr 2024 14:11:28 +0200 Subject: Adding upstream version 1.66.0+dfsg1. Signed-off-by: Daniel Baumann --- .../rustc_hir_analysis/src/astconv/generics.rs | 662 +++++++++++++++++++++ 1 file changed, 662 insertions(+) create mode 100644 compiler/rustc_hir_analysis/src/astconv/generics.rs (limited to 'compiler/rustc_hir_analysis/src/astconv/generics.rs') diff --git a/compiler/rustc_hir_analysis/src/astconv/generics.rs b/compiler/rustc_hir_analysis/src/astconv/generics.rs new file mode 100644 index 000000000..47915b4bd --- /dev/null +++ b/compiler/rustc_hir_analysis/src/astconv/generics.rs @@ -0,0 +1,662 @@ +use super::IsMethodCall; +use crate::astconv::{ + AstConv, CreateSubstsForGenericArgsCtxt, ExplicitLateBound, GenericArgCountMismatch, + GenericArgCountResult, GenericArgPosition, +}; +use crate::errors::AssocTypeBindingNotAllowed; +use crate::structured_errors::{GenericArgsInfo, StructuredDiagnostic, WrongNumberOfGenericArgs}; +use rustc_ast::ast::ParamKindOrd; +use rustc_errors::{struct_span_err, Applicability, Diagnostic, MultiSpan}; +use rustc_hir as hir; +use rustc_hir::def::{DefKind, Res}; +use rustc_hir::def_id::DefId; +use rustc_hir::GenericArg; +use rustc_infer::infer::TyCtxtInferExt; +use rustc_middle::ty::{ + self, subst, subst::SubstsRef, GenericParamDef, GenericParamDefKind, IsSuggestable, Ty, TyCtxt, +}; +use rustc_session::lint::builtin::LATE_BOUND_LIFETIME_ARGUMENTS; +use rustc_span::{symbol::kw, Span}; +use smallvec::SmallVec; + +impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { + /// Report an error that a generic argument did not match the generic parameter that was + /// expected. + fn generic_arg_mismatch_err( + tcx: TyCtxt<'_>, + arg: &GenericArg<'_>, + param: &GenericParamDef, + possible_ordering_error: bool, + help: Option<&str>, + ) { + let sess = tcx.sess; + let mut err = struct_span_err!( + sess, + arg.span(), + E0747, + "{} provided when a {} was expected", + arg.descr(), + param.kind.descr(), + ); + + if let GenericParamDefKind::Const { .. } = param.kind { + if matches!(arg, GenericArg::Type(hir::Ty { kind: hir::TyKind::Infer, .. })) { + err.help("const arguments cannot yet be inferred with `_`"); + if sess.is_nightly_build() { + err.help( + "add `#![feature(generic_arg_infer)]` to the crate attributes to enable", + ); + } + } + } + + let add_braces_suggestion = |arg: &GenericArg<'_>, err: &mut Diagnostic| { + let suggestions = vec![ + (arg.span().shrink_to_lo(), String::from("{ ")), + (arg.span().shrink_to_hi(), String::from(" }")), + ]; + err.multipart_suggestion( + "if this generic argument was intended as a const parameter, \ + surround it with braces", + suggestions, + Applicability::MaybeIncorrect, + ); + }; + + // Specific suggestion set for diagnostics + match (arg, ¶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 infcx = tcx.infer_ctxt().build(); + let param_type = + infcx.resolve_numeric_literals_with_default(tcx.type_of(param.def_id)); + if param_type.is_suggestable(tcx, false) { + err.span_suggestion( + tcx.def_span(src_def_id), + "consider changing this type parameter to be a `const` generic", + format!("const {}: {}", param_name, param_type), + Applicability::MaybeIncorrect, + ); + }; + } + } + _ => add_braces_suggestion(arg, &mut err), + }, + ( + GenericArg::Type(hir::Ty { kind: hir::TyKind::Path(_), .. }), + GenericParamDefKind::Const { .. }, + ) => add_braces_suggestion(arg, &mut err), + ( + GenericArg::Type(hir::Ty { kind: hir::TyKind::Array(_, len), .. }), + GenericParamDefKind::Const { .. }, + ) if tcx.type_of(param.def_id) == tcx.types.usize => { + let snippet = sess.source_map().span_to_snippet(tcx.hir().span(len.hir_id())); + if let Ok(snippet) = snippet { + err.span_suggestion( + arg.span(), + "array type provided where a `usize` was expected, try", + format!("{{ {} }}", snippet), + Applicability::MaybeIncorrect, + ); + } + } + (GenericArg::Const(cnst), GenericParamDefKind::Type { .. }) => { + let body = tcx.hir().body(cnst.value.body); + if let rustc_hir::ExprKind::Path(rustc_hir::QPath::Resolved(_, path)) = + body.value.kind + { + if let Res::Def(DefKind::Fn { .. }, id) = path.res { + err.help(&format!( + "`{}` is a function item, not a type", + tcx.item_name(id) + )); + err.help("function item types cannot be named directly"); + } + } + } + _ => {} + } + + let kind_ord = param.kind.to_ord(); + let arg_ord = arg.to_ord(); + + // This note is only true when generic parameters are strictly ordered by their kind. + if possible_ordering_error && kind_ord.cmp(&arg_ord) != core::cmp::Ordering::Equal { + let (first, last) = if kind_ord < arg_ord { + (param.kind.descr(), arg.descr()) + } else { + (arg.descr(), param.kind.descr()) + }; + err.note(&format!("{} arguments must be provided before {} arguments", first, last)); + if let Some(help) = help { + err.help(help); + } + } + + err.emit(); + } + + /// Creates the relevant generic argument substitutions + /// corresponding to a set of generic parameters. This is a + /// rather complex function. Let us try to explain the role + /// of each of its parameters: + /// + /// To start, we are given the `def_id` of the thing we are + /// creating the substitutions for, and a partial set of + /// substitutions `parent_substs`. In general, the substitutions + /// for an item begin with substitutions for all the "parents" of + /// that item -- e.g., for a method it might include the + /// parameters from the impl. + /// + /// Therefore, the method begins by walking down these parents, + /// starting with the outermost parent and proceed inwards until + /// it reaches `def_id`. For each parent `P`, it will check `parent_substs` + /// first to see if the parent's substitutions are listed in there. If so, + /// we can append those and move on. Otherwise, it invokes the + /// three callback functions: + /// + /// - `args_for_def_id`: given the `DefId` `P`, supplies back the + /// generic arguments that were given to that parent from within + /// the path; so e.g., if you have `::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>, + 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::>(); + param_types_present.sort_by_key(|(ord, _)| *ord); + let (mut param_types_present, ordered_params): ( + Vec, + Vec, + ) = 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::>() + .join(", then "), + ordered_params + .into_iter() + .filter_map(|param| { + if param.name == kw::SelfUpper { + None + } else { + Some(param.name.to_string()) + } + }) + .collect::>() + .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 && let Some(b) = gen_args.bindings.first() { + Self::prohibit_assoc_ty_binding(tcx, b.span); + } + + let explicit_late_bound = + Self::prohibit_explicit_late_bound_lifetimes(tcx, gen_params, gen_args, gen_pos); + + let mut invalid_args = vec![]; + + let mut check_lifetime_args = + |min_expected_args: usize, + max_expected_args: usize, + provided_args: usize, + late_bounds_ignore: bool| { + if (min_expected_args..=max_expected_args).contains(&provided_args) { + return Ok(()); + } + + if late_bounds_ignore { + return Ok(()); + } + + if provided_args > max_expected_args { + invalid_args.extend( + gen_args.args[max_expected_args..provided_args] + .iter() + .map(|arg| arg.span()), + ); + }; + + let gen_args_info = if provided_args > min_expected_args { + invalid_args.extend( + gen_args.args[min_expected_args..provided_args] + .iter() + .map(|arg| arg.span()), + ); + let num_redundant_args = provided_args - min_expected_args; + GenericArgsInfo::ExcessLifetimes { num_redundant_args } + } else { + let num_missing_args = min_expected_args - provided_args; + GenericArgsInfo::MissingLifetimes { num_missing_args } + }; + + let reported = WrongNumberOfGenericArgs::new( + tcx, + gen_args_info, + seg, + gen_params, + has_self as usize, + gen_args, + def_id, + ) + .diagnostic() + .emit(); + + Err(reported) + }; + + let min_expected_lifetime_args = if infer_lifetimes { 0 } else { param_counts.lifetimes }; + let max_expected_lifetime_args = param_counts.lifetimes; + let num_provided_lifetime_args = gen_args.num_lifetime_params(); + + let lifetimes_correct = check_lifetime_args( + min_expected_lifetime_args, + max_expected_lifetime_args, + num_provided_lifetime_args, + explicit_late_bound == ExplicitLateBound::Yes, + ); + + let mut check_types_and_consts = |expected_min, + expected_max, + expected_max_with_synth, + provided, + params_offset, + args_offset| { + debug!( + ?expected_min, + ?expected_max, + ?provided, + ?params_offset, + ?args_offset, + "check_types_and_consts" + ); + if (expected_min..=expected_max).contains(&provided) { + return Ok(()); + } + + let num_default_params = expected_max - expected_min; + + let gen_args_info = if provided > expected_max { + invalid_args.extend( + gen_args.args[args_offset + expected_max..args_offset + provided] + .iter() + .map(|arg| arg.span()), + ); + let num_redundant_args = provided - expected_max; + + // Provide extra note if synthetic arguments like `impl Trait` are specified. + let synth_provided = provided <= expected_max_with_synth; + + GenericArgsInfo::ExcessTypesOrConsts { + num_redundant_args, + num_default_params, + args_offset, + synth_provided, + } + } else { + let num_missing_args = expected_max - provided; + + GenericArgsInfo::MissingTypesOrConsts { + num_missing_args, + num_default_params, + args_offset, + } + }; + + debug!(?gen_args_info); + + let reported = WrongNumberOfGenericArgs::new( + tcx, + gen_args_info, + seg, + gen_params, + params_offset, + gen_args, + def_id, + ) + .diagnostic() + .emit_unless(gen_args.has_err()); + + Err(reported) + }; + + let args_correct = { + let expected_min = if infer_args { + 0 + } else { + param_counts.consts + named_type_param_count + - default_counts.types + - default_counts.consts + }; + debug!(?expected_min); + debug!(arg_counts.lifetimes=?gen_args.num_lifetime_params()); + + check_types_and_consts( + expected_min, + param_counts.consts + named_type_param_count, + param_counts.consts + named_type_param_count + synth_type_param_count, + gen_args.num_generic_params(), + param_counts.lifetimes + has_self as usize, + gen_args.num_lifetime_params(), + ) + }; + + GenericArgCountResult { + explicit_late_bound, + correct: lifetimes_correct.and(args_correct).map_err(|reported| { + GenericArgCountMismatch { reported: Some(reported), invalid_args } + }), + } + } + + /// Emits an error regarding forbidden type binding associations + pub fn prohibit_assoc_ty_binding(tcx: TyCtxt<'_>, span: Span) { + tcx.sess.emit_err(AssocTypeBindingNotAllowed { span }); + } + + /// Prohibits explicit lifetime arguments if late-bound lifetime parameters + /// are present. This is used both for datatypes and function calls. + pub(crate) fn prohibit_explicit_late_bound_lifetimes( + tcx: TyCtxt<'_>, + def: &ty::Generics, + args: &hir::GenericArgs<'_>, + position: GenericArgPosition, + ) -> ExplicitLateBound { + let param_counts = def.own_counts(); + let infer_lifetimes = position != GenericArgPosition::Type && !args.has_lifetime_params(); + + if infer_lifetimes { + return ExplicitLateBound::No; + } + + if let Some(span_late) = def.has_late_bound_regions { + let msg = "cannot specify lifetime arguments explicitly \ + if late bound lifetime parameters are present"; + let note = "the late bound lifetime parameter is introduced here"; + let span = args.args[0].span(); + + if position == GenericArgPosition::Value + && args.num_lifetime_params() != param_counts.lifetimes + { + let mut err = tcx.sess.struct_span_err(span, msg); + err.span_note(span_late, note); + err.emit(); + } else { + let mut multispan = MultiSpan::from_span(span); + multispan.push_span_label(span_late, note); + tcx.struct_span_lint_hir( + LATE_BOUND_LIFETIME_ARGUMENTS, + args.args[0].hir_id(), + multispan, + msg, + |lint| lint, + ); + } + + ExplicitLateBound::Yes + } else { + ExplicitLateBound::No + } + } +} -- cgit v1.2.3