From be564703b0751927b8b341cbe21b2347b32f00dd Mon Sep 17 00:00:00 2001 From: Boxy Date: Thu, 10 Apr 2025 15:30:28 +0100 Subject: [PATCH] Check for element being `const` before resolving repeat count --- .../rustc_hir_typeck/src/fn_ctxt/checks.rs | 95 +++++++++---------- ...py-check-const-element-uninferred-count.rs | 13 ++- ...heck-const-element-uninferred-count.stderr | 37 ++------ 3 files changed, 65 insertions(+), 80 deletions(-) diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index 403d0bede33..1d174651900 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -115,6 +115,23 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { let deferred_repeat_expr_checks = deferred_repeat_expr_checks .drain(..) .flat_map(|(element, element_ty, count)| { + // Actual constants as the repeat element get inserted repeatedly instead of getting copied via Copy + // so we don't need to attempt to structurally resolve the repeat count which may unnecessarily error. + match &element.kind { + hir::ExprKind::ConstBlock(..) => return None, + hir::ExprKind::Path(qpath) => { + let res = self.typeck_results.borrow().qpath_res(qpath, element.hir_id); + if let Res::Def( + DefKind::Const | DefKind::AssocConst | DefKind::AnonConst, + _, + ) = res + { + return None; + } + } + _ => {} + } + // We want to emit an error if the const is not structurally resolveable as otherwise // we can find up conservatively proving `Copy` which may infer the repeat expr count // to something that never required `Copy` in the first place. @@ -135,12 +152,40 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // expr's `Copy` check. .collect::>(); + let enforce_copy_bound = |element: &hir::Expr<'_>, element_ty| { + // If someone calls a const fn or constructs a const value, they can extract that + // out into a separate constant (or a const block in the future), so we check that + // to tell them that in the diagnostic. Does not affect typeck. + let is_constable = match element.kind { + hir::ExprKind::Call(func, _args) => match *self.node_ty(func.hir_id).kind() { + ty::FnDef(def_id, _) if self.tcx.is_stable_const_fn(def_id) => { + traits::IsConstable::Fn + } + _ => traits::IsConstable::No, + }, + hir::ExprKind::Path(qpath) => { + match self.typeck_results.borrow().qpath_res(&qpath, element.hir_id) { + Res::Def(DefKind::Ctor(_, CtorKind::Const), _) => traits::IsConstable::Ctor, + _ => traits::IsConstable::No, + } + } + _ => traits::IsConstable::No, + }; + + let lang_item = self.tcx.require_lang_item(LangItem::Copy, None); + let code = traits::ObligationCauseCode::RepeatElementCopy { + is_constable, + elt_span: element.span, + }; + self.require_type_meets(element_ty, element.span, code, lang_item); + }; + for (element, element_ty, count) in deferred_repeat_expr_checks { match count.kind() { ty::ConstKind::Value(val) if val.try_to_target_usize(self.tcx).is_none_or(|count| count > 1) => { - self.enforce_repeat_element_needs_copy_bound(element, element_ty) + enforce_copy_bound(element, element_ty) } // If the length is 0 or 1 we don't actually copy the element, we either don't create it // or we just use the one value. @@ -151,9 +196,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { ty::ConstKind::Param(_) | ty::ConstKind::Expr(_) | ty::ConstKind::Placeholder(_) - | ty::ConstKind::Unevaluated(_) => { - self.enforce_repeat_element_needs_copy_bound(element, element_ty) - } + | ty::ConstKind::Unevaluated(_) => enforce_copy_bound(element, element_ty), ty::ConstKind::Bound(_, _) | ty::ConstKind::Infer(_) | ty::ConstKind::Error(_) => { unreachable!() @@ -162,50 +205,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { } } - /// Requires that `element_ty` is `Copy` (unless it's a const expression itself). - pub(super) fn enforce_repeat_element_needs_copy_bound( - &self, - element: &hir::Expr<'_>, - element_ty: Ty<'tcx>, - ) { - // Actual constants as the repeat element get inserted repeatedly instead of getting copied via Copy. - match &element.kind { - hir::ExprKind::ConstBlock(..) => return, - hir::ExprKind::Path(qpath) => { - let res = self.typeck_results.borrow().qpath_res(qpath, element.hir_id); - if let Res::Def(DefKind::Const | DefKind::AssocConst | DefKind::AnonConst, _) = res - { - return; - } - } - _ => {} - } - - // If someone calls a const fn or constructs a const value, they can extract that - // out into a separate constant (or a const block in the future), so we check that - // to tell them that in the diagnostic. Does not affect typeck. - let is_constable = match element.kind { - hir::ExprKind::Call(func, _args) => match *self.node_ty(func.hir_id).kind() { - ty::FnDef(def_id, _) if self.tcx.is_stable_const_fn(def_id) => { - traits::IsConstable::Fn - } - _ => traits::IsConstable::No, - }, - hir::ExprKind::Path(qpath) => { - match self.typeck_results.borrow().qpath_res(&qpath, element.hir_id) { - Res::Def(DefKind::Ctor(_, CtorKind::Const), _) => traits::IsConstable::Ctor, - _ => traits::IsConstable::No, - } - } - _ => traits::IsConstable::No, - }; - - let lang_item = self.tcx.require_lang_item(LangItem::Copy, None); - let code = - traits::ObligationCauseCode::RepeatElementCopy { is_constable, elt_span: element.span }; - self.require_type_meets(element_ty, element.span, code, lang_item); - } - pub(in super::super) fn check_method_argument_types( &self, sp: Span, diff --git a/tests/ui/repeat-expr/copy-check-const-element-uninferred-count.rs b/tests/ui/repeat-expr/copy-check-const-element-uninferred-count.rs index 8ef0c2690ba..6115146539c 100644 --- a/tests/ui/repeat-expr/copy-check-const-element-uninferred-count.rs +++ b/tests/ui/repeat-expr/copy-check-const-element-uninferred-count.rs @@ -20,7 +20,6 @@ fn tie_and_make_goal>(_: &T, _: &[String; N]) {} fn const_block() { // Deferred repeat expr `String; ?n` let a = [const { String::new() }; _]; - //~^ ERROR: type annotations needed for `[String; _]` // `?int: Trait` goal tie_and_make_goal(&1, &a); @@ -36,7 +35,6 @@ fn const_item() { // Deferred repeat expr `String; ?n` let a = [MY_CONST; _]; - //~^ ERROR: type annotations needed for `[String; _]` // `?int: Trait` goal tie_and_make_goal(&1, &a); @@ -54,7 +52,6 @@ fn assoc_const() { // Deferred repeat expr `String; ?n` let a = [<() as Dummy>::ASSOC; _]; - //~^ ERROR: type annotations needed for `[String; _]` // `?int: Trait` goal tie_and_make_goal(&1, &a); @@ -62,4 +59,14 @@ fn assoc_const() { // ... same as `const_block` } +fn const_block_but_uninferred() { + // Deferred repeat expr `String; ?n` + let a = [const { String::new() }; _]; + //~^ ERROR: type annotations needed for `[String; _]` + + // Even if we don't structurally resolve the repeat count as part of repeat expr + // checks, we still error on the repeat count being uninferred as we require all + // types/consts to be inferred by the end of type checking. +} + fn main() {} diff --git a/tests/ui/repeat-expr/copy-check-const-element-uninferred-count.stderr b/tests/ui/repeat-expr/copy-check-const-element-uninferred-count.stderr index 8229b0b2b37..2f52537fa94 100644 --- a/tests/ui/repeat-expr/copy-check-const-element-uninferred-count.stderr +++ b/tests/ui/repeat-expr/copy-check-const-element-uninferred-count.stderr @@ -1,36 +1,15 @@ -error[E0282]: type annotations needed for `[String; _]` - --> $DIR/copy-check-const-element-uninferred-count.rs:22:9 +error[E0284]: type annotations needed for `[String; _]` + --> $DIR/copy-check-const-element-uninferred-count.rs:64:9 | LL | let a = [const { String::new() }; _]; - | ^ ----------------------- type must be known at this point + | ^ ---------------------------- type must be known at this point | -help: consider giving `a` an explicit type, where the value of const parameter `N` is specified + = note: the length of array `[String; _]` must be type `usize` +help: consider giving `a` an explicit type, where the placeholders `_` are specified | -LL | let a: [_; N] = [const { String::new() }; _]; +LL | let a: [_; _] = [const { String::new() }; _]; | ++++++++ -error[E0282]: type annotations needed for `[String; _]` - --> $DIR/copy-check-const-element-uninferred-count.rs:38:9 - | -LL | let a = [MY_CONST; _]; - | ^ -------- type must be known at this point - | -help: consider giving `a` an explicit type, where the value of const parameter `N` is specified - | -LL | let a: [_; N] = [MY_CONST; _]; - | ++++++++ +error: aborting due to 1 previous error -error[E0282]: type annotations needed for `[String; _]` - --> $DIR/copy-check-const-element-uninferred-count.rs:56:9 - | -LL | let a = [<() as Dummy>::ASSOC; _]; - | ^ -------------------- type must be known at this point - | -help: consider giving `a` an explicit type, where the value of const parameter `N` is specified - | -LL | let a: [_; N] = [<() as Dummy>::ASSOC; _]; - | ++++++++ - -error: aborting due to 3 previous errors - -For more information about this error, try `rustc --explain E0282`. +For more information about this error, try `rustc --explain E0284`.