Skip to content

Commit b724d9c

Browse files
authored
Rollup merge of #113925 - clubby789:const-ctor-repeat, r=estebank
Improve diagnostic for const ctors in array repeat expressions Fixes #113912
2 parents 7adc89b + 86b1122 commit b724d9c

File tree

7 files changed

+126
-28
lines changed

7 files changed

+126
-28
lines changed

compiler/rustc_hir_typeck/src/expr.rs

+28-7
Original file line numberDiff line numberDiff line change
@@ -1551,21 +1551,42 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
15511551
}
15521552
_ => {}
15531553
}
1554-
// If someone calls a const fn, they can extract that call out into a separate constant (or a const
1555-
// block in the future), so we check that to tell them that in the diagnostic. Does not affect typeck.
1556-
let is_const_fn = match element.kind {
1554+
// If someone calls a const fn or constructs a const value, they can extract that
1555+
// out into a separate constant (or a const block in the future), so we check that
1556+
// to tell them that in the diagnostic. Does not affect typeck.
1557+
let is_constable = match element.kind {
15571558
hir::ExprKind::Call(func, _args) => match *self.node_ty(func.hir_id).kind() {
1558-
ty::FnDef(def_id, _) => tcx.is_const_fn(def_id),
1559-
_ => false,
1559+
ty::FnDef(def_id, _) if tcx.is_const_fn(def_id) => traits::IsConstable::Fn,
1560+
_ => traits::IsConstable::No,
15601561
},
1561-
_ => false,
1562+
hir::ExprKind::Path(qpath) => {
1563+
match self.typeck_results.borrow().qpath_res(&qpath, element.hir_id) {
1564+
Res::Def(DefKind::Ctor(_, CtorKind::Const), _) => traits::IsConstable::Ctor,
1565+
_ => traits::IsConstable::No,
1566+
}
1567+
}
1568+
_ => traits::IsConstable::No,
15621569
};
15631570

15641571
// If the length is 0, we don't create any elements, so we don't copy any. If the length is 1, we
15651572
// don't copy that one element, we move it. Only check for Copy if the length is larger.
15661573
if count.try_eval_target_usize(tcx, self.param_env).map_or(true, |len| len > 1) {
15671574
let lang_item = self.tcx.require_lang_item(LangItem::Copy, None);
1568-
let code = traits::ObligationCauseCode::RepeatElementCopy { is_const_fn };
1575+
let code = traits::ObligationCauseCode::RepeatElementCopy {
1576+
is_constable,
1577+
elt_type: element_ty,
1578+
elt_span: element.span,
1579+
elt_stmt_span: self
1580+
.tcx
1581+
.hir()
1582+
.parent_iter(element.hir_id)
1583+
.find_map(|(_, node)| match node {
1584+
hir::Node::Item(it) => Some(it.span),
1585+
hir::Node::Stmt(stmt) => Some(stmt.span),
1586+
_ => None,
1587+
})
1588+
.expect("array repeat expressions must be inside an item or statement"),
1589+
};
15691590
self.require_type_meets(element_ty, element.span, code, lang_item);
15701591
}
15711592
}

compiler/rustc_middle/src/traits/mod.rs

+23-3
Original file line numberDiff line numberDiff line change
@@ -305,9 +305,14 @@ pub enum ObligationCauseCode<'tcx> {
305305
SizedCoroutineInterior(LocalDefId),
306306
/// `[expr; N]` requires `type_of(expr): Copy`.
307307
RepeatElementCopy {
308-
/// If element is a `const fn` we display a help message suggesting to move the
309-
/// function call to a new `const` item while saying that `T` doesn't implement `Copy`.
310-
is_const_fn: bool,
308+
/// If element is a `const fn` or const ctor we display a help message suggesting
309+
/// to move it to a new `const` item while saying that `T` doesn't implement `Copy`.
310+
is_constable: IsConstable,
311+
elt_type: Ty<'tcx>,
312+
elt_span: Span,
313+
/// Span of the statement/item in which the repeat expression occurs. We can use this to
314+
/// place a `const` declaration before it
315+
elt_stmt_span: Span,
311316
},
312317

313318
/// Types of fields (other than the last, except for packed structs) in a struct must be sized.
@@ -455,6 +460,21 @@ pub enum ObligationCauseCode<'tcx> {
455460
TypeAlias(InternedObligationCauseCode<'tcx>, Span, DefId),
456461
}
457462

463+
/// Whether a value can be extracted into a const.
464+
/// Used for diagnostics around array repeat expressions.
465+
#[derive(Copy, Clone, Debug, PartialEq, Eq, HashStable, TyEncodable, TyDecodable)]
466+
pub enum IsConstable {
467+
No,
468+
/// Call to a const fn
469+
Fn,
470+
/// Use of a const ctor
471+
Ctor,
472+
}
473+
474+
crate::TrivialTypeTraversalAndLiftImpls! {
475+
IsConstable,
476+
}
477+
458478
/// The 'location' at which we try to perform HIR-based wf checking.
459479
/// This information is used to obtain an `hir::Ty`, which
460480
/// we can walk in order to obtain precise spans for any

compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs

+20-9
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ use rustc_infer::infer::error_reporting::TypeErrCtxt;
2828
use rustc_infer::infer::type_variable::{TypeVariableOrigin, TypeVariableOriginKind};
2929
use rustc_infer::infer::{DefineOpaqueTypes, InferOk, LateBoundRegionConversionTime};
3030
use rustc_middle::hir::map;
31+
use rustc_middle::traits::IsConstable;
3132
use rustc_middle::ty::error::TypeError::{self, Sorts};
3233
use rustc_middle::ty::{
3334
self, suggest_arbitrary_trait_bound, suggest_constraining_type_param, AdtKind, GenericArgs,
@@ -2768,20 +2769,30 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
27682769
));
27692770
}
27702771
}
2771-
ObligationCauseCode::RepeatElementCopy { is_const_fn } => {
2772+
ObligationCauseCode::RepeatElementCopy { is_constable, elt_type, elt_span, elt_stmt_span } => {
27722773
err.note(
27732774
"the `Copy` trait is required because this value will be copied for each element of the array",
27742775
);
2775-
2776-
if is_const_fn {
2777-
err.help(
2778-
"consider creating a new `const` item and initializing it with the result \
2779-
of the function call to be used in the repeat position, like \
2780-
`const VAL: Type = const_fn();` and `let x = [VAL; 42];`",
2781-
);
2776+
let value_kind = match is_constable {
2777+
IsConstable::Fn => Some("the result of the function call"),
2778+
IsConstable::Ctor => Some("the result of the constructor"),
2779+
_ => None
2780+
};
2781+
let sm = tcx.sess.source_map();
2782+
if let Some(value_kind) = value_kind &&
2783+
let Ok(snip) = sm.span_to_snippet(elt_span)
2784+
{
2785+
let help_msg = format!(
2786+
"consider creating a new `const` item and initializing it with {value_kind} \
2787+
to be used in the repeat position");
2788+
let indentation = sm.indentation_before(elt_stmt_span).unwrap_or_default();
2789+
err.multipart_suggestion(help_msg, vec![
2790+
(elt_stmt_span.shrink_to_lo(), format!("const ARRAY_REPEAT_VALUE: {elt_type} = {snip};\n{indentation}")),
2791+
(elt_span, "ARRAY_REPEAT_VALUE".to_string())
2792+
], Applicability::MachineApplicable);
27822793
}
27832794

2784-
if self.tcx.sess.is_nightly_build() && is_const_fn {
2795+
if self.tcx.sess.is_nightly_build() && matches!(is_constable, IsConstable::Fn|IsConstable::Ctor) {
27852796
err.help(
27862797
"create an inline `const` block, see RFC #2920 \
27872798
<https://github.com/rust-lang/rfcs/pull/2920> for more information",

tests/ui/consts/const-blocks/fn-call-in-non-const.stderr

+5-1
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,17 @@ LL | let _: [Option<Bar>; 2] = [no_copy(); 2];
66
|
77
= note: required for `Option<Bar>` to implement `Copy`
88
= note: the `Copy` trait is required because this value will be copied for each element of the array
9-
= help: consider creating a new `const` item and initializing it with the result of the function call to be used in the repeat position, like `const VAL: Type = const_fn();` and `let x = [VAL; 42];`
109
= help: create an inline `const` block, see RFC #2920 <https://github.com/rust-lang/rfcs/pull/2920> for more information
1110
help: consider annotating `Bar` with `#[derive(Copy)]`
1211
|
1312
LL + #[derive(Copy)]
1413
LL | struct Bar;
1514
|
15+
help: consider creating a new `const` item and initializing it with the result of the function call to be used in the repeat position
16+
|
17+
LL ~ const ARRAY_REPEAT_VALUE: Option<Bar> = no_copy();
18+
LL ~ let _: [Option<Bar>; 2] = [ARRAY_REPEAT_VALUE; 2];
19+
|
1620

1721
error: aborting due to previous error
1822

tests/ui/consts/const-blocks/trait-error.stderr

+5-1
Original file line numberDiff line numberDiff line change
@@ -10,9 +10,13 @@ note: required for `Foo<String>` to implement `Copy`
1010
LL | #[derive(Copy, Clone)]
1111
| ^^^^ unsatisfied trait bound introduced in this `derive` macro
1212
= note: the `Copy` trait is required because this value will be copied for each element of the array
13-
= help: consider creating a new `const` item and initializing it with the result of the function call to be used in the repeat position, like `const VAL: Type = const_fn();` and `let x = [VAL; 42];`
1413
= help: create an inline `const` block, see RFC #2920 <https://github.com/rust-lang/rfcs/pull/2920> for more information
1514
= note: this error originates in the derive macro `Copy` (in Nightly builds, run with -Z macro-backtrace for more info)
15+
help: consider creating a new `const` item and initializing it with the result of the function call to be used in the repeat position
16+
|
17+
LL ~ const ARRAY_REPEAT_VALUE: Foo<String> = Foo(String::new());
18+
LL ~ [ARRAY_REPEAT_VALUE; 4];
19+
|
1620

1721
error: aborting due to previous error
1822

tests/ui/consts/const-fn-in-vec.rs

+6-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,11 @@
1+
static _MAYBE_STRINGS: [Option<String>; 5] = [None; 5];
2+
//~^ ERROR the trait bound `String: Copy` is not satisfied
3+
14
fn main() {
25
// should hint to create an inline `const` block
36
// or to create a new `const` item
4-
let strings: [String; 5] = [String::new(); 5];
7+
let _strings: [String; 5] = [String::new(); 5];
8+
//~^ ERROR the trait bound `String: Copy` is not satisfied
9+
let _maybe_strings: [Option<String>; 5] = [None; 5];
510
//~^ ERROR the trait bound `String: Copy` is not satisfied
6-
println!("{:?}", strings);
711
}
+39-5
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,47 @@
11
error[E0277]: the trait bound `String: Copy` is not satisfied
2-
--> $DIR/const-fn-in-vec.rs:4:33
2+
--> $DIR/const-fn-in-vec.rs:1:47
33
|
4-
LL | let strings: [String; 5] = [String::new(); 5];
5-
| ^^^^^^^^^^^^^ the trait `Copy` is not implemented for `String`
4+
LL | static _MAYBE_STRINGS: [Option<String>; 5] = [None; 5];
5+
| ^^^^ the trait `Copy` is not implemented for `String`
66
|
7+
= note: required for `Option<String>` to implement `Copy`
78
= note: the `Copy` trait is required because this value will be copied for each element of the array
8-
= help: consider creating a new `const` item and initializing it with the result of the function call to be used in the repeat position, like `const VAL: Type = const_fn();` and `let x = [VAL; 42];`
99
= help: create an inline `const` block, see RFC #2920 <https://github.com/rust-lang/rfcs/pull/2920> for more information
10+
help: consider creating a new `const` item and initializing it with the result of the constructor to be used in the repeat position
11+
|
12+
LL + const ARRAY_REPEAT_VALUE: Option<String> = None;
13+
LL ~ static _MAYBE_STRINGS: [Option<String>; 5] = [ARRAY_REPEAT_VALUE; 5];
14+
|
15+
16+
error[E0277]: the trait bound `String: Copy` is not satisfied
17+
--> $DIR/const-fn-in-vec.rs:7:34
18+
|
19+
LL | let _strings: [String; 5] = [String::new(); 5];
20+
| ^^^^^^^^^^^^^ the trait `Copy` is not implemented for `String`
21+
|
22+
= note: the `Copy` trait is required because this value will be copied for each element of the array
23+
= help: create an inline `const` block, see RFC #2920 <https://github.com/rust-lang/rfcs/pull/2920> for more information
24+
help: consider creating a new `const` item and initializing it with the result of the function call to be used in the repeat position
25+
|
26+
LL ~ const ARRAY_REPEAT_VALUE: String = String::new();
27+
LL ~ let _strings: [String; 5] = [ARRAY_REPEAT_VALUE; 5];
28+
|
29+
30+
error[E0277]: the trait bound `String: Copy` is not satisfied
31+
--> $DIR/const-fn-in-vec.rs:9:48
32+
|
33+
LL | let _maybe_strings: [Option<String>; 5] = [None; 5];
34+
| ^^^^ the trait `Copy` is not implemented for `String`
35+
|
36+
= note: required for `Option<String>` to implement `Copy`
37+
= note: the `Copy` trait is required because this value will be copied for each element of the array
38+
= help: create an inline `const` block, see RFC #2920 <https://github.com/rust-lang/rfcs/pull/2920> for more information
39+
help: consider creating a new `const` item and initializing it with the result of the constructor to be used in the repeat position
40+
|
41+
LL ~ const ARRAY_REPEAT_VALUE: Option<String> = None;
42+
LL ~ let _maybe_strings: [Option<String>; 5] = [ARRAY_REPEAT_VALUE; 5];
43+
|
1044

11-
error: aborting due to previous error
45+
error: aborting due to 3 previous errors
1246

1347
For more information about this error, try `rustc --explain E0277`.

0 commit comments

Comments
 (0)