diff --git a/compiler/rustc_hir_analysis/src/diagnostics.rs b/compiler/rustc_hir_analysis/src/diagnostics.rs index 20b8a04099049..ef287b737851f 100644 --- a/compiler/rustc_hir_analysis/src/diagnostics.rs +++ b/compiler/rustc_hir_analysis/src/diagnostics.rs @@ -17,6 +17,13 @@ pub(crate) use precise_captures::*; pub(crate) mod remove_or_use_generic; +#[derive(Diagnostic)] +#[diag("complex const arguments must be placed inside of a `const` block")] +pub(crate) struct ComplexConstArg { + #[primary_span] + pub span: Span, +} + #[derive(Diagnostic)] #[diag("ambiguous associated {$assoc_kind} `{$assoc_ident}` in bounds of `{$qself}`")] pub(crate) struct AmbiguousAssocItem<'a> { diff --git a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs index 4bdfc328a8522..92f89c2834408 100644 --- a/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs +++ b/compiler/rustc_hir_analysis/src/hir_ty_lowering/mod.rs @@ -1,3 +1,5 @@ +// ignore-tidy-file-filelength + //! HIR ty lowering: Lowers type-system entities[^1] from the [HIR][hir] to //! the [`rustc_middle::ty`] representation. //! @@ -2523,6 +2525,31 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { ty::Const::new_value(tcx, valtree, ty) } + fn try_recover_misrepresented_function_call( + &self, + hir_self_ty: &hir::Ty<'_>, + span: Span, + ) -> Option { + // Only an enum can host a tuple-variant constructor (`>::Some(..)`). + // For any other self type, a type-relative call is an associated function, not a + // constructor, and must be wrapped in `const { ... }`. We catch that here, before + // lowering the self type, so a generic struct/union written without its args + // (`FieldName::len()`, from `tracing`'s macros) reports this clear error instead + // of a spurious E0107 "missing generics" (#157152), and a primitive or foreign + // type reports it instead of an opaque downstream resolution error. Enums, + // aliases, `Self` and type parameters are let through: each may resolve to an + // enum, so they must reach constructor lowering. + let self_ty_res = match hir_self_ty.kind { + hir::TyKind::Path(hir::QPath::Resolved(_, path)) => path.res, + _ => Res::Err, + }; + matches!( + self_ty_res, + Res::Def(DefKind::Struct | DefKind::Union | DefKind::ForeignTy, _) | Res::PrimTy(_) + ) + .then(|| self.dcx().emit_err(diagnostics::ComplexConstArg { span })) + } + fn lower_const_arg_tuple_call( &self, hir_id: HirId, @@ -2543,6 +2570,10 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { self.lower_resolved_const_path(opt_self_ty, path, hir_id) } hir::QPath::TypeRelative(hir_self_ty, segment) => { + if let Some(e) = self.try_recover_misrepresented_function_call(hir_self_ty, span) { + return ty::Const::new_error(tcx, e); + } + let self_ty = self.lower_ty(hir_self_ty); match self.lower_type_relative_const_path( self_ty, @@ -2576,10 +2607,7 @@ impl<'tcx> dyn HirTyLowerer<'tcx> + '_ { (tcx.adt_def(parent_did), fn_args, parent_did) } _ => { - let e = self.dcx().span_err( - span, - "complex const arguments must be placed inside of a `const` block", - ); + let e = self.dcx().emit_err(diagnostics::ComplexConstArg { span }); return Const::new_error(tcx, e); } }; diff --git a/library/alloc/src/vec/partial_eq.rs b/library/alloc/src/vec/partial_eq.rs index 943c9309836d3..2d5a839cc8c30 100644 --- a/library/alloc/src/vec/partial_eq.rs +++ b/library/alloc/src/vec/partial_eq.rs @@ -32,6 +32,12 @@ __impl_slice_eq1! { [A: Allocator] Cow<'_, [T]>, Vec where T: Clone, #[sta __impl_slice_eq1! { [] Cow<'_, [T]>, &[U] where T: Clone, #[stable(feature = "rust1", since = "1.0.0")] } #[cfg(not(no_global_oom_handling))] __impl_slice_eq1! { [] Cow<'_, [T]>, &mut [U] where T: Clone, #[stable(feature = "rust1", since = "1.0.0")] } +#[cfg(not(no_global_oom_handling))] +__impl_slice_eq1! { [A: Allocator] Vec, Cow<'_, [U]> where U: Clone, #[stable(feature = "partialeq_cow_for_vec_and_slice", since = "CURRENT_RUSTC_VERSION")] } +#[cfg(not(no_global_oom_handling))] +__impl_slice_eq1! { [] &[T], Cow<'_, [U]> where U: Clone, #[stable(feature = "partialeq_cow_for_vec_and_slice", since = "CURRENT_RUSTC_VERSION")] } +#[cfg(not(no_global_oom_handling))] +__impl_slice_eq1! { [] &mut [T], Cow<'_, [U]> where U: Clone, #[stable(feature = "partialeq_cow_for_vec_and_slice", since = "CURRENT_RUSTC_VERSION")] } __impl_slice_eq1! { const, [A: Allocator, const N: usize] Vec, [U; N], #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] #[stable(feature = "rust1", since = "1.0.0")] } __impl_slice_eq1! { const, [A: Allocator, const N: usize] Vec, &[U; N], #[rustc_const_unstable(feature = "const_cmp", issue = "143800")] #[stable(feature = "rust1", since = "1.0.0")] } diff --git a/library/alloctests/tests/vec.rs b/library/alloctests/tests/vec.rs index 077005afd5d38..787452df7f680 100644 --- a/library/alloctests/tests/vec.rs +++ b/library/alloctests/tests/vec.rs @@ -1339,6 +1339,25 @@ fn test_from_cow() { assert_eq!(Vec::from(Cow::Owned(owned)), vec!["owned", "(vec)"]); } +#[test] +fn test_partial_eq_cow_symmetric() { + let v: Vec = vec![1, 2, 3]; + let c: Cow<'_, [i32]> = Cow::Borrowed(&[1, 2, 3]); + + assert_eq!(c, v); + assert_eq!(v, c); + + let s: &[i32] = &[1, 2, 3]; + assert_eq!(s, c); + + let mut arr = [1, 2, 3]; + let ms: &mut [i32] = &mut arr; + assert_eq!(ms, c); + + let v2: Vec = vec![1, 2, 4]; + assert!(v2 != c); +} + #[allow(dead_code)] fn assert_covariance() { fn drain<'new>(d: Drain<'static, &'static str>) -> Drain<'new, &'new str> { diff --git a/src/bootstrap/src/core/build_steps/test.rs b/src/bootstrap/src/core/build_steps/test.rs index 3a7ab744c0402..5c16416266139 100644 --- a/src/bootstrap/src/core/build_steps/test.rs +++ b/src/bootstrap/src/core/build_steps/test.rs @@ -2565,6 +2565,9 @@ Please disable assertions with `rust.debug-assertions = false`. if builder.config.rust_optimize_tests { cmd.arg("--optimize-tests"); } + if !builder.config.docs_minification { + cmd.arg("--disable-minification"); + } if builder.config.rust_randomize_layout { cmd.arg("--rust-randomized-layout"); } diff --git a/src/tools/compiletest/src/cli.rs b/src/tools/compiletest/src/cli.rs index c2bccab30804f..45681eabe03a3 100644 --- a/src/tools/compiletest/src/cli.rs +++ b/src/tools/compiletest/src/cli.rs @@ -257,6 +257,9 @@ struct Args { /// Run tests with optimizations enabled. #[arg(long)] optimize_tests: bool, + /// Pass `--disable-minification` to rustdoc when generating docs for tests. + #[arg(long)] + disable_minification: bool, /// Run tests verbosely, showing all output. #[arg(long)] verbose: bool, @@ -441,6 +444,7 @@ pub(crate) fn parse_config(args: Vec) -> Config { cxxflags: args.cxxflags, default_codegen_backend, diff_command: args.compiletest_diff_tool, + disable_minification: args.disable_minification, edition: args.edition, diff --git a/src/tools/compiletest/src/common.rs b/src/tools/compiletest/src/common.rs index 871431fe0d70f..4123dcf5b600a 100644 --- a/src/tools/compiletest/src/common.rs +++ b/src/tools/compiletest/src/common.rs @@ -553,6 +553,11 @@ pub(crate) struct Config { /// *only* applied to the [`PassFailMode::RunPass`] test crate and not its auxiliaries. pub(crate) optimize_tests: bool, + /// Whether rustdoc should disable CSS/JS minification when generating docs for tests. + /// + /// Forwarded from bootstrap's `build.docs-minification = false`. + pub(crate) disable_minification: bool, + /// Target platform tuple. pub(crate) target: String, diff --git a/src/tools/compiletest/src/directives/tests.rs b/src/tools/compiletest/src/directives/tests.rs index 992ace208a42f..6facfe362c4eb 100644 --- a/src/tools/compiletest/src/directives/tests.rs +++ b/src/tools/compiletest/src/directives/tests.rs @@ -122,6 +122,7 @@ struct ConfigBuilder { rustc_debug_assertions: bool, std_debug_assertions: bool, std_remap_debuginfo: bool, + disable_minification: bool, } impl ConfigBuilder { @@ -200,6 +201,11 @@ impl ConfigBuilder { self } + fn disable_minification(&mut self, is_enabled: bool) -> &mut Self { + self.disable_minification = is_enabled; + self + } + fn build(&mut self) -> Config { let args = &[ "compiletest", @@ -266,6 +272,9 @@ impl ConfigBuilder { if self.std_remap_debuginfo { args.push("--with-std-remap-debuginfo".to_owned()); } + if self.disable_minification { + args.push("--disable-minification".to_owned()); + } args.push("--rustc-path".to_string()); args.push(std::env::var("TEST_RUSTC").expect("must be configured by bootstrap")); @@ -309,6 +318,15 @@ fn should_fail() { assert_eq!(d.should_fail, ShouldFail::Yes); } +#[test] +fn disable_minification_flag() { + let config: Config = cfg().build(); + assert!(!config.disable_minification); + + let config: Config = cfg().disable_minification(true).build(); + assert!(config.disable_minification); +} + #[test] fn revisions() { let config: Config = cfg().build(); diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index 8d40ff093d571..a08a96f0d7be5 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -1049,10 +1049,18 @@ impl<'test> TestCx<'test> { match kind { DocKind::Html => {} DocKind::Json => { - rustdoc.arg("--output-format").arg("json").arg("-Zunstable-options"); + rustdoc.arg("--output-format").arg("json"); } } + // Both JSON output and `--disable-minification` are unstable rustdoc options. + if matches!(kind, DocKind::Json) || self.config.disable_minification { + rustdoc.arg("-Zunstable-options"); + } + if self.config.disable_minification { + rustdoc.arg("--disable-minification"); + } + if let Some(ref linker) = self.config.target_linker { rustdoc.arg(format!("-Clinker={}", linker)); } @@ -1611,6 +1619,15 @@ impl<'test> TestCx<'test> { compiler.arg("-Zwasm-proc-macros"); } + // `--disable-minification` is an unstable rustdoc option. Rustdoc UI tests intentionally + // exercise diagnostics for unstable options, so don't enable them for that suite. + if compiler_kind == CompilerKind::Rustdoc + && self.config.disable_minification + && self.config.mode != TestMode::Ui + { + compiler.arg("-Zunstable-options").arg("--disable-minification"); + } + // Hide libstd sources from ui tests to make sure we generate the stderr // output that users will see. // Without this, we may be producing good diagnostics in-tree but users diff --git a/src/tools/compiletest/src/rustdoc_gui_test.rs b/src/tools/compiletest/src/rustdoc_gui_test.rs index 8965b7b145849..b2d23bcf8ec0a 100644 --- a/src/tools/compiletest/src/rustdoc_gui_test.rs +++ b/src/tools/compiletest/src/rustdoc_gui_test.rs @@ -96,6 +96,7 @@ fn incomplete_config_for_rustdoc_gui_test() -> Config { target_rustcflags: Default::default(), rust_randomized_layout: Default::default(), optimize_tests: Default::default(), + disable_minification: Default::default(), target: Default::default(), host: Default::default(), cdb: Default::default(), diff --git a/tests/ui/const-generics/gca/direct-const-arg-fn-call.rs b/tests/ui/const-generics/gca/direct-const-arg-fn-call.rs new file mode 100644 index 0000000000000..f4a604e2b4700 --- /dev/null +++ b/tests/ui/const-generics/gca/direct-const-arg-fn-call.rs @@ -0,0 +1,84 @@ +//! Regression test for #157152. +//! +//! Under `min_generic_const_args` with `macroless_generic_const_args`, a braced const +//! argument containing an associated-function call (e.g. `FieldName::len()`, as generated +//! by `tracing`'s logging macros as `FieldName<{ FieldName::len(name) }>`) was lowered as +//! a tuple-struct constructor. Lowering the callee's `Self` type `FieldName`, written +//! without its `const N: usize` argument, then produced a spurious "missing generics" +//! error (E0107) plus follow-on errors, which made `tracing` fail to compile in any crate +//! enabling the feature. +//! +//! It should instead report that the call must be wrapped in a `const` block, and +//! the wrapped form must compile. The same holds for any self type that cannot host a +//! tuple-variant constructor (unions, primitives, foreign types), not just structs. +//@ compile-flags: -Znext-solver + +#![feature(min_generic_const_args, macroless_generic_const_args)] +#![feature(generic_const_args)] +#![feature(extern_types)] +#![expect(incomplete_features)] + +struct FieldName([u8; N]); + +impl FieldName<0> { + const fn len() -> usize { + 5 + } + + const fn len_of(name: &str) -> usize { + name.len() + } +} + +// The associated-function call is not a constructor, so the bare braces are +// rejected with a clear diagnostic instead of a spurious "missing generics" error. +fn bad(_: FieldName<{ FieldName::len() }>) {} +//~^ ERROR complex const arguments must be placed inside of a `const` block + +// Wrapping the call in a `const` block makes it an anonymous const and compiles. +fn good(_: FieldName<{ const { FieldName::len() } }>) {} + +// The exact shape from #157152: `tracing`'s macros expand a field name to +// `FieldName::len(stringify!(field))`. Same as `bad` but with a string argument, which +// the diagnostic ignores; the self type is still a bare generic struct. +fn bad_tracing(_: FieldName<{ FieldName::len_of("id") }>) {} +//~^ ERROR complex const arguments must be placed inside of a `const` block + +fn good_tracing(_: FieldName<{ const { FieldName::len_of("id") } }>) {} + +union Tag { + bytes: [u8; N], +} + +impl Tag<0> { + const fn width() -> usize { + 7 + } +} + +// Unions behave exactly like structs: the call is an associated function, not a +// constructor, so the bare braces are rejected the same way. +fn bad_union(_: Tag<{ Tag::width() }>) {} +//~^ ERROR complex const arguments must be placed inside of a `const` block + +fn good_union(_: Tag<{ const { Tag::width() } }>) {} + +// A primitive can't host a constructor either, and has no generics to omit, so it never +// hits the "missing generics" path. No `good_` counterpart: `from_str_radix` returns a +// `Result`, not a `usize`, so the wrapped form can't form a valid const arg. This case +// only checks that the bare form is rejected. +fn bad_prim(_: FieldName<{ u32::from_str_radix("10", 10) }>) {} +//~^ ERROR complex const arguments must be placed inside of a `const` block + +unsafe extern "C" { + type Opaque; +} + +// A foreign type has no constructor and no inherent associated functions. The guard +// rejects it from the self type's resolution alone, before the `foo` segment is resolved. +// Without that, downstream resolution gives an opaque "invalid base path" error (plus an +// E0223) rather than this clear one. +fn bad_foreign(_: FieldName<{ Opaque::foo() }>) {} +//~^ ERROR complex const arguments must be placed inside of a `const` block + +fn main() {} diff --git a/tests/ui/const-generics/gca/direct-const-arg-fn-call.stderr b/tests/ui/const-generics/gca/direct-const-arg-fn-call.stderr new file mode 100644 index 0000000000000..b73cc02dae915 --- /dev/null +++ b/tests/ui/const-generics/gca/direct-const-arg-fn-call.stderr @@ -0,0 +1,32 @@ +error: complex const arguments must be placed inside of a `const` block + --> $DIR/direct-const-arg-fn-call.rs:35:23 + | +LL | fn bad(_: FieldName<{ FieldName::len() }>) {} + | ^^^^^^^^^^^^^^^^ + +error: complex const arguments must be placed inside of a `const` block + --> $DIR/direct-const-arg-fn-call.rs:44:31 + | +LL | fn bad_tracing(_: FieldName<{ FieldName::len_of("id") }>) {} + | ^^^^^^^^^^^^^^^^^^^^^^^ + +error: complex const arguments must be placed inside of a `const` block + --> $DIR/direct-const-arg-fn-call.rs:61:23 + | +LL | fn bad_union(_: Tag<{ Tag::width() }>) {} + | ^^^^^^^^^^^^ + +error: complex const arguments must be placed inside of a `const` block + --> $DIR/direct-const-arg-fn-call.rs:70:28 + | +LL | fn bad_prim(_: FieldName<{ u32::from_str_radix("10", 10) }>) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: complex const arguments must be placed inside of a `const` block + --> $DIR/direct-const-arg-fn-call.rs:81:31 + | +LL | fn bad_foreign(_: FieldName<{ Opaque::foo() }>) {} + | ^^^^^^^^^^^^^ + +error: aborting due to 5 previous errors + diff --git a/tests/ui/consts/too_generic_eval_ice.current.stderr b/tests/ui/consts/too_generic_eval_ice.current.stderr index 02bcaee80154f..061945e344ede 100644 --- a/tests/ui/consts/too_generic_eval_ice.current.stderr +++ b/tests/ui/consts/too_generic_eval_ice.current.stderr @@ -30,15 +30,15 @@ LL | [5; Self::HOST_SIZE] == [6; 0] | = help: the trait `PartialEq<[{integer}; 0]>` is not implemented for `[{integer}; Self::HOST_SIZE]` = help: the following other types implement trait `PartialEq`: + `&[T]` implements `PartialEq>` `&[T]` implements `PartialEq>` `&[T]` implements `PartialEq<[U; N]>` `&[u8; N]` implements `PartialEq` `&[u8; N]` implements `PartialEq` `&[u8]` implements `PartialEq` `&[u8]` implements `PartialEq` - `&mut [T]` implements `PartialEq>` - `&mut [T]` implements `PartialEq<[U; N]>` - and 11 others + `&mut [T]` implements `PartialEq>` + and 13 others error: aborting due to 4 previous errors diff --git a/tests/ui/macros/assert-ne-no-invalid-help-issue-146204.stderr b/tests/ui/macros/assert-ne-no-invalid-help-issue-146204.stderr index 359deee7bee4b..64e40ebdd7bdf 100644 --- a/tests/ui/macros/assert-ne-no-invalid-help-issue-146204.stderr +++ b/tests/ui/macros/assert-ne-no-invalid-help-issue-146204.stderr @@ -6,15 +6,15 @@ LL | assert_ne!(buf, b"----"); | = help: the trait `PartialEq<&[u8; 4]>` is not implemented for `[u8; 4]` = help: the following other types implement trait `PartialEq`: + `&[T]` implements `PartialEq>` `&[T]` implements `PartialEq>` `&[T]` implements `PartialEq<[U; N]>` `&[u8; N]` implements `PartialEq` `&[u8; N]` implements `PartialEq` `&[u8]` implements `PartialEq` `&[u8]` implements `PartialEq` - `&mut [T]` implements `PartialEq>` - `&mut [T]` implements `PartialEq<[U; N]>` - and 11 others + `&mut [T]` implements `PartialEq>` + and 13 others error[E0277]: can't compare `[u8; 4]` with `&[u8; 4]` --> $DIR/assert-ne-no-invalid-help-issue-146204.rs:19:5 @@ -24,15 +24,15 @@ LL | assert_eq!(buf, b"----"); | = help: the trait `PartialEq<&[u8; 4]>` is not implemented for `[u8; 4]` = help: the following other types implement trait `PartialEq`: + `&[T]` implements `PartialEq>` `&[T]` implements `PartialEq>` `&[T]` implements `PartialEq<[U; N]>` `&[u8; N]` implements `PartialEq` `&[u8; N]` implements `PartialEq` `&[u8]` implements `PartialEq` `&[u8]` implements `PartialEq` - `&mut [T]` implements `PartialEq>` - `&mut [T]` implements `PartialEq<[U; N]>` - and 11 others + `&mut [T]` implements `PartialEq>` + and 13 others error[E0277]: can't compare `[u8; 4]` with `&[u8; 4]` --> $DIR/assert-ne-no-invalid-help-issue-146204.rs:5:30