Auto merge of #141366 - matthiaskrgr:rollup-utvtyy3, r=matthiaskrgr

Rollup of 8 pull requests

Successful merges:

 - #140526 (docs: Specify that common sort functions sort in an ascending direction)
 - #141230 (std: fix doctest and explain for `as_slices` and `as_mut_slices` in `VecDeque`)
 - #141341 (limit impls of `VaArgSafe` to just types that are actually safe)
 - #141347 (incorrectly prefer builtin `dyn` impls :3)
 - #141351 (Move -Zcrate-attr injection to just after crate root parsing)
 - #141356 (lower bodies' params to thir before the body's value)
 - #141357 (`unpretty=thir-tree`: don't require the final expr to be the body's value)
 - #141363 (Document why we allow escaping bound vars in LTA norm)

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2025-05-21 23:02:09 +00:00
commit 5df0f729f5
15 changed files with 207 additions and 85 deletions

View file

@ -45,7 +45,7 @@ use crate::interface::Compiler;
use crate::{errors, limits, proc_macro_decls, util};
pub fn parse<'a>(sess: &'a Session) -> ast::Crate {
let krate = sess
let mut krate = sess
.time("parse_crate", || {
let mut parser = unwrap_or_emit_fatal(match &sess.io.input {
Input::File(file) => new_parser_from_file(&sess.psess, file, None),
@ -64,6 +64,12 @@ pub fn parse<'a>(sess: &'a Session) -> ast::Crate {
input_stats::print_ast_stats(&krate, "PRE EXPANSION AST STATS", "ast-stats-1");
}
rustc_builtin_macros::cmdline_attrs::inject(
&mut krate,
&sess.psess,
&sess.opts.unstable_opts.crate_attr,
);
krate
}
@ -805,17 +811,11 @@ pub static DEFAULT_QUERY_PROVIDERS: LazyLock<Providers> = LazyLock::new(|| {
pub fn create_and_enter_global_ctxt<T, F: for<'tcx> FnOnce(TyCtxt<'tcx>) -> T>(
compiler: &Compiler,
mut krate: rustc_ast::Crate,
krate: rustc_ast::Crate,
f: F,
) -> T {
let sess = &compiler.sess;
rustc_builtin_macros::cmdline_attrs::inject(
&mut krate,
&sess.psess,
&sess.opts.unstable_opts.crate_attr,
);
let pre_configured_attrs = rustc_expand::config::pre_configure_attrs(sess, &krate.attrs);
let crate_name = get_crate_name(sess, &pre_configured_attrs);

View file

@ -450,10 +450,6 @@ fn construct_fn<'tcx>(
let span = tcx.def_span(fn_def);
let fn_id = tcx.local_def_id_to_hir_id(fn_def);
// The representation of thir for `-Zunpretty=thir-tree` relies on
// the entry expression being the last element of `thir.exprs`.
assert_eq!(expr.as_usize(), thir.exprs.len() - 1);
// Figure out what primary body this item has.
let body = tcx.hir_body_owned_by(fn_def);
let span_with_body = tcx.hir_span_with_body(fn_id);

View file

@ -27,8 +27,8 @@ pub(crate) fn thir_body(
if let Some(reported) = cx.typeck_results.tainted_by_errors {
return Err(reported);
}
let expr = cx.mirror_expr(body.value);
// Lower the params before the body's expression so errors from params are shown first.
let owner_id = tcx.local_def_id_to_hir_id(owner_def);
if let Some(fn_decl) = tcx.hir_fn_decl_by_hir_id(owner_id) {
let closure_env_param = cx.closure_env_param(owner_def, owner_id);
@ -48,6 +48,7 @@ pub(crate) fn thir_body(
}
}
let expr = cx.mirror_expr(body.value);
Ok((tcx.alloc_steal_thir(cx.thir), expr))
}

View file

@ -8,10 +8,10 @@ use rustc_span::def_id::LocalDefId;
/// Create a THIR tree for debugging.
pub fn thir_tree(tcx: TyCtxt<'_>, owner_def: LocalDefId) -> String {
match super::cx::thir_body(tcx, owner_def) {
Ok((thir, _)) => {
Ok((thir, expr)) => {
let thir = thir.steal();
let mut printer = ThirPrinter::new(&thir);
printer.print();
printer.print(expr);
printer.into_buffer()
}
Err(_) => "error".into(),
@ -58,7 +58,7 @@ impl<'a, 'tcx> ThirPrinter<'a, 'tcx> {
}
}
fn print(&mut self) {
fn print(&mut self, body_expr: ExprId) {
print_indented!(self, "params: [", 0);
for param in self.thir.params.iter() {
self.print_param(param, 1);
@ -66,8 +66,7 @@ impl<'a, 'tcx> ThirPrinter<'a, 'tcx> {
print_indented!(self, "]", 0);
print_indented!(self, "body:", 0);
let expr = ExprId::from_usize(self.thir.exprs.len() - 1);
self.print_expr(expr, 1);
self.print_expr(body_expr, 1);
}
fn into_buffer(self) -> String {

View file

@ -9,7 +9,7 @@ use rustc_type_ir::{
self as ty, Interner, Movability, TraitPredicate, TypeVisitableExt as _, TypingMode,
Upcast as _, elaborate,
};
use tracing::{instrument, trace};
use tracing::{debug, instrument, trace};
use crate::delegate::SolverDelegate;
use crate::solve::assembly::structural_traits::{self, AsyncCallableRelevantTypes};
@ -17,7 +17,7 @@ use crate::solve::assembly::{self, AllowInferenceConstraints, AssembleCandidates
use crate::solve::inspect::ProbeKind;
use crate::solve::{
BuiltinImplSource, CandidateSource, Certainty, EvalCtxt, Goal, GoalSource, MaybeCause,
NoSolution, ParamEnvSource, QueryResult,
NoSolution, ParamEnvSource, QueryResult, has_only_region_constraints,
};
impl<D, I> assembly::GoalKind<D> for TraitPredicate<I>
@ -1253,6 +1253,45 @@ where
D: SolverDelegate<Interner = I>,
I: Interner,
{
/// FIXME(#57893): For backwards compatability with the old trait solver implementation,
/// we need to handle overlap between builtin and user-written impls for trait objects.
///
/// This overlap is unsound in general and something which we intend to fix separately.
/// To avoid blocking the stabilization of the trait solver, we add this hack to avoid
/// breakage in cases which are *mostly fine*™. Importantly, this preference is strictly
/// weaker than the old behavior.
///
/// We only prefer builtin over user-written impls if there are no inference constraints.
/// Importantly, we also only prefer the builtin impls for trait goals, and not during
/// normalization. This means the only case where this special-case results in exploitable
/// unsoundness should be lifetime dependent user-written impls.
pub(super) fn unsound_prefer_builtin_dyn_impl(&mut self, candidates: &mut Vec<Candidate<I>>) {
match self.typing_mode() {
TypingMode::Coherence => return,
TypingMode::Analysis { .. }
| TypingMode::Borrowck { .. }
| TypingMode::PostBorrowckAnalysis { .. }
| TypingMode::PostAnalysis => {}
}
if candidates
.iter()
.find(|c| {
matches!(c.source, CandidateSource::BuiltinImpl(BuiltinImplSource::Object(_)))
})
.is_some_and(|c| has_only_region_constraints(c.result))
{
candidates.retain(|c| {
if matches!(c.source, CandidateSource::Impl(_)) {
debug!(?c, "unsoundly dropping impl in favor of builtin dyn-candidate");
false
} else {
true
}
});
}
}
#[instrument(level = "debug", skip(self), ret)]
pub(super) fn merge_trait_candidates(
&mut self,
@ -1313,6 +1352,7 @@ where
}
self.filter_specialized_impls(AllowInferenceConstraints::No, &mut candidates);
self.unsound_prefer_builtin_dyn_impl(&mut candidates);
// If there are *only* global where bounds, then make sure to return that this
// is still reported as being proven-via the param-env so that rigid projections

View file

@ -299,12 +299,21 @@ impl<'a, 'b, 'tcx> AssocTypeNormalizer<'a, 'b, 'tcx> {
);
}
// We don't replace bound vars in the generic arguments of the free alias with
// placeholders. This doesn't cause any issues as instantiating parameters with
// bound variables is special-cased to rewrite the debruijn index to be higher
// whenever we fold through a binder.
//
// However, we do replace any escaping bound vars in the resulting goals with
// placeholders as the trait solver does not expect to encounter escaping bound
// vars in obligations.
//
// FIXME(lazy_type_alias): Check how much this actually matters for perf before
// stabilization. This is a bit weird and generally not how we handle binders in
// the compiler so ideally we'd do the same boundvar->placeholder->boundvar dance
// that other kinds of normalization do.
let infcx = self.selcx.infcx;
self.obligations.extend(
// FIXME(BoxyUwU):
// FIXME(lazy_type_alias):
// It seems suspicious to instantiate the predicates with arguments that might be bound vars,
// we might wind up instantiating one of these bound vars underneath a hrtb.
infcx.tcx.predicates_of(free.def_id).instantiate_own(infcx.tcx, free.args).map(
|(mut predicate, span)| {
if free.has_escaping_bound_vars() {

View file

@ -1312,6 +1312,8 @@ impl<T, A: Allocator> VecDeque<T, A> {
///
/// If [`make_contiguous`] was previously called, all elements of the
/// deque will be in the first slice and the second slice will be empty.
/// Otherwise, the exact split point depends on implementation details
/// and is not guaranteed.
///
/// [`make_contiguous`]: VecDeque::make_contiguous
///
@ -1326,12 +1328,18 @@ impl<T, A: Allocator> VecDeque<T, A> {
/// deque.push_back(1);
/// deque.push_back(2);
///
/// assert_eq!(deque.as_slices(), (&[0, 1, 2][..], &[][..]));
/// let expected = [0, 1, 2];
/// let (front, back) = deque.as_slices();
/// assert_eq!(&expected[..front.len()], front);
/// assert_eq!(&expected[front.len()..], back);
///
/// deque.push_front(10);
/// deque.push_front(9);
///
/// assert_eq!(deque.as_slices(), (&[9, 10][..], &[0, 1, 2][..]));
/// let expected = [9, 10, 0, 1, 2];
/// let (front, back) = deque.as_slices();
/// assert_eq!(&expected[..front.len()], front);
/// assert_eq!(&expected[front.len()..], back);
/// ```
#[inline]
#[stable(feature = "deque_extras_15", since = "1.5.0")]
@ -1347,6 +1355,8 @@ impl<T, A: Allocator> VecDeque<T, A> {
///
/// If [`make_contiguous`] was previously called, all elements of the
/// deque will be in the first slice and the second slice will be empty.
/// Otherwise, the exact split point depends on implementation details
/// and is not guaranteed.
///
/// [`make_contiguous`]: VecDeque::make_contiguous
///
@ -1363,9 +1373,22 @@ impl<T, A: Allocator> VecDeque<T, A> {
/// deque.push_front(10);
/// deque.push_front(9);
///
/// deque.as_mut_slices().0[0] = 42;
/// deque.as_mut_slices().1[0] = 24;
/// assert_eq!(deque.as_slices(), (&[42, 10][..], &[24, 1][..]));
/// // Since the split point is not guaranteed, we may need to update
/// // either slice.
/// let mut update_nth = |index: usize, val: u32| {
/// let (front, back) = deque.as_mut_slices();
/// if index > front.len() - 1 {
/// back[index - front.len()] = val;
/// } else {
/// front[index] = val;
/// }
/// };
///
/// update_nth(0, 42);
/// update_nth(2, 24);
///
/// let v: Vec<_> = deque.into();
/// assert_eq!(v, [42, 10, 24, 1]);
/// ```
#[inline]
#[stable(feature = "deque_extras_15", since = "1.5.0")]

View file

@ -69,7 +69,7 @@ use crate::boxed::Box;
use crate::vec::Vec;
impl<T> [T] {
/// Sorts the slice, preserving initial order of equal elements.
/// Sorts the slice in ascending order, preserving initial order of equal elements.
///
/// This sort is stable (i.e., does not reorder equal elements) and *O*(*n* \* log(*n*))
/// worst-case.
@ -137,7 +137,8 @@ impl<T> [T] {
stable_sort(self, T::lt);
}
/// Sorts the slice with a comparison function, preserving initial order of equal elements.
/// Sorts the slice in ascending order with a comparison function, preserving initial order of
/// equal elements.
///
/// This sort is stable (i.e., does not reorder equal elements) and *O*(*n* \* log(*n*))
/// worst-case.
@ -197,7 +198,8 @@ impl<T> [T] {
stable_sort(self, |a, b| compare(a, b) == Less);
}
/// Sorts the slice with a key extraction function, preserving initial order of equal elements.
/// Sorts the slice in ascending order with a key extraction function, preserving initial order
/// of equal elements.
///
/// This sort is stable (i.e., does not reorder equal elements) and *O*(*m* \* *n* \* log(*n*))
/// worst-case, where the key function is *O*(*m*).
@ -252,7 +254,8 @@ impl<T> [T] {
stable_sort(self, |a, b| f(a).lt(&f(b)));
}
/// Sorts the slice with a key extraction function, preserving initial order of equal elements.
/// Sorts the slice in ascending order with a key extraction function, preserving initial order
/// of equal elements.
///
/// This sort is stable (i.e., does not reorder equal elements) and *O*(*m* \* *n* + *n* \*
/// log(*n*)) worst-case, where the key function is *O*(*m*).

View file

@ -28,7 +28,7 @@ pub mod c_str;
issue = "44930",
reason = "the `c_variadic` feature has not been properly tested on all supported platforms"
)]
pub use self::va_list::{VaList, VaListImpl};
pub use self::va_list::{VaArgSafe, VaList, VaListImpl};
#[unstable(
feature = "c_variadic",

View file

@ -223,39 +223,57 @@ impl<'a, 'f: 'a> DerefMut for VaList<'a, 'f> {
}
}
// The VaArgSafe trait needs to be used in public interfaces, however, the trait
// itself must not be allowed to be used outside this module. Allowing users to
// implement the trait for a new type (thereby allowing the va_arg intrinsic to
// be used on a new type) is likely to cause undefined behavior.
//
// FIXME(dlrobertson): In order to use the VaArgSafe trait in a public interface
// but also ensure it cannot be used elsewhere, the trait needs to be public
// within a private module. Once RFC 2145 has been implemented look into
// improving this.
mod sealed_trait {
/// Trait which permits the allowed types to be used with [super::VaListImpl::arg].
pub unsafe trait VaArgSafe {}
mod sealed {
pub trait Sealed {}
impl Sealed for i32 {}
impl Sealed for i64 {}
impl Sealed for isize {}
impl Sealed for u32 {}
impl Sealed for u64 {}
impl Sealed for usize {}
impl Sealed for f64 {}
impl<T> Sealed for *mut T {}
impl<T> Sealed for *const T {}
}
macro_rules! impl_va_arg_safe {
($($t:ty),+) => {
$(
unsafe impl sealed_trait::VaArgSafe for $t {}
)+
}
}
/// Trait which permits the allowed types to be used with [`VaListImpl::arg`].
///
/// # Safety
///
/// This trait must only be implemented for types that C passes as varargs without implicit promotion.
///
/// In C varargs, integers smaller than [`c_int`] and floats smaller than [`c_double`]
/// are implicitly promoted to [`c_int`] and [`c_double`] respectively. Implementing this trait for
/// types that are subject to this promotion rule is invalid.
///
/// [`c_int`]: core::ffi::c_int
/// [`c_double`]: core::ffi::c_double
pub unsafe trait VaArgSafe: sealed::Sealed {}
impl_va_arg_safe! {i8, i16, i32, i64, usize}
impl_va_arg_safe! {u8, u16, u32, u64, isize}
impl_va_arg_safe! {f64}
// i8 and i16 are implicitly promoted to c_int in C, and cannot implement `VaArgSafe`.
unsafe impl VaArgSafe for i32 {}
unsafe impl VaArgSafe for i64 {}
unsafe impl VaArgSafe for isize {}
unsafe impl<T> sealed_trait::VaArgSafe for *mut T {}
unsafe impl<T> sealed_trait::VaArgSafe for *const T {}
// u8 and u16 are implicitly promoted to c_int in C, and cannot implement `VaArgSafe`.
unsafe impl VaArgSafe for u32 {}
unsafe impl VaArgSafe for u64 {}
unsafe impl VaArgSafe for usize {}
// f32 is implicitly promoted to c_double in C, and cannot implement `VaArgSafe`.
unsafe impl VaArgSafe for f64 {}
unsafe impl<T> VaArgSafe for *mut T {}
unsafe impl<T> VaArgSafe for *const T {}
impl<'f> VaListImpl<'f> {
/// Advance to the next arg.
#[inline]
pub unsafe fn arg<T: sealed_trait::VaArgSafe>(&mut self) -> T {
pub unsafe fn arg<T: VaArgSafe>(&mut self) -> T {
// SAFETY: the caller must uphold the safety contract for `va_arg`.
unsafe { va_arg(self) }
}
@ -317,4 +335,4 @@ unsafe fn va_copy<'f>(dest: *mut VaListImpl<'f>, src: &VaListImpl<'f>);
/// argument `ap` points to.
#[rustc_intrinsic]
#[rustc_nounwind]
unsafe fn va_arg<T: sealed_trait::VaArgSafe>(ap: &mut VaListImpl<'_>) -> T;
unsafe fn va_arg<T: VaArgSafe>(ap: &mut VaListImpl<'_>) -> T;

View file

@ -2986,7 +2986,7 @@ impl<T> [T] {
self.binary_search_by(|k| f(k).cmp(b))
}
/// Sorts the slice **without** preserving the initial order of equal elements.
/// Sorts the slice in ascending order **without** preserving the initial order of equal elements.
///
/// This sort is unstable (i.e., may reorder equal elements), in-place (i.e., does not
/// allocate), and *O*(*n* \* log(*n*)) worst-case.
@ -3047,8 +3047,8 @@ impl<T> [T] {
sort::unstable::sort(self, &mut T::lt);
}
/// Sorts the slice with a comparison function, **without** preserving the initial order of
/// equal elements.
/// Sorts the slice in ascending order with a comparison function, **without** preserving the
/// initial order of equal elements.
///
/// This sort is unstable (i.e., may reorder equal elements), in-place (i.e., does not
/// allocate), and *O*(*n* \* log(*n*)) worst-case.
@ -3102,8 +3102,8 @@ impl<T> [T] {
sort::unstable::sort(self, &mut |a, b| compare(a, b) == Ordering::Less);
}
/// Sorts the slice with a key extraction function, **without** preserving the initial order of
/// equal elements.
/// Sorts the slice in ascending order with a key extraction function, **without** preserving
/// the initial order of equal elements.
///
/// This sort is unstable (i.e., may reorder equal elements), in-place (i.e., does not
/// allocate), and *O*(*n* \* log(*n*)) worst-case.

View file

@ -172,7 +172,7 @@ pub use core::ffi::c_void;
all supported platforms",
issue = "44930"
)]
pub use core::ffi::{VaList, VaListImpl};
pub use core::ffi::{VaArgSafe, VaList, VaListImpl};
#[stable(feature = "core_ffi_c", since = "1.64.0")]
pub use core::ffi::{
c_char, c_double, c_float, c_int, c_long, c_longlong, c_schar, c_short, c_uchar, c_uint,

View file

@ -30,9 +30,9 @@ pub unsafe extern "C" fn check_list_0(mut ap: VaList) -> usize {
#[no_mangle]
pub unsafe extern "C" fn check_list_1(mut ap: VaList) -> usize {
continue_if!(ap.arg::<c_int>() == -1);
continue_if!(ap.arg::<c_char>() == 'A' as c_char);
continue_if!(ap.arg::<c_char>() == '4' as c_char);
continue_if!(ap.arg::<c_char>() == ';' as c_char);
continue_if!(ap.arg::<c_int>() == 'A' as c_int);
continue_if!(ap.arg::<c_int>() == '4' as c_int);
continue_if!(ap.arg::<c_int>() == ';' as c_int);
continue_if!(ap.arg::<c_int>() == 0x32);
continue_if!(ap.arg::<c_int>() == 0x10000001);
continue_if!(compare_c_str(ap.arg::<*const c_char>(), "Valid!"));
@ -43,7 +43,7 @@ pub unsafe extern "C" fn check_list_1(mut ap: VaList) -> usize {
pub unsafe extern "C" fn check_list_2(mut ap: VaList) -> usize {
continue_if!(ap.arg::<c_double>().floor() == 3.14f64.floor());
continue_if!(ap.arg::<c_long>() == 12);
continue_if!(ap.arg::<c_char>() == 'a' as c_char);
continue_if!(ap.arg::<c_int>() == 'a' as c_int);
continue_if!(ap.arg::<c_double>().floor() == 6.18f64.floor());
continue_if!(compare_c_str(ap.arg::<*const c_char>(), "Hello"));
continue_if!(ap.arg::<c_int>() == 42);
@ -55,7 +55,7 @@ pub unsafe extern "C" fn check_list_2(mut ap: VaList) -> usize {
pub unsafe extern "C" fn check_list_copy_0(mut ap: VaList) -> usize {
continue_if!(ap.arg::<c_double>().floor() == 6.28f64.floor());
continue_if!(ap.arg::<c_int>() == 16);
continue_if!(ap.arg::<c_char>() == 'A' as c_char);
continue_if!(ap.arg::<c_int>() == 'A' as c_int);
continue_if!(compare_c_str(ap.arg::<*const c_char>(), "Skip Me!"));
ap.with_copy(
|mut ap| {
@ -75,7 +75,7 @@ pub unsafe extern "C" fn check_varargs_0(_: c_int, mut ap: ...) -> usize {
pub unsafe extern "C" fn check_varargs_1(_: c_int, mut ap: ...) -> usize {
continue_if!(ap.arg::<c_double>().floor() == 3.14f64.floor());
continue_if!(ap.arg::<c_long>() == 12);
continue_if!(ap.arg::<c_char>() == 'A' as c_char);
continue_if!(ap.arg::<c_int>() == 'A' as c_int);
continue_if!(ap.arg::<c_longlong>() == 1);
0
}

View file

@ -26,6 +26,19 @@ LL | pub fn test<A: Foo, B: Foo>(arg: EFoo) {
LL | B::X => println!("B::X"),
| ^^^^ `const` depends on a generic parameter
error[E0158]: constant pattern cannot depend on generic parameters
--> $DIR/associated-const-type-parameter-pattern.rs:28:48
|
LL | pub trait Foo {
| -------------
LL | const X: EFoo;
| ------------- constant defined here
...
LL | pub fn test_let_pat<A: Foo, B: Foo>(arg: EFoo, A::X: EFoo) {
| - ^^^^ `const` depends on a generic parameter
| |
| constant depends on this generic parameter
error[E0158]: constant pattern cannot depend on generic parameters
--> $DIR/associated-const-type-parameter-pattern.rs:30:9
|
@ -40,19 +53,6 @@ LL |
LL | let A::X = arg;
| ^^^^ `const` depends on a generic parameter
error[E0158]: constant pattern cannot depend on generic parameters
--> $DIR/associated-const-type-parameter-pattern.rs:28:48
|
LL | pub trait Foo {
| -------------
LL | const X: EFoo;
| ------------- constant defined here
...
LL | pub fn test_let_pat<A: Foo, B: Foo>(arg: EFoo, A::X: EFoo) {
| - ^^^^ `const` depends on a generic parameter
| |
| constant depends on this generic parameter
error: aborting due to 4 previous errors
For more information about this error, try `rustc --explain E0158`.

View file

@ -0,0 +1,33 @@
//@ check-pass
//@ revisions: current next
//@[next] compile-flags: -Znext-solver
//@ ignore-compare-mode-next-solver (explicit revisions)
// A regression test for trait-system-refactor-initiative#183. While
// this concrete instance is likely not practically unsound, the general
// pattern is, see #57893.
use std::any::TypeId;
unsafe trait TidAble<'a>: Tid<'a> {}
trait TidExt<'a>: Tid<'a> {
fn downcast_box(self: Box<Self>) {
loop {}
}
}
impl<'a, X: ?Sized + Tid<'a>> TidExt<'a> for X {}
unsafe trait Tid<'a>: 'a {}
unsafe impl<'a, T: ?Sized + TidAble<'a>> Tid<'a> for T {}
impl<'a> dyn Tid<'a> + 'a {
fn downcast_any_box(self: Box<Self>) {
self.downcast_box();
}
}
unsafe impl<'a> TidAble<'a> for dyn Tid<'a> + 'a {}
fn main() {}