Skip to content

Commit 6268d0a

Browse files
committed
Auto merge of #143350 - matthiaskrgr:rollup-zcuvkve, r=matthiaskrgr
Rollup of 9 pull requests Successful merges: - #143192 (Improve CSS for source code block line numbers) - #143251 (bootstrap: add build.tidy-extra-checks option) - #143273 (Make the enum check work for negative discriminants) - #143292 (Explicitly handle all nodes in `generics_of` when computing parent) - #143316 (Add bootstrap check snapshot tests) - #143321 (byte-addresses memory -> byte-addressed memory) - #143324 (interpret: move the native call preparation logic into Miri) - #143325 (Use non-global interner in `test_string_interning` in bootstrap) - #143327 (miri: improve errors for type validity assertion failures) r? `@ghost` `@rustbot` modify labels: rollup
2 parents 25face9 + bc0262d commit 6268d0a

File tree

39 files changed

+716
-325
lines changed

39 files changed

+716
-325
lines changed

bootstrap.example.toml

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -467,6 +467,15 @@
467467
# Whether to use the precompiled stage0 libtest with compiletest.
468468
#build.compiletest-use-stage0-libtest = true
469469

470+
# Default value for the `--extra-checks` flag of tidy.
471+
#
472+
# See `./x test tidy --help` for details.
473+
#
474+
# Note that if any value is manually given to bootstrap such as
475+
# `./x test tidy --extra-checks=js`, this value is ignored.
476+
# Use `--extra-checks=''` to temporarily disable all extra checks.
477+
#build.tidy-extra-checks = ""
478+
470479
# Indicates whether ccache is used when building certain artifacts (e.g. LLVM).
471480
# Set to `true` to use the first `ccache` in PATH, or set an absolute path to use
472481
# a specific version.

compiler/rustc_const_eval/src/const_eval/machine.rs

Lines changed: 41 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use rustc_hir::{self as hir, CRATE_HIR_ID, LangItem};
1010
use rustc_middle::mir::AssertMessage;
1111
use rustc_middle::mir::interpret::ReportedErrorInfo;
1212
use rustc_middle::query::TyCtxtAt;
13-
use rustc_middle::ty::layout::{HasTypingEnv, TyAndLayout};
13+
use rustc_middle::ty::layout::{HasTypingEnv, TyAndLayout, ValidityRequirement};
1414
use rustc_middle::ty::{self, Ty, TyCtxt};
1515
use rustc_middle::{bug, mir};
1616
use rustc_span::{Span, Symbol, sym};
@@ -23,8 +23,8 @@ use crate::fluent_generated as fluent;
2323
use crate::interpret::{
2424
self, AllocId, AllocInit, AllocRange, ConstAllocation, CtfeProvenance, FnArg, Frame,
2525
GlobalAlloc, ImmTy, InterpCx, InterpResult, OpTy, PlaceTy, Pointer, RangeSet, Scalar,
26-
compile_time_machine, interp_ok, throw_exhaust, throw_inval, throw_ub, throw_ub_custom,
27-
throw_unsup, throw_unsup_format,
26+
compile_time_machine, err_inval, interp_ok, throw_exhaust, throw_inval, throw_ub,
27+
throw_ub_custom, throw_unsup, throw_unsup_format,
2828
};
2929

