Skip to content

Rollup of 9 pull requests #143350

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 27 commits into from
Jul 3, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
8e329b2
Improve CSS for source code block line numbers
GuillaumeGomez Jun 29, 2025
7c3bdda
Update rustdoc GUI tests
GuillaumeGomez Jun 29, 2025
3c391a6
Automatically derive stage in step metadata where possible
Kobzol Jun 25, 2025
4dfa59d
Add snapshot tests for checking compiler, library and rustc tools
Kobzol Jun 25, 2025
a7c6251
Add compiletest check tests
Kobzol Jul 2, 2025
029304e
Add codegen check tests
Kobzol Jul 2, 2025
c17da9e
Add Rust Analyzer check tests
Kobzol Jul 2, 2025
07a1b82
Add bootstrap tool check test
Kobzol Jul 2, 2025
e6c64df
Add cross-compilation check tests
Kobzol Jul 2, 2025
3f3c498
Apply review comments
Kobzol Jul 2, 2025
626ca82
byte-addresses memory -> byte-addressed memory
hkBst Jul 2, 2025
7d35f2f
Use non-global interner in `test_string_interning` in bootstrap
Kobzol Jul 2, 2025
de1278b
interpret: move the native call preparation logic into Miri
RalfJung Jul 2, 2025
8362508
miri: improve errors for type validity assertion failures
RalfJung Jul 2, 2025
90b2d24
bootstrap: add build.tidy-extra-checks option
lolbinarycat Jun 30, 2025
1d3cbb3
bootstrap: add CONFIG_CHANGE_HISTORY entry for build.tidy-extra-checks
lolbinarycat Jun 30, 2025
0330525
Explicitly handle all nodes in generics_of when computing parent
compiler-errors Jul 1, 2025
8a0d8dd
Make the enum check work for negative discriminants
1c3t3a Jul 1, 2025
6d54983
Rollup merge of #143192 - GuillaumeGomez:code-line-number, r=lolbinary
matthiaskrgr Jul 3, 2025
c3e3f43
Rollup merge of #143251 - lolbinarycat:bootstrap-toml-tidy-extra-chec…
matthiaskrgr Jul 3, 2025
ddda937
Rollup merge of #143273 - 1c3t3a:enum-check-negative, r=SparrowLii
matthiaskrgr Jul 3, 2025
547dc74
Rollup merge of #143292 - compiler-errors:explicit-generic, r=oli-obk
matthiaskrgr Jul 3, 2025
4a3c2fc
Rollup merge of #143316 - Kobzol:bootstrap-check-tests, r=jieyouxu
matthiaskrgr Jul 3, 2025
f000754
Rollup merge of #143321 - hkBst:typo-1, r=compiler-errors
matthiaskrgr Jul 3, 2025
7e600de
Rollup merge of #143324 - RalfJung:native-call-prep, r=oli-obk
matthiaskrgr Jul 3, 2025
5aa7dd8
Rollup merge of #143325 - Kobzol:bootstrap-interner, r=clubby789
matthiaskrgr Jul 3, 2025
bc0262d
Rollup merge of #143327 - RalfJung:miri-type-validity-error, r=oli-obk
matthiaskrgr Jul 3, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions bootstrap.example.toml
Original file line number Diff line number Diff line change
Expand Up @@ -467,6 +467,15 @@
# Whether to use the precompiled stage0 libtest with compiletest.
#build.compiletest-use-stage0-libtest = true

# Default value for the `--extra-checks` flag of tidy.
#
# See `./x test tidy --help` for details.
#
# Note that if any value is manually given to bootstrap such as
# `./x test tidy --extra-checks=js`, this value is ignored.
# Use `--extra-checks=''` to temporarily disable all extra checks.
#build.tidy-extra-checks = ""

