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:
commit
be2a3f8642
7 changed files with 95 additions and 41 deletions
|
@ -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",
|
||||||
|
|
|
@ -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" }
|
||||||
|
|
|
@ -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)]
|
||||||
|
|
|
@ -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)]
|
||||||
|
|
|
@ -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, ¶m.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;
|
||||||
|
|
10
src/test/ui/const-generics/suggest_const_for_array.rs
Normal file
10
src/test/ui/const-generics/suggest_const_for_array.rs
Normal 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
|
||||||
|
}
|
15
src/test/ui/const-generics/suggest_const_for_array.stderr
Normal file
15
src/test/ui/const-generics/suggest_const_for_array.stderr
Normal 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`.
|
Loading…
Add table
Add a link
Reference in a new issue