Auto merge of #146650 - matthiaskrgr:rollup-rjrklz9, r=matthiaskrgr

Rollup of 5 pull requests

Successful merges:

 - rust-lang/rust#146442 (Display ?Sized, const, and lifetime parameters in trait item suggestions across a crate boundary)
 - rust-lang/rust#146474 (Improve `core::ascii` coverage)
 - rust-lang/rust#146605 (Bump rustfix 0.8.1 -> 0.8.7)
 - rust-lang/rust#146611 (bootstrap: emit hint if a config key is used in the wrong section)
 - rust-lang/rust#146618 (Do not run ui test if options specific to LLVM are used when another codegen backend is used)

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors
2025-09-16 21:53:05 +00:00
15 changed files with 183 additions and 43 deletions

View File

@@ -4817,9 +4817,9 @@ dependencies = [
[[package]]
name = "rustfix"
version = "0.8.1"
version = "0.8.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "81864b097046da5df3758fdc6e4822bbb70afa06317e8ca45ea1b51cb8c5e5a4"
checksum = "82fa69b198d894d84e23afde8e9ab2af4400b2cba20d6bf2b428a8b01c222c5a"
dependencies = [
"serde",
"serde_json",

View File

@@ -70,6 +70,7 @@ pub mod intrinsic;
mod region;
pub mod wfcheck;
use std::borrow::Cow;
use std::num::NonZero;
pub use check::{check_abi, check_custom_abi};
@@ -86,7 +87,7 @@ use rustc_middle::query::Providers;
use rustc_middle::ty::error::{ExpectedFound, TypeError};
use rustc_middle::ty::print::with_types_for_signature;
use rustc_middle::ty::{
self, GenericArgs, GenericArgsRef, GenericParamDefKind, Ty, TyCtxt, TypingMode,
self, GenericArgs, GenericArgsRef, OutlivesPredicate, Region, Ty, TyCtxt, TypingMode,
};
use rustc_middle::{bug, span_bug};
use rustc_session::parse::feature_err;
@@ -335,6 +336,7 @@ fn bounds_from_generic_predicates<'tcx>(
assoc: ty::AssocItem,
) -> (String, String) {
let mut types: FxIndexMap<Ty<'tcx>, Vec<DefId>> = FxIndexMap::default();
let mut regions: FxIndexMap<Region<'tcx>, Vec<Region<'tcx>>> = FxIndexMap::default();
let mut projections = vec![];
for (predicate, _) in predicates {
debug!("predicate {:?}", predicate);
@@ -351,20 +353,23 @@ fn bounds_from_generic_predicates<'tcx>(
ty::ClauseKind::Projection(projection_pred) => {
projections.push(bound_predicate.rebind(projection_pred));
}
ty::ClauseKind::RegionOutlives(OutlivesPredicate(a, b)) => {
regions.entry(a).or_default().push(b);
}
_ => {}
}
}
let mut where_clauses = vec![];
let generics = tcx.generics_of(assoc.def_id);
let types_str = generics
let params = generics
.own_params
.iter()
.filter(|p| matches!(p.kind, GenericParamDefKind::Type { synthetic: false, .. }))
.map(|p| {
// we just checked that it's a type, so the unwrap can't fail
let ty = tcx.mk_param_from_def(p).as_type().unwrap();
if let Some(bounds) = types.get(&ty) {
.filter(|p| !p.kind.is_synthetic())
.map(|p| match tcx.mk_param_from_def(p).kind() {
ty::GenericArgKind::Type(ty) => {
let bounds =
types.get(&ty).map(Cow::Borrowed).unwrap_or_else(|| Cow::Owned(Vec::new()));
let mut bounds_str = vec![];
for bound in bounds.iter().copied() {
let mut projections_str = vec![];
@@ -377,7 +382,11 @@ fn bounds_from_generic_predicates<'tcx>(
projections_str.push(format!("{} = {}", name, p.term));
}
}
let bound_def_path = tcx.def_path_str(bound);
let bound_def_path = if tcx.is_lang_item(bound, LangItem::MetaSized) {
String::from("?Sized")
} else {
tcx.def_path_str(bound)
};
if projections_str.is_empty() {
where_clauses.push(format!("{}: {}", ty, bound_def_path));
} else {
@@ -393,8 +402,21 @@ fn bounds_from_generic_predicates<'tcx>(
} else {
format!("{}: {}", ty, bounds_str.join(" + "))
}
} else {
ty.to_string()
}
ty::GenericArgKind::Const(ct) => {
format!("const {ct}: {}", tcx.type_of(p.def_id).skip_binder())
}
ty::GenericArgKind::Lifetime(region) => {
if let Some(v) = regions.get(&region)
&& !v.is_empty()
{
format!(
"{region}: {}",
v.into_iter().map(Region::to_string).collect::<Vec<_>>().join(" + ")
)
} else {
region.to_string()
}
}
})
.collect::<Vec<_>>();
@@ -409,7 +431,7 @@ fn bounds_from_generic_predicates<'tcx>(
}
let generics =
if types_str.is_empty() { "".to_string() } else { format!("<{}>", types_str.join(", ")) };
if params.is_empty() { "".to_string() } else { format!("<{}>", params.join(", ")) };
let where_clauses = if where_clauses.is_empty() {
"".to_string()

View File

@@ -505,3 +505,10 @@ fn test_escape_ascii_iter() {
let _ = it.advance_back_by(4);
assert_eq!(it.to_string(), r#"fastpath\xffremainder"#);
}
#[test]
fn test_invalid_u8() {
for c in 128..=255 {
assert_eq!(core::ascii::Char::from_u8(c), None);
}
}

View File

@@ -1853,13 +1853,7 @@ fn load_toml_config(
} else {
toml_path.clone()
});
(
get_toml(&toml_path).unwrap_or_else(|e| {
eprintln!("ERROR: Failed to parse '{}': {e}", toml_path.display());
exit!(2);
}),
path,
)
(get_toml(&toml_path).unwrap_or_else(|e| bad_config(&toml_path, e)), path)
} else {
(TomlConfig::default(), None)
}
@@ -1892,10 +1886,8 @@ fn postprocess_toml(
.unwrap()
.join(include_path);
let included_toml = get_toml(&include_path).unwrap_or_else(|e| {
eprintln!("ERROR: Failed to parse '{}': {e}", include_path.display());
exit!(2);
});
let included_toml =
get_toml(&include_path).unwrap_or_else(|e| bad_config(&include_path, e));
toml.merge(
Some(include_path),
&mut Default::default(),
@@ -2398,3 +2390,98 @@ pub(crate) fn read_file_by_commit<'a>(
git.arg("show").arg(format!("{commit}:{}", file.to_str().unwrap()));
git.run_capture_stdout(dwn_ctx.exec_ctx).stdout()
}
fn bad_config(toml_path: &Path, e: toml::de::Error) -> ! {
eprintln!("ERROR: Failed to parse '{}': {e}", toml_path.display());
let e_s = e.to_string();
if e_s.contains("unknown field")
&& let Some(field_name) = e_s.split("`").nth(1)
&& let sections = find_correct_section_for_field(field_name)
&& !sections.is_empty()
{
if sections.len() == 1 {
match sections[0] {
WouldBeValidFor::TopLevel { is_section } => {
if is_section {
eprintln!(
"hint: section name `{field_name}` used as a key within a section"
);
} else {
eprintln!("hint: try using `{field_name}` as a top level key");
}
}
WouldBeValidFor::Section(section) => {
eprintln!("hint: try moving `{field_name}` to the `{section}` section")
}
}
} else {
eprintln!(
"hint: `{field_name}` would be valid {}",
join_oxford_comma(sections.iter(), "or"),
);
}
}
exit!(2);
}
#[derive(Copy, Clone, Debug)]
enum WouldBeValidFor {
TopLevel { is_section: bool },
Section(&'static str),
}
fn join_oxford_comma(
mut parts: impl ExactSizeIterator<Item = impl std::fmt::Display>,
conj: &str,
) -> String {
use std::fmt::Write;
let mut out = String::new();
assert!(parts.len() > 1);
while let Some(part) = parts.next() {
if parts.len() == 0 {
write!(&mut out, "{conj} {part}")
} else {
write!(&mut out, "{part}, ")
}
.unwrap();
}
out
}
impl std::fmt::Display for WouldBeValidFor {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::TopLevel { .. } => write!(f, "at top level"),
Self::Section(section_name) => write!(f, "in section `{section_name}`"),
}
}
}
fn find_correct_section_for_field(field_name: &str) -> Vec<WouldBeValidFor> {
let sections = ["build", "install", "llvm", "gcc", "rust", "dist"];
sections
.iter()
.map(Some)
.chain([None])
.filter_map(|section_name| {
let dummy_config_str = if let Some(section_name) = section_name {
format!("{section_name}.{field_name} = 0\n")
} else {
format!("{field_name} = 0\n")
};
let is_unknown_field = toml::from_str::<toml::Value>(&dummy_config_str)
.and_then(TomlConfig::deserialize)
.err()
.is_some_and(|e| e.to_string().contains("unknown field"));
if is_unknown_field {
None
} else {
Some(section_name.copied().map(WouldBeValidFor::Section).unwrap_or_else(|| {
WouldBeValidFor::TopLevel { is_section: sections.contains(&field_name) }
}))
}
})
.collect()
}

View File

@@ -203,6 +203,10 @@ impl CodegenBackend {
Self::Llvm => "llvm",
}
}
pub fn is_llvm(self) -> bool {
matches!(self, Self::Llvm)
}
}
/// Configuration for `compiletest` *per invocation*.