# Indicates whether ccache is used when building certain artifacts (e.g. LLVM).
# Set to `true` to use the first `ccache` in PATH, or set an absolute path to use
# a specific version.
Expand Down
44 changes: 41 additions & 3 deletions compiler/rustc_const_eval/src/const_eval/machine.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ use rustc_hir::{self as hir, CRATE_HIR_ID, LangItem};
use rustc_middle::mir::AssertMessage;
use rustc_middle::mir::interpret::ReportedErrorInfo;
use rustc_middle::query::TyCtxtAt;
use rustc_middle::ty::layout::{HasTypingEnv, TyAndLayout};
use rustc_middle::ty::layout::{HasTypingEnv, TyAndLayout, ValidityRequirement};
use rustc_middle::ty::{self, Ty, TyCtxt};
use rustc_middle::{bug, mir};
use rustc_span::{Span, Symbol, sym};
Expand All @@ -23,8 +23,8 @@ use crate::fluent_generated as fluent;
use crate::interpret::{
self, AllocId, AllocInit, AllocRange, ConstAllocation, CtfeProvenance, FnArg, Frame,
GlobalAlloc, ImmTy, InterpCx, InterpResult, OpTy, PlaceTy, Pointer, RangeSet, Scalar,
compile_time_machine, interp_ok, throw_exhaust, throw_inval, throw_ub, throw_ub_custom,
throw_unsup, throw_unsup_format,
compile_time_machine, err_inval, interp_ok, throw_exhaust, throw_inval, throw_ub,
throw_ub_custom, throw_unsup, throw_unsup_format,
};

