Auto merge of #129143 - workingjubilee:rollup-h0hzumu, r=workingjubilee

Rollup of 9 pull requests

Successful merges:

 - #128064 (Improve docs for Waker::noop and LocalWaker::noop)
 - #128922 (rust-analyzer: use in-tree `pattern_analysis` crate)
 - #128965 (Remove `print::Pat` from the printing of `WitnessPat`)
 - #129018 (Migrate `rlib-format-packed-bundled-libs` and `native-link-modifier-bundle` `run-make` tests to rmake)
 - #129037 (Port `run-make/libtest-json` and `run-make/libtest-junit` to rmake)
 - #129078 (`ParamEnvAnd::fully_perform`: we have an `ocx`, use it)
 - #129110 (Add a comment explaining the return type of `Ty::kind`.)
 - #129111 (Port the `sysroot-crates-are-unstable` Python script to rmake)
 - #129135 (crashes: more tests)

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2024-08-16 04:10:41 +00:00
commit 69e36d65f9
29 changed files with 564 additions and 350 deletions

View File

@ -970,6 +970,10 @@ impl<'tcx> rustc_type_ir::inherent::Ty<TyCtxt<'tcx>> for Ty<'tcx> {
/// Type utilities
impl<'tcx> Ty<'tcx> {
// It would be nicer if this returned the value instead of a reference,
// like how `Predicate::kind` and `Region::kind` do. (It would result in
// many fewer subsequent dereferences.) But that gives a small but
// noticeable performance hit. See #126069 for details.
#[inline(always)]
pub fn kind(self) -> &'tcx TyKind<'tcx> {
self.0.0

View File

@ -774,17 +774,16 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> {
}
}
/// Convert to a [`print::Pat`] for diagnostic purposes.
fn hoist_pat_range(&self, range: &IntRange, ty: RevealedTy<'tcx>) -> print::Pat<'tcx> {
use print::{Pat, PatKind};
/// Prints an [`IntRange`] to a string for diagnostic purposes.
fn print_pat_range(&self, range: &IntRange, ty: RevealedTy<'tcx>) -> String {
use MaybeInfiniteInt::*;
let cx = self;
let kind = if matches!((range.lo, range.hi), (NegInfinity, PosInfinity)) {
PatKind::Wild
if matches!((range.lo, range.hi), (NegInfinity, PosInfinity)) {
"_".to_string()
} else if range.is_singleton() {
let lo = cx.hoist_pat_range_bdy(range.lo, ty);
let value = lo.as_finite().unwrap();
PatKind::Constant { value }
value.to_string()
} else {
// We convert to an inclusive range for diagnostics.
let mut end = rustc_hir::RangeEnd::Included;
@ -807,32 +806,24 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> {
range.hi
};
let hi = cx.hoist_pat_range_bdy(hi, ty);
PatKind::Range(Box::new(PatRange { lo, hi, end, ty: ty.inner() }))
};
Pat { ty: ty.inner(), kind }
PatRange { lo, hi, end, ty: ty.inner() }.to_string()
}
}
/// Prints a [`WitnessPat`] to an owned string, for diagnostic purposes.
///
/// This panics for patterns that don't appear in diagnostics, like float ranges.
pub fn print_witness_pat(&self, pat: &WitnessPat<'p, 'tcx>) -> String {
// This works by converting the witness pattern to a `print::Pat`
// and then printing that, but callers don't need to know that.
self.hoist_witness_pat(pat).to_string()
}
/// Convert to a [`print::Pat`] for diagnostic purposes. This panics for patterns that don't
/// appear in diagnostics, like float ranges.
fn hoist_witness_pat(&self, pat: &WitnessPat<'p, 'tcx>) -> print::Pat<'tcx> {
use print::{FieldPat, Pat, PatKind};
let cx = self;
let hoist = |p| Box::new(cx.hoist_witness_pat(p));
let kind = match pat.ctor() {
Bool(b) => PatKind::Constant { value: mir::Const::from_bool(cx.tcx, *b) },
IntRange(range) => return self.hoist_pat_range(range, *pat.ty()),
let print = |p| cx.print_witness_pat(p);
match pat.ctor() {
Bool(b) => b.to_string(),
Str(s) => s.to_string(),
IntRange(range) => return self.print_pat_range(range, *pat.ty()),
Struct if pat.ty().is_box() => {
// Outside of the `alloc` crate, the only way to create a struct pattern
// of type `Box` is to use a `box` pattern via #[feature(box_patterns)].
PatKind::Box { subpattern: hoist(&pat.fields[0]) }
format!("box {}", print(&pat.fields[0]))
}
Struct | Variant(_) | UnionField => {
let enum_info = match *pat.ty().kind() {
@ -847,12 +838,29 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> {
let subpatterns = pat
.iter_fields()
.enumerate()
.map(|(i, pat)| FieldPat { field: FieldIdx::new(i), pattern: hoist(pat) })
.map(|(i, pat)| print::FieldPat {
field: FieldIdx::new(i),
pattern: print(pat),
is_wildcard: would_print_as_wildcard(cx.tcx, pat),
})
.collect::<Vec<_>>();
PatKind::StructLike { enum_info, subpatterns }
let mut s = String::new();
print::write_struct_like(
&mut s,
self.tcx,
pat.ty().inner(),
&enum_info,
&subpatterns,
)
.unwrap();
s
}
Ref => {
let mut s = String::new();
print::write_ref_like(&mut s, pat.ty().inner(), &print(&pat.fields[0])).unwrap();
s
}
Ref => PatKind::Deref { subpattern: hoist(&pat.fields[0]) },
Slice(slice) => {
let (prefix_len, has_dot_dot) = match slice.kind {
SliceKind::FixedLen(len) => (len, false),
@ -879,14 +887,15 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> {
}
}
let prefix = prefix.iter().map(hoist).collect();
let suffix = suffix.iter().map(hoist).collect();
let prefix = prefix.iter().map(print).collect::<Vec<_>>();
let suffix = suffix.iter().map(print).collect::<Vec<_>>();
PatKind::Slice { prefix, has_dot_dot, suffix }
let mut s = String::new();
print::write_slice_like(&mut s, &prefix, has_dot_dot, &suffix).unwrap();
s
}
&Str(value) => PatKind::Constant { value },
Never if self.tcx.features().never_patterns => PatKind::Never,
Never | Wildcard | NonExhaustive | Hidden | PrivateUninhabited => PatKind::Wild,
Never if self.tcx.features().never_patterns => "!".to_string(),
Never | Wildcard | NonExhaustive | Hidden | PrivateUninhabited => "_".to_string(),
Missing { .. } => bug!(
"trying to convert a `Missing` constructor into a `Pat`; this is probably a bug,
`Missing` should have been processed in `apply_constructors`"
@ -894,9 +903,7 @@ impl<'p, 'tcx: 'p> RustcPatCtxt<'p, 'tcx> {
F16Range(..) | F32Range(..) | F64Range(..) | F128Range(..) | Opaque(..) | Or => {
bug!("can't convert to pattern: {:?}", pat)
}
};
Pat { ty: pat.ty().inner(), kind }
}
}
}
@ -972,7 +979,7 @@ impl<'p, 'tcx: 'p> PatCx for RustcPatCtxt<'p, 'tcx> {
overlaps_on: IntRange,
overlaps_with: &[&crate::pat::DeconstructedPat<Self>],
) {
let overlap_as_pat = self.hoist_pat_range(&overlaps_on, *pat.ty());
let overlap_as_pat = self.print_pat_range(&overlaps_on, *pat.ty());
let overlaps: Vec<_> = overlaps_with
.iter()
.map(|pat| pat.data().span)
@ -1012,7 +1019,7 @@ impl<'p, 'tcx: 'p> PatCx for RustcPatCtxt<'p, 'tcx> {
suggested_range.end = rustc_hir::RangeEnd::Included;
suggested_range.to_string()
};
let gap_as_pat = self.hoist_pat_range(&gap, *pat.ty());
let gap_as_pat = self.print_pat_range(&gap, *pat.ty());
if gapped_with.is_empty() {
// If `gapped_with` is empty, `gap == T::MAX`.
self.tcx.emit_node_span_lint(

View File

@ -11,75 +11,16 @@
use std::fmt;
use rustc_middle::thir::PatRange;
use rustc_middle::bug;
use rustc_middle::ty::{self, AdtDef, Ty, TyCtxt};
use rustc_middle::{bug, mir};
use rustc_span::sym;
use rustc_target::abi::{FieldIdx, VariantIdx};
#[derive(Clone, Debug)]
pub(crate) struct FieldPat<'tcx> {
pub(crate) struct FieldPat {
pub(crate) field: FieldIdx,
pub(crate) pattern: Box<Pat<'tcx>>,
}
#[derive(Clone, Debug)]
pub(crate) struct Pat<'tcx> {
pub(crate) ty: Ty<'tcx>,
pub(crate) kind: PatKind<'tcx>,
}
#[derive(Clone, Debug)]
pub(crate) enum PatKind<'tcx> {
Wild,
StructLike {
enum_info: EnumInfo<'tcx>,
subpatterns: Vec<FieldPat<'tcx>>,
},
Box {
subpattern: Box<Pat<'tcx>>,
},
Deref {
subpattern: Box<Pat<'tcx>>,
},
Constant {
value: mir::Const<'tcx>,
},
Range(Box<PatRange<'tcx>>),
Slice {
prefix: Box<[Box<Pat<'tcx>>]>,
/// True if this slice-like pattern should include a `..` between the
/// prefix and suffix.
has_dot_dot: bool,
suffix: Box<[Box<Pat<'tcx>>]>,
},
Never,
}
impl<'tcx> fmt::Display for Pat<'tcx> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.kind {
PatKind::Wild => write!(f, "_"),
PatKind::Never => write!(f, "!"),
PatKind::Box { ref subpattern } => write!(f, "box {subpattern}"),
PatKind::StructLike { ref enum_info, ref subpatterns } => {
ty::tls::with(|tcx| write_struct_like(f, tcx, self.ty, enum_info, subpatterns))
}
PatKind::Deref { ref subpattern } => write_ref_like(f, self.ty, subpattern),
PatKind::Constant { value } => write!(f, "{value}"),
PatKind::Range(ref range) => write!(f, "{range}"),
PatKind::Slice { ref prefix, has_dot_dot, ref suffix } => {
write_slice_like(f, prefix, has_dot_dot, suffix)
}
}
}
pub(crate) pattern: String,
pub(crate) is_wildcard: bool,
}
/// Returns a closure that will return `""` when called the first time,
@ -103,12 +44,12 @@ pub(crate) enum EnumInfo<'tcx> {
NotEnum,
}
fn write_struct_like<'tcx>(
pub(crate) fn write_struct_like<'tcx>(
f: &mut impl fmt::Write,
tcx: TyCtxt<'_>,
ty: Ty<'tcx>,
enum_info: &EnumInfo<'tcx>,
subpatterns: &[FieldPat<'tcx>],
subpatterns: &[FieldPat],
) -> fmt::Result {
let variant_and_name = match *enum_info {
EnumInfo::Enum { adt_def, variant_index } => {
@ -139,12 +80,12 @@ fn write_struct_like<'tcx>(
write!(f, " {{ ")?;
let mut printed = 0;
for p in subpatterns {
if let PatKind::Wild = p.pattern.kind {
for &FieldPat { field, ref pattern, is_wildcard } in subpatterns {
if is_wildcard {
continue;
}
let name = variant.fields[p.field].name;
write!(f, "{}{}: {}", start_or_comma(), name, p.pattern)?;
let field_name = variant.fields[field].name;
write!(f, "{}{field_name}: {pattern}", start_or_comma())?;
printed += 1;
}
@ -184,10 +125,10 @@ fn write_struct_like<'tcx>(
Ok(())
}
fn write_ref_like<'tcx>(
pub(crate) fn write_ref_like<'tcx>(
f: &mut impl fmt::Write,
ty: Ty<'tcx>,
subpattern: &Pat<'tcx>,
subpattern: &str,
) -> fmt::Result {
match ty.kind() {
ty::Ref(_, _, mutbl) => {
@ -198,11 +139,11 @@ fn write_ref_like<'tcx>(
write!(f, "{subpattern}")
}
fn write_slice_like<'tcx>(
pub(crate) fn write_slice_like(
f: &mut impl fmt::Write,
prefix: &[Box<Pat<'tcx>>],
prefix: &[String],
has_dot_dot: bool,
suffix: &[Box<Pat<'tcx>>],
suffix: &[String],
) -> fmt::Result {
let mut start_or_comma = start_or_comma();
write!(f, "[")?;

View File

@ -168,44 +168,12 @@ where
// collecting region constraints via `region_constraints`.
let (mut output, _) = scrape_region_constraints(
infcx,
|_ocx| {
let (output, ei, mut obligations, _) =
|ocx| {
let (output, ei, obligations, _) =
Q::fully_perform_into(self, infcx, &mut region_constraints, span)?;
error_info = ei;
// Typically, instantiating NLL query results does not
// create obligations. However, in some cases there
// are unresolved type variables, and unify them *can*
// create obligations. In that case, we have to go
// fulfill them. We do this via a (recursive) query.
while !obligations.is_empty() {
trace!("{:#?}", obligations);
let mut progress = false;
for obligation in std::mem::take(&mut obligations) {
let obligation = infcx.resolve_vars_if_possible(obligation);
match ProvePredicate::fully_perform_into(
obligation.param_env.and(ProvePredicate::new(obligation.predicate)),
infcx,
&mut region_constraints,
span,
) {
Ok(((), _, new, certainty)) => {
obligations.extend(new);
progress = true;
if let Certainty::Ambiguous = certainty {
obligations.push(obligation);
}
}
Err(_) => obligations.push(obligation),
}
}
if !progress {
infcx.dcx().span_bug(
span,
format!("ambiguity processing {obligations:?} from {self:?}"),
);
}
}
ocx.register_obligations(obligations);
Ok(output)
},
"fully_perform",

View File

@ -530,10 +530,18 @@ impl Waker {
/// Returns a reference to a `Waker` that does nothing when used.
///
// Note! Much of the documentation for this method is duplicated
// in the docs for `LocalWaker::noop`.
// If you edit it, consider editing the other copy too.
//
/// This is mostly useful for writing tests that need a [`Context`] to poll
/// some futures, but are not expecting those futures to wake the waker or
/// do not need to do anything specific if it happens.
///
/// More generally, using `Waker::noop()` to poll a future
/// means discarding the notification of when the future should be polled again.
/// So it should only be used when such a notification will not be needed to make progress.
///
/// If an owned `Waker` is needed, `clone()` this one.
///
/// # Examples
@ -783,12 +791,22 @@ impl LocalWaker {
Self { waker }
}
/// Creates a new `LocalWaker` that does nothing when `wake` is called.
/// Returns a reference to a `LocalWaker` that does nothing when used.
///
// Note! Much of the documentation for this method is duplicated
// in the docs for `Waker::noop`.
// If you edit it, consider editing the other copy too.
//
/// This is mostly useful for writing tests that need a [`Context`] to poll
/// some futures, but are not expecting those futures to wake the waker or
/// do not need to do anything specific if it happens.
///
/// More generally, using `LocalWaker::noop()` to poll a future
/// means discarding the notification of when the future should be polled again,
/// So it should only be used when such a notification will not be needed to make progress.
///
/// If an owned `LocalWaker` is needed, `clone()` this one.
///
/// # Examples
///
/// ```

View File

@ -297,6 +297,12 @@ impl LlvmAr {
self
}
/// Print the table of contents.
pub fn table_of_contents(&mut self) -> &mut Self {
self.cmd.arg("t");
self
}
/// Provide an output, then an input file. Bundled in one function, as llvm-ar has
/// no "--output"-style flag.
pub fn output_input(&mut self, out: impl AsRef<Path>, input: impl AsRef<Path>) -> &mut Self {

View File

@ -15,8 +15,10 @@ extern crate rustc_abi;
#[cfg(not(feature = "in-rust-tree"))]
extern crate ra_ap_rustc_abi as rustc_abi;
// Use the crates.io version unconditionally until the API settles enough that we can switch to
// using the in-tree one.
#[cfg(feature = "in-rust-tree")]
extern crate rustc_pattern_analysis;
#[cfg(not(feature = "in-rust-tree"))]
extern crate ra_ap_rustc_pattern_analysis as rustc_pattern_analysis;
mod builder;

View File

@ -6,13 +6,9 @@ run-make/incr-add-rust-src-component/Makefile
run-make/issue-84395-lto-embed-bitcode/Makefile
run-make/jobserver-error/Makefile
run-make/libs-through-symlinks/Makefile
run-make/libtest-json/Makefile
run-make/libtest-junit/Makefile
run-make/libtest-thread-limit/Makefile
run-make/macos-deployment-target/Makefile
run-make/native-link-modifier-bundle/Makefile
run-make/reproducible-build/Makefile
run-make/rlib-format-packed-bundled-libs/Makefile
run-make/split-debuginfo/Makefile
run-make/symbol-mangling-hashed/Makefile
run-make/translation/Makefile

11
tests/crashes/128695.rs Normal file
View File

@ -0,0 +1,11 @@
//@ known-bug: rust-lang/rust#128695
//@ edition: 2021
use core::pin::{pin, Pin};
fn main() {
let fut = pin!(async {
let async_drop_fut = pin!(core::future::async_drop(async {}));
(async_drop_fut).await;
});
}

25
tests/crashes/128810.rs Normal file
View File

@ -0,0 +1,25 @@
//@ known-bug: rust-lang/rust#128810
#![feature(fn_delegation)]
use std::marker::PhantomData;
pub struct InvariantRef<'a, T: ?Sized>(&'a T, PhantomData<&'a mut &'a T>);
impl<'a> InvariantRef<'a, ()> {
pub const NEW: Self = InvariantRef::new(&());
}
trait Trait {
fn foo(&self) -> u8 { 0 }
fn bar(&self) -> u8 { 1 }
fn meh(&self) -> u8 { 2 }
}
struct Z(u8);
impl Trait for Z {
reuse <u8 as Trait>::{foo, bar, meh} { &const { InvariantRef::<'a>::NEW } }
}
fn main() { }

5
tests/crashes/128848.rs Normal file
View File

@ -0,0 +1,5 @@
//@ known-bug: rust-lang/rust#128848
fn f<T>(a: T, b: T, c: T) {
f.call_once()
}

18
tests/crashes/128870.rs Normal file
View File

@ -0,0 +1,18 @@
//@ known-bug: rust-lang/rust#128870
//@ compile-flags: -Zvalidate-mir
#[repr(packed)]
#[repr(u32)]
enum E {
A,
B,
C,
}
fn main() {
union InvalidTag {
int: u32,
e: E,
}
let _invalid_tag = InvalidTag { int: 4 };
}

16
tests/crashes/129075.rs Normal file
View File

@ -0,0 +1,16 @@
//@ known-bug: rust-lang/rust#129075
//@ compile-flags: -Zvalidate-mir -Zinline-mir=yes
struct Foo<T>([T; 2]);
impl<T: Default + Copy> Default for Foo<T> {
fn default(&mut self) -> Self {
Foo([Default::default(); 2])
}
}
fn field_array() {
let a: i32;
let b;
Foo([a, b]) = Default::default();
}

10
tests/crashes/129095.rs Normal file
View File

@ -0,0 +1,10 @@
//@ known-bug: rust-lang/rust#129095
//@ compile-flags: -Zmir-opt-level=5 -Zvalidate-mir
pub fn function_with_bytes<const BYTES: &'static [u8; 4]>() -> &'static [u8] {
BYTES
}
pub fn main() {
assert_eq!(function_with_bytes::<b"AAAAb">(), &[0x41, 0x41, 0x41, 0x41]);
}

15
tests/crashes/129099.rs Normal file
View File

@ -0,0 +1,15 @@
//@ known-bug: rust-lang/rust#129099
#![feature(type_alias_impl_trait)]
fn dyn_hoops<T: Sized>() -> dyn for<'a> Iterator<Item = impl Captures<'a>> {
loop {}
}
pub fn main() {
type Opaque = impl Sized;
fn define() -> Opaque {
let x: Opaque = dyn_hoops::<()>(0);
x
}
}

10
tests/crashes/129109.rs Normal file
View File

@ -0,0 +1,10 @@
//@ known-bug: rust-lang/rust#129109
//@ compile-flags: -Zmir-opt-level=5 -Zvalidate-mir
extern "C" {
pub static mut symbol: [i8];
}
fn main() {
println!("C", unsafe { &symbol });
}

21
tests/crashes/129127.rs Normal file
View File

@ -0,0 +1,21 @@
//@ known-bug: rust-lang/rust#129127
//@ compile-flags: -Zmir-opt-level=5 -Zvalidate-mir -Zcross-crate-inline-threshold=always
pub struct Rows<'a>();
impl<'a> Iterator for Rows<'a> {
type Item = ();
fn next() -> Option<Self::Item> {
let mut rows = Rows();
rows.map(|row| row).next()
}
}
fn main() {
let mut rows = Rows();
rows.next();
}

View File

@ -1,20 +0,0 @@
# ignore-cross-compile
# needs-unwind
include ../tools.mk
# Test expected libtest's JSON output
OUTPUT_FILE_DEFAULT := $(TMPDIR)/libtest-json-output-default.json
OUTPUT_FILE_STDOUT_SUCCESS := $(TMPDIR)/libtest-json-output-stdout-success.json
all: f.rs validate_json.py output-default.json output-stdout-success.json
$(RUSTC) --test f.rs
RUST_BACKTRACE=0 $(call RUN,f) -Z unstable-options --test-threads=1 --format=json > $(OUTPUT_FILE_DEFAULT) || true
RUST_BACKTRACE=0 $(call RUN,f) -Z unstable-options --test-threads=1 --format=json --show-output > $(OUTPUT_FILE_STDOUT_SUCCESS) || true
cat $(OUTPUT_FILE_DEFAULT) | "$(PYTHON)" validate_json.py
cat $(OUTPUT_FILE_STDOUT_SUCCESS) | "$(PYTHON)" validate_json.py
# Normalize the actual output and compare to expected output file
cat $(OUTPUT_FILE_DEFAULT) | sed 's/"exec_time": [0-9.]*/"exec_time": $$TIME/' | diff output-default.json -
cat $(OUTPUT_FILE_STDOUT_SUCCESS) | sed 's/"exec_time": [0-9.]*/"exec_time": $$TIME/' | diff output-stdout-success.json -

View File

@ -7,4 +7,4 @@
{ "type": "test", "name": "c", "event": "ok" }
{ "type": "test", "event": "started", "name": "d" }
{ "type": "test", "name": "d", "event": "ignored", "message": "msg" }
{ "type": "suite", "event": "failed", "passed": 2, "failed": 1, "ignored": 1, "measured": 0, "filtered_out": 0, "exec_time": $TIME }
{ "type": "suite", "event": "failed", "passed": 2, "failed": 1, "ignored": 1, "measured": 0, "filtered_out": 0, "exec_time": "$EXEC_TIME" }

View File

@ -7,4 +7,4 @@
{ "type": "test", "name": "c", "event": "ok", "stdout": "thread 'c' panicked at f.rs:15:5:\nassertion failed: false\n" }
{ "type": "test", "event": "started", "name": "d" }
{ "type": "test", "name": "d", "event": "ignored", "message": "msg" }
{ "type": "suite", "event": "failed", "passed": 2, "failed": 1, "ignored": 1, "measured": 0, "filtered_out": 0, "exec_time": $TIME }
{ "type": "suite", "event": "failed", "passed": 2, "failed": 1, "ignored": 1, "measured": 0, "filtered_out": 0, "exec_time": "$EXEC_TIME" }

View File

@ -0,0 +1,31 @@
// Check libtest's JSON output against snapshots.
//@ ignore-cross-compile
//@ needs-unwind (test file contains #[should_panic] test)
use run_make_support::{cmd, diff, python_command, rustc};
fn main() {
rustc().arg("--test").input("f.rs").run();
run_tests(&[], "output-default.json");
run_tests(&["--show-output"], "output-stdout-success.json");
}
#[track_caller]
fn run_tests(extra_args: &[&str], expected_file: &str) {
let cmd_out = cmd("./f")
.env("RUST_BACKTRACE", "0")
.args(&["-Zunstable-options", "--test-threads=1", "--format=json"])
.args(extra_args)
.run_fail();
let test_stdout = &cmd_out.stdout_utf8();
python_command().arg("validate_json.py").stdin(test_stdout).run();
diff()
.expected_file(expected_file)
.actual_text("stdout", test_stdout)
.normalize(r#"(?<prefix>"exec_time": )[0-9.]+"#, r#"${prefix}"$$EXEC_TIME""#)
.run();
}

View File

@ -1,20 +0,0 @@
# ignore-cross-compile
# needs-unwind contains should_panic test
include ../tools.mk
# Test expected libtest's junit output
OUTPUT_FILE_DEFAULT := $(TMPDIR)/libtest-junit-output-default.xml
OUTPUT_FILE_STDOUT_SUCCESS := $(TMPDIR)/libtest-junit-output-stdout-success.xml
all: f.rs validate_junit.py output-default.xml output-stdout-success.xml
$(RUSTC) --test f.rs
RUST_BACKTRACE=0 $(call RUN,f) -Z unstable-options --test-threads=1 --format=junit > $(OUTPUT_FILE_DEFAULT) || true
RUST_BACKTRACE=0 $(call RUN,f) -Z unstable-options --test-threads=1 --format=junit --show-output > $(OUTPUT_FILE_STDOUT_SUCCESS) || true
cat $(OUTPUT_FILE_DEFAULT) | "$(PYTHON)" validate_junit.py
cat $(OUTPUT_FILE_STDOUT_SUCCESS) | "$(PYTHON)" validate_junit.py
# Normalize the actual output and compare to expected output file
cat $(OUTPUT_FILE_DEFAULT) | sed 's/time="[0-9.]*"/time="$$TIME"/g' | diff output-default.xml -
cat $(OUTPUT_FILE_STDOUT_SUCCESS) | sed 's/time="[0-9.]*"/time="$$TIME"/g' | diff output-stdout-success.xml -

View File

@ -0,0 +1,31 @@
// Check libtest's JUnit (XML) output against snapshots.
//@ ignore-cross-compile
//@ needs-unwind (test file contains #[should_panic] test)
use run_make_support::{cmd, diff, python_command, rustc};
fn main() {
rustc().arg("--test").input("f.rs").run();
run_tests(&[], "output-default.xml");
run_tests(&["--show-output"], "output-stdout-success.xml");
}
#[track_caller]
fn run_tests(extra_args: &[&str], expected_file: &str) {
let cmd_out = cmd("./f")
.env("RUST_BACKTRACE", "0")
.args(&["-Zunstable-options", "--test-threads=1", "--format=junit"])
.args(extra_args)
.run_fail();
let test_stdout = &cmd_out.stdout_utf8();
python_command().arg("validate_junit.py").stdin(test_stdout).run();
diff()
.expected_file(expected_file)
.actual_text("stdout", test_stdout)
.normalize(r#"\btime="[0-9.]+""#, r#"time="$$TIME""#)
.run();
}

View File

@ -1,38 +0,0 @@
# ignore-cross-compile
# ignore-windows-msvc
include ../tools.mk
# We're using the llvm-nm instead of the system nm to ensure it is compatible
# with the LLVM bitcode generated by rustc.
# Except on Windows where piping/IO redirection under MSYS2 is wonky with llvm-nm.
ifndef IS_WINDOWS
NM = "$(LLVM_BIN_DIR)"/llvm-nm
else
NM = nm
endif
all: $(call NATIVE_STATICLIB,native-staticlib)
# Build a staticlib and a rlib, the `native_func` symbol will be bundled into them
$(RUSTC) bundled.rs --crate-type=staticlib --crate-type=rlib
$(NM) $(TMPDIR)/libbundled.a | $(CGREP) -e "T _*native_func"
$(NM) $(TMPDIR)/libbundled.a | $(CGREP) -e "U _*native_func"
$(NM) $(TMPDIR)/libbundled.rlib | $(CGREP) -e "T _*native_func"
$(NM) $(TMPDIR)/libbundled.rlib | $(CGREP) -e "U _*native_func"
# Build a staticlib and a rlib, the `native_func` symbol will not be bundled into it
$(RUSTC) non-bundled.rs --crate-type=staticlib --crate-type=rlib
$(NM) $(TMPDIR)/libnon_bundled.a | $(CGREP) -ve "T _*native_func"
$(NM) $(TMPDIR)/libnon_bundled.a | $(CGREP) -e "U _*native_func"
$(NM) $(TMPDIR)/libnon_bundled.rlib | $(CGREP) -ve "T _*native_func"
$(NM) $(TMPDIR)/libnon_bundled.rlib | $(CGREP) -e "U _*native_func"
# Build a cdylib, `native-staticlib` will not appear on the linker line because it was bundled previously
# The cdylib will contain the `native_func` symbol in the end
$(RUSTC) cdylib-bundled.rs --crate-type=cdylib --print link-args | $(CGREP) -ve '-l[" ]*native-staticlib'
$(NM) $(call DYLIB,cdylib_bundled) | $(CGREP) -e "[Tt] _*native_func"
# Build a cdylib, `native-staticlib` will appear on the linker line because it was not bundled previously
# The cdylib will contain the `native_func` symbol in the end
$(RUSTC) cdylib-non-bundled.rs --crate-type=cdylib --print link-args | $(CGREP) -e '-l[" ]*native-staticlib'
$(NM) $(call DYLIB,cdylib_non_bundled) | $(CGREP) -e "[Tt] _*native_func"

View File

@ -0,0 +1,90 @@
// This test exercises the `bundle` link argument, which can be turned on or off.
// When building a rlib or staticlib, +bundle means that all object files from the native static
// library will be added to the rlib or staticlib archive, and then used from it during linking of
// the final binary.
// When building a rlib -bundle means that the native static library is registered as a dependency
// of that rlib "by name", and object files from it are included only during linking of the final
// binary, the file search by that name is also performed during final linking.
// When building a staticlib -bundle means that the native static library is simply not included
// into the archive and some higher level build system will need to add it later during linking of
// the final binary.
// This modifier has no effect when building other targets like executables or dynamic libraries.
// The default for this modifier is +bundle.
// See https://github.com/rust-lang/rust/pull/95818
//@ ignore-cross-compile
// Reason: cross-compilation fails to export native symbols
use run_make_support::{
build_native_static_lib, dynamic_lib_name, is_msvc, llvm_nm, rust_lib_name, rustc,
static_lib_name,
};
fn main() {
build_native_static_lib("native-staticlib");
// Build a staticlib and a rlib, the `native_func` symbol will be bundled into them
rustc().input("bundled.rs").crate_type("staticlib").crate_type("rlib").run();
llvm_nm()
.input(static_lib_name("bundled"))
.run()
.assert_stdout_contains_regex("T _*native_func");
llvm_nm()
.input(static_lib_name("bundled"))
.run()
.assert_stdout_contains_regex("U _*native_func");
llvm_nm().input(rust_lib_name("bundled")).run().assert_stdout_contains_regex("T _*native_func");
llvm_nm().input(rust_lib_name("bundled")).run().assert_stdout_contains_regex("U _*native_func");
// Build a staticlib and a rlib, the `native_func` symbol will not be bundled into it
build_native_static_lib("native-staticlib");
rustc().input("non-bundled.rs").crate_type("staticlib").crate_type("rlib").run();
llvm_nm()
.input(static_lib_name("non_bundled"))
.run()
.assert_stdout_not_contains_regex("T _*native_func");
llvm_nm()
.input(static_lib_name("non_bundled"))
.run()
.assert_stdout_contains_regex("U _*native_func");
llvm_nm()
.input(rust_lib_name("non_bundled"))
.run()
.assert_stdout_not_contains_regex("T _*native_func");
llvm_nm()
.input(rust_lib_name("non_bundled"))
.run()
.assert_stdout_contains_regex("U _*native_func");
// This part of the test does not function on Windows MSVC - no symbols are printed.
if !is_msvc() {
// Build a cdylib, `native-staticlib` will not appear on the linker line because it was
// bundled previously. The cdylib will contain the `native_func` symbol in the end.
rustc()
.input("cdylib-bundled.rs")
.crate_type("cdylib")
.print("link-args")
.run()
.assert_stdout_not_contains(r#"-l[" ]*native-staticlib"#);
llvm_nm()
.input(dynamic_lib_name("cdylib_bundled"))
.run()
.assert_stdout_contains_regex("[Tt] _*native_func");
// Build a cdylib, `native-staticlib` will appear on the linker line because it was not
// bundled previously. The cdylib will contain the `native_func` symbol in the end
rustc()
.input("cdylib-non-bundled.rs")
.crate_type("cdylib")
.print("link-args")
.run()
.assert_stdout_contains_regex(r#"-l[" ]*native-staticlib"#);
llvm_nm()
.input(dynamic_lib_name("cdylib_non_bundled"))
.run()
.assert_stdout_contains_regex("[Tt] _*native_func");
}
}

View File

@ -1,39 +0,0 @@
include ../tools.mk
# ignore-cross-compile
# Make sure rlib format with -Zpacked_bundled_libs is correct.
# We're using the llvm-nm instead of the system nm to ensure it is compatible
# with the LLVM bitcode generated by rustc.
# Except on Windows where piping/IO redirection under MSYS2 is wonky with llvm-nm.
ifndef IS_WINDOWS
NM = "$(LLVM_BIN_DIR)"/llvm-nm
else
NM = nm
endif
all: $(call NATIVE_STATICLIB,native_dep_1) $(call NATIVE_STATICLIB,native_dep_2) $(call NATIVE_STATICLIB,native_dep_3)
$(RUSTC) rust_dep_up.rs --crate-type=rlib -Zpacked_bundled_libs
$(NM) $(TMPDIR)/librust_dep_up.rlib | $(CGREP) -e "U.*native_f2"
$(NM) $(TMPDIR)/librust_dep_up.rlib | $(CGREP) -e "U.*native_f3"
$(NM) $(TMPDIR)/librust_dep_up.rlib | $(CGREP) -e "T.*rust_dep_up"
$(AR) t $(TMPDIR)/librust_dep_up.rlib | $(CGREP) "native_dep_2"
$(AR) t $(TMPDIR)/librust_dep_up.rlib | $(CGREP) "native_dep_3"
$(RUSTC) rust_dep_local.rs --extern rlib=$(TMPDIR)/librust_dep_up.rlib -Zpacked_bundled_libs --crate-type=rlib
$(NM) $(TMPDIR)/librust_dep_local.rlib | $(CGREP) -e "U.*native_f1"
$(NM) $(TMPDIR)/librust_dep_local.rlib | $(CGREP) -e "T.*rust_dep_local"
$(AR) t $(TMPDIR)/librust_dep_local.rlib | $(CGREP) "native_dep_1"
# Make sure compiler doesn't use files, that it shouldn't know about.
rm $(TMPDIR)/*native_dep_*
$(RUSTC) main.rs --extern lib=$(TMPDIR)/librust_dep_local.rlib -o $(TMPDIR)/main.exe -Zpacked_bundled_libs --print link-args | $(CGREP) -e "native_dep_1.*native_dep_2.*native_dep_3"
ifndef IS_MSVC
$(NM) $(TMPDIR)/main.exe | $(CGREP) -e "T.*native_f1"
$(NM) $(TMPDIR)/main.exe | $(CGREP) -e "T.*native_f2"
$(NM) $(TMPDIR)/main.exe | $(CGREP) -e "T.*native_f3"
$(NM) $(TMPDIR)/main.exe | $(CGREP) -e "T.*rust_dep_local"
$(NM) $(TMPDIR)/main.exe | $(CGREP) -e "T.*rust_dep_up"
endif

View File

@ -0,0 +1,84 @@
// `-Z packed_bundled_libs` is an unstable rustc flag that makes the compiler
// only require a native library and no supplementary object files to compile.
// Output files compiled with this flag should still contain all expected symbols -
// that is what this test checks.
// See https://github.com/rust-lang/rust/pull/100101
//@ ignore-cross-compile
// Reason: cross-compilation fails to export native symbols
use run_make_support::{
bin_name, build_native_static_lib, cwd, filename_contains, is_msvc, llvm_ar, llvm_nm, rfs,
rust_lib_name, rustc, shallow_find_files,
};
fn main() {
build_native_static_lib("native_dep_1");
build_native_static_lib("native_dep_2");
build_native_static_lib("native_dep_3");
rustc().input("rust_dep_up.rs").crate_type("rlib").arg("-Zpacked_bundled_libs").run();
llvm_nm()
.input(rust_lib_name("rust_dep_up"))
.run()
.assert_stdout_contains_regex("U.*native_f2");
llvm_nm()
.input(rust_lib_name("rust_dep_up"))
.run()
.assert_stdout_contains_regex("U.*native_f3");
llvm_nm()
.input(rust_lib_name("rust_dep_up"))
.run()
.assert_stdout_contains_regex("T.*rust_dep_up");
llvm_ar()
.table_of_contents()
.arg(rust_lib_name("rust_dep_up"))
.run()
.assert_stdout_contains("native_dep_2");
llvm_ar()
.table_of_contents()
.arg(rust_lib_name("rust_dep_up"))
.run()
.assert_stdout_contains("native_dep_3");
rustc()
.input("rust_dep_local.rs")
.extern_("rlib", rust_lib_name("rust_dep_up"))
.arg("-Zpacked_bundled_libs")
.crate_type("rlib")
.run();
llvm_nm()
.input(rust_lib_name("rust_dep_local"))
.run()
.assert_stdout_contains_regex("U.*native_f1");
llvm_nm()
.input(rust_lib_name("rust_dep_local"))
.run()
.assert_stdout_contains_regex("T.*rust_dep_local");
llvm_ar()
.table_of_contents()
.arg(rust_lib_name("rust_dep_local"))
.run()
.assert_stdout_contains("native_dep_1");
// Ensure the compiler will not use files it should not know about.
for file in shallow_find_files(cwd(), |path| filename_contains(path, "native_dep_")) {
rfs::remove_file(file);
}
rustc()
.input("main.rs")
.extern_("lib", rust_lib_name("rust_dep_local"))
.output(bin_name("main"))
.arg("-Zpacked_bundled_libs")
.print("link-args")
.run()
.assert_stdout_contains_regex("native_dep_1.*native_dep_2.*native_dep_3");
// The binary "main" will not contain any symbols on MSVC.
if !is_msvc() {
llvm_nm().input(bin_name("main")).run().assert_stdout_contains_regex("T.*native_f1");
llvm_nm().input(bin_name("main")).run().assert_stdout_contains_regex("T.*native_f2");
llvm_nm().input(bin_name("main")).run().assert_stdout_contains_regex("T.*native_f3");
llvm_nm().input(bin_name("main")).run().assert_stdout_contains_regex("T.*rust_dep_local");
llvm_nm().input(bin_name("main")).run().assert_stdout_contains_regex("T.*rust_dep_up");
}
}

View File

@ -1,5 +1,102 @@
use run_make_support::python_command;
// Check that crates in the sysroot are treated as unstable, unless they are
// on a list of known-stable sysroot crates.
use std::path::{Path, PathBuf};
use std::str;
use run_make_support::{rfs, rustc, target};
fn is_stable_crate(name: &str) -> bool {
matches!(name, "std" | "alloc" | "core" | "proc_macro")
}
fn main() {
python_command().arg("test.py").run();
for cr in get_unstable_sysroot_crates() {
check_crate_is_unstable(&cr);
}
println!("Done");
}
#[derive(Debug)]
struct Crate {
name: String,
path: PathBuf,
}
fn check_crate_is_unstable(cr: &Crate) {
let Crate { name, path } = cr;
print!("- Verifying that sysroot crate '{name}' is an unstable crate ...");
// Trying to use this crate from a user program should fail.
let output = rustc()
.crate_type("rlib")
.target(target())
.extern_(name, path)
.input("-")
.stdin(format!("extern crate {name};"))
.run_fail();
// Make sure it failed for the intended reason, not some other reason.
// (The actual feature required varies between crates.)
output.assert_stderr_contains("use of unstable library feature");
println!(" OK");
}
fn get_unstable_sysroot_crates() -> Vec<Crate> {
let sysroot = PathBuf::from(rustc().print("sysroot").run().stdout_utf8().trim());
let sysroot_libs_dir = sysroot.join("lib").join("rustlib").join(target()).join("lib");
println!("Sysroot libs dir: {sysroot_libs_dir:?}");
// Generate a list of all library crates in the sysroot.
let sysroot_crates = get_all_crates_in_dir(&sysroot_libs_dir);
println!(
"Found {} sysroot crates: {:?}",
sysroot_crates.len(),
sysroot_crates.iter().map(|cr| &cr.name).collect::<Vec<_>>()
);
// Self-check: If we didn't find `core`, we probably checked the wrong directory.
assert!(
sysroot_crates.iter().any(|cr| cr.name == "core"),
"Couldn't find `core` in {sysroot_libs_dir:?}"
);
let unstable_sysroot_crates =
sysroot_crates.into_iter().filter(|cr| !is_stable_crate(&cr.name)).collect::<Vec<_>>();
// Self-check: There should be at least one unstable crate in the directory.
assert!(
!unstable_sysroot_crates.is_empty(),
"Couldn't find any unstable crates in {sysroot_libs_dir:?}"
);
unstable_sysroot_crates
}
fn get_all_crates_in_dir(libs_dir: &Path) -> Vec<Crate> {
let mut libs = vec![];
rfs::read_dir_entries(libs_dir, |path| {
if !path.is_file() {
return;
}
if let Some(name) = crate_name_from_path(path) {
libs.push(Crate { name, path: path.to_owned() });
}
});
libs.sort_by(|a, b| a.name.cmp(&b.name));
libs
}
/// Treat a file as a crate if its name begins with `lib` and ends with `.rlib`.
/// The crate name is the part before the first hyphen (if any).
fn crate_name_from_path(path: &Path) -> Option<String> {
let name = path
.file_name()?
.to_str()?
.strip_prefix("lib")?
.strip_suffix(".rlib")?
.split('-')
.next()
.expect("split always yields at least one string");
Some(name.to_owned())
}

View File

@ -1,75 +0,0 @@
import sys
import os
from os import listdir
from os.path import isfile, join
from subprocess import PIPE, Popen
# This is n list of files which are stable crates or simply are not crates,
# we don't check for the instability of these crates as they're all stable!
STABLE_CRATES = ['std', 'alloc', 'core', 'proc_macro',
'rsbegin.o', 'rsend.o', 'dllcrt2.o', 'crt2.o', 'clang_rt']
def convert_to_string(s):
if s.__class__.__name__ == 'bytes':
return s.decode('utf-8')
return s
def set_ld_lib_path():
var = os.environ.get("LD_LIB_PATH_ENVVAR")
rpath = os.environ.get("HOST_RPATH_DIR")
if var and rpath:
path = os.environ.get(var)
if path:
os.environ[var] = rpath + os.pathsep + path
else:
os.environ[var] = rpath
def exec_command(command, to_input=None):
child = None
if to_input is None:
child = Popen(command, stdout=PIPE, stderr=PIPE)
else:
child = Popen(command, stdout=PIPE, stderr=PIPE, stdin=PIPE)
stdout, stderr = child.communicate(input=to_input)
return (convert_to_string(stdout), convert_to_string(stderr))
def check_lib(lib):
if lib['name'] in STABLE_CRATES:
return True
print('verifying if {} is an unstable crate'.format(lib['name']))
stdout, stderr = exec_command([os.environ['RUSTC'], '-', '--crate-type', 'rlib',
'--target', os.environ['TARGET'],
'--extern', '{}={}'.format(lib['name'], lib['path'])],
to_input=('extern crate {};'.format(lib['name'])).encode('utf-8'))
if 'use of unstable library feature' not in '{}{}'.format(stdout, stderr):
print('crate {} "{}" is not unstable'.format(lib['name'], lib['path']))
print('{}{}'.format(stdout, stderr))
print('')
return False
return True
# Generate a list of all crates in the sysroot. To do this we list all files in
# rustc's sysroot, look at the filename, strip everything after the `-`, and
# strip the leading `lib` (if present)
def get_all_libs(dir_path):
return [{ 'path': join(dir_path, f), 'name': f[3:].split('-')[0] }
for f in listdir(dir_path)
if isfile(join(dir_path, f)) and f.endswith('.rlib') and f not in STABLE_CRATES]
set_ld_lib_path()
sysroot = exec_command([os.environ['RUSTC'], '--print', 'sysroot'])[0].replace('\n', '')
assert sysroot, "Could not read the rustc sysroot!"
libs = get_all_libs(join(sysroot, 'lib/rustlib/{}/lib'.format(os.environ['TARGET'])))
ret = 0
for lib in libs:
if not check_lib(lib):
# We continue so users can see all the not unstable crates.
ret = 1
sys.exit(ret)