Skip to content

Commit 5d0a80b

Browse files
committed
Check for element being const before resolving repeat count
1 parent 71373c5 commit 5d0a80b

File tree

3 files changed

+65
-80
lines changed

3 files changed

+65
-80
lines changed

compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs

+47-48
Original file line numberDiff line numberDiff line change
@@ -108,6 +108,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
108108
let deferred_repeat_expr_checks = deferred_repeat_expr_checks
109109
.drain(..)
110110
.flat_map(|(element, element_ty, count)| {
111+
// Actual constants as the repeat element get inserted repeatedly instead of getting copied via Copy
112+
// so we don't need to attempt to structurally resolve the repeat count which may unnecessarily error.
113+
match &element.kind {
114+
hir::ExprKind::ConstBlock(..) => return None,
115+
hir::ExprKind::Path(qpath) => {
116+
let res = self.typeck_results.borrow().qpath_res(qpath, element.hir_id);
117+
if let Res::Def(
118+
DefKind::Const | DefKind::AssocConst | DefKind::AnonConst,
119+
_,
120+
) = res
121+
{
122+
return None;
123+
}
124+
}
125+
_ => {}
126+
}
127+
111128
// We want to emit an error if the const is not structurally resolveable as otherwise
112129
// we can find up conservatively proving `Copy` which may infer the repeat expr count
113130
// to something that never required `Copy` in the first place.
@@ -128,12 +145,40 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
128145
// expr's `Copy` check.
129146
.collect::<Vec<_>>();
130147

148+
let enforce_copy_bound = |element: &hir::Expr<'_>, element_ty| {
149+
// If someone calls a const fn or constructs a const value, they can extract that
150+
// out into a separate constant (or a const block in the future), so we check that
151+
// to tell them that in the diagnostic. Does not affect typeck.
152+
let is_constable = match element.kind {
153+
hir::ExprKind::Call(func, _args) => match *self.node_ty(func.hir_id).kind() {
154+
ty::FnDef(def_id, _) if self.tcx.is_stable_const_fn(def_id) => {
155+
traits::IsConstable::Fn
156+
}
157+
_ => traits::IsConstable::No,
158+
},
159+
hir::ExprKind::Path(qpath) => {
160+
match self.typeck_results.borrow().qpath_res(&qpath, element.hir_id) {
161+
Res::Def(DefKind::Ctor(_, CtorKind::Const), _) => traits::IsConstable::Ctor,
162+
_ => traits::IsConstable::No,
163+
}
164+
}
165+
_ => traits::IsConstable::No,
166+
};
167+
168+
let lang_item = self.tcx.require_lang_item(LangItem::Copy, None);
169+
let code = traits::ObligationCauseCode::RepeatElementCopy {
170+
is_constable,
171+
elt_span: element.span,
172+
};
173+
self.require_type_meets(element_ty, element.span, code, lang_item);
174+
};
175+
131176
for (element, element_ty, count) in deferred_repeat_expr_checks {
132177
match count.kind() {
133178
ty::ConstKind::Value(val)
134179
if val.try_to_target_usize(self.tcx).is_none_or(|count| count > 1) =>
135180
{
136-
self.enforce_repeat_element_needs_copy_bound(element, element_ty)
181+
enforce_copy_bound(element, element_ty)
137182
}
138183
// If the length is 0 or 1 we don't actually copy the element, we either don't create it
139184
// or we just use the one value.
@@ -144,9 +189,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
144189
ty::ConstKind::Param(_)
145190
| ty::ConstKind::Expr(_)
146191
| ty::ConstKind::Placeholder(_)
147-
| ty::ConstKind::Unevaluated(_) => {
148-
self.enforce_repeat_element_needs_copy_bound(element, element_ty)
149-
}
192+
| ty::ConstKind::Unevaluated(_) => enforce_copy_bound(element, element_ty),
150193

151194
ty::ConstKind::Bound(_, _) | ty::ConstKind::Infer(_) | ty::ConstKind::Error(_) => {
152195
unreachable!()
@@ -155,50 +198,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
155198
}
156199
}
157200

158-
/// Requires that `element_ty` is `Copy` (unless it's a const expression itself).
159-
pub(super) fn enforce_repeat_element_needs_copy_bound(
160-
&self,
161-
element: &hir::Expr<'_>,
162-
element_ty: Ty<'tcx>,
163-
) {
164-
// Actual constants as the repeat element get inserted repeatedly instead of getting copied via Copy.
165-
match &element.kind {
166-
hir::ExprKind::ConstBlock(..) => return,
167-
hir::ExprKind::Path(qpath) => {
168-
let res = self.typeck_results.borrow().qpath_res(qpath, element.hir_id);
169-
if let Res::Def(DefKind::Const | DefKind::AssocConst | DefKind::AnonConst, _) = res
170-
{
171-
return;
172-
}
173-
}
174-
_ => {}
175-
}
176-
177-
// If someone calls a const fn or constructs a const value, they can extract that
178-
// out into a separate constant (or a const block in the future), so we check that
179-
// to tell them that in the diagnostic. Does not affect typeck.
180-
let is_constable = match element.kind {
181-
hir::ExprKind::Call(func, _args) => match *self.node_ty(func.hir_id).kind() {
182-
ty::FnDef(def_id, _) if self.tcx.is_stable_const_fn(def_id) => {
183-
traits::IsConstable::Fn
184-
}
185-
_ => traits::IsConstable::No,
186-
},
187-
hir::ExprKind::Path(qpath) => {
188-
match self.typeck_results.borrow().qpath_res(&qpath, element.hir_id) {
189-
Res::Def(DefKind::Ctor(_, CtorKind::Const), _) => traits::IsConstable::Ctor,
190-
_ => traits::IsConstable::No,
191-
}
192-
}
193-
_ => traits::IsConstable::No,
194-
};
195-
196-
let lang_item = self.tcx.require_lang_item(LangItem::Copy, None);
197-
let code =
198-
traits::ObligationCauseCode::RepeatElementCopy { is_constable, elt_span: element.span };
199-
self.require_type_meets(element_ty, element.span, code, lang_item);
200-
}
201-
202201
/// Generic function that factors out common logic from function calls,
203202
/// method calls and overloaded operators.
204203
pub(in super::super) fn check_argument_types(

tests/ui/repeat-expr/copy-check-const-element-uninferred-count.rs

+10-3
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,6 @@ fn tie_and_make_goal<const N: usize, T: Trait<N>>(_: &T, _: &[String; N]) {}
2020
fn const_block() {
2121
// Deferred repeat expr `String; ?n`
2222
let a = [const { String::new() }; _];
23-
//~^ ERROR: type annotations needed for `[String; _]`
2423

2524
// `?int: Trait<?n>` goal
2625
tie_and_make_goal(&1, &a);
@@ -36,7 +35,6 @@ fn const_item() {
3635

3736
// Deferred repeat expr `String; ?n`
3837
let a = [MY_CONST; _];
39-
//~^ ERROR: type annotations needed for `[String; _]`
4038

4139
// `?int: Trait<?n>` goal
4240
tie_and_make_goal(&1, &a);
@@ -54,12 +52,21 @@ fn assoc_const() {
5452

5553
// Deferred repeat expr `String; ?n`
5654
let a = [<() as Dummy>::ASSOC; _];
57-
//~^ ERROR: type annotations needed for `[String; _]`
5855

5956
// `?int: Trait<?n>` goal
6057
tie_and_make_goal(&1, &a);
6158

6259
// ... same as `const_block`
6360
}
6461

62+
fn const_block_but_uninferred() {
63+
// Deferred repeat expr `String; ?n`
64+
let a = [const { String::new() }; _];
65+
//~^ ERROR: type annotations needed for `[String; _]`
66+
67+
// Even if we don't structurally resolve the repeat count as part of repeat expr
68+
// checks, we still error on the repeat count being uninferred as we require all
69+
// types/consts to be inferred by the end of type checking.
70+
}
71+
6572
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -1,36 +1,15 @@
1-
error[E0282]: type annotations needed for `[String; _]`
2-
--> $DIR/copy-check-const-element-uninferred-count.rs:22:9
1+
error[E0284]: type annotations needed for `[String; _]`
2+
--> $DIR/copy-check-const-element-uninferred-count.rs:64:9
33
|
44
LL | let a = [const { String::new() }; _];
5-
| ^ ----------------------- type must be known at this point
5+
| ^ ---------------------------- type must be known at this point
66
|
7-
help: consider giving `a` an explicit type, where the value of const parameter `N` is specified
7+
= note: the length of array `[String; _]` must be type `usize`
8+
help: consider giving `a` an explicit type, where the placeholders `_` are specified
89
|
9-
LL | let a: [_; N] = [const { String::new() }; _];
10+
LL | let a: [_; _] = [const { String::new() }; _];
1011
| ++++++++
1112

12-
error[E0282]: type annotations needed for `[String; _]`
13-
--> $DIR/copy-check-const-element-uninferred-count.rs:38:9
14-
|
15-
LL | let a = [MY_CONST; _];
16-
| ^ -------- type must be known at this point
17-
|
18-
help: consider giving `a` an explicit type, where the value of const parameter `N` is specified
19-
|
20-
LL | let a: [_; N] = [MY_CONST; _];
21-
| ++++++++
22-
23-
error[E0282]: type annotations needed for `[String; _]`
24-
--> $DIR/copy-check-const-element-uninferred-count.rs:56:9
25-
|
26-
LL | let a = [<() as Dummy>::ASSOC; _];
27-
| ^ -------------------- type must be known at this point
28-
|
29-
help: consider giving `a` an explicit type, where the value of const parameter `N` is specified
30-
|
31-
LL | let a: [_; N] = [<() as Dummy>::ASSOC; _];
32-
| ++++++++
33-
34-
error: aborting due to 3 previous errors
13+
error: aborting due to 1 previous error
3514

36-
For more information about this error, try `rustc --explain E0282`.
15+
For more information about this error, try `rustc --explain E0284`.

0 commit comments

Comments
 (0)