/// When hitting this many interpreted terminators we emit a deny by default lint
Expand Down Expand Up @@ -462,6 +462,44 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> {
// (We know the value here in the machine of course, but this is the runtime of that code,
// not the optimization stage.)
sym::is_val_statically_known => ecx.write_scalar(Scalar::from_bool(false), dest)?,

// We handle these here since Miri does not want to have them.
sym::assert_inhabited
| sym::assert_zero_valid
| sym::assert_mem_uninitialized_valid => {
let ty = instance.args.type_at(0);
let requirement = ValidityRequirement::from_intrinsic(intrinsic_name).unwrap();

let should_panic = !ecx
.tcx
.check_validity_requirement((requirement, ecx.typing_env().as_query_input(ty)))
.map_err(|_| err_inval!(TooGeneric))?;

if should_panic {
let layout = ecx.layout_of(ty)?;

let msg = match requirement {
// For *all* intrinsics we first check `is_uninhabited` to give a more specific
// error message.
_ if layout.is_uninhabited() => format!(
"aborted execution: attempted to instantiate uninhabited type `{ty}`"
),
ValidityRequirement::Inhabited => bug!("handled earlier"),
ValidityRequirement::Zero => format!(
"aborted execution: attempted to zero-initialize type `{ty}`, which is invalid"
),
ValidityRequirement::UninitMitigated0x01Fill => format!(
"aborted execution: attempted to leave type `{ty}` uninitialized, which is invalid"
),
ValidityRequirement::Uninit => bug!("assert_uninit_valid doesn't exist"),
};

Self::panic_nounwind(ecx, &msg)?;
// Skip the `return_to_block` at the end (we panicked, we do not return).
return interp_ok(None);
}
}

_ => {
// We haven't handled the intrinsic, let's see if we can use a fallback body.
if ecx.tcx.intrinsic(instance.def_id()).unwrap().must_be_overridden {
Expand Down
41 changes: 3 additions & 38 deletions compiler/rustc_const_eval/src/interpret/intrinsics.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ use std::assert_matches::assert_matches;
use rustc_abi::Size;
use rustc_apfloat::ieee::{Double, Half, Quad, Single};
use rustc_middle::mir::{self, BinOp, ConstValue, NonDivergingIntrinsic};
use rustc_middle::ty::layout::{TyAndLayout, ValidityRequirement};
use rustc_middle::ty::layout::TyAndLayout;
use rustc_middle::ty::{Ty, TyCtxt};
use rustc_middle::{bug, ty};
use rustc_span::{Symbol, sym};
Expand All @@ -17,8 +17,8 @@ use super::memory::MemoryKind;
use super::util::ensure_monomorphic_enough;
use super::{
Allocation, CheckInAllocMsg, ConstAllocation, ImmTy, InterpCx, InterpResult, Machine, OpTy,
PlaceTy, Pointer, PointerArithmetic, Provenance, Scalar, err_inval, err_ub_custom,
err_unsup_format, interp_ok, throw_inval, throw_ub_custom, throw_ub_format,
PlaceTy, Pointer, PointerArithmetic, Provenance, Scalar, err_ub_custom, err_unsup_format,
interp_ok, throw_inval, throw_ub_custom, throw_ub_format,
};
use crate::fluent_generated as fluent;

Expand Down Expand Up @@ -372,41 +372,6 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
self.exact_div(&val, &size, dest)?;
}

sym::assert_inhabited
| sym::assert_zero_valid
| sym::assert_mem_uninitialized_valid => {
let ty = instance.args.type_at(0);
let requirement = ValidityRequirement::from_intrinsic(intrinsic_name).unwrap();

let should_panic = !self
.tcx
.check_validity_requirement((requirement, self.typing_env.as_query_input(ty)))
.map_err(|_| err_inval!(TooGeneric))?;

if should_panic {
let layout = self.layout_of(ty)?;

let msg = match requirement {
// For *all* intrinsics we first check `is_uninhabited` to give a more specific
// error message.
_ if layout.is_uninhabited() => format!(
"aborted execution: attempted to instantiate uninhabited type `{ty}`"
),
ValidityRequirement::Inhabited => bug!("handled earlier"),
ValidityRequirement::Zero => format!(
"aborted execution: attempted to zero-initialize type `{ty}`, which is invalid"
),
ValidityRequirement::UninitMitigated0x01Fill => format!(
"aborted execution: attempted to leave type `{ty}` uninitialized, which is invalid"
),
ValidityRequirement::Uninit => bug!("assert_uninit_valid doesn't exist"),
};

M::panic_nounwind(self, &msg)?;
// Skip the `return_to_block` at the end (we panicked, we do not return).
return interp_ok(true);
}
}
sym::simd_insert => {
let index = u64::from(self.read_scalar(&args[1])?.to_u32()?);
let elem = &args[2];
Expand Down
61 changes: 27 additions & 34 deletions compiler/rustc_const_eval/src/interpret/memory.rs
Original file line number Diff line number Diff line change
Expand Up @@ -655,7 +655,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
/// The caller is responsible for calling the access hooks!
///
/// You almost certainly want to use `get_ptr_alloc`/`get_ptr_alloc_mut` instead.
fn get_alloc_raw(
pub fn get_alloc_raw(
&self,
id: AllocId,
) -> InterpResult<'tcx, &Allocation<M::Provenance, M::AllocExtra, M::Bytes>> {
Expand Down Expand Up @@ -757,7 +757,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
///
/// Also returns a ptr to `self.extra` so that the caller can use it in parallel with the
/// allocation.
fn get_alloc_raw_mut(
///
/// You almost certainly want to use `get_ptr_alloc`/`get_ptr_alloc_mut` instead.
pub fn get_alloc_raw_mut(
&mut self,
id: AllocId,
) -> InterpResult<'tcx, (&mut Allocation<M::Provenance, M::AllocExtra, M::Bytes>, &mut M)> {
Expand Down Expand Up @@ -976,47 +978,36 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
interp_ok(())
}

/// Handle the effect an FFI call might have on the state of allocations.
/// This overapproximates the modifications which external code might make to memory:
/// We set all reachable allocations as initialized, mark all reachable provenances as exposed
/// and overwrite them with `Provenance::WILDCARD`.
///
/// The allocations in `ids` are assumed to be already exposed.
pub fn prepare_for_native_call(&mut self, ids: Vec<AllocId>) -> InterpResult<'tcx> {
/// Visit all allocations reachable from the given start set, by recursively traversing the
/// provenance information of those allocations.
pub fn visit_reachable_allocs(
&mut self,
start: Vec<AllocId>,
mut visit: impl FnMut(&mut Self, AllocId, &AllocInfo) -> InterpResult<'tcx>,
) -> InterpResult<'tcx> {
let mut done = FxHashSet::default();
let mut todo = ids;
let mut todo = start;
while let Some(id) = todo.pop() {
if !done.insert(id) {
// We already saw this allocation before, don't process it again.
continue;
}
let info = self.get_alloc_info(id);

// If there is no data behind this pointer, skip this.
if !matches!(info.kind, AllocKind::LiveData) {
continue;
}

// Expose all provenances in this allocation, and add them to `todo`.
let alloc = self.get_alloc_raw(id)?;
for prov in alloc.provenance().provenances() {
M::expose_provenance(self, prov)?;
if let Some(id) = prov.get_alloc_id() {
todo.push(id);
// Recurse, if there is data here.
// Do this *before* invoking the callback, as the callback might mutate the
// allocation and e.g. replace all provenance by wildcards!
if matches!(info.kind, AllocKind::LiveData) {
let alloc = self.get_alloc_raw(id)?;
for prov in alloc.provenance().provenances() {
if let Some(id) = prov.get_alloc_id() {
todo.push(id);
}
}
}
// Also expose the provenance of the interpreter-level allocation, so it can
// be read by FFI. The `black_box` is defensive programming as LLVM likes
// to (incorrectly) optimize away ptr2int casts whose result is unused.
std::hint::black_box(alloc.get_bytes_unchecked_raw().expose_provenance());

// Prepare for possible write from native code if mutable.
if info.mutbl.is_mut() {
self.get_alloc_raw_mut(id)?
.0
.prepare_for_native_write()
.map_err(|e| e.to_interp_error(id))?;
}

// Call the callback.
visit(self, id, &info)?;
}
interp_ok(())
}
Expand Down Expand Up @@ -1073,7 +1064,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
todo.extend(static_roots(self));
while let Some(id) = todo.pop() {
if reachable.insert(id) {
// This is a new allocation, add the allocation it points to `todo`.
// This is a new allocation, add the allocations it points to `todo`.
// We only need to care about `alloc_map` memory here, as entirely unchanged
// global memory cannot point to memory relevant for the leak check.
if let Some((_, alloc)) = self.memory.alloc_map.get(id) {
todo.extend(
alloc.provenance().provenances().filter_map(|prov| prov.get_alloc_id()),
Expand Down
21 changes: 16 additions & 5 deletions compiler/rustc_hir_analysis/src/collect/generics_of.rs
Original file line number Diff line number Diff line change
@@ -1,12 +1,11 @@
use std::assert_matches::assert_matches;
use std::ops::ControlFlow;

use hir::intravisit::{self, Visitor};
use hir::{GenericParamKind, HirId, Node};
use rustc_hir::def::DefKind;
use rustc_hir::def_id::LocalDefId;
use rustc_hir::intravisit::VisitorExt;
use rustc_hir::{self as hir, AmbigArg};
use rustc_hir::intravisit::{self, Visitor, VisitorExt};
use rustc_hir::{self as hir, AmbigArg, GenericParamKind, HirId, Node};
use rustc_middle::span_bug;
use rustc_middle::ty::{self, TyCtxt};
use rustc_session::lint;
use rustc_span::{Span, Symbol, kw};
Expand Down Expand Up @@ -212,7 +211,19 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
// inherit the generics of the item.
Some(parent.to_def_id())
}
_ => None,

// All of these nodes have no parent from which to inherit generics.
Node::Item(_) | Node::ForeignItem(_) => None,

// Params don't really have generics, but we use it when instantiating their value paths.
Node::GenericParam(_) => None,

Node::Synthetic => span_bug!(
tcx.def_span(def_id),
"synthetic HIR should have its `generics_of` explicitly fed"
),

_ => span_bug!(tcx.def_span(def_id), "unhandled node {node:?}"),
};

enum Defaults {
Expand Down
9 changes: 1 addition & 8 deletions compiler/rustc_middle/src/mir/interpret/allocation.rs
Original file line number Diff line number Diff line change
Expand Up @@ -799,7 +799,7 @@ impl<Prov: Provenance, Extra, Bytes: AllocBytes> Allocation<Prov, Extra, Bytes>
/// Initialize all previously uninitialized bytes in the entire allocation, and set
/// provenance of everything to `Wildcard`. Before calling this, make sure all
/// provenance in this allocation is exposed!
pub fn prepare_for_native_write(&mut self) -> AllocResult {
pub fn prepare_for_native_access(&mut self) {
let full_range = AllocRange { start: Size::ZERO, size: Size::from_bytes(self.len()) };
// Overwrite uninitialized bytes with 0, to ensure we don't leak whatever their value happens to be.
for chunk in self.init_mask.range_as_init_chunks(full_range) {
Expand All @@ -814,13 +814,6 @@ impl<Prov: Provenance, Extra, Bytes: AllocBytes> Allocation<Prov, Extra, Bytes>

// Set provenance of all bytes to wildcard.
self.provenance.write_wildcards(self.len());

// Also expose the provenance of the interpreter-level allocation, so it can
// be written by FFI. The `black_box` is defensive programming as LLVM likes
// to (incorrectly) optimize away ptr2int casts whose result is unused.
std::hint::black_box(self.get_bytes_unchecked_raw_mut().expose_provenance());

Ok(())
}

/// Remove all provenance in the given memory range.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ impl<Prov: Provenance> ProvenanceMap<Prov> {
}
}

/// Check if here is ptr-sized provenance at the given index.
/// Check if there is ptr-sized provenance at the given index.
/// Does not mean anything for bytewise provenance! But can be useful as an optimization.
pub fn get_ptr(&self, offset: Size) -> Option<Prov> {
self.ptrs.get(&offset).copied()
Expand Down
30 changes: 26 additions & 4 deletions compiler/rustc_mir_transform/src/check_enums.rs
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,7 @@ enum EnumCheckType<'tcx> {
},
}

#[derive(Debug, Copy, Clone)]
struct TyAndSize<'tcx> {
pub ty: Ty<'tcx>,
pub size: Size,
Expand Down Expand Up @@ -337,7 +338,7 @@ fn insert_direct_enum_check<'tcx>(
let invalid_discr_block_data = BasicBlockData::new(None, false);
let invalid_discr_block = basic_blocks.push(invalid_discr_block_data);
let block_data = &mut basic_blocks[current_block];
let discr = insert_discr_cast_to_u128(
let discr_place = insert_discr_cast_to_u128(
tcx,
local_decls,
block_data,
Expand All @@ -348,13 +349,34 @@ fn insert_direct_enum_check<'tcx>(
source_info,
);

// Mask out the bits of the discriminant type.
let mask = discr.size.unsigned_int_max();
let discr_masked =
local_decls.push(LocalDecl::with_source_info(tcx.types.u128, source_info)).into();
let rvalue = Rvalue::BinaryOp(
BinOp::BitAnd,
Box::new((
Operand::Copy(discr_place),
Operand::Constant(Box::new(ConstOperand {
span: source_info.span,
user_ty: None,
const_: Const::Val(ConstValue::from_u128(mask), tcx.types.u128),
})),
)),
);
block_data
.statements
.push(Statement::new(source_info, StatementKind::Assign(Box::new((discr_masked, rvalue)))));

// Branch based on the discriminant value.
block_data.terminator = Some(Terminator {
source_info,
kind: TerminatorKind::SwitchInt {
discr: Operand::Copy(discr),
discr: Operand::Copy(discr_masked),
targets: SwitchTargets::new(
discriminants.into_iter().map(|discr| (discr, new_block)),
discriminants
.into_iter()
.map(|discr_val| (discr.size.truncate(discr_val), new_block)),
invalid_discr_block,
),
},
Expand All @@ -371,7 +393,7 @@ fn insert_direct_enum_check<'tcx>(
})),
expected: true,
target: new_block,
msg: Box::new(AssertKind::InvalidEnumConstruction(Operand::Copy(discr))),
msg: Box::new(AssertKind::InvalidEnumConstruction(Operand::Copy(discr_masked))),
// This calls panic_invalid_enum_construction, which is #[rustc_nounwind].
// We never want to insert an unwind into unsafe code, because unwinding could
// make a failing UB check turn into much worse UB when we start unwinding.
Expand Down
2 changes: 1 addition & 1 deletion library/core/src/ffi/c_char.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Equivalent to C's `char` type.

[C's `char` type] is completely unlike [Rust's `char` type]; while Rust's type represents a unicode scalar value, C's `char` type is just an ordinary integer. On modern architectures this type will always be either [`i8`] or [`u8`], as they use byte-addresses memory with 8-bit bytes.
[C's `char` type] is completely unlike [Rust's `char` type]; while Rust's type represents a unicode scalar value, C's `char` type is just an ordinary integer. On modern architectures this type will always be either [`i8`] or [`u8`], as they use byte-addressed memory with 8-bit bytes.

C chars are most commonly used to make C strings. Unlike Rust, where the length of a string is included alongside the string, C strings mark the end of a string with the character `'\0'`. See `CStr` for more information.

Expand Down
Loading
Loading