1
Fork 0

Rollup merge of #80538 - JulianKnodt:err_usize, r=lcnr

Add check for `[T;N]`/`usize` mismatch in astconv

Helps clarify the issue in #80506
by adding a specific check for mismatches between [T;N] and usize.

r? `@lcnr`
This commit is contained in:
Yuki Okushi 2021-01-05 09:52:37 +09:00 committed by GitHub
commit be2a3f8642
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
7 changed files with 95 additions and 41 deletions

View file

@ -3745,6 +3745,7 @@ version = "0.0.0"
dependencies = [ dependencies = [
"rustc_ast", "rustc_ast",
"rustc_data_structures", "rustc_data_structures",
"rustc_feature",
"rustc_index", "rustc_index",
"rustc_macros", "rustc_macros",
"rustc_serialize", "rustc_serialize",

View file

@ -9,6 +9,7 @@ doctest = false
[dependencies] [dependencies]
rustc_target = { path = "../rustc_target" } rustc_target = { path = "../rustc_target" }
rustc_feature = { path = "../rustc_feature" }
rustc_macros = { path = "../rustc_macros" } rustc_macros = { path = "../rustc_macros" }
rustc_data_structures = { path = "../rustc_data_structures" } rustc_data_structures = { path = "../rustc_data_structures" }
rustc_index = { path = "../rustc_index" } rustc_index = { path = "../rustc_index" }

View file

@ -290,6 +290,14 @@ impl GenericArg<'_> {
GenericArg::Const(_) => "const", GenericArg::Const(_) => "const",
} }
} }
pub fn to_ord(&self, feats: &rustc_feature::Features) -> ast::ParamKindOrd {
match self {
GenericArg::Lifetime(_) => ast::ParamKindOrd::Lifetime,
GenericArg::Type(_) => ast::ParamKindOrd::Type,
GenericArg::Const(_) => ast::ParamKindOrd::Const { unordered: feats.const_generics },
}
}
} }
#[derive(Debug, HashStable_Generic)] #[derive(Debug, HashStable_Generic)]

View file

@ -801,6 +801,15 @@ impl GenericParamDefKind {
GenericParamDefKind::Const => "constant", GenericParamDefKind::Const => "constant",
} }
} }
pub fn to_ord(&self, tcx: TyCtxt<'_>) -> ast::ParamKindOrd {
match self {
GenericParamDefKind::Lifetime => ast::ParamKindOrd::Lifetime,
GenericParamDefKind::Type { .. } => ast::ParamKindOrd::Type,
GenericParamDefKind::Const => {
ast::ParamKindOrd::Const { unordered: tcx.features().const_generics }
}
}
}
} }
#[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable)] #[derive(Clone, Debug, TyEncodable, TyDecodable, HashStable)]

View file

