Skip to content

Commit 2db4ff4

Browse files
committed
Auto merge of #127261 - jhpratt:rollup-cpoayvr, r=jhpratt
Rollup of 8 pull requests Successful merges: - #123588 (Stabilize `hint::assert_unchecked`) - #126403 (Actually report normalization-based type errors correctly for alias-relate obligations in new solver) - #126917 (Disable rmake test `inaccessible-temp-dir` on riscv64) - #127115 (unreferenced-used-static: run test everywhere) - #127204 (Stabilize atomic_bool_fetch_not) - #127239 (remove unnecessary ignore-endian-big from stack-overflow-trait-infer …) - #127245 (Add a test for `generic_const_exprs`) - #127246 (Give remote-test-client a longer timeout) r? `@ghost` `@rustbot` modify labels: rollup
2 parents d68fe4e + d801730 commit 2db4ff4

File tree

25 files changed

+266
-104
lines changed

25 files changed

+266
-104
lines changed

compiler/rustc_trait_selection/src/traits/error_reporting/type_err_ctxt_ext.rs

+101-48
Original file line numberDiff line numberDiff line change
@@ -1586,60 +1586,113 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
15861586
}
15871587

15881588
self.probe(|_| {
1589-
let ocx = ObligationCtxt::new(self);
1590-
15911589
// try to find the mismatched types to report the error with.
15921590
//
15931591
// this can fail if the problem was higher-ranked, in which
15941592
// cause I have no idea for a good error message.
15951593
let bound_predicate = predicate.kind();
1596-
let (values, err) = if let ty::PredicateKind::Clause(ty::ClauseKind::Projection(data)) =
1597-
bound_predicate.skip_binder()
1598-
{
1599-
let data = self.instantiate_binder_with_fresh_vars(
1600-
obligation.cause.span,
1601-
infer::BoundRegionConversionTime::HigherRankedType,
1602-
bound_predicate.rebind(data),
1603-
);
1604-
let unnormalized_term = data.projection_term.to_term(self.tcx);
1605-
// FIXME(-Znext-solver): For diagnostic purposes, it would be nice
1606-
// to deeply normalize this type.
1607-
let normalized_term =
1608-
ocx.normalize(&obligation.cause, obligation.param_env, unnormalized_term);
1609-
1610-
debug!(?obligation.cause, ?obligation.param_env);
1611-
1612-
debug!(?normalized_term, data.ty = ?data.term);
1594+
let (values, err) = match bound_predicate.skip_binder() {
1595+
ty::PredicateKind::Clause(ty::ClauseKind::Projection(data)) => {
1596+
let ocx = ObligationCtxt::new(self);
1597+
1598+
let data = self.instantiate_binder_with_fresh_vars(
1599+
obligation.cause.span,
1600+
infer::BoundRegionConversionTime::HigherRankedType,
1601+
bound_predicate.rebind(data),
1602+
);
1603+
let unnormalized_term = data.projection_term.to_term(self.tcx);
1604+
// FIXME(-Znext-solver): For diagnostic purposes, it would be nice
1605+
// to deeply normalize this type.
1606+
let normalized_term =
1607+
ocx.normalize(&obligation.cause, obligation.param_env, unnormalized_term);
1608+
1609+
let is_normalized_term_expected = !matches!(
1610+
obligation.cause.code().peel_derives(),
1611+
ObligationCauseCode::WhereClause(..)
1612+
| ObligationCauseCode::WhereClauseInExpr(..)
1613+
| ObligationCauseCode::Coercion { .. }
1614+
);
16131615

1614-
let is_normalized_term_expected = !matches!(
1615-
obligation.cause.code().peel_derives(),
1616-
|ObligationCauseCode::WhereClause(..)| ObligationCauseCode::WhereClauseInExpr(
1617-
..
1618-
) | ObligationCauseCode::Coercion { .. }
1619-
);
1616+
let (expected, actual) = if is_normalized_term_expected {
1617+
(normalized_term, data.term)
1618+
} else {
1619+
(data.term, normalized_term)
1620+
};
16201621

1621-
let (expected, actual) = if is_normalized_term_expected {
1622-
(normalized_term, data.term)
1623-
} else {
1624-
(data.term, normalized_term)
1625-
};
1622+
// constrain inference variables a bit more to nested obligations from normalize so
1623+
// we can have more helpful errors.
1624+
//
1625+
// we intentionally drop errors from normalization here,
1626+
// since the normalization is just done to improve the error message.
1627+
let _ = ocx.select_where_possible();
16261628

1627-
// constrain inference variables a bit more to nested obligations from normalize so
1628-
// we can have more helpful errors.
1629-
//
1630-
// we intentionally drop errors from normalization here,
1631-
// since the normalization is just done to improve the error message.
1632-
let _ = ocx.select_where_possible();
1629+
if let Err(new_err) =
1630+
ocx.eq(&obligation.cause, obligation.param_env, expected, actual)
1631+
{
1632+
(
1633+
Some((
1634+
data.projection_term,
1635+
is_normalized_term_expected,
1636+
self.resolve_vars_if_possible(normalized_term),
1637+
data.term,
1638+
)),
1639+
new_err,
1640+
)
1641+
} else {
1642+
(None, error.err)
1643+
}
1644+
}
1645+
ty::PredicateKind::AliasRelate(lhs, rhs, _) => {
1646+
let derive_better_type_error =
1647+
|alias_term: ty::AliasTerm<'tcx>, expected_term: ty::Term<'tcx>| {
1648+
let ocx = ObligationCtxt::new(self);
1649+
let normalized_term = match expected_term.unpack() {
1650+
ty::TermKind::Ty(_) => self.next_ty_var(DUMMY_SP).into(),
1651+
ty::TermKind::Const(_) => self.next_const_var(DUMMY_SP).into(),
1652+
};
1653+
ocx.register_obligation(Obligation::new(
1654+
self.tcx,
1655+
ObligationCause::dummy(),
1656+
obligation.param_env,
1657+
ty::PredicateKind::NormalizesTo(ty::NormalizesTo {
1658+
alias: alias_term,
1659+
term: normalized_term,
1660+
}),
1661+
));
1662+
let _ = ocx.select_where_possible();
1663+
if let Err(terr) = ocx.eq(
1664+
&ObligationCause::dummy(),
1665+
obligation.param_env,
1666+
expected_term,
1667+
normalized_term,
1668+
) {
1669+
Some((terr, self.resolve_vars_if_possible(normalized_term)))
1670+
} else {
1671+
None
1672+
}
1673+
};
16331674

1634-
if let Err(new_err) =
1635-
ocx.eq(&obligation.cause, obligation.param_env, expected, actual)
1636-
{
1637-
(Some((data, is_normalized_term_expected, normalized_term, data.term)), new_err)
1638-
} else {
1639-
(None, error.err)
1675+
if let Some(lhs) = lhs.to_alias_term()
1676+
&& let Some((better_type_err, expected_term)) =
1677+
derive_better_type_error(lhs, rhs)
1678+
{
1679+
(
1680+
Some((lhs, true, self.resolve_vars_if_possible(expected_term), rhs)),
1681+
better_type_err,
1682+
)
1683+
} else if let Some(rhs) = rhs.to_alias_term()
1684+
&& let Some((better_type_err, expected_term)) =
1685+
derive_better_type_error(rhs, lhs)
1686+
{
1687+
(
1688+
Some((rhs, true, self.resolve_vars_if_possible(expected_term), lhs)),
1689+
better_type_err,
1690+
)
1691+
} else {
1692+
(None, error.err)
1693+
}
16401694
}
1641-
} else {
1642-
(None, error.err)
1695+
_ => (None, error.err),
16431696
};
16441697

16451698
let msg = values
@@ -1737,15 +1790,15 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
17371790

17381791
fn maybe_detailed_projection_msg(
17391792
&self,
1740-
pred: ty::ProjectionPredicate<'tcx>,
1793+
projection_term: ty::AliasTerm<'tcx>,
17411794
normalized_ty: ty::Term<'tcx>,
17421795
expected_ty: ty::Term<'tcx>,
17431796
) -> Option<String> {
1744-
let trait_def_id = pred.projection_term.trait_def_id(self.tcx);
1745-
let self_ty = pred.projection_term.self_ty();
1797+
let trait_def_id = projection_term.trait_def_id(self.tcx);
1798+
let self_ty = projection_term.self_ty();
17461799

17471800
with_forced_trimmed_paths! {
1748-
if self.tcx.is_lang_item(pred.projection_term.def_id,LangItem::FnOnceOutput) {
1801+
if self.tcx.is_lang_item(projection_term.def_id, LangItem::FnOnceOutput) {
17491802
let fn_kind = self_ty.prefix_string(self.tcx);
17501803
let item = match self_ty.kind() {
17511804
ty::FnDef(def, _) => self.tcx.item_name(*def).to_string(),

library/alloc/src/lib.rs

-1
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,6 @@
127127
#![feature(fmt_internals)]
128128
#![feature(fn_traits)]
129129
#![feature(hasher_prefixfree_extras)]
130-
#![feature(hint_assert_unchecked)]
131130
#![feature(inplace_iteration)]
132131
#![feature(iter_advance_by)]
133132
#![feature(iter_next_chunk)]

library/core/src/hint.rs

+74-23
Original file line numberDiff line numberDiff line change
@@ -111,41 +111,92 @@ pub const unsafe fn unreachable_unchecked() -> ! {
111111

112112
/// Makes a *soundness* promise to the compiler that `cond` holds.
113113
///
114-
/// This may allow the optimizer to simplify things,
115-
/// but it might also make the generated code slower.
116-
/// Either way, calling it will most likely make compilation take longer.
114+
/// This may allow the optimizer to simplify things, but it might also make the generated code
115+
/// slower. Either way, calling it will most likely make compilation take longer.
117116
///
118-
/// This is a situational tool for micro-optimization, and is allowed to do nothing.
119-
/// Any use should come with a repeatable benchmark to show the value
120-
/// and allow removing it later should the optimizer get smarter and no longer need it.
117+
/// You may know this from other places as
118+
/// [`llvm.assume`](https://llvm.org/docs/LangRef.html#llvm-assume-intrinsic) or, in C,
119+
/// [`__builtin_assume`](https://clang.llvm.org/docs/LanguageExtensions.html#builtin-assume).
121120
///
122-
/// The more complicated the condition the less likely this is to be fruitful.
123-
/// For example, `assert_unchecked(foo.is_sorted())` is a complex enough value
124-
/// that the compiler is unlikely to be able to take advantage of it.
121+
/// This promotes a correctness requirement to a soundness requirement. Don't do that without
122+
/// very good reason.
125123
///
126-
/// There's also no need to `assert_unchecked` basic properties of things. For
127-
/// example, the compiler already knows the range of `count_ones`, so there's no
128-
/// benefit to `let n = u32::count_ones(x); assert_unchecked(n <= u32::BITS);`.
124+
/// # Usage
129125
///
130-
/// If ever you're tempted to write `assert_unchecked(false)`, then you're
131-
/// actually looking for [`unreachable_unchecked()`].
126+
/// This is a situational tool for micro-optimization, and is allowed to do nothing. Any use
127+
/// should come with a repeatable benchmark to show the value, with the expectation to drop it
128+
/// later should the optimizer get smarter and no longer need it.
132129
///
133-
/// You may know this from other places
134-
/// as [`llvm.assume`](https://llvm.org/docs/LangRef.html#llvm-assume-intrinsic)
135-
/// or [`__builtin_assume`](https://clang.llvm.org/docs/LanguageExtensions.html#builtin-assume).
130+
/// The more complicated the condition, the less likely this is to be useful. For example,
131+
/// `assert_unchecked(foo.is_sorted())` is a complex enough value that the compiler is unlikely
132+
/// to be able to take advantage of it.
136133
///
137-
/// This promotes a correctness requirement to a soundness requirement.
138-
/// Don't do that without very good reason.
134+
/// There's also no need to `assert_unchecked` basic properties of things. For example, the
135+
/// compiler already knows the range of `count_ones`, so there is no benefit to
136+
/// `let n = u32::count_ones(x); assert_unchecked(n <= u32::BITS);`.
137+
///
138+
/// `assert_unchecked` is logically equivalent to `if !cond { unreachable_unchecked(); }`. If
139+
/// ever you are tempted to write `assert_unchecked(false)`, you should instead use
140+
/// [`unreachable_unchecked()`] directly.
139141
///
140142
/// # Safety
141143
///
142-
/// `cond` must be `true`. It's immediate UB to call this with `false`.
144+
/// `cond` must be `true`. It is immediate UB to call this with `false`.
145+
///
146+
/// # Example
147+
///
148+
/// ```
149+
/// use core::hint;
143150
///
151+
/// /// # Safety
152+
/// ///
153+
/// /// `p` must be nonnull and valid
154+
/// pub unsafe fn next_value(p: *const i32) -> i32 {
155+
/// // SAFETY: caller invariants guarantee that `p` is not null
156+
/// unsafe { hint::assert_unchecked(!p.is_null()) }
157+
///
158+
/// if p.is_null() {
159+
/// return -1;
160+
/// } else {
161+
/// // SAFETY: caller invariants guarantee that `p` is valid
162+
/// unsafe { *p + 1 }
163+
/// }
164+
/// }
165+
/// ```
166+
///
167+
/// Without the `assert_unchecked`, the above function produces the following with optimizations
168+
/// enabled:
169+
///
170+
/// ```asm
171+
/// next_value:
172+
/// test rdi, rdi
173+
/// je .LBB0_1
174+
/// mov eax, dword ptr [rdi]
175+
/// inc eax
176+
/// ret
177+
/// .LBB0_1:
178+
/// mov eax, -1
179+
/// ret
180+
/// ```
181+
///
182+
/// Adding the assertion allows the optimizer to remove the extra check:
183+
///
184+
/// ```asm
185+
/// next_value:
186+
/// mov eax, dword ptr [rdi]
187+
/// inc eax
188+
/// ret
189+
/// ```
190+
///
191+
/// This example is quite unlike anything that would be used in the real world: it is redundant
192+
/// to put an an assertion right next to code that checks the same thing, and dereferencing a
193+
/// pointer already has the builtin assumption that it is nonnull. However, it illustrates the
194+
/// kind of changes the optimizer can make even when the behavior is less obviously related.
195+
#[track_caller]
144196
#[inline(always)]
145197
#[doc(alias = "assume")]
146-
#[track_caller]
147-
#[unstable(feature = "hint_assert_unchecked", issue = "119131")]
148-
#[rustc_const_unstable(feature = "const_hint_assert_unchecked", issue = "119131")]
198+
#[stable(feature = "hint_assert_unchecked", since = "CURRENT_RUSTC_VERSION")]
199+
#[rustc_const_stable(feature = "hint_assert_unchecked", since = "CURRENT_RUSTC_VERSION")]
149200
pub const unsafe fn assert_unchecked(cond: bool) {
150201
// SAFETY: The caller promised `cond` is true.
151202
unsafe {

library/core/src/intrinsics.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -959,7 +959,7 @@ extern "rust-intrinsic" {
959959
/// not be used if the invariant can be discovered by the optimizer on its
960960
/// own, or if it does not enable any significant optimizations.
961961
///
962-
/// This intrinsic does not have a stable counterpart.
962+
/// The stabilized version of this intrinsic is [`core::hint::assert_unchecked`].
963963
#[rustc_const_stable(feature = "const_assume", since = "1.77.0")]
964964
#[rustc_nounwind]
965965
#[unstable(feature = "core_intrinsics", issue = "none")]

library/core/src/lib.rs

-1
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,6 @@
127127
#![feature(const_fmt_arguments_new)]
128128
#![feature(const_hash)]
129129
#![feature(const_heap)]
130-
#![feature(const_hint_assert_unchecked)]
131130
#![feature(const_index_range_slice_index)]
132131
#![feature(const_int_from_str)]
133132
#![feature(const_intrinsic_copy)]

library/core/src/sync/atomic.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -1069,7 +1069,6 @@ impl AtomicBool {
10691069
/// # Examples
10701070
///
10711071
/// ```
1072-
/// #![feature(atomic_bool_fetch_not)]
10731072
/// use std::sync::atomic::{AtomicBool, Ordering};
10741073
///
10751074
/// let foo = AtomicBool::new(true);
@@ -1081,7 +1080,7 @@ impl AtomicBool {
10811080
/// assert_eq!(foo.load(Ordering::SeqCst), true);
10821081
/// ```
10831082
#[inline]
1084-
#[unstable(feature = "atomic_bool_fetch_not", issue = "98485")]
1083+
#[stable(feature = "atomic_bool_fetch_not", since = "CURRENT_RUSTC_VERSION")]
10851084
#[cfg(target_has_atomic = "8")]
10861085
#[cfg_attr(miri, track_caller)] // even without panics, this helps for Miri backtraces
10871086
pub fn fetch_not(&self, order: Ordering) -> bool {

library/std/src/lib.rs

-1
Original file line numberDiff line numberDiff line change
@@ -334,7 +334,6 @@
334334
#![feature(fmt_internals)]
335335
#![feature(hasher_prefixfree_extras)]
336336
#![feature(hashmap_internals)]
337-
#![feature(hint_assert_unchecked)]
338337
#![feature(ip)]
339338
#![feature(maybe_uninit_slice)]
340339
#![feature(maybe_uninit_write_slice)]

src/tools/remote-test-client/src/main.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ fn spawn_emulator(target: &str, server: &Path, tmpdir: &Path, rootfs: Option<Pat
7171

7272
// Wait for the emulator to come online
7373
loop {
74-
let dur = Duration::from_millis(100);
74+
let dur = Duration::from_millis(2000);
7575
if let Ok(mut client) = TcpStream::connect(&device_address) {
7676
t!(client.set_read_timeout(Some(dur)));
7777
t!(client.set_write_timeout(Some(dur)));

tests/run-make/inaccessible-temp-dir/rmake.rs

+6-2
Original file line numberDiff line numberDiff line change
@@ -13,13 +13,18 @@
1313
// use a directory with non-existing parent like `/does-not-exist/output`.
1414
// See https://github.com/rust-lang/rust/issues/66530
1515

16+
//@ ignore-riscv64
17+
// FIXME: The riscv build container runs as root, and can always write
18+
// into `inaccessible/tmp`. Ideally, the riscv64-gnu docker container
19+
// would use a non-root user, but this leads to issues with
20+
// `mkfs.ext4 -d`, as well as mounting a loop device for the rootfs.
1621
//@ ignore-arm
1722
// Reason: linker error on `armhf-gnu`
1823
//@ ignore-windows
1924
// Reason: `set_readonly` has no effect on directories
2025
// and does not prevent modification.
2126

22-
use run_make_support::{fs_wrapper, rustc, target, test_while_readonly};
27+
use run_make_support::{fs_wrapper, rustc, test_while_readonly};
2328

2429
fn main() {
2530
// Create an inaccessible directory.
@@ -28,7 +33,6 @@ fn main() {
2833
// Run rustc with `-Z temps-dir` set to a directory *inside* the inaccessible one,
2934
// so that it can't create `tmp`.
3035
rustc()
31-
.target(target())
3236
.input("program.rs")
3337
.arg("-Ztemps-dir=inaccessible/tmp")
3438
.run_fail()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
#![feature(generic_const_exprs)]
2+
#![feature(adt_const_params)]
3+
#![allow(incomplete_features)]
4+
#![allow(dead_code)]
5+
6+
#[derive(PartialEq, Eq)]
7+
struct U;
8+
9+
struct S<const N: U>()
10+
where
11+
S<{ U }>:;
12+
//~^ ERROR: overflow evaluating the requirement `S<{ U }> well-formed`
13+
14+
fn main() {}

0 commit comments

Comments
 (0)