From 9a2856837c6a09b39b4830c0cfeecf2f68b27e8e Mon Sep 17 00:00:00 2001 From: Boxy <rust@boxyuwu.dev> Date: Thu, 10 Apr 2025 15:11:55 +0100 Subject: [PATCH] GAI logic on stable too --- compiler/rustc_hir_typeck/src/expr.rs | 51 +-------------- .../rustc_hir_typeck/src/fn_ctxt/checks.rs | 48 +++++++++++++- .../lang-item-generic-requirements.rs | 2 + .../lang-item-generic-requirements.stderr | 20 +++++- ...py-check-const-element-uninferred-count.rs | 65 +++++++++++++++++++ ...heck-const-element-uninferred-count.stderr | 36 ++++++++++ .../copy-inference-side-effects-are-lazy.rs | 9 +-- ...opy-inference-side-effects-are-lazy.stderr | 17 +++++ 8 files changed, 186 insertions(+), 62 deletions(-) create mode 100644 tests/ui/repeat-expr/copy-check-const-element-uninferred-count.rs create mode 100644 tests/ui/repeat-expr/copy-check-const-element-uninferred-count.stderr create mode 100644 tests/ui/repeat-expr/copy-inference-side-effects-are-lazy.stderr diff --git a/compiler/rustc_hir_typeck/src/expr.rs b/compiler/rustc_hir_typeck/src/expr.rs index 3475d15e948..56167c8b7b3 100644 --- a/compiler/rustc_hir_typeck/src/expr.rs +++ b/compiler/rustc_hir_typeck/src/expr.rs @@ -1868,62 +1868,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> { // We defer checking whether the element type is `Copy` as it is possible to have // an inference variable as a repeat count and it seems unlikely that `Copy` would // have inference side effects required for type checking to succeed. - if tcx.features().generic_arg_infer() { - self.deferred_repeat_expr_checks.borrow_mut().push((element, element_ty, count)); - // If the length is 0, we don't create any elements, so we don't copy any. - // If the length is 1, we don't copy that one element, we move it. Only check - // for `Copy` if the length is larger, or unevaluated. - } else if count.try_to_target_usize(self.tcx).is_none_or(|x| x > 1) { - self.enforce_repeat_element_needs_copy_bound(element, element_ty); - } + self.deferred_repeat_expr_checks.borrow_mut().push((element, element_ty, count)); let ty = Ty::new_array_with_const_len(tcx, t, count); self.register_wf_obligation(ty.into(), expr.span, ObligationCauseCode::WellFormed(None)); ty } - /// 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>, - ) { - let tcx = self.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 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); - } - fn check_expr_tuple( &self, elts: &'tcx [hir::Expr<'tcx>], diff --git a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs index cdced64b53c..403d0bede33 100644 --- a/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs +++ b/compiler/rustc_hir_typeck/src/fn_ctxt/checks.rs @@ -4,10 +4,10 @@ use itertools::Itertools; use rustc_data_structures::fx::FxIndexSet; use rustc_errors::codes::*; use rustc_errors::{Applicability, Diag, ErrorGuaranteed, MultiSpan, a_or_an, listify, pluralize}; -use rustc_hir::def::{CtorOf, DefKind, Res}; +use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res}; use rustc_hir::def_id::DefId; use rustc_hir::intravisit::Visitor; -use rustc_hir::{ExprKind, HirId, Node, QPath}; +use rustc_hir::{ExprKind, HirId, LangItem, Node, QPath}; use rustc_hir_analysis::check::intrinsicck::InlineAsmCtxt; use rustc_hir_analysis::check::potentially_plural_count; use rustc_hir_analysis::hir_ty_lowering::HirTyLowerer; @@ -162,6 +162,50 @@ 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/lang-items/lang-item-generic-requirements.rs b/tests/ui/lang-items/lang-item-generic-requirements.rs index 90ed5f3f0ef..7676b5557d2 100644 --- a/tests/ui/lang-items/lang-item-generic-requirements.rs +++ b/tests/ui/lang-items/lang-item-generic-requirements.rs @@ -49,12 +49,14 @@ fn ice() { // Use index let arr = [0; 5]; let _ = arr[2]; + //~^ ERROR cannot index into a value of type `[{integer}; 5]` // Use phantomdata let _ = MyPhantomData::<(), i32>; // Use Foo let _: () = Foo; + //~^ ERROR mismatched types } // use `start` diff --git a/tests/ui/lang-items/lang-item-generic-requirements.stderr b/tests/ui/lang-items/lang-item-generic-requirements.stderr index 3de67d65940..409fa05d637 100644 --- a/tests/ui/lang-items/lang-item-generic-requirements.stderr +++ b/tests/ui/lang-items/lang-item-generic-requirements.stderr @@ -76,9 +76,23 @@ LL | r + a; | | | {integer} +error[E0608]: cannot index into a value of type `[{integer}; 5]` + --> $DIR/lang-item-generic-requirements.rs:51:16 + | +LL | let _ = arr[2]; + | ^^^ + +error[E0308]: mismatched types + --> $DIR/lang-item-generic-requirements.rs:58:17 + | +LL | let _: () = Foo; + | -- ^^^ expected `()`, found `Foo` + | | + | expected due to this + error: requires `copy` lang_item -error: aborting due to 10 previous errors +error: aborting due to 12 previous errors -Some errors have detailed explanations: E0369, E0392, E0718. -For more information about an error, try `rustc --explain E0369`. +Some errors have detailed explanations: E0308, E0369, E0392, E0608, E0718. +For more information about an error, try `rustc --explain E0308`. 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 new file mode 100644 index 00000000000..8ef0c2690ba --- /dev/null +++ b/tests/ui/repeat-expr/copy-check-const-element-uninferred-count.rs @@ -0,0 +1,65 @@ +#![feature(generic_arg_infer)] + +// Test when deferring repeat expr copy checks to end of typechecking whether elements +// that are const items allow for repeat counts to go uninferred without an error being +// emitted if they would later wind up inferred by integer fallback. +// +// This test should be updated if we wind up deferring repeat expr checks until *after* +// integer fallback as the point of the test is not *specifically* about integer fallback +// but rather about the behaviour of `const` element exprs. + +trait Trait<const N: usize> {} + +// We impl `Trait` for both `i32` and `u32` to avoid being able +// to prove `?int: Trait<?n>` from there only being one impl. +impl Trait<2> for i32 {} +impl Trait<2> for u32 {} + +fn tie_and_make_goal<const N: usize, T: Trait<N>>(_: &T, _: &[String; N]) {} + +fn const_block() { + // Deferred repeat expr `String; ?n` + let a = [const { String::new() }; _]; + //~^ ERROR: type annotations needed for `[String; _]` + + // `?int: Trait<?n>` goal + tie_and_make_goal(&1, &a); + + // If repeat expr checks structurally resolve the `?n`s before checking if the + // element is a `const` then we would error here. Otherwise we avoid doing so, + // integer fallback occurs, allowing `?int: Trait<?n>` goals to make progress, + // inferring the repeat counts (to `2` but that doesn't matter as the element is `const`). +} + +fn const_item() { + const MY_CONST: String = String::new(); + + // Deferred repeat expr `String; ?n` + let a = [MY_CONST; _]; + //~^ ERROR: type annotations needed for `[String; _]` + + // `?int: Trait<?n>` goal + tie_and_make_goal(&1, &a); + + // ... same as `const_block` +} + +fn assoc_const() { + trait Dummy { + const ASSOC: String; + } + impl Dummy for () { + const ASSOC: String = String::new(); + } + + // Deferred repeat expr `String; ?n` + let a = [<() as Dummy>::ASSOC; _]; + //~^ ERROR: type annotations needed for `[String; _]` + + // `?int: Trait<?n>` goal + tie_and_make_goal(&1, &a); + + // ... same as `const_block` +} + +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 new file mode 100644 index 00000000000..8229b0b2b37 --- /dev/null +++ b/tests/ui/repeat-expr/copy-check-const-element-uninferred-count.stderr @@ -0,0 +1,36 @@ +error[E0282]: type annotations needed for `[String; _]` + --> $DIR/copy-check-const-element-uninferred-count.rs:22:9 + | +LL | let a = [const { String::new() }; _]; + | ^ ----------------------- 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] = [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[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`. diff --git a/tests/ui/repeat-expr/copy-inference-side-effects-are-lazy.rs b/tests/ui/repeat-expr/copy-inference-side-effects-are-lazy.rs index 0b0672d9c2b..d50466ac4bb 100644 --- a/tests/ui/repeat-expr/copy-inference-side-effects-are-lazy.rs +++ b/tests/ui/repeat-expr/copy-inference-side-effects-are-lazy.rs @@ -1,8 +1,3 @@ -//@revisions: current gai -//@[current] check-pass - -#![cfg_attr(gai, feature(generic_arg_infer))] - use std::marker::PhantomData; struct Foo<T>(PhantomData<T>); @@ -20,6 +15,6 @@ fn extract<T, const N: usize>(_: [Foo<T>; N]) -> T { fn main() { let x = [Foo(PhantomData); 2]; - //[gai]~^ ERROR: type annotations needed - _ = extract(x).max(2); + //~^ ERROR: type annotations needed + extract(x).max(2); } diff --git a/tests/ui/repeat-expr/copy-inference-side-effects-are-lazy.stderr b/tests/ui/repeat-expr/copy-inference-side-effects-are-lazy.stderr new file mode 100644 index 00000000000..ba44beb76db --- /dev/null +++ b/tests/ui/repeat-expr/copy-inference-side-effects-are-lazy.stderr @@ -0,0 +1,17 @@ +error[E0282]: type annotations needed for `[Foo<_>; 2]` + --> $DIR/copy-inference-side-effects-are-lazy.rs:17:9 + | +LL | let x = [Foo(PhantomData); 2]; + | ^ +LL | +LL | extract(x).max(2); + | ---------- type must be known at this point + | +help: consider giving `x` an explicit type, where the type for type parameter `T` is specified + | +LL | let x: [Foo<T>; 2] = [Foo(PhantomData); 2]; + | +++++++++++++ + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0282`.