diff --git a/src/librustc_typeck/astconv/errors.rs b/src/librustc_typeck/astconv/errors.rs new file mode 100644 index 0000000000000..685243f54cb90 --- /dev/null +++ b/src/librustc_typeck/astconv/errors.rs @@ -0,0 +1,388 @@ +use crate::astconv::AstConv; +use rustc_ast::util::lev_distance::find_best_match_for_name; +use rustc_data_structures::fx::FxHashMap; +use rustc_errors::{pluralize, struct_span_err, Applicability}; +use rustc_hir as hir; +use rustc_hir::def_id::DefId; +use rustc_middle::ty; +use rustc_session::parse::feature_err; +use rustc_span::symbol::{sym, Ident}; +use rustc_span::{Span, 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, + def_id: DefId, + span: Span, + empty_generic_args: bool, + ) { + if missing_type_params.is_empty() { + return; + } + let display = + missing_type_params.iter().map(|n| format!("`{}`", n)).collect::>().join(", "); + let mut err = struct_span_err!( + self.tcx().sess, + span, + E0393, + "the type parameter{} {} must be explicitly specified", + pluralize!(missing_type_params.len()), + display, + ); + err.span_label( + self.tcx().def_span(def_id), + &format!( + "type parameter{} {} must be specified for this", + pluralize!(missing_type_params.len()), + display, + ), + ); + let mut suggested = false; + if let (Ok(snippet), true) = ( + self.tcx().sess.source_map().span_to_snippet(span), + // 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. + 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`. + err.span_suggestion( + span, + &format!( + "set the type parameter{plural} to the desired type{plural}", + plural = pluralize!(missing_type_params.len()), + ), + format!("{}<{}>", snippet, missing_type_params.join(", ")), + Applicability::HasPlaceholders, + ); + suggested = true; + } + } + if !suggested { + err.span_label( + span, + format!( + "missing reference{} to {}", + pluralize!(missing_type_params.len()), + display, + ), + ); + } + err.note( + "because of the default `Self` reference, type parameters must be \ + specified on object types", + ); + err.emit(); + } + + /// 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: &'a hir::PathSegment<'a>, + ) { + let trait_def = self.tcx().trait_def(trait_def_id); + + if !self.tcx().features().unboxed_closures + && trait_segment.generic_args().parenthesized != trait_def.paren_sugar + { + let sess = &self.tcx().sess.parse_sess; + // For now, require that parenthetical notation be used only with `Fn()` etc. + let (msg, sugg) = if trait_def.paren_sugar { + ( + "the precise format of `Fn`-family traits' type parameters is subject to \ + change", + Some(format!( + "{}{} -> {}", + trait_segment.ident, + 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::, _>>() + .map(|a| a.join(", ")), + _ => sess.source_map().span_to_snippet(ty.span), + } + .map(|s| format!("({})", s)) + .ok(), + _ => None, + }) + .unwrap_or_else(|| "()".to_string()), + trait_segment + .generic_args() + .bindings + .iter() + .find_map(|b| match (b.ident.name == sym::Output, &b.kind) { + (true, hir::TypeBindingKind::Equality { ty }) => { + sess.source_map().span_to_snippet(ty.span).ok() + } + _ => None, + }) + .unwrap_or_else(|| "()".to_string()), + )), + ) + } else { + ("parenthetical notation is only stable when used with `Fn`-family traits", None) + }; + let mut err = feature_err(sess, sym::unboxed_closures, span, msg); + if let Some(sugg) = sugg { + let msg = "use parenthetical notation instead"; + err.span_suggestion(span, msg, sugg, Applicability::MaybeIncorrect); + } + err.emit(); + } + } + + pub(crate) fn complain_about_assoc_type_not_found( + &self, + all_candidates: impl Fn() -> I, + ty_param_name: &str, + assoc_name: Ident, + span: Span, + ) where + I: Iterator>, + { + // 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() + .map(|r| self.tcx().associated_items(r.def_id()).in_definition_order()) + .flatten() + .filter_map( + |item| if item.kind == ty::AssocKind::Type { Some(item.ident.name) } else { None }, + ) + .collect(); + + if let (Some(suggested_name), true) = ( + find_best_match_for_name(all_candidate_names.iter(), 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.to_string(), + Applicability::MaybeIncorrect, + ); + } else { + 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 super-traits), 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>, + potential_assoc_types: Vec, + 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> = 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 = 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(); + names.push(format!( + "`{}` (from trait `{}`)", + assoc_item.ident, + 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` + // + // 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` + [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![]; + 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.ident.name).or_insert(0) += 1; + } + let mut dupes = false; + for item in assoc_items { + let prefix = if names[&item.ident.name] > 1 { + let trait_def_id = item.container.id(); + 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.ident)); + } + } + if potential_assoc_types.len() == assoc_items.len() { + // Only suggest when the amount of missing associated types equals the number of + // extra type arguments present, as that gives us a relatively high confidence + // that the user forgot to give the associtated type's name. The canonical + // example would be trying to use `Iterator` instead of + // `Iterator`. + for (potential, item) in potential_assoc_types.iter().zip(assoc_items.iter()) { + if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(*potential) { + suggestions.push((*potential, format!("{} = {}", item.ident, snippet))); + } + } + } 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.ident)).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`. + 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 super-traits with + // the same associated type name. + err.help(where_msg); + } + if suggestions.len() != 1 { + // 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.ident.name).or_insert(0) += 1; + } + let mut label = vec![]; + for item in assoc_items { + let postfix = if names[&item.ident.name] > 1 { + let trait_def_id = item.container.id(); + format!(" (from trait `{}`)", tcx.def_path_str(trait_def_id)) + } else { + String::new() + }; + label.push(format!("`{}`{}", item.ident, 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/src/librustc_typeck/astconv/generics.rs b/src/librustc_typeck/astconv/generics.rs new file mode 100644 index 0000000000000..84dab6de95819 --- /dev/null +++ b/src/librustc_typeck/astconv/generics.rs @@ -0,0 +1,596 @@ +use crate::astconv::{ + AstConv, ExplicitLateBound, GenericArgCountMismatch, GenericArgCountResult, GenericArgPosition, +}; +use rustc_ast::ast::ParamKindOrd; +use rustc_errors::{pluralize, struct_span_err, DiagnosticId, ErrorReported}; +use rustc_hir as hir; +use rustc_hir::def_id::DefId; +use rustc_hir::{GenericArg, GenericArgs}; +use rustc_middle::ty::{ + self, subst, subst::SubstsRef, GenericParamDef, GenericParamDefKind, Ty, TyCtxt, +}; +use rustc_session::{lint::builtin::LATE_BOUND_LIFETIME_ARGUMENTS, Session}; +use rustc_span::{symbol::kw, MultiSpan, 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( + sess: &Session, + arg: &GenericArg<'_>, + kind: &'static str, + help: Option<&str>, + ) { + let mut err = struct_span_err!( + sess, + arg.span(), + E0747, + "{} provided when a {} was expected", + arg.descr(), + kind, + ); + + let unordered = sess.features_untracked().const_generics; + let kind_ord = match kind { + "lifetime" => ParamKindOrd::Lifetime, + "type" => ParamKindOrd::Type, + "constant" => ParamKindOrd::Const { unordered }, + // It's more concise to match on the string representation, though it means + // the match is non-exhaustive. + _ => bug!("invalid generic parameter kind {}", kind), + }; + let arg_ord = match arg { + GenericArg::Lifetime(_) => ParamKindOrd::Lifetime, + GenericArg::Type(_) => ParamKindOrd::Type, + GenericArg::Const(_) => ParamKindOrd::Const { unordered }, + }; + + // This note is only true when generic parameters are strictly ordered by their kind. + if kind_ord.cmp(&arg_ord) != core::cmp::Ordering::Equal { + let (first, last) = + if kind_ord < arg_ord { (kind, arg.descr()) } else { (arg.descr(), kind) }; + 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<'b>( + tcx: TyCtxt<'tcx>, + def_id: DefId, + parent_substs: &[subst::GenericArg<'tcx>], + has_self: bool, + self_ty: Option>, + arg_count: GenericArgCountResult, + args_for_def_id: impl Fn(DefId) -> (Option<&'b GenericArgs<'b>>, bool), + mut provided_kind: impl FnMut(&GenericParamDef, &GenericArg<'_>) -> subst::GenericArg<'tcx>, + mut inferred_kind: impl FnMut( + Option<&[subst::GenericArg<'tcx>]>, + &GenericParamDef, + bool, + ) -> subst::GenericArg<'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(|| 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) = args_for_def_id(def_id); + + let mut args = + generic_args.iter().flat_map(|generic_args| generic_args.args.iter()).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(_), GenericParamDefKind::Type { .. }, _) + | (GenericArg::Const(_), GenericParamDefKind::Const, _) => { + substs.push(provided_kind(param, arg)); + args.next(); + params.next(); + } + ( + 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(inferred_kind(None, param, infer_args)); + force_infer_lt = Some(arg); + 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(); + } + (_, kind, _) => { + // 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() + && arg_count.explicit_late_bound == ExplicitLateBound::No + { + // 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 + .clone() + .into_iter() + .map(|param| { + ( + match param.kind { + GenericParamDefKind::Lifetime => { + ParamKindOrd::Lifetime + } + GenericParamDefKind::Type { .. } => { + ParamKindOrd::Type + } + GenericParamDefKind::Const => { + ParamKindOrd::Const { + unordered: tcx + .sess + .features_untracked() + .const_generics, + } + } + }, + param, + ) + }) + .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.sess, + arg, + kind.descr(), + Some(&format!( + "reorder the arguments: {}: `<{}>`", + param_types_present + .into_iter() + .map(|ord| format!("{}s", ord.to_string())) + .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 = + force_infer_lt.expect("lifetimes ought to have been inferred"); + Self::generic_arg_mismatch_err(tcx.sess, provided, kind, None); + } + + break; + } + + (None, Some(¶m)) => { + // If there are fewer arguments than parameters, it means + // we're inferring the remaining arguments. + substs.push(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: &ty::Generics, + seg: &hir::PathSegment<'_>, + is_method_call: bool, + ) -> GenericArgCountResult { + let empty_args = hir::GenericArgs::none(); + let suppress_mismatch = Self::check_impl_trait(tcx, seg, &def); + Self::check_generic_arg_count( + tcx, + span, + def, + if let Some(ref args) = seg.args { args } else { &empty_args }, + if is_method_call { GenericArgPosition::MethodCall } else { GenericArgPosition::Value }, + def.parent.is_none() && def.has_self, // `has_self` + seg.infer_args || suppress_mismatch, // `infer_args` + ) + } + + /// Checks that the correct number of generic arguments have been provided. + /// This is used both for datatypes and function calls. + pub(crate) fn check_generic_arg_count( + tcx: TyCtxt<'_>, + span: Span, + def: &ty::Generics, + args: &hir::GenericArgs<'_>, + position: GenericArgPosition, + has_self: bool, + infer_args: bool, + ) -> GenericArgCountResult { + // At this stage we are guaranteed that the generic arguments are in the correct order, e.g. + // that lifetimes will proceed types. So it suffices to check the number of each generic + // arguments in order to validate them with respect to the generic parameters. + let param_counts = def.own_counts(); + let arg_counts = args.own_counts(); + let infer_lifetimes = position != GenericArgPosition::Type && arg_counts.lifetimes == 0; + + let mut defaults: ty::GenericParamCount = Default::default(); + for param in &def.params { + match param.kind { + GenericParamDefKind::Lifetime => {} + GenericParamDefKind::Type { has_default, .. } => { + defaults.types += has_default as usize + } + GenericParamDefKind::Const => { + // FIXME(const_generics:defaults) + } + }; + } + + if position != GenericArgPosition::Type && !args.bindings.is_empty() { + Self::prohibit_assoc_ty_binding(tcx, args.bindings[0].span); + } + + let explicit_late_bound = + Self::prohibit_explicit_late_bound_lifetimes(tcx, def, args, position); + + let check_kind_count = |kind, + required, + permitted, + provided, + offset, + unexpected_spans: &mut Vec, + silent| { + debug!( + "check_kind_count: kind: {} required: {} permitted: {} provided: {} offset: {}", + kind, required, permitted, provided, offset + ); + // We enforce the following: `required` <= `provided` <= `permitted`. + // For kinds without defaults (e.g.., lifetimes), `required == permitted`. + // For other kinds (i.e., types), `permitted` may be greater than `required`. + if required <= provided && provided <= permitted { + return Ok(()); + } + + if silent { + return Err(true); + } + + // Unfortunately lifetime and type parameter mismatches are typically styled + // differently in diagnostics, which means we have a few cases to consider here. + let (bound, quantifier) = if required != permitted { + if provided < required { + (required, "at least ") + } else { + // provided > permitted + (permitted, "at most ") + } + } else { + (required, "") + }; + + let (spans, label) = if required == permitted && provided > permitted { + // In the case when the user has provided too many arguments, + // we want to point to the unexpected arguments. + let spans: Vec = args.args[offset + permitted..offset + provided] + .iter() + .map(|arg| arg.span()) + .collect(); + unexpected_spans.extend(spans.clone()); + (spans, format!("unexpected {} argument", kind)) + } else { + ( + vec![span], + format!( + "expected {}{} {} argument{}", + quantifier, + bound, + kind, + pluralize!(bound), + ), + ) + }; + + let mut err = tcx.sess.struct_span_err_with_code( + spans.clone(), + &format!( + "wrong number of {} arguments: expected {}{}, found {}", + kind, quantifier, bound, provided, + ), + DiagnosticId::Error("E0107".into()), + ); + for span in spans { + err.span_label(span, label.as_str()); + } + err.emit(); + + Err(true) + }; + + let mut arg_count_correct = Ok(()); + let mut unexpected_spans = vec![]; + + if !infer_lifetimes || arg_counts.lifetimes > param_counts.lifetimes { + arg_count_correct = check_kind_count( + "lifetime", + param_counts.lifetimes, + param_counts.lifetimes, + arg_counts.lifetimes, + 0, + &mut unexpected_spans, + explicit_late_bound == ExplicitLateBound::Yes, + ) + .and(arg_count_correct); + } + // FIXME(const_generics:defaults) + if !infer_args || arg_counts.consts > param_counts.consts { + arg_count_correct = check_kind_count( + "const", + param_counts.consts, + param_counts.consts, + arg_counts.consts, + arg_counts.lifetimes + arg_counts.types, + &mut unexpected_spans, + false, + ) + .and(arg_count_correct); + } + // Note that type errors are currently be emitted *after* const errors. + if !infer_args || arg_counts.types > param_counts.types - defaults.types - has_self as usize + { + arg_count_correct = check_kind_count( + "type", + param_counts.types - defaults.types - has_self as usize, + param_counts.types - has_self as usize, + arg_counts.types, + arg_counts.lifetimes, + &mut unexpected_spans, + false, + ) + .and(arg_count_correct); + } + + GenericArgCountResult { + explicit_late_bound, + correct: arg_count_correct.map_err(|reported_err| GenericArgCountMismatch { + reported: if reported_err { Some(ErrorReported) } else { None }, + invalid_args: unexpected_spans, + }), + } + } + + /// Report error if there is an explicit type parameter when using `impl Trait`. + pub(crate) fn check_impl_trait( + tcx: TyCtxt<'_>, + seg: &hir::PathSegment<'_>, + generics: &ty::Generics, + ) -> bool { + let explicit = !seg.infer_args; + let impl_trait = generics.params.iter().any(|param| match param.kind { + ty::GenericParamDefKind::Type { + synthetic: Some(hir::SyntheticTyParamKind::ImplTrait), + .. + } => true, + _ => false, + }); + + if explicit && impl_trait { + let spans = seg + .generic_args() + .args + .iter() + .filter_map(|arg| match arg { + GenericArg::Type(_) => Some(arg.span()), + _ => None, + }) + .collect::>(); + + let mut err = struct_span_err! { + tcx.sess, + spans.clone(), + E0632, + "cannot provide explicit generic arguments when `impl Trait` is \ + used in argument position" + }; + + for span in spans { + err.span_label(span, "explicit generic argument not allowed"); + } + + err.emit(); + } + + impl_trait + } + + /// Emits an error regarding forbidden type binding associations + pub fn prohibit_assoc_ty_binding(tcx: TyCtxt<'_>, span: Span) { + let mut err = struct_span_err!( + tcx.sess, + span, + E0229, + "associated type bindings are not allowed here" + ); + err.span_label(span, "associated type not allowed here").emit(); + } + + /// 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 arg_counts = args.own_counts(); + let infer_lifetimes = position != GenericArgPosition::Type && arg_counts.lifetimes == 0; + + if infer_lifetimes { + ExplicitLateBound::No + } else 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 + && arg_counts.lifetimes != 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.to_string()); + tcx.struct_span_lint_hir( + LATE_BOUND_LIFETIME_ARGUMENTS, + args.args[0].id(), + multispan, + |lint| lint.build(msg).emit(), + ); + } + ExplicitLateBound::Yes + } else { + ExplicitLateBound::No + } + } +} diff --git a/src/librustc_typeck/astconv.rs b/src/librustc_typeck/astconv/mod.rs similarity index 65% rename from src/librustc_typeck/astconv.rs rename to src/librustc_typeck/astconv/mod.rs index 86081b150710d..15c19afdb4a20 100644 --- a/src/librustc_typeck/astconv.rs +++ b/src/librustc_typeck/astconv/mod.rs @@ -1,17 +1,17 @@ -// ignore-tidy-filelength FIXME(#67418) Split up this file. //! 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`. -// ignore-tidy-filelength +mod errors; +mod generics; +use crate::bounds::Bounds; use crate::collect::PlaceholderHirTyCollector; use crate::middle::resolve_lifetime as rl; use crate::require_c_abi_if_c_variadic; -use rustc_ast::{util::lev_distance::find_best_match_for_name, ParamKindOrd}; +use rustc_ast::util::lev_distance::find_best_match_for_name; use rustc_data_structures::fx::{FxHashMap, FxHashSet}; -use rustc_errors::ErrorReported; -use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticId, FatalError}; +use rustc_errors::{struct_span_err, Applicability, ErrorReported, FatalError}; use rustc_hir as hir; use rustc_hir::def::{CtorOf, DefKind, Namespace, Res}; use rustc_hir::def_id::{DefId, LocalDefId}; @@ -19,15 +19,11 @@ use rustc_hir::intravisit::{walk_generics, Visitor as _}; use rustc_hir::lang_items::SizedTraitLangItem; use rustc_hir::{Constness, GenericArg, GenericArgs}; use rustc_middle::ty::subst::{self, InternalSubsts, Subst, SubstsRef}; -use rustc_middle::ty::{ - self, Const, DefIdTree, ToPredicate, Ty, TyCtxt, TypeFoldable, WithConstness, -}; -use rustc_middle::ty::{GenericParamDef, GenericParamDefKind}; -use rustc_session::lint::builtin::{AMBIGUOUS_ASSOCIATED_ITEMS, LATE_BOUND_LIFETIME_ARGUMENTS}; -use rustc_session::parse::feature_err; -use rustc_session::Session; -use rustc_span::symbol::{kw, sym, Ident, Symbol}; -use rustc_span::{MultiSpan, Span, DUMMY_SP}; +use rustc_middle::ty::GenericParamDefKind; +use rustc_middle::ty::{self, Const, DefIdTree, Ty, TyCtxt, TypeFoldable}; +use rustc_session::lint::builtin::AMBIGUOUS_ASSOCIATED_ITEMS; +use rustc_span::symbol::{Ident, Symbol}; +use rustc_span::{Span, DUMMY_SP}; use rustc_target::spec::abi; use rustc_trait_selection::traits; use rustc_trait_selection::traits::astconv_object_safety_violations; @@ -138,8 +134,10 @@ pub enum ExplicitLateBound { 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)] -enum GenericArgPosition { +pub(crate) enum GenericArgPosition { Type, Value, // e.g., functions MethodCall, @@ -243,533 +241,6 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { substs } - /// Report error if there is an explicit type parameter when using `impl Trait`. - fn check_impl_trait( - tcx: TyCtxt<'_>, - seg: &hir::PathSegment<'_>, - generics: &ty::Generics, - ) -> bool { - let explicit = !seg.infer_args; - let impl_trait = generics.params.iter().any(|param| match param.kind { - ty::GenericParamDefKind::Type { - synthetic: Some(hir::SyntheticTyParamKind::ImplTrait), - .. - } => true, - _ => false, - }); - - if explicit && impl_trait { - let spans = seg - .generic_args() - .args - .iter() - .filter_map(|arg| match arg { - GenericArg::Type(_) => Some(arg.span()), - _ => None, - }) - .collect::>(); - - let mut err = struct_span_err! { - tcx.sess, - spans.clone(), - E0632, - "cannot provide explicit generic arguments when `impl Trait` is \ - used in argument position" - }; - - for span in spans { - err.span_label(span, "explicit generic argument not allowed"); - } - - err.emit(); - } - - impl_trait - } - - /// 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: &ty::Generics, - seg: &hir::PathSegment<'_>, - is_method_call: bool, - ) -> GenericArgCountResult { - let empty_args = hir::GenericArgs::none(); - let suppress_mismatch = Self::check_impl_trait(tcx, seg, &def); - Self::check_generic_arg_count( - tcx, - span, - def, - if let Some(ref args) = seg.args { args } else { &empty_args }, - if is_method_call { GenericArgPosition::MethodCall } else { GenericArgPosition::Value }, - def.parent.is_none() && def.has_self, // `has_self` - seg.infer_args || suppress_mismatch, // `infer_args` - ) - } - - /// Checks that the correct number of generic arguments have been provided. - /// This is used both for datatypes and function calls. - fn check_generic_arg_count( - tcx: TyCtxt<'_>, - span: Span, - def: &ty::Generics, - args: &hir::GenericArgs<'_>, - position: GenericArgPosition, - has_self: bool, - infer_args: bool, - ) -> GenericArgCountResult { - // At this stage we are guaranteed that the generic arguments are in the correct order, e.g. - // that lifetimes will proceed types. So it suffices to check the number of each generic - // arguments in order to validate them with respect to the generic parameters. - let param_counts = def.own_counts(); - let arg_counts = args.own_counts(); - let infer_lifetimes = position != GenericArgPosition::Type && arg_counts.lifetimes == 0; - - let mut defaults: ty::GenericParamCount = Default::default(); - for param in &def.params { - match param.kind { - GenericParamDefKind::Lifetime => {} - GenericParamDefKind::Type { has_default, .. } => { - defaults.types += has_default as usize - } - GenericParamDefKind::Const => { - // FIXME(const_generics:defaults) - } - }; - } - - if position != GenericArgPosition::Type && !args.bindings.is_empty() { - AstConv::prohibit_assoc_ty_binding(tcx, args.bindings[0].span); - } - - let explicit_late_bound = - Self::prohibit_explicit_late_bound_lifetimes(tcx, def, args, position); - - let check_kind_count = |kind, - required, - permitted, - provided, - offset, - unexpected_spans: &mut Vec, - silent| { - debug!( - "check_kind_count: kind: {} required: {} permitted: {} provided: {} offset: {}", - kind, required, permitted, provided, offset - ); - // We enforce the following: `required` <= `provided` <= `permitted`. - // For kinds without defaults (e.g.., lifetimes), `required == permitted`. - // For other kinds (i.e., types), `permitted` may be greater than `required`. - if required <= provided && provided <= permitted { - return Ok(()); - } - - if silent { - return Err(true); - } - - // Unfortunately lifetime and type parameter mismatches are typically styled - // differently in diagnostics, which means we have a few cases to consider here. - let (bound, quantifier) = if required != permitted { - if provided < required { - (required, "at least ") - } else { - // provided > permitted - (permitted, "at most ") - } - } else { - (required, "") - }; - - let (spans, label) = if required == permitted && provided > permitted { - // In the case when the user has provided too many arguments, - // we want to point to the unexpected arguments. - let spans: Vec = args.args[offset + permitted..offset + provided] - .iter() - .map(|arg| arg.span()) - .collect(); - unexpected_spans.extend(spans.clone()); - (spans, format!("unexpected {} argument", kind)) - } else { - ( - vec![span], - format!( - "expected {}{} {} argument{}", - quantifier, - bound, - kind, - pluralize!(bound), - ), - ) - }; - - let mut err = tcx.sess.struct_span_err_with_code( - spans.clone(), - &format!( - "wrong number of {} arguments: expected {}{}, found {}", - kind, quantifier, bound, provided, - ), - DiagnosticId::Error("E0107".into()), - ); - for span in spans { - err.span_label(span, label.as_str()); - } - err.emit(); - - Err(true) - }; - - let mut arg_count_correct = Ok(()); - let mut unexpected_spans = vec![]; - - if !infer_lifetimes || arg_counts.lifetimes > param_counts.lifetimes { - arg_count_correct = check_kind_count( - "lifetime", - param_counts.lifetimes, - param_counts.lifetimes, - arg_counts.lifetimes, - 0, - &mut unexpected_spans, - explicit_late_bound == ExplicitLateBound::Yes, - ) - .and(arg_count_correct); - } - // FIXME(const_generics:defaults) - if !infer_args || arg_counts.consts > param_counts.consts { - arg_count_correct = check_kind_count( - "const", - param_counts.consts, - param_counts.consts, - arg_counts.consts, - arg_counts.lifetimes + arg_counts.types, - &mut unexpected_spans, - false, - ) - .and(arg_count_correct); - } - // Note that type errors are currently be emitted *after* const errors. - if !infer_args || arg_counts.types > param_counts.types - defaults.types - has_self as usize - { - arg_count_correct = check_kind_count( - "type", - param_counts.types - defaults.types - has_self as usize, - param_counts.types - has_self as usize, - arg_counts.types, - arg_counts.lifetimes, - &mut unexpected_spans, - false, - ) - .and(arg_count_correct); - } - - GenericArgCountResult { - explicit_late_bound, - correct: arg_count_correct.map_err(|reported_err| GenericArgCountMismatch { - reported: if reported_err { Some(ErrorReported) } else { None }, - invalid_args: unexpected_spans, - }), - } - } - - /// Report an error that a generic argument did not match the generic parameter that was - /// expected. - fn generic_arg_mismatch_err( - sess: &Session, - arg: &GenericArg<'_>, - kind: &'static str, - help: Option<&str>, - ) { - let mut err = struct_span_err!( - sess, - arg.span(), - E0747, - "{} provided when a {} was expected", - arg.descr(), - kind, - ); - - let unordered = sess.features_untracked().const_generics; - let kind_ord = match kind { - "lifetime" => ParamKindOrd::Lifetime, - "type" => ParamKindOrd::Type, - "constant" => ParamKindOrd::Const { unordered }, - // It's more concise to match on the string representation, though it means - // the match is non-exhaustive. - _ => bug!("invalid generic parameter kind {}", kind), - }; - let arg_ord = match arg { - GenericArg::Lifetime(_) => ParamKindOrd::Lifetime, - GenericArg::Type(_) => ParamKindOrd::Type, - GenericArg::Const(_) => ParamKindOrd::Const { unordered }, - }; - - // This note is only true when generic parameters are strictly ordered by their kind. - if kind_ord.cmp(&arg_ord) != core::cmp::Ordering::Equal { - let (first, last) = - if kind_ord < arg_ord { (kind, arg.descr()) } else { (arg.descr(), kind) }; - 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<'b>( - tcx: TyCtxt<'tcx>, - def_id: DefId, - parent_substs: &[subst::GenericArg<'tcx>], - has_self: bool, - self_ty: Option>, - arg_count: GenericArgCountResult, - args_for_def_id: impl Fn(DefId) -> (Option<&'b GenericArgs<'b>>, bool), - mut provided_kind: impl FnMut(&GenericParamDef, &GenericArg<'_>) -> subst::GenericArg<'tcx>, - mut inferred_kind: impl FnMut( - Option<&[subst::GenericArg<'tcx>]>, - &GenericParamDef, - bool, - ) -> subst::GenericArg<'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(|| 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) = args_for_def_id(def_id); - - let mut args = - generic_args.iter().flat_map(|generic_args| generic_args.args.iter()).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(_), GenericParamDefKind::Type { .. }, _) - | (GenericArg::Const(_), GenericParamDefKind::Const, _) => { - substs.push(provided_kind(param, arg)); - args.next(); - params.next(); - } - ( - 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(inferred_kind(None, param, infer_args)); - force_infer_lt = Some(arg); - 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(); - } - (_, kind, _) => { - // 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() - && arg_count.explicit_late_bound == ExplicitLateBound::No - { - // 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 - .clone() - .into_iter() - .map(|param| { - ( - match param.kind { - GenericParamDefKind::Lifetime => { - ParamKindOrd::Lifetime - } - GenericParamDefKind::Type { .. } => { - ParamKindOrd::Type - } - GenericParamDefKind::Const => { - ParamKindOrd::Const { - unordered: tcx - .sess - .features_untracked() - .const_generics, - } - } - }, - param, - ) - }) - .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.sess, - arg, - kind.descr(), - Some(&format!( - "reorder the arguments: {}: `<{}>`", - param_types_present - .into_iter() - .map(|ord| format!("{}s", ord.to_string())) - .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 = - force_infer_lt.expect("lifetimes ought to have been inferred"); - Self::generic_arg_mismatch_err(tcx.sess, provided, kind, None); - } - - break; - } - - (None, Some(¶m)) => { - // If there are fewer arguments than parameters, it means - // we're inferring the remaining arguments. - substs.push(inferred_kind(Some(&substs), param, infer_args)); - params.next(); - } - - (None, None) => break, - } - } - } - - tcx.intern_substs(&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. @@ -1023,79 +494,6 @@ 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. - fn complain_about_missing_type_params( - &self, - missing_type_params: Vec, - def_id: DefId, - span: Span, - empty_generic_args: bool, - ) { - if missing_type_params.is_empty() { - return; - } - let display = - missing_type_params.iter().map(|n| format!("`{}`", n)).collect::>().join(", "); - let mut err = struct_span_err!( - self.tcx().sess, - span, - E0393, - "the type parameter{} {} must be explicitly specified", - pluralize!(missing_type_params.len()), - display, - ); - err.span_label( - self.tcx().def_span(def_id), - &format!( - "type parameter{} {} must be specified for this", - pluralize!(missing_type_params.len()), - display, - ), - ); - let mut suggested = false; - if let (Ok(snippet), true) = ( - self.tcx().sess.source_map().span_to_snippet(span), - // 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. - 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`. - err.span_suggestion( - span, - &format!( - "set the type parameter{plural} to the desired type{plural}", - plural = pluralize!(missing_type_params.len()), - ), - format!("{}<{}>", snippet, missing_type_params.join(", ")), - Applicability::HasPlaceholders, - ); - suggested = true; - } - } - if !suggested { - err.span_label( - span, - format!( - "missing reference{} to {}", - pluralize!(missing_type_params.len()), - display, - ), - ); - } - err.note( - "because of the default `Self` reference, type parameters must be \ - specified on object types", - ); - err.emit(); - } - /// 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. @@ -1242,76 +640,11 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { let (substs, assoc_bindings, _) = self.create_substs_for_ast_trait_ref(span, trait_def_id, self_ty, trait_segment); if let Some(b) = assoc_bindings.first() { - AstConv::prohibit_assoc_ty_binding(self.tcx(), b.span); + Self::prohibit_assoc_ty_binding(self.tcx(), b.span); } ty::TraitRef::new(trait_def_id, substs) } - /// 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. - fn complain_about_internal_fn_trait( - &self, - span: Span, - trait_def_id: DefId, - trait_segment: &'a hir::PathSegment<'a>, - ) { - let trait_def = self.tcx().trait_def(trait_def_id); - - if !self.tcx().features().unboxed_closures - && trait_segment.generic_args().parenthesized != trait_def.paren_sugar - { - let sess = &self.tcx().sess.parse_sess; - // For now, require that parenthetical notation be used only with `Fn()` etc. - let (msg, sugg) = if trait_def.paren_sugar { - ( - "the precise format of `Fn`-family traits' type parameters is subject to \ - change", - Some(format!( - "{}{} -> {}", - trait_segment.ident, - 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::, _>>() - .map(|a| a.join(", ")), - _ => sess.source_map().span_to_snippet(ty.span), - } - .map(|s| format!("({})", s)) - .ok(), - _ => None, - }) - .unwrap_or_else(|| "()".to_string()), - trait_segment - .generic_args() - .bindings - .iter() - .find_map(|b| match (b.ident.name == sym::Output, &b.kind) { - (true, hir::TypeBindingKind::Equality { ty }) => { - sess.source_map().span_to_snippet(ty.span).ok() - } - _ => None, - }) - .unwrap_or_else(|| "()".to_string()), - )), - ) - } else { - ("parenthetical notation is only stable when used with `Fn`-family traits", None) - }; - let mut err = feature_err(sess, sym::unboxed_closures, span, msg); - if let Some(sugg) = sugg { - let msg = "use parenthetical notation instead"; - err.span_suggestion(span, msg, sugg, Applicability::MaybeIncorrect); - } - err.emit(); - } - } - fn create_substs_for_ast_trait_ref<'a>( &self, span: Span, @@ -1912,196 +1245,6 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { ty } - /// 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 super-traits), we instead - /// emit a generic note suggesting using a `where` clause to constraint instead. - fn complain_about_missing_associated_types( - &self, - associated_types: FxHashMap>, - potential_assoc_types: Vec, - 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> = 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 = 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(); - names.push(format!( - "`{}` (from trait `{}`)", - assoc_item.ident, - 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` - // - // 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` - [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![]; - 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.ident.name).or_insert(0) += 1; - } - let mut dupes = false; - for item in assoc_items { - let prefix = if names[&item.ident.name] > 1 { - let trait_def_id = item.container.id(); - 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.ident)); - } - } - if potential_assoc_types.len() == assoc_items.len() { - // Only suggest when the amount of missing associated types equals the number of - // extra type arguments present, as that gives us a relatively high confidence - // that the user forgot to give the associtated type's name. The canonical - // example would be trying to use `Iterator` instead of - // `Iterator`. - for (potential, item) in potential_assoc_types.iter().zip(assoc_items.iter()) { - if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(*potential) { - suggestions.push((*potential, format!("{} = {}", item.ident, snippet))); - } - } - } 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.ident)).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`. - 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 super-traits with - // the same associated type name. - err.help(where_msg); - } - if suggestions.len() != 1 { - // 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.ident.name).or_insert(0) += 1; - } - let mut label = vec![]; - for item in assoc_items { - let postfix = if names[&item.ident.name] > 1 { - let trait_def_id = item.container.id(); - format!(" (from trait `{}`)", tcx.def_path_str(trait_def_id)) - } else { - String::new() - }; - label.push(format!("`{}`{}", item.ident, 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(); - } - fn report_ambiguous_associated_type( &self, span: Span, @@ -2289,52 +1432,6 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { Ok(bound) } - fn complain_about_assoc_type_not_found( - &self, - all_candidates: impl Fn() -> I, - ty_param_name: &str, - assoc_name: Ident, - span: Span, - ) where - I: Iterator>, - { - // 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() - .map(|r| self.tcx().associated_items(r.def_id()).in_definition_order()) - .flatten() - .filter_map( - |item| if item.kind == ty::AssocKind::Type { Some(item.ident.name) } else { None }, - ) - .collect(); - - if let (Some(suggested_name), true) = ( - find_best_match_for_name(all_candidate_names.iter(), 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.to_string(), - Applicability::MaybeIncorrect, - ); - } else { - err.span_label(span, format!("associated type `{}` not found", assoc_name)); - } - - err.emit(); - } - // 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 @@ -2637,57 +1734,6 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { has_err } - pub fn prohibit_assoc_ty_binding(tcx: TyCtxt<'_>, span: Span) { - let mut err = struct_span_err!( - tcx.sess, - span, - E0229, - "associated type bindings are not allowed here" - ); - err.span_label(span, "associated type not allowed here").emit(); - } - - /// Prohibits explicit lifetime arguments if late-bound lifetime parameters - /// are present. This is used both for datatypes and function calls. - fn prohibit_explicit_late_bound_lifetimes( - tcx: TyCtxt<'_>, - def: &ty::Generics, - args: &hir::GenericArgs<'_>, - position: GenericArgPosition, - ) -> ExplicitLateBound { - let param_counts = def.own_counts(); - let arg_counts = args.own_counts(); - let infer_lifetimes = position != GenericArgPosition::Type && arg_counts.lifetimes == 0; - - if infer_lifetimes { - ExplicitLateBound::No - } else 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 - && arg_counts.lifetimes != 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.to_string()); - tcx.struct_span_lint_hir( - LATE_BOUND_LIFETIME_ARGUMENTS, - args.args[0].id(), - multispan, - |lint| lint.build(msg).emit(), - ); - } - ExplicitLateBound::Yes - } else { - ExplicitLateBound::No - } - } - // FIXME(eddyb, varkor) handle type paths here too, not just value ones. pub fn def_ids_for_value_path_segments( &self, @@ -3248,92 +2294,3 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o { Some(r) } } - -/// Collects together a list of bounds that are applied to some type, -/// after they've been converted into `ty` form (from the HIR -/// representations). 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() { } -/// ^^^^^^^^^ 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 -#[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::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, Constness)>, - - /// A list of projection equality bounds. So if you had `T: - /// Iterator` this would include `::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, -} - -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( - &self, - tcx: TyCtxt<'tcx>, - param_ty: Ty<'tcx>, - ) -> Vec<(ty::Predicate<'tcx>, Span)> { - // 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(|sized| { - let trait_ref = ty::Binder::bind(ty::TraitRef { - def_id: sized, - substs: tcx.mk_substs_trait(param_ty, &[]), - }); - (trait_ref.without_const().to_predicate(tcx), span) - }) - }); - - sized_predicate - .into_iter() - .chain( - self.region_bounds - .iter() - .map(|&(region_bound, span)| { - // Account for the binder being introduced below; no need to shift `param_ty` - // because, at present at least, it either only refers to early-bound regions, - // or it's a generic associated type that deliberately has escaping bound vars. - let region_bound = ty::fold::shift_region(tcx, region_bound, 1); - let outlives = ty::OutlivesPredicate(param_ty, region_bound); - (ty::Binder::bind(outlives).to_predicate(tcx), span) - }) - .chain(self.trait_bounds.iter().map(|&(bound_trait_ref, span, constness)| { - let predicate = bound_trait_ref.with_constness(constness).to_predicate(tcx); - (predicate, span) - })) - .chain( - self.projection_bounds - .iter() - .map(|&(projection, span)| (projection.to_predicate(tcx), span)), - ), - ) - .collect() - } -} diff --git a/src/librustc_typeck/bounds.rs b/src/librustc_typeck/bounds.rs new file mode 100644 index 0000000000000..63295f5faacc9 --- /dev/null +++ b/src/librustc_typeck/bounds.rs @@ -0,0 +1,93 @@ +//! Bounds are restrictions applied to some types after they've been converted into the +//! `ty` form from the HIR. + +use rustc_hir::Constness; +use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt, WithConstness}; +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() { } +/// ^^^^^^^^^ 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::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, Constness)>, + + /// A list of projection equality bounds. So if you had `T: + /// Iterator` this would include `::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, +} + +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( + &self, + tcx: TyCtxt<'tcx>, + param_ty: Ty<'tcx>, + ) -> Vec<(ty::Predicate<'tcx>, Span)> { + // 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(|sized| { + let trait_ref = ty::Binder::bind(ty::TraitRef { + def_id: sized, + substs: tcx.mk_substs_trait(param_ty, &[]), + }); + (trait_ref.without_const().to_predicate(tcx), span) + }) + }); + + sized_predicate + .into_iter() + .chain( + self.region_bounds + .iter() + .map(|&(region_bound, span)| { + // Account for the binder being introduced below; no need to shift `param_ty` + // because, at present at least, it either only refers to early-bound regions, + // or it's a generic associated type that deliberately has escaping bound vars. + let region_bound = ty::fold::shift_region(tcx, region_bound, 1); + let outlives = ty::OutlivesPredicate(param_ty, region_bound); + (ty::Binder::bind(outlives).to_predicate(tcx), span) + }) + .chain(self.trait_bounds.iter().map(|&(bound_trait_ref, span, constness)| { + let predicate = bound_trait_ref.with_constness(constness).to_predicate(tcx); + (predicate, span) + })) + .chain( + self.projection_bounds + .iter() + .map(|&(projection, span)| (projection.to_predicate(tcx), span)), + ), + ) + .collect() + } +} diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index 7fb4026117be9..9afac60bb5bb7 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -14,7 +14,8 @@ //! 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, Bounds, SizedByDefault}; +use crate::astconv::{AstConv, SizedByDefault}; +use crate::bounds::Bounds; use crate::check::intrinsic::intrinsic_operation_unsafety; use crate::constrained_generic_params as cgp; use crate::middle::resolve_lifetime as rl; diff --git a/src/librustc_typeck/lib.rs b/src/librustc_typeck/lib.rs index 4248253172598..62f92fe7ffa48 100644 --- a/src/librustc_typeck/lib.rs +++ b/src/librustc_typeck/lib.rs @@ -79,6 +79,7 @@ pub mod check; pub mod expr_use_visitor; mod astconv; +mod bounds; mod check_unused; mod coherence; mod collect; @@ -109,7 +110,8 @@ use rustc_trait_selection::traits::{ use std::iter; -use astconv::{AstConv, Bounds}; +use astconv::AstConv; +use bounds::Bounds; fn require_c_abi_if_c_variadic(tcx: TyCtxt<'_>, decl: &hir::FnDecl<'_>, abi: Abi, span: Span) { if decl.c_variadic && !(abi == Abi::C || abi == Abi::Cdecl) {