astconv: Split astconv.rs into its own module with submodules
To separate the astconv.rs file, I split it into its own module with a subtrait called GenericAstConv. This subtrait handles methods related to generics, be it types or lifetimes. typeck: Add bounds module and Bounds struct bounds: Run fmt, add documentation generic_astconv: Add subtrait GenericAstConv Some methods of AstConv deal exclusively with Generics. Therefore, it makes sense to have them in their own trait. Some other methods from AstConv might be added to it later generic_astconv: Add more methods from AstConv Add check_generic_arg_count_for_call() and check_generic_arg_count() astconv: Add module for clarity generic: Rename GenericAstConv -> AstConvGeneric generic: add more methods to AstConvGeneric astconv: Remove AstConvGeneric trait, add impl dyn AstConv in other module astconv: Add errors module to handle AstConv complaints fmt: format code in astconv/ astconv: Remove old file astconv: Fix visibility on GenericArgPosition astconv: Fix visibility on GenericArgPosition astconv: Fix function visibility on other originally private functions
This commit is contained in:
parent
b51651ae9d
commit
e6642e41e0
388
src/librustc_typeck/astconv/errors.rs
Normal file
388
src/librustc_typeck/astconv/errors.rs
Normal file
@ -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<String>,
|
||||
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::<Vec<_>>().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<Type>`.
|
||||
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::<Result<Vec<_>, _>>()
|
||||
.map(|a| a.join(", ")),
|
||||
_ => sess.source_map().span_to_snippet(ty.span),
|
||||
}
|
||||
.map(|s| format!("({})", s))
|
||||
.ok(),
|
||||
_ => None,
|
||||
})
|
||||
.unwrap_or_else(|| "()".to_string()),
|
||||
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<I>(
|
||||
&self,
|
||||
all_candidates: impl Fn() -> I,
|
||||
ty_param_name: &str,
|
||||
assoc_name: Ident,
|
||||
span: Span,
|
||||
) where
|
||||
I: Iterator<Item = ty::PolyTraitRef<'tcx>>,
|
||||
{
|
||||
// The fallback span is needed because `assoc_name` might be an `Fn()`'s `Output` without a
|
||||
// valid span, so we point at the whole path segment instead.
|
||||
let span = if assoc_name.span != DUMMY_SP { assoc_name.span } else { span };
|
||||
let mut err = struct_span_err!(
|
||||
self.tcx().sess,
|
||||
span,
|
||||
E0220,
|
||||
"associated type `{}` not found for `{}`",
|
||||
assoc_name,
|
||||
ty_param_name
|
||||
);
|
||||
|
||||
let all_candidate_names: Vec<_> = all_candidates()
|
||||
.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<Span, BTreeSet<DefId>>,
|
||||
potential_assoc_types: Vec<Span>,
|
||||
trait_bounds: &[hir::PolyTraitRef<'_>],
|
||||
) {
|
||||
if associated_types.values().all(|v| v.is_empty()) {
|
||||
return;
|
||||
}
|
||||
let tcx = self.tcx();
|
||||
// FIXME: Marked `mut` so that we can replace the spans further below with a more
|
||||
// appropriate one, but this should be handled earlier in the span assignment.
|
||||
let mut associated_types: FxHashMap<Span, Vec<_>> = associated_types
|
||||
.into_iter()
|
||||
.map(|(span, def_ids)| {
|
||||
(span, def_ids.into_iter().map(|did| tcx.associated_item(did)).collect())
|
||||
})
|
||||
.collect();
|
||||
let mut names = vec![];
|
||||
|
||||
// Account for things like `dyn Foo + 'a`, like in tests `issue-22434.rs` and
|
||||
// `issue-22560.rs`.
|
||||
let mut trait_bound_spans: Vec<Span> = vec![];
|
||||
for (span, items) in &associated_types {
|
||||
if !items.is_empty() {
|
||||
trait_bound_spans.push(*span);
|
||||
}
|
||||
for assoc_item in items {
|
||||
let trait_def_id = assoc_item.container.id();
|
||||
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<Output = Type>`
|
||||
//
|
||||
// we would output:
|
||||
//
|
||||
// error[E0191]: the value of the associated type `Output`
|
||||
// (from trait `std::ops::BitXor`) must be specified
|
||||
// --> $DIR/issue-28344.rs:4:17
|
||||
// |
|
||||
// LL | let x: u8 = BitXor::bitor(0 as u8, 0 as u8);
|
||||
// | ^^^^^^^^^^^^^ help: specify the associated type:
|
||||
// | `BitXor::bitor<Output = Type>`
|
||||
[segment] if segment.args.is_none() => {
|
||||
trait_bound_spans = vec![segment.ident.span];
|
||||
associated_types = associated_types
|
||||
.into_iter()
|
||||
.map(|(_, items)| (segment.ident.span, items))
|
||||
.collect();
|
||||
}
|
||||
_ => {}
|
||||
}
|
||||
}
|
||||
names.sort();
|
||||
trait_bound_spans.sort();
|
||||
let mut err = struct_span_err!(
|
||||
tcx.sess,
|
||||
trait_bound_spans,
|
||||
E0191,
|
||||
"the value of the associated type{} {} must be specified",
|
||||
pluralize!(names.len()),
|
||||
names.join(", "),
|
||||
);
|
||||
let mut suggestions = vec![];
|
||||
let mut types_count = 0;
|
||||
let mut where_constraints = vec![];
|
||||
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<isize>` instead of
|
||||
// `Iterator<Item = isize>`.
|
||||
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<Item = Type>`.
|
||||
format!("{}<{}>", snippet, types.join(", "))
|
||||
};
|
||||
suggestions.push((*span, code));
|
||||
} else if dupes {
|
||||
where_constraints.push(*span);
|
||||
}
|
||||
}
|
||||
let where_msg = "consider introducing a new type parameter, adding `where` constraints \
|
||||
using the fully-qualified path to the associated types";
|
||||
if !where_constraints.is_empty() && suggestions.is_empty() {
|
||||
// If there are duplicates associated type names and a single trait bound do not
|
||||
// use structured suggestion, it means that there are multiple 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();
|
||||
}
|
||||
}
|
596
src/librustc_typeck/astconv/generics.rs
Normal file
596
src/librustc_typeck/astconv/generics.rs
Normal file
@ -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 `<T as Foo>::Bar`, the `DefId`
|
||||
/// might refer to the trait `Foo`, and the arguments might be
|
||||
/// `[T]`. The boolean value indicates whether to infer values
|
||||
/// for arguments whose values were not explicitly provided.
|
||||
/// - `provided_kind`: given the generic parameter and the value from `args_for_def_id`,
|
||||
/// instantiate a `GenericArg`.
|
||||
/// - `inferred_kind`: if no parameter was provided, and inference is enabled, then
|
||||
/// creates a suitable inference variable.
|
||||
pub fn create_substs_for_generic_args<'b>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
def_id: DefId,
|
||||
parent_substs: &[subst::GenericArg<'tcx>],
|
||||
has_self: bool,
|
||||
self_ty: Option<Ty<'tcx>>,
|
||||
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::<Vec<(ParamKindOrd, GenericParamDef)>>();
|
||||
param_types_present.sort_by_key(|(ord, _)| *ord);
|
||||
let (mut param_types_present, ordered_params): (
|
||||
Vec<ParamKindOrd>,
|
||||
Vec<GenericParamDef>,
|
||||
) = param_types_present.into_iter().unzip();
|
||||
param_types_present.dedup();
|
||||
|
||||
Self::generic_arg_mismatch_err(
|
||||
tcx.sess,
|
||||
arg,
|
||||
kind.descr(),
|
||||
Some(&format!(
|
||||
"reorder the arguments: {}: `<{}>`",
|
||||
param_types_present
|
||||
.into_iter()
|
||||
.map(|ord| format!("{}s", ord.to_string()))
|
||||
.collect::<Vec<String>>()
|
||||
.join(", then "),
|
||||
ordered_params
|
||||
.into_iter()
|
||||
.filter_map(|param| {
|
||||
if param.name == kw::SelfUpper {
|
||||
None
|
||||
} else {
|
||||
Some(param.name.to_string())
|
||||
}
|
||||
})
|
||||
.collect::<Vec<String>>()
|
||||
.join(", ")
|
||||
)),
|
||||
);
|
||||
}
|
||||
|
||||
// We've reported the error, but we want to make sure that this
|
||||
// problem doesn't bubble down and create additional, irrelevant
|
||||
// errors. In this case, we're simply going to ignore the argument
|
||||
// and any following arguments. The rest of the parameters will be
|
||||
// inferred.
|
||||
while args.next().is_some() {}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
(Some(&arg), None) => {
|
||||
// We should never be able to reach this point with well-formed input.
|
||||
// There are three situations in which we can encounter this issue.
|
||||
//
|
||||
// 1. The number of arguments is incorrect. In this case, an error
|
||||
// will already have been emitted, and we can ignore it.
|
||||
// 2. There are late-bound lifetime parameters present, yet the
|
||||
// lifetime arguments have also been explicitly specified by the
|
||||
// user.
|
||||
// 3. We've inferred some lifetimes, which have been provided later (i.e.
|
||||
// after a type or const). We want to throw an error in this case.
|
||||
|
||||
if arg_count.correct.is_ok()
|
||||
&& arg_count.explicit_late_bound == ExplicitLateBound::No
|
||||
{
|
||||
let kind = arg.descr();
|
||||
assert_eq!(kind, "lifetime");
|
||||
let provided =
|
||||
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<Span>,
|
||||
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<Span> = 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::<Vec<_>>();
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
File diff suppressed because it is too large
Load Diff
93
src/librustc_typeck/bounds.rs
Normal file
93
src/librustc_typeck/bounds.rs
Normal file
@ -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<T: Bar + Baz>() { }
|
||||
/// ^^^^^^^^^ bounding the type parameter `T`
|
||||
///
|
||||
/// impl dyn Bar + Baz
|
||||
/// ^^^^^^^^^ bounding the forgotten dynamic type
|
||||
/// ```
|
||||
///
|
||||
/// Our representation is a bit mixed here -- in some cases, we
|
||||
/// include the self type (e.g., `trait_bounds`) but in others we do not
|
||||
#[derive(Default, PartialEq, Eq, Clone, Debug)]
|
||||
pub struct Bounds<'tcx> {
|
||||
/// A list of region bounds on the (implicit) self type. So if you
|
||||
/// had `T: 'a + 'b` this might would be a list `['a, 'b]` (but
|
||||
/// the `T` is not explicitly included).
|
||||
pub region_bounds: Vec<(ty::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<Item = u32>` this would include `<T as
|
||||
/// Iterator>::Item => u32`. Note that the self-type is explicit
|
||||
/// here.
|
||||
pub projection_bounds: Vec<(ty::PolyProjectionPredicate<'tcx>, Span)>,
|
||||
|
||||
/// `Some` if there is *no* `?Sized` predicate. The `span`
|
||||
/// is the location in the source of the `T` declaration which can
|
||||
/// be cited as the source of the `T: Sized` requirement.
|
||||
pub implicitly_sized: Option<Span>,
|
||||
}
|
||||
|
||||
impl<'tcx> Bounds<'tcx> {
|
||||
/// Converts a bounds list into a flat set of predicates (like
|
||||
/// where-clauses). Because some of our bounds listings (e.g.,
|
||||
/// regions) don't include the self-type, you must supply the
|
||||
/// self-type here (the `param_ty` parameter).
|
||||
pub fn predicates(
|
||||
&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()
|
||||
}
|
||||
}
|
@ -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;
|
||||
|
@ -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) {
|
||||
|
Loading…
Reference in New Issue
Block a user