3030
/// When hitting this many interpreted terminators we emit a deny by default lint
@@ -462,6 +462,44 @@ impl<'tcx> interpret::Machine<'tcx> for CompileTimeMachine<'tcx> {
462462
// (We know the value here in the machine of course, but this is the runtime of that code,
463463
// not the optimization stage.)
464464
sym::is_val_statically_known => ecx.write_scalar(Scalar::from_bool(false), dest)?,
465+
466+
// We handle these here since Miri does not want to have them.
467+
sym::assert_inhabited
468+
| sym::assert_zero_valid
469+
| sym::assert_mem_uninitialized_valid => {
470+
let ty = instance.args.type_at(0);
471+
let requirement = ValidityRequirement::from_intrinsic(intrinsic_name).unwrap();
472+
473+
let should_panic = !ecx
474+
.tcx
475+
.check_validity_requirement((requirement, ecx.typing_env().as_query_input(ty)))
476+
.map_err(|_| err_inval!(TooGeneric))?;
477+
478+
if should_panic {
479+
let layout = ecx.layout_of(ty)?;
480+
481+
let msg = match requirement {
482+
// For *all* intrinsics we first check `is_uninhabited` to give a more specific
483+
// error message.
484+
_ if layout.is_uninhabited() => format!(
485+
"aborted execution: attempted to instantiate uninhabited type `{ty}`"
486+
),
487+
ValidityRequirement::Inhabited => bug!("handled earlier"),
488+
ValidityRequirement::Zero => format!(
489+
"aborted execution: attempted to zero-initialize type `{ty}`, which is invalid"
490+
),
491+
ValidityRequirement::UninitMitigated0x01Fill => format!(
492+
"aborted execution: attempted to leave type `{ty}` uninitialized, which is invalid"
493+
),
494+
ValidityRequirement::Uninit => bug!("assert_uninit_valid doesn't exist"),
495+
};
496+
497+
Self::panic_nounwind(ecx, &msg)?;
498+
// Skip the `return_to_block` at the end (we panicked, we do not return).
499+
return interp_ok(None);
500+
}
501+
}
502+
465503
_ => {
466504
// We haven't handled the intrinsic, let's see if we can use a fallback body.
467505
if ecx.tcx.intrinsic(instance.def_id()).unwrap().must_be_overridden {

compiler/rustc_const_eval/src/interpret/intrinsics.rs

Lines changed: 3 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ use std::assert_matches::assert_matches;
77
use rustc_abi::Size;
88
use rustc_apfloat::ieee::{Double, Half, Quad, Single};
99
use rustc_middle::mir::{self, BinOp, ConstValue, NonDivergingIntrinsic};
10-
use rustc_middle::ty::layout::{TyAndLayout, ValidityRequirement};
10+
use rustc_middle::ty::layout::TyAndLayout;
1111
use rustc_middle::ty::{Ty, TyCtxt};
1212
use rustc_middle::{bug, ty};
1313
use rustc_span::{Symbol, sym};
@@ -17,8 +17,8 @@ use super::memory::MemoryKind;
1717
use super::util::ensure_monomorphic_enough;
1818
use super::{
1919
Allocation, CheckInAllocMsg, ConstAllocation, ImmTy, InterpCx, InterpResult, Machine, OpTy,
20-
PlaceTy, Pointer, PointerArithmetic, Provenance, Scalar, err_inval, err_ub_custom,
21-
err_unsup_format, interp_ok, throw_inval, throw_ub_custom, throw_ub_format,
20+
PlaceTy, Pointer, PointerArithmetic, Provenance, Scalar, err_ub_custom, err_unsup_format,
21+
interp_ok, throw_inval, throw_ub_custom, throw_ub_format,
2222
};
2323
use crate::fluent_generated as fluent;
2424

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

375-
sym::assert_inhabited
376-
| sym::assert_zero_valid
377-
| sym::assert_mem_uninitialized_valid => {
378-
let ty = instance.args.type_at(0);
379-
let requirement = ValidityRequirement::from_intrinsic(intrinsic_name).unwrap();
380-
381-
let should_panic = !self
382-
.tcx
383-
.check_validity_requirement((requirement, self.typing_env.as_query_input(ty)))
384-
.map_err(|_| err_inval!(TooGeneric))?;
385-
386-
if should_panic {
387-
let layout = self.layout_of(ty)?;
388-
389-
let msg = match requirement {
390-
// For *all* intrinsics we first check `is_uninhabited` to give a more specific
391-
// error message.
392-
_ if layout.is_uninhabited() => format!(
393-
"aborted execution: attempted to instantiate uninhabited type `{ty}`"
394-
),
395-
ValidityRequirement::Inhabited => bug!("handled earlier"),
396-
ValidityRequirement::Zero => format!(
397-
"aborted execution: attempted to zero-initialize type `{ty}`, which is invalid"
398-
),
399-
ValidityRequirement::UninitMitigated0x01Fill => format!(
400-
"aborted execution: attempted to leave type `{ty}` uninitialized, which is invalid"
401-
),
402-
ValidityRequirement::Uninit => bug!("assert_uninit_valid doesn't exist"),
403-
};
404-
405-
M::panic_nounwind(self, &msg)?;
406-
// Skip the `return_to_block` at the end (we panicked, we do not return).
407-
return interp_ok(true);
408-
}
409-
}
410375
sym::simd_insert => {
411376
let index = u64::from(self.read_scalar(&args[1])?.to_u32()?);
412377
let elem = &args[2];

compiler/rustc_const_eval/src/interpret/memory.rs

Lines changed: 27 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -655,7 +655,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
655655
/// The caller is responsible for calling the access hooks!
656656
///
657657
/// You almost certainly want to use `get_ptr_alloc`/`get_ptr_alloc_mut` instead.
658-
fn get_alloc_raw(
658+
pub fn get_alloc_raw(
659659
&self,
660660
id: AllocId,
661661
) -> InterpResult<'tcx, &Allocation<M::Provenance, M::AllocExtra, M::Bytes>> {
@@ -757,7 +757,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
757757
///
758758
/// Also returns a ptr to `self.extra` so that the caller can use it in parallel with the
759759
/// allocation.
760-
fn get_alloc_raw_mut(
760+
///
761+
/// You almost certainly want to use `get_ptr_alloc`/`get_ptr_alloc_mut` instead.
762+
pub fn get_alloc_raw_mut(
761763
&mut self,
762764
id: AllocId,
763765
) -> InterpResult<'tcx, (&mut Allocation<M::Provenance, M::AllocExtra, M::Bytes>, &mut M)> {
@@ -976,47 +978,36 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
976978
interp_ok(())
977979
}
978980

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

995-
// If there is no data behind this pointer, skip this.
996-
if !matches!(info.kind, AllocKind::LiveData) {
997-
continue;
998-
}
999-
1000-
// Expose all provenances in this allocation, and add them to `todo`.
1001-
let alloc = self.get_alloc_raw(id)?;
1002-
for prov in alloc.provenance().provenances() {
1003-
M::expose_provenance(self, prov)?;
1004-
if let Some(id) = prov.get_alloc_id() {
1005-
todo.push(id);
997+
// Recurse, if there is data here.
998+
// Do this *before* invoking the callback, as the callback might mutate the
999+
// allocation and e.g. replace all provenance by wildcards!
1000+
if matches!(info.kind, AllocKind::LiveData) {
1001+
let alloc = self.get_alloc_raw(id)?;
1002+
for prov in alloc.provenance().provenances() {
1003+
if let Some(id) = prov.get_alloc_id() {
1004+
todo.push(id);
1005+
}
10061006
}
10071007
}
1008-
// Also expose the provenance of the interpreter-level allocation, so it can
1009-
// be read by FFI. The `black_box` is defensive programming as LLVM likes
1010-
// to (incorrectly) optimize away ptr2int casts whose result is unused.
1011-
std::hint::black_box(alloc.get_bytes_unchecked_raw().expose_provenance());
1012-
1013-
// Prepare for possible write from native code if mutable.
1014-
if info.mutbl.is_mut() {
1015-
self.get_alloc_raw_mut(id)?
1016-
.0
1017-
.prepare_for_native_write()
1018-
.map_err(|e| e.to_interp_error(id))?;
1019-
}
1008+
1009+
// Call the callback.
1010+
visit(self, id, &info)?;
10201011
}
10211012
interp_ok(())
10221013
}
@@ -1073,7 +1064,9 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
10731064
todo.extend(static_roots(self));
10741065
while let Some(id) = todo.pop() {
10751066
if reachable.insert(id) {
1076-
// This is a new allocation, add the allocation it points to `todo`.
1067+
// This is a new allocation, add the allocations it points to `todo`.
1068+
// We only need to care about `alloc_map` memory here, as entirely unchanged
1069+
// global memory cannot point to memory relevant for the leak check.
10771070
if let Some((_, alloc)) = self.memory.alloc_map.get(id) {
10781071
todo.extend(
10791072
alloc.provenance().provenances().filter_map(|prov| prov.get_alloc_id()),

compiler/rustc_hir_analysis/src/collect/generics_of.rs

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
11
use std::assert_matches::assert_matches;
22
use std::ops::ControlFlow;
33

4-
use hir::intravisit::{self, Visitor};
5-
use hir::{GenericParamKind, HirId, Node};
64
use rustc_hir::def::DefKind;
75
use rustc_hir::def_id::LocalDefId;
8-
use rustc_hir::intravisit::VisitorExt;
9-
use rustc_hir::{self as hir, AmbigArg};
6+
use rustc_hir::intravisit::{self, Visitor, VisitorExt};
7+
use rustc_hir::{self as hir, AmbigArg, GenericParamKind, HirId, Node};
8+
use rustc_middle::span_bug;
109
use rustc_middle::ty::{self, TyCtxt};
1110
use rustc_session::lint;
1211
use rustc_span::{Span, Symbol, kw};
@@ -212,7 +211,19 @@ pub(super) fn generics_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::Generics {
212211
// inherit the generics of the item.
213212
Some(parent.to_def_id())
214213
}
215-
_ => None,
214+
215+
// All of these nodes have no parent from which to inherit generics.
216+
Node::Item(_) | Node::ForeignItem(_) => None,
217+
218+
// Params don't really have generics, but we use it when instantiating their value paths.
219+
Node::GenericParam(_) => None,
220+
221+
Node::Synthetic => span_bug!(
222+
tcx.def_span(def_id),
223+
"synthetic HIR should have its `generics_of` explicitly fed"
224+
),
225+
226+
_ => span_bug!(tcx.def_span(def_id), "unhandled node {node:?}"),
216227
};
217228

218229
enum Defaults {

compiler/rustc_middle/src/mir/interpret/allocation.rs

Lines changed: 1 addition & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -799,7 +799,7 @@ impl<Prov: Provenance, Extra, Bytes: AllocBytes> Allocation<Prov, Extra, Bytes>
799799
/// Initialize all previously uninitialized bytes in the entire allocation, and set
800800
/// provenance of everything to `Wildcard`. Before calling this, make sure all
801801
/// provenance in this allocation is exposed!
802-
pub fn prepare_for_native_write(&mut self) -> AllocResult {
802+
pub fn prepare_for_native_access(&mut self) {
803803
let full_range = AllocRange { start: Size::ZERO, size: Size::from_bytes(self.len()) };
804804
// Overwrite uninitialized bytes with 0, to ensure we don't leak whatever their value happens to be.
805805
for chunk in self.init_mask.range_as_init_chunks(full_range) {
@@ -814,13 +814,6 @@ impl<Prov: Provenance, Extra, Bytes: AllocBytes> Allocation<Prov, Extra, Bytes>
814814

815815
// Set provenance of all bytes to wildcard.
816816
self.provenance.write_wildcards(self.len());
817-
818-
// Also expose the provenance of the interpreter-level allocation, so it can
819-
// be written by FFI. The `black_box` is defensive programming as LLVM likes
820-
// to (incorrectly) optimize away ptr2int casts whose result is unused.
821-
std::hint::black_box(self.get_bytes_unchecked_raw_mut().expose_provenance());
822-
823-
Ok(())
824817
}
825818

826819
/// Remove all provenance in the given memory range.

compiler/rustc_middle/src/mir/interpret/allocation/provenance_map.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -120,7 +120,7 @@ impl<Prov: Provenance> ProvenanceMap<Prov> {
120120
}
121121
}
122122

123-
/// Check if here is ptr-sized provenance at the given index.
123+
/// Check if there is ptr-sized provenance at the given index.
124124
/// Does not mean anything for bytewise provenance! But can be useful as an optimization.
125125
pub fn get_ptr(&self, offset: Size) -> Option<Prov> {
126126
self.ptrs.get(&offset).copied()

compiler/rustc_mir_transform/src/check_enums.rs

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,7 @@ enum EnumCheckType<'tcx> {
120120
},
121121
}
122122

123+
#[derive(Debug, Copy, Clone)]
123124
struct TyAndSize<'tcx> {
124125
pub ty: Ty<'tcx>,
125126
pub size: Size,
@@ -337,7 +338,7 @@ fn insert_direct_enum_check<'tcx>(
337338
let invalid_discr_block_data = BasicBlockData::new(None, false);
338339
let invalid_discr_block = basic_blocks.push(invalid_discr_block_data);
339340
let block_data = &mut basic_blocks[current_block];
340-
let discr = insert_discr_cast_to_u128(
341+
let discr_place = insert_discr_cast_to_u128(
341342
tcx,
342343
local_decls,
343344
block_data,
@@ -348,13 +349,34 @@ fn insert_direct_enum_check<'tcx>(
348349
source_info,
349350
);
350351

352+
// Mask out the bits of the discriminant type.
353+
let mask = discr.size.unsigned_int_max();
354+
let discr_masked =
355+
local_decls.push(LocalDecl::with_source_info(tcx.types.u128, source_info)).into();
356+
let rvalue = Rvalue::BinaryOp(
357+
BinOp::BitAnd,
358+
Box::new((
359+
Operand::Copy(discr_place),
360+
Operand::Constant(Box::new(ConstOperand {
361+
span: source_info.span,
362+
user_ty: None,
363+
const_: Const::Val(ConstValue::from_u128(mask), tcx.types.u128),
364+
})),
365+
)),
366+
);
367+
block_data
368+
.statements
369+
.push(Statement::new(source_info, StatementKind::Assign(Box::new((discr_masked, rvalue)))));
370+
351371
// Branch based on the discriminant value.
352372
block_data.terminator = Some(Terminator {
353373
source_info,
354374
kind: TerminatorKind::SwitchInt {
355-
discr: Operand::Copy(discr),
375+
discr: Operand::Copy(discr_masked),
356376
targets: SwitchTargets::new(
357-
discriminants.into_iter().map(|discr| (discr, new_block)),
377+
discriminants
378+
.into_iter()
379+
.map(|discr_val| (discr.size.truncate(discr_val), new_block)),
358380
invalid_discr_block,
359381
),
360382
},
@@ -371,7 +393,7 @@ fn insert_direct_enum_check<'tcx>(
371393
})),
372394
expected: true,
373395
target: new_block,
374-
msg: Box::new(AssertKind::InvalidEnumConstruction(Operand::Copy(discr))),
396+
msg: Box::new(AssertKind::InvalidEnumConstruction(Operand::Copy(discr_masked))),
375397
// This calls panic_invalid_enum_construction, which is #[rustc_nounwind].
376398
// We never want to insert an unwind into unsafe code, because unwinding could
377399
// make a failing UB check turn into much worse UB when we start unwinding.

library/core/src/ffi/c_char.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
Equivalent to C's `char` type.
22

3-
[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.
3+
[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.
44

55
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.
66

0 commit comments

Comments
 (0)