View File

@@ -81,8 +81,8 @@ pub(super) fn handle_needs(
},
Need {
name: "needs-enzyme",
condition: config.has_enzyme,
ignore_reason: "ignored when LLVM Enzyme is disabled",
condition: config.has_enzyme && config.default_codegen_backend.is_llvm(),
ignore_reason: "ignored when LLVM Enzyme is disabled or LLVM is not the default codegen backend",
},
Need {
name: "needs-run-enabled",
@@ -161,8 +161,8 @@ pub(super) fn handle_needs(
},
Need {
name: "needs-llvm-zstd",
condition: cache.llvm_zstd,
ignore_reason: "ignored if LLVM wasn't build with zstd for ELF section compression",
condition: cache.llvm_zstd && config.default_codegen_backend.is_llvm(),
ignore_reason: "ignored if LLVM wasn't build with zstd for ELF section compression or LLVM is not the default codegen backend",
},
Need {
name: "needs-rustc-debug-assertions",
@@ -279,7 +279,10 @@ pub(super) fn handle_needs(
// Handled elsewhere.
if name == "needs-llvm-components" {
return IgnoreDecision::Continue;
if config.default_codegen_backend.is_llvm() {
return IgnoreDecision::Continue;
}
return IgnoreDecision::Ignore { reason: "LLVM specific test".into() };
}
let mut found_valid = false;

View File

@@ -8,7 +8,7 @@ trait Get {
}
trait Other {
fn uhoh<U: Get>(&self, foo: U, bar: <Self as Get>::Value) where Self: Sized, Self: Get, Self: Get {}
fn uhoh<U: Get>(&self, foo: U, bar: <Self as Get>::Value) where Self: Sized, Self: Get {}
//~^ ERROR the trait bound `Self: Get` is not satisfied
//~| ERROR the trait bound `Self: Get` is not satisfied
}

View File

@@ -31,7 +31,7 @@ impl<T> ProjectedMyTrait for T
fn require_trait<T: MyTrait>(_: T) {}
fn foo<T : MyTrait + 'static + 'static, U : MyTrait + 'static + 'static>(wrap: Wrapper<'_, Option<T>>, wrap1: Wrapper<'_, Option<U>>) {
fn foo<T : MyTrait + 'static, U : MyTrait + 'static>(wrap: Wrapper<'_, Option<T>>, wrap1: Wrapper<'_, Option<U>>) {
//~^ HELP consider restricting the type parameter to the `'static` lifetime
//~| HELP consider restricting the type parameter to the `'static` lifetime
require_trait(wrap);

View File

@@ -66,7 +66,7 @@ fn asteroids() -> impl FnOnce() -> bool {
// https://github.com/rust-lang/rust/issues/105179
fn r#match() -> i32 {
((match () { () => 1 })) + match () { () => 1 } //~ ERROR expected expression, found `+`
(match () { () => 1 }) + match () { () => 1 } //~ ERROR expected expression, found `+`
//~^ ERROR mismatched types
}
@@ -82,7 +82,7 @@ fn matches() -> bool {
(match () { _ => true }) && match () { _ => true }; //~ ERROR mismatched types
//~^ ERROR expected `;`, found keyword `match`
(match () { _ => true }) && true; //~ ERROR mismatched types
((match () { _ => true })) && true //~ ERROR mismatched types
(match () { _ => true }) && true //~ ERROR mismatched types
//~^ ERROR mismatched types
}
fn main() {}

View File

@@ -4,9 +4,12 @@ extern crate dep;
use dep::*;
struct Local;
impl Trait for Local {}
//~^ ERROR not all trait items implemented
//~| HELP implement the missing item: `fn foo(_: impl Sized) { todo!() }`
//~| HELP implement the missing item: `fn bar<T>(_: impl Sized) { todo!() }`
//~| HELP implement the missing item: `fn bar<T>(_: impl Sized) where Foo<T>: MetaSized { todo!() }`
//~| HELP implement the missing item: `fn baz<const N: usize>() { todo!() }`
//~| HELP implement the missing item: `fn quux<'a: 'b, 'b, T>() where T: ?Sized { todo!() }`
fn main() {}

View File

@@ -1,11 +1,13 @@
error[E0046]: not all trait items implemented, missing: `foo`, `bar`
--> $DIR/apitit-unimplemented-method.rs:7:1
error[E0046]: not all trait items implemented, missing: `foo`, `bar`, `baz`, `quux`
--> $DIR/apitit-unimplemented-method.rs:8:1
|
LL | impl Trait for Local {}
| ^^^^^^^^^^^^^^^^^^^^ missing `foo`, `bar` in implementation
| ^^^^^^^^^^^^^^^^^^^^ missing `foo`, `bar`, `baz`, `quux` in implementation
|
= help: implement the missing item: `fn foo(_: impl Sized) { todo!() }`
= help: implement the missing item: `fn bar<T>(_: impl Sized) { todo!() }`
= help: implement the missing item: `fn bar<T>(_: impl Sized) where Foo<T>: MetaSized { todo!() }`
= help: implement the missing item: `fn baz<const N: usize>() { todo!() }`
= help: implement the missing item: `fn quux<'a: 'b, 'b, T>() where T: ?Sized { todo!() }`
error: aborting due to 1 previous error

View File

@@ -1,4 +1,16 @@
#![feature(sized_hierarchy)]
use std::marker::MetaSized;
pub struct Foo<T> {
inner: T,
}
pub trait Trait {
fn foo(_: impl Sized);
fn bar<T>(_: impl Sized);
fn bar<T>(_: impl Sized)
where
Foo<T>: MetaSized;
fn baz<'a, const N: usize>();
fn quux<'a: 'b, 'b, T: ?Sized>();
}

View File

@@ -8,7 +8,7 @@ pub struct Vector2<T: Debug + Copy + Clone> {
}
#[derive(Debug, Copy, Clone)]
pub struct AABB<K: Debug + std::marker::Copy + std::marker::Copy + std::marker::Copy + std::marker::Copy> {
pub struct AABB<K: Debug + std::marker::Copy> {
pub loc: Vector2<K>, //~ ERROR the trait bound `K: Copy` is not satisfied
//~^ ERROR the trait bound `K: Copy` is not satisfied
//~| ERROR the trait bound `K: Copy` is not satisfied

View File

@@ -8,7 +8,7 @@ pub struct Vector2<T: Debug + Copy + Clone>{
}
#[derive(Debug, Copy, Clone)] //~ ERROR the trait `Copy` cannot be implemented for this type
pub struct AABB<K: Copy + Debug + std::fmt::Debug + std::fmt::Debug + std::fmt::Debug>{
pub struct AABB<K: Copy + Debug + std::fmt::Debug>{
pub loc: Vector2<K>, //~ ERROR `K` doesn't implement `Debug`
//~^ ERROR `K` doesn't implement `Debug`
pub size: Vector2<K> //~ ERROR `K` doesn't implement `Debug`

View File

@@ -10,7 +10,7 @@ struct ConstrainedStruct<X: Copy> {
}
#[allow(dead_code)]
trait InsufficientlyConstrainedGeneric<X=()> where Self: Sized, X: std::marker::Copy, X: std::marker::Copy {
trait InsufficientlyConstrainedGeneric<X=()> where Self: Sized, X: std::marker::Copy {
fn return_the_constrained_type(&self, x: X) -> ConstrainedStruct<X> {
//~^ ERROR the trait bound `X: Copy` is not satisfied
ConstrainedStruct { x }
@@ -20,7 +20,7 @@ trait InsufficientlyConstrainedGeneric<X=()> where Self: Sized, X: std::marker::
// Regression test for #120838
#[allow(dead_code)]
trait InsufficientlyConstrainedGenericWithEmptyWhere<X=()> where Self: Sized, X: std::marker::Copy, X: std::marker::Copy {
trait InsufficientlyConstrainedGenericWithEmptyWhere<X=()> where Self: Sized, X: std::marker::Copy {
fn return_the_constrained_type(&self, x: X) -> ConstrainedStruct<X> {
//~^ ERROR the trait bound `X: Copy` is not satisfied
ConstrainedStruct { x }