@ -11,7 +11,7 @@ use rustc_hir::GenericArg;
use rustc_middle::ty::{ use rustc_middle::ty::{
self, subst, subst::SubstsRef, GenericParamDef, GenericParamDefKind, Ty, TyCtxt, self, subst, subst::SubstsRef, GenericParamDef, GenericParamDefKind, Ty, TyCtxt,
}; };
use rustc_session::{lint::builtin::LATE_BOUND_LIFETIME_ARGUMENTS, Session}; use rustc_session::lint::builtin::LATE_BOUND_LIFETIME_ARGUMENTS;
use rustc_span::{symbol::kw, MultiSpan, Span}; use rustc_span::{symbol::kw, MultiSpan, Span};
use smallvec::SmallVec; use smallvec::SmallVec;
@ -20,62 +20,72 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
/// Report an error that a generic argument did not match the generic parameter that was /// Report an error that a generic argument did not match the generic parameter that was
/// expected. /// expected.
fn generic_arg_mismatch_err( fn generic_arg_mismatch_err(
sess: &Session, tcx: TyCtxt<'_>,
arg: &GenericArg<'_>, arg: &GenericArg<'_>,
kind: &'static str, param: &GenericParamDef,
possible_ordering_error: bool, possible_ordering_error: bool,
help: Option<&str>, help: Option<&str>,
) { ) {
let sess = tcx.sess;
let mut err = struct_span_err!( let mut err = struct_span_err!(
sess, sess,
arg.span(), arg.span(),
E0747, E0747,
"{} provided when a {} was expected", "{} provided when a {} was expected",
arg.descr(), arg.descr(),
kind, param.kind.descr(),
); );
let unordered = sess.features_untracked().const_generics; if let GenericParamDefKind::Const { .. } = param.kind {
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),
};
if let ParamKindOrd::Const { .. } = kind_ord {
if let GenericArg::Type(hir::Ty { kind: hir::TyKind::Infer, .. }) = arg { if let GenericArg::Type(hir::Ty { kind: hir::TyKind::Infer, .. }) = arg {
err.help("const arguments cannot yet be inferred with `_`"); err.help("const arguments cannot yet be inferred with `_`");
} }
} }
let arg_ord = match arg { // Specific suggestion set for diagnostics
GenericArg::Lifetime(_) => ParamKindOrd::Lifetime, match (arg, &param.kind) {
GenericArg::Type(_) => ParamKindOrd::Type, (
GenericArg::Const(_) => ParamKindOrd::Const { unordered }, GenericArg::Type(hir::Ty { kind: hir::TyKind::Path { .. }, .. }),
}; GenericParamDefKind::Const { .. },
) => {
if matches!(arg, GenericArg::Type(hir::Ty { kind: hir::TyKind::Path { .. }, .. })) let suggestions = vec![
&& matches!(kind_ord, ParamKindOrd::Const { .. }) (arg.span().shrink_to_lo(), String::from("{ ")),
{ (arg.span().shrink_to_hi(), String::from(" }")),
let suggestions = vec![ ];
(arg.span().shrink_to_lo(), String::from("{ ")), err.multipart_suggestion(
(arg.span().shrink_to_hi(), String::from(" }")), "if this generic argument was intended as a const parameter, \
];
err.multipart_suggestion(
"if this generic argument was intended as a const parameter, \
try surrounding it with braces:", try surrounding it with braces:",
suggestions, suggestions,
Applicability::MaybeIncorrect, Applicability::MaybeIncorrect,
); );
}
(
GenericArg::Type(hir::Ty { kind: hir::TyKind::Array(_, len), .. }),
GenericParamDefKind::Const { .. },
) if tcx.type_of(param.def_id) == tcx.types.usize => {
let snippet = sess.source_map().span_to_snippet(tcx.hir().span(len.hir_id));
if let Ok(snippet) = snippet {
err.span_suggestion(
arg.span(),
"array type provided where a `usize` was expected, try",
format!("{{ {} }}", snippet),
Applicability::MaybeIncorrect,
);
}
}
_ => {}
} }
let kind_ord = param.kind.to_ord(tcx);
let arg_ord = arg.to_ord(&tcx.features());
// This note is only true when generic parameters are strictly ordered by their kind. // This note is only true when generic parameters are strictly ordered by their kind.
if possible_ordering_error && kind_ord.cmp(&arg_ord) != core::cmp::Ordering::Equal { if possible_ordering_error && kind_ord.cmp(&arg_ord) != core::cmp::Ordering::Equal {
let (first, last) = let (first, last) = if kind_ord < arg_ord {
if kind_ord < arg_ord { (kind, arg.descr()) } else { (arg.descr(), kind) }; (param.kind.descr(), arg.descr())
} else {
(arg.descr(), param.kind.descr())
};
err.note(&format!("{} arguments must be provided before {} arguments", first, last)); err.note(&format!("{} arguments must be provided before {} arguments", first, last));
if let Some(help) = help { if let Some(help) = help {
err.help(help); err.help(help);
@ -203,7 +213,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
// We expected a lifetime argument, but got a type or const // We expected a lifetime argument, but got a type or const
// argument. That means we're inferring the lifetimes. // argument. That means we're inferring the lifetimes.
substs.push(ctx.inferred_kind(None, param, infer_args)); substs.push(ctx.inferred_kind(None, param, infer_args));
force_infer_lt = Some(arg); force_infer_lt = Some((arg, param));
params.next(); params.next();
} }
(GenericArg::Lifetime(_), _, ExplicitLateBound::Yes) => { (GenericArg::Lifetime(_), _, ExplicitLateBound::Yes) => {
@ -213,7 +223,7 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
// ignore it. // ignore it.
args.next(); args.next();
} }
(_, kind, _) => { (_, _, _) => {
// We expected one kind of parameter, but the user provided // We expected one kind of parameter, but the user provided
// another. This is an error. However, if we already know that // another. This is an error. However, if we already know that
// the arguments don't match up with the parameters, we won't issue // the arguments don't match up with the parameters, we won't issue
@ -256,9 +266,9 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
param_types_present.dedup(); param_types_present.dedup();
Self::generic_arg_mismatch_err( Self::generic_arg_mismatch_err(
tcx.sess, tcx,
arg, arg,
kind.descr(), param,
!args_iter.clone().is_sorted_by_key(|arg| match arg { !args_iter.clone().is_sorted_by_key(|arg| match arg {
GenericArg::Lifetime(_) => ParamKindOrd::Lifetime, GenericArg::Lifetime(_) => ParamKindOrd::Lifetime,
GenericArg::Type(_) => ParamKindOrd::Type, GenericArg::Type(_) => ParamKindOrd::Type,
@ -315,9 +325,9 @@ impl<'o, 'tcx> dyn AstConv<'tcx> + 'o {
{ {
let kind = arg.descr(); let kind = arg.descr();
assert_eq!(kind, "lifetime"); assert_eq!(kind, "lifetime");
let provided = let (provided_arg, param) =
force_infer_lt.expect("lifetimes ought to have been inferred"); force_infer_lt.expect("lifetimes ought to have been inferred");
Self::generic_arg_mismatch_err(tcx.sess, provided, kind, false, None); Self::generic_arg_mismatch_err(tcx, provided_arg, param, false, None);
} }
break; break;

View file

@ -0,0 +1,10 @@
#![crate_type = "lib"]
fn example<const N: usize>() {}
fn other() {
example::<[usize; 3]>();
//~^ ERROR type provided when a const
example::<[usize; 4+5]>();
//~^ ERROR type provided when a const
}

View file

@ -0,0 +1,15 @@
error[E0747]: type provided when a constant was expected
--> $DIR/suggest_const_for_array.rs:6:13
|
LL | example::<[usize; 3]>();
| ^^^^^^^^^^ help: array type provided where a `usize` was expected, try: `{ 3 }`
error[E0747]: type provided when a constant was expected
--> $DIR/suggest_const_for_array.rs:8:13
|
LL | example::<[usize; 4+5]>();
| ^^^^^^^^^^^^ help: array type provided where a `usize` was expected, try: `{ 4+5 }`
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0747`.