1
Fork 0

Auto merge of #136085 - jhpratt:rollup-dxmb2h2, r=jhpratt

Rollup of 7 pull requests

Successful merges:

 - #133951 (Make the wasm_c_abi future compat warning a hard error)
 - #134283 (fix(libtest): Deprecate '--logfile')
 - #135785 (use `PassMode::Direct` for vector types on `s390x`)
 - #135948 (Update emscripten std tests)
 - #135951 (Use `fmt::from_fn` in more places in the compiler)
 - #136031 (Expand polonius MIR dump)
 - #136032 (Account for mutable borrow in argument suggestion)

Failed merges:

 - #135635 (Move `std::io::pipe` code into its own file)

r? `@ghost`
`@rustbot` modify labels: rollup
This commit is contained in:
bors 2025-01-26 06:42:03 +00:00
commit c2270becb6
55 changed files with 743 additions and 409 deletions

View file

@ -1,7 +1,9 @@
use std::io;
use rustc_middle::mir::pretty::{PrettyPrintMirOptions, dump_mir_with_options};
use rustc_middle::mir::{Body, ClosureRegionRequirements, PassWhere};
use rustc_middle::mir::pretty::{
PassWhere, PrettyPrintMirOptions, create_dump_file, dump_enabled, dump_mir_to_writer,
};
use rustc_middle::mir::{Body, ClosureRegionRequirements};
use rustc_middle::ty::TyCtxt;
use rustc_session::config::MirIncludeSpans;
@ -10,9 +12,6 @@ use crate::polonius::{LocalizedOutlivesConstraint, LocalizedOutlivesConstraintSe
use crate::{BorrowckInferCtxt, RegionInferenceContext};
/// `-Zdump-mir=polonius` dumps MIR annotated with NLL and polonius specific information.
// Note: this currently duplicates most of NLL MIR, with some additions for the localized outlives
// constraints. This is ok for now as this dump will change in the near future to an HTML file to
// become more useful.
pub(crate) fn dump_polonius_mir<'tcx>(
infcx: &BorrowckInferCtxt<'tcx>,
body: &Body<'tcx>,
@ -26,12 +25,100 @@ pub(crate) fn dump_polonius_mir<'tcx>(
return;
}
if !dump_enabled(tcx, "polonius", body.source.def_id()) {
return;
}
let localized_outlives_constraints = localized_outlives_constraints
.expect("missing localized constraints with `-Zpolonius=next`");
// We want the NLL extra comments printed by default in NLL MIR dumps (they were removed in
// #112346). Specifying `-Z mir-include-spans` on the CLI still has priority: for example,
// they're always disabled in mir-opt tests to make working with blessed dumps easier.
let _: io::Result<()> = try {
let mut file = create_dump_file(tcx, "html", false, "polonius", &0, body)?;
emit_polonius_dump(
tcx,
body,
regioncx,
borrow_set,
localized_outlives_constraints,
closure_region_requirements,
&mut file,
)?;
};
}
/// The polonius dump consists of:
/// - the NLL MIR
/// - the list of polonius localized constraints
/// - a mermaid graph of the CFG
fn emit_polonius_dump<'tcx>(
tcx: TyCtxt<'tcx>,
body: &Body<'tcx>,
regioncx: &RegionInferenceContext<'tcx>,
borrow_set: &BorrowSet<'tcx>,
localized_outlives_constraints: LocalizedOutlivesConstraintSet,
closure_region_requirements: &Option<ClosureRegionRequirements<'tcx>>,
out: &mut dyn io::Write,
) -> io::Result<()> {
// Prepare the HTML dump file prologue.
writeln!(out, "<!DOCTYPE html>")?;
writeln!(out, "<html>")?;
writeln!(out, "<head><title>Polonius MIR dump</title></head>")?;
writeln!(out, "<body>")?;
// Section 1: the NLL + Polonius MIR.
writeln!(out, "<div>")?;
writeln!(out, "Raw MIR dump")?;
writeln!(out, "<code><pre>")?;
emit_html_mir(
tcx,
body,
regioncx,
borrow_set,
localized_outlives_constraints,
closure_region_requirements,
out,
)?;
writeln!(out, "</pre></code>")?;
writeln!(out, "</div>")?;
// Section 2: mermaid visualization of the CFG.
writeln!(out, "<div>")?;
writeln!(out, "Control-flow graph")?;
writeln!(out, "<code><pre class='mermaid'>")?;
emit_mermaid_cfg(body, out)?;
writeln!(out, "</pre></code>")?;
writeln!(out, "</div>")?;
// Finalize the dump with the HTML epilogue.
writeln!(
out,
"<script src='https://cdn.jsdelivr.net/npm/mermaid/dist/mermaid.min.js'></script>"
)?;
writeln!(out, "<script>")?;
writeln!(out, "mermaid.initialize({{ startOnLoad: false, maxEdges: 100 }});")?;
writeln!(out, "mermaid.run({{ querySelector: '.mermaid' }})")?;
writeln!(out, "</script>")?;
writeln!(out, "</body>")?;
writeln!(out, "</html>")?;
Ok(())
}
/// Emits the polonius MIR, as escaped HTML.
fn emit_html_mir<'tcx>(
tcx: TyCtxt<'tcx>,
body: &Body<'tcx>,
regioncx: &RegionInferenceContext<'tcx>,
borrow_set: &BorrowSet<'tcx>,
localized_outlives_constraints: LocalizedOutlivesConstraintSet,
closure_region_requirements: &Option<ClosureRegionRequirements<'tcx>>,
out: &mut dyn io::Write,
) -> io::Result<()> {
// Buffer the regular MIR dump to be able to escape it.
let mut buffer = Vec::new();
// We want the NLL extra comments printed by default in NLL MIR dumps. Specifying `-Z
// mir-include-spans` on the CLI still has priority.
let options = PrettyPrintMirOptions {
include_extra_comments: matches!(
tcx.sess.opts.unstable_opts.mir_include_spans,
@ -39,12 +126,12 @@ pub(crate) fn dump_polonius_mir<'tcx>(
),
};
dump_mir_with_options(
dump_mir_to_writer(
tcx,
false,
"polonius",
&0,
body,
&mut buffer,
|pass_where, out| {
emit_polonius_mir(
tcx,
@ -57,7 +144,27 @@ pub(crate) fn dump_polonius_mir<'tcx>(
)
},
options,
);
)?;
// Escape the handful of characters that need it. We don't need to be particularly efficient:
// we're actually writing into a buffered writer already. Note that MIR dumps are valid UTF-8.
let buffer = String::from_utf8_lossy(&buffer);
for ch in buffer.chars() {
let escaped = match ch {
'>' => "&gt;",
'<' => "&lt;",
'&' => "&amp;",
'\'' => "&#39;",
'"' => "&quot;",
_ => {
// The common case, no escaping needed.
write!(out, "{}", ch)?;
continue;
}
};
write!(out, "{}", escaped)?;
}
Ok(())
}
/// Produces the actual NLL + Polonius MIR sections to emit during the dumping process.
@ -102,3 +209,55 @@ fn emit_polonius_mir<'tcx>(
Ok(())
}
/// Emits a mermaid flowchart of the CFG blocks and edges, similar to the graphviz version.
fn emit_mermaid_cfg(body: &Body<'_>, out: &mut dyn io::Write) -> io::Result<()> {
use rustc_middle::mir::{TerminatorEdges, TerminatorKind};
// The mermaid chart type: a top-down flowchart.
writeln!(out, "flowchart TD")?;
// Emit the block nodes.
for (block_idx, block) in body.basic_blocks.iter_enumerated() {
let block_idx = block_idx.as_usize();
let cleanup = if block.is_cleanup { " (cleanup)" } else { "" };
writeln!(out, "{block_idx}[\"bb{block_idx}{cleanup}\"]")?;
}
// Emit the edges between blocks, from the terminator edges.
for (block_idx, block) in body.basic_blocks.iter_enumerated() {
let block_idx = block_idx.as_usize();
let terminator = block.terminator();
match terminator.edges() {
TerminatorEdges::None => {}
TerminatorEdges::Single(bb) => {
writeln!(out, "{block_idx} --> {}", bb.as_usize())?;
}
TerminatorEdges::Double(bb1, bb2) => {
if matches!(terminator.kind, TerminatorKind::FalseEdge { .. }) {
writeln!(out, "{block_idx} --> {}", bb1.as_usize())?;
writeln!(out, "{block_idx} -- imaginary --> {}", bb2.as_usize())?;
} else {
writeln!(out, "{block_idx} --> {}", bb1.as_usize())?;
writeln!(out, "{block_idx} -- unwind --> {}", bb2.as_usize())?;
}
}
TerminatorEdges::AssignOnReturn { return_, cleanup, .. } => {
for to_idx in return_ {
writeln!(out, "{block_idx} --> {}", to_idx.as_usize())?;
}
if let Some(to_idx) = cleanup {
writeln!(out, "{block_idx} -- unwind --> {}", to_idx.as_usize())?;
}
}
TerminatorEdges::SwitchInt { targets, .. } => {
for to_idx in targets.all_targets() {
writeln!(out, "{block_idx} --> {}", to_idx.as_usize())?;
}
}
}
}
Ok(())
}

View file

@ -1285,13 +1285,13 @@ impl fmt::Debug for OwnerNodes<'_> {
.field("node", &self.nodes[ItemLocalId::ZERO])
.field(
"parents",
&self
.nodes
.iter_enumerated()
.map(|(id, parented_node)| {
debug_fn(move |f| write!(f, "({id:?}, {:?})", parented_node.parent))
})
.collect::<Vec<_>>(),
&fmt::from_fn(|f| {
f.debug_list()
.entries(self.nodes.iter_enumerated().map(|(id, parented_node)| {
fmt::from_fn(move |f| write!(f, "({id:?}, {:?})", parented_node.parent))
}))
.finish()
}),
)
.field("bodies", &self.bodies)
.field("opt_hash_including_bodies", &self.opt_hash_including_bodies)
@ -4638,15 +4638,5 @@ mod size_asserts {
// tidy-alphabetical-end
}
fn debug_fn(f: impl Fn(&mut fmt::Formatter<'_>) -> fmt::Result) -> impl fmt::Debug {
struct DebugFn<F>(F);
impl<F: Fn(&mut fmt::Formatter<'_>) -> fmt::Result> fmt::Debug for DebugFn<F> {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
(self.0)(fmt)
}
}
DebugFn(f)
}
#[cfg(test)]
mod tests;

View file

@ -6,6 +6,7 @@
#![allow(internal_features)]
#![feature(associated_type_defaults)]
#![feature(closure_track_caller)]
#![feature(debug_closure_helpers)]
#![feature(exhaustive_patterns)]
#![feature(let_chains)]
#![feature(never_type)]

View file

@ -184,6 +184,50 @@ enum Scope<'a> {
},
}
impl<'a> Scope<'a> {
// A helper for debugging scopes without printing parent scopes
fn debug_truncated(&'a self) -> impl fmt::Debug + 'a {
fmt::from_fn(move |f| match self {
Self::Binder { bound_vars, scope_type, hir_id, where_bound_origin, s: _ } => f
.debug_struct("Binder")
.field("bound_vars", bound_vars)
.field("scope_type", scope_type)
.field("hir_id", hir_id)
.field("where_bound_origin", where_bound_origin)
.field("s", &"..")
.finish(),
Self::Opaque { captures, def_id, s: _ } => f
.debug_struct("Opaque")
.field("def_id", def_id)
.field("captures", &captures.borrow())
.field("s", &"..")
.finish(),
Self::Body { id, s: _ } => {
f.debug_struct("Body").field("id", id).field("s", &"..").finish()
}
Self::ObjectLifetimeDefault { lifetime, s: _ } => f
.debug_struct("ObjectLifetimeDefault")
.field("lifetime", lifetime)
.field("s", &"..")
.finish(),
Self::Supertrait { bound_vars, s: _ } => f
.debug_struct("Supertrait")
.field("bound_vars", bound_vars)
.field("s", &"..")
.finish(),
Self::TraitRefBoundary { s: _ } => f.debug_struct("TraitRefBoundary").finish(),
Self::LateBoundary { s: _, what, deny_late_regions } => f
.debug_struct("LateBoundary")
.field("what", what)
.field("deny_late_regions", deny_late_regions)
.finish(),
Self::Root { opt_parent_item } => {
f.debug_struct("Root").field("opt_parent_item", &opt_parent_item).finish()
}
})
}
}
#[derive(Copy, Clone, Debug)]
enum BinderScopeType {
/// Any non-concatenating binder scopes.
@ -200,52 +244,6 @@ enum BinderScopeType {
Concatenating,
}
// A helper struct for debugging scopes without printing parent scopes
struct TruncatedScopeDebug<'a>(&'a Scope<'a>);
impl<'a> fmt::Debug for TruncatedScopeDebug<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self.0 {
Scope::Binder { bound_vars, scope_type, hir_id, where_bound_origin, s: _ } => f
.debug_struct("Binder")
.field("bound_vars", bound_vars)
.field("scope_type", scope_type)
.field("hir_id", hir_id)
.field("where_bound_origin", where_bound_origin)
.field("s", &"..")
.finish(),
Scope::Opaque { captures, def_id, s: _ } => f
.debug_struct("Opaque")
.field("def_id", def_id)
.field("captures", &captures.borrow())
.field("s", &"..")
.finish(),
Scope::Body { id, s: _ } => {
f.debug_struct("Body").field("id", id).field("s", &"..").finish()
}
Scope::ObjectLifetimeDefault { lifetime, s: _ } => f
.debug_struct("ObjectLifetimeDefault")
.field("lifetime", lifetime)
.field("s", &"..")
.finish(),
Scope::Supertrait { bound_vars, s: _ } => f
.debug_struct("Supertrait")
.field("bound_vars", bound_vars)
.field("s", &"..")
.finish(),
Scope::TraitRefBoundary { s: _ } => f.debug_struct("TraitRefBoundary").finish(),
Scope::LateBoundary { s: _, what, deny_late_regions } => f
.debug_struct("LateBoundary")
.field("what", what)
.field("deny_late_regions", deny_late_regions)
.finish(),
Scope::Root { opt_parent_item } => {
f.debug_struct("Root").field("opt_parent_item", &opt_parent_item).finish()
}
}
}
}
type ScopeRef<'a> = &'a Scope<'a>;
pub(crate) fn provide(providers: &mut Providers) {
@ -1144,7 +1142,7 @@ impl<'a, 'tcx> BoundVarContext<'a, 'tcx> {
{
let BoundVarContext { tcx, map, .. } = self;
let mut this = BoundVarContext { tcx: *tcx, map, scope: &wrap_scope };
let span = debug_span!("scope", scope = ?TruncatedScopeDebug(this.scope));
let span = debug_span!("scope", scope = ?this.scope.debug_truncated());
{
let _enter = span.enter();
f(&mut this);

View file

@ -63,6 +63,7 @@ This API is completely unstable and subject to change.
#![doc(rust_logo)]
#![feature(assert_matches)]
#![feature(coroutines)]
#![feature(debug_closure_helpers)]
#![feature(if_let_guard)]
#![feature(iter_from_coroutine)]
#![feature(iter_intersperse)]

View file

@ -851,32 +851,32 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
&& let hir::PatKind::Binding(hir::BindingMode::MUT, _hir_id, ident, _) = pat.kind
// Look for the type corresponding to the argument pattern we have in the argument list.
&& let Some(ty_sugg) = fn_decl
&& let Some(ty_ref) = fn_decl
.inputs
.iter()
.filter_map(|ty| {
if ty.span == *ty_span
&& let hir::TyKind::Ref(lt, x) = ty.kind
{
// `&'name Ty` -> `&'name mut Ty` or `&Ty` -> `&mut Ty`
Some((
x.ty.span.shrink_to_lo(),
format!(
"{}mut ",
if lt.ident.span.lo() == lt.ident.span.hi() { "" } else { " " }
),
))
} else {
None
}
.filter_map(|ty| match ty.kind {
hir::TyKind::Ref(lt, mut_ty) if ty.span == *ty_span => Some((lt, mut_ty)),
_ => None,
})
.next()
{
let sugg = vec![
ty_sugg,
let mut sugg = if ty_ref.1.mutbl.is_mut() {
// Leave `&'name mut Ty` and `&mut Ty` as they are (#136028).
vec![]
} else {
// `&'name Ty` -> `&'name mut Ty` or `&Ty` -> `&mut Ty`
vec![(
ty_ref.1.ty.span.shrink_to_lo(),
format!(
"{}mut ",
if ty_ref.0.ident.span.lo() == ty_ref.0.ident.span.hi() { "" } else { " " },
),
)]
};
sugg.extend([
(pat.span.until(ident.span), String::new()),
(lhs.span.shrink_to_lo(), "*".to_string()),
];
]);
// We suggest changing the argument from `mut ident: &Ty` to `ident: &'_ mut Ty` and the
// assignment from `ident = val;` to `*ident = val;`.
err.multipart_suggestion_verbose(

View file

@ -974,6 +974,3 @@ lint_uses_power_alignment = repr(C) does not follow the power alignment rule. Th
lint_variant_size_differences =
enum variant is more than three times larger ({$largest} bytes) than the next largest
lint_wasm_c_abi =
older versions of the `wasm-bindgen` crate will be incompatible with future versions of Rust; please update to `wasm-bindgen` v0.2.88

View file

@ -430,7 +430,6 @@ pub(super) fn decorate_lint(
BuiltinLintDiag::UnusedCrateDependency { extern_crate, local_crate } => {
lints::UnusedCrateDependency { extern_crate, local_crate }.decorate_lint(diag)
}
BuiltinLintDiag::WasmCAbi => lints::WasmCAbi.decorate_lint(diag),
BuiltinLintDiag::IllFormedAttributeInput { suggestions } => {
lints::IllFormedAttributeInput {
num_suggestions: suggestions.len(),

View file

@ -2553,10 +2553,6 @@ pub(crate) struct UnusedCrateDependency {
pub local_crate: Symbol,
}
#[derive(LintDiagnostic)]
#[diag(lint_wasm_c_abi)]
pub(crate) struct WasmCAbi;
#[derive(LintDiagnostic)]
#[diag(lint_ill_formed_attribute_input)]
pub(crate) struct IllFormedAttributeInput {

View file

@ -144,7 +144,6 @@ declare_lint_pass! {
UNUSED_VARIABLES,
USELESS_DEPRECATED,
WARNINGS,
WASM_C_ABI,
// tidy-alphabetical-end
]
}
@ -4681,44 +4680,6 @@ declare_lint! {
};
}
declare_lint! {
/// The `wasm_c_abi` lint detects crate dependencies that are incompatible
/// with future versions of Rust that will emit spec-compliant C ABI.
///
/// ### Example
///
/// ```rust,ignore (needs extern crate)
/// #![deny(wasm_c_abi)]
/// ```
///
/// This will produce:
///
/// ```text
/// error: the following packages contain code that will be rejected by a future version of Rust: wasm-bindgen v0.2.87
/// |
/// note: the lint level is defined here
/// --> src/lib.rs:1:9
/// |
/// 1 | #![deny(wasm_c_abi)]
/// | ^^^^^^^^^^
/// ```
///
/// ### Explanation
///
/// Rust has historically emitted non-spec-compliant C ABI. This has caused
/// incompatibilities between other compilers and Wasm targets. In a future
/// version of Rust this will be fixed and therefore dependencies relying
/// on the non-spec-compliant C ABI will stop functioning.
pub WASM_C_ABI,
Deny,
"detects dependencies that are incompatible with the Wasm C ABI",
@future_incompatible = FutureIncompatibleInfo {
reason: FutureIncompatibilityReason::FutureReleaseErrorReportInDeps,
reference: "issue #71871 <https://github.com/rust-lang/rust/issues/71871>",
};
crate_level_only
}
declare_lint! {
/// The `uncovered_param_in_projection` lint detects a violation of one of Rust's orphan rules for
/// foreign trait implementations that concerns the use of type parameters inside trait associated

View file

@ -795,7 +795,6 @@ pub enum BuiltinLintDiag {
extern_crate: Symbol,
local_crate: Symbol,
},
WasmCAbi,
IllFormedAttributeInput {
suggestions: Vec<String>,
},

View file

@ -290,6 +290,9 @@ metadata_unsupported_abi =
metadata_unsupported_abi_i686 =
ABI not supported by `#[link(kind = "raw-dylib")]` on i686
metadata_wasm_c_abi =
older versions of the `wasm-bindgen` crate are incompatible with current versions of Rust; please update to `wasm-bindgen` v0.2.88
metadata_wasm_import_form =
wasm import module must be of the form `wasm_import_module = "string"`

View file

@ -1076,12 +1076,7 @@ impl<'a, 'tcx> CrateLoader<'a, 'tcx> {
// Make a point span rather than covering the whole file
let span = krate.spans.inner_span.shrink_to_lo();
self.sess.psess.buffer_lint(
lint::builtin::WASM_C_ABI,
span,
ast::CRATE_NODE_ID,
BuiltinLintDiag::WasmCAbi,
);
self.sess.dcx().emit_err(errors::WasmCAbi { span });
}
}

View file

@ -732,3 +732,10 @@ pub struct ImportNameTypeRaw {
#[primary_span]
pub span: Span,
}
#[derive(Diagnostic)]
#[diag(metadata_wasm_c_abi)]
pub(crate) struct WasmCAbi {
#[primary_span]
pub span: Span,
}

View file

@ -40,6 +40,7 @@
#![feature(const_type_name)]
#![feature(core_intrinsics)]
#![feature(coroutines)]
#![feature(debug_closure_helpers)]
#![feature(decl_macro)]
#![feature(discriminant_kind)]
#![feature(extern_types)]

View file

@ -1,8 +1,7 @@
use std::collections::BTreeSet;
use std::fmt::{Display, Write as _};
use std::fs;
use std::io::{self, Write as _};
use std::path::{Path, PathBuf};
use std::{fs, io};
use rustc_abi::Size;
use rustc_ast::InlineAsmTemplatePiece;
@ -149,37 +148,59 @@ pub fn dump_enabled(tcx: TyCtxt<'_>, pass_name: &str, def_id: DefId) -> bool {
// `def_path_str()` would otherwise trigger `type_of`, and this can
// run while we are already attempting to evaluate `type_of`.
/// Most use-cases of dumping MIR should use the [dump_mir] entrypoint instead, which will also
/// check if dumping MIR is enabled, and if this body matches the filters passed on the CLI.
///
/// That being said, if the above requirements have been validated already, this function is where
/// most of the MIR dumping occurs, if one needs to export it to a file they have created with
/// [create_dump_file], rather than to a new file created as part of [dump_mir], or to stdout/stderr
/// for debugging purposes.
pub fn dump_mir_to_writer<'tcx, F>(
tcx: TyCtxt<'tcx>,
pass_name: &str,
disambiguator: &dyn Display,
body: &Body<'tcx>,
w: &mut dyn io::Write,
mut extra_data: F,
options: PrettyPrintMirOptions,
) -> io::Result<()>
where
F: FnMut(PassWhere, &mut dyn io::Write) -> io::Result<()>,
{
// see notes on #41697 above
let def_path =
ty::print::with_forced_impl_filename_line!(tcx.def_path_str(body.source.def_id()));
// ignore-tidy-odd-backticks the literal below is fine
write!(w, "// MIR for `{def_path}")?;
match body.source.promoted {
None => write!(w, "`")?,
Some(promoted) => write!(w, "::{promoted:?}`")?,
}
writeln!(w, " {disambiguator} {pass_name}")?;
if let Some(ref layout) = body.coroutine_layout_raw() {
writeln!(w, "/* coroutine_layout = {layout:#?} */")?;
}
writeln!(w)?;
extra_data(PassWhere::BeforeCFG, w)?;
write_user_type_annotations(tcx, body, w)?;
write_mir_fn(tcx, body, &mut extra_data, w, options)?;
extra_data(PassWhere::AfterCFG, w)
}
fn dump_matched_mir_node<'tcx, F>(
tcx: TyCtxt<'tcx>,
pass_num: bool,
pass_name: &str,
disambiguator: &dyn Display,
body: &Body<'tcx>,
mut extra_data: F,
extra_data: F,
options: PrettyPrintMirOptions,
) where
F: FnMut(PassWhere, &mut dyn io::Write) -> io::Result<()>,
{
let _: io::Result<()> = try {
let mut file = create_dump_file(tcx, "mir", pass_num, pass_name, disambiguator, body)?;
// see notes on #41697 above
let def_path =
ty::print::with_forced_impl_filename_line!(tcx.def_path_str(body.source.def_id()));
// ignore-tidy-odd-backticks the literal below is fine
write!(file, "// MIR for `{def_path}")?;
match body.source.promoted {
None => write!(file, "`")?,
Some(promoted) => write!(file, "::{promoted:?}`")?,
}
writeln!(file, " {disambiguator} {pass_name}")?;
if let Some(ref layout) = body.coroutine_layout_raw() {
writeln!(file, "/* coroutine_layout = {layout:#?} */")?;
}
writeln!(file)?;
extra_data(PassWhere::BeforeCFG, &mut file)?;
write_user_type_annotations(tcx, body, &mut file)?;
write_mir_fn(tcx, body, &mut extra_data, &mut file, options)?;
extra_data(PassWhere::AfterCFG, &mut file)?;
dump_mir_to_writer(tcx, pass_name, disambiguator, body, &mut file, extra_data, options)?;
};
if tcx.sess.opts.unstable_opts.dump_mir_graphviz {

View file

@ -1,6 +1,5 @@
//! Values computed by queries that use MIR.
use std::cell::Cell;
use std::fmt::{self, Debug};
use rustc_abi::{FieldIdx, VariantIdx};
@ -62,55 +61,26 @@ pub struct CoroutineLayout<'tcx> {
impl Debug for CoroutineLayout<'_> {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
/// Prints an iterator of (key, value) tuples as a map.
struct MapPrinter<'a, K, V>(Cell<Option<Box<dyn Iterator<Item = (K, V)> + 'a>>>);
impl<'a, K, V> MapPrinter<'a, K, V> {
fn new(iter: impl Iterator<Item = (K, V)> + 'a) -> Self {
Self(Cell::new(Some(Box::new(iter))))
}
}
impl<'a, K: Debug, V: Debug> Debug for MapPrinter<'a, K, V> {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
fmt.debug_map().entries(self.0.take().unwrap()).finish()
}
}
/// Prints the coroutine variant name.
struct GenVariantPrinter(VariantIdx);
impl From<VariantIdx> for GenVariantPrinter {
fn from(idx: VariantIdx) -> Self {
GenVariantPrinter(idx)
}
}
impl Debug for GenVariantPrinter {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
let variant_name = ty::CoroutineArgs::variant_name(self.0);
if fmt.alternate() {
write!(fmt, "{:9}({:?})", variant_name, self.0)
} else {
write!(fmt, "{variant_name}")
}
}
}
/// Forces its contents to print in regular mode instead of alternate mode.
struct OneLinePrinter<T>(T);
impl<T: Debug> Debug for OneLinePrinter<T> {
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(fmt, "{:?}", self.0)
}
}
fmt.debug_struct("CoroutineLayout")
.field("field_tys", &MapPrinter::new(self.field_tys.iter_enumerated()))
.field(
"variant_fields",
&MapPrinter::new(
self.variant_fields
.iter_enumerated()
.map(|(k, v)| (GenVariantPrinter(k), OneLinePrinter(v))),
),
)
.field_with("field_tys", |fmt| {
fmt.debug_map().entries(self.field_tys.iter_enumerated()).finish()
})
.field_with("variant_fields", |fmt| {
let mut map = fmt.debug_map();
for (idx, fields) in self.variant_fields.iter_enumerated() {
map.key_with(|fmt| {
let variant_name = ty::CoroutineArgs::variant_name(idx);
if fmt.alternate() {
write!(fmt, "{variant_name:9}({idx:?})")
} else {
write!(fmt, "{variant_name}")
}
});
// Force variant fields to print in regular mode instead of alternate mode.
map.value_with(|fmt| write!(fmt, "{fields:?}"));
}
map.finish()
})
.field("storage_conflicts", &self.storage_conflicts)
.finish()
}

View file

@ -581,9 +581,11 @@ impl<'tcx> TerminatorKind<'tcx> {
pub enum TerminatorEdges<'mir, 'tcx> {
/// For terminators that have no successor, like `return`.
None,
/// For terminators that a single successor, like `goto`, and `assert` without cleanup block.
/// For terminators that have a single successor, like `goto`, and `assert` without a cleanup
/// block.
Single(BasicBlock),
/// For terminators that two successors, `assert` with cleanup block and `falseEdge`.
/// For terminators that have two successors, like `assert` with a cleanup block, and
/// `falseEdge`.
Double(BasicBlock, BasicBlock),
/// Special action for `Yield`, `Call` and `InlineAsm` terminators.
AssignOnReturn {

View file

@ -2325,51 +2325,41 @@ macro_rules! sty_debug_print {
}
impl<'tcx> TyCtxt<'tcx> {
pub fn debug_stats(self) -> impl std::fmt::Debug + 'tcx {
struct DebugStats<'tcx>(TyCtxt<'tcx>);
pub fn debug_stats(self) -> impl fmt::Debug + 'tcx {
fmt::from_fn(move |fmt| {
sty_debug_print!(
fmt,
self,
Adt,
Array,
Slice,
RawPtr,
Ref,
FnDef,
FnPtr,
UnsafeBinder,
Placeholder,
Coroutine,
CoroutineWitness,
Dynamic,
Closure,
CoroutineClosure,
Tuple,
Bound,
Param,
Infer,
Alias,
Pat,
Foreign
)?;
impl<'tcx> std::fmt::Debug for DebugStats<'tcx> {
fn fmt(&self, fmt: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
sty_debug_print!(
fmt,
self.0,
Adt,
Array,
Slice,
RawPtr,
Ref,
FnDef,
FnPtr,
UnsafeBinder,
Placeholder,
Coroutine,
CoroutineWitness,
Dynamic,
Closure,
CoroutineClosure,
Tuple,
Bound,
Param,
Infer,
Alias,
Pat,
Foreign
)?;
writeln!(fmt, "GenericArgs interner: #{}", self.interners.args.len())?;
writeln!(fmt, "Region interner: #{}", self.interners.region.len())?;
writeln!(fmt, "Const Allocation interner: #{}", self.interners.const_allocation.len())?;
writeln!(fmt, "Layout interner: #{}", self.interners.layout.len())?;
writeln!(fmt, "GenericArgs interner: #{}", self.0.interners.args.len())?;
writeln!(fmt, "Region interner: #{}", self.0.interners.region.len())?;
writeln!(
fmt,
"Const Allocation interner: #{}",
self.0.interners.const_allocation.len()
)?;
writeln!(fmt, "Layout interner: #{}", self.0.interners.layout.len())?;
Ok(())
}
}
DebugStats(self)
Ok(())
})
}
}

View file

@ -1598,45 +1598,35 @@ impl<'a> Parser<'a> {
// Only used when debugging.
#[allow(unused)]
pub(crate) fn debug_lookahead(&self, lookahead: usize) -> impl fmt::Debug + '_ {
struct DebugParser<'dbg> {
parser: &'dbg Parser<'dbg>,
lookahead: usize,
}
fmt::from_fn(move |f| {
let mut dbg_fmt = f.debug_struct("Parser"); // or at least, one view of
impl fmt::Debug for DebugParser<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let Self { parser, lookahead } = self;
let mut dbg_fmt = f.debug_struct("Parser"); // or at least, one view of
// we don't need N spans, but we want at least one, so print all of prev_token
dbg_fmt.field("prev_token", &parser.prev_token);
let mut tokens = vec![];
for i in 0..*lookahead {
let tok = parser.look_ahead(i, |tok| tok.kind.clone());
let is_eof = tok == TokenKind::Eof;
tokens.push(tok);
if is_eof {
// Don't look ahead past EOF.
break;
}
// we don't need N spans, but we want at least one, so print all of prev_token
dbg_fmt.field("prev_token", &self.prev_token);
let mut tokens = vec![];
for i in 0..lookahead {
let tok = self.look_ahead(i, |tok| tok.kind.clone());
let is_eof = tok == TokenKind::Eof;
tokens.push(tok);
if is_eof {
// Don't look ahead past EOF.
break;
}
dbg_fmt.field_with("tokens", |field| field.debug_list().entries(tokens).finish());
dbg_fmt.field("approx_token_stream_pos", &parser.num_bump_calls);
// some fields are interesting for certain values, as they relate to macro parsing
if let Some(subparser) = parser.subparser_name {
dbg_fmt.field("subparser_name", &subparser);
}
if let Recovery::Forbidden = parser.recovery {
dbg_fmt.field("recovery", &parser.recovery);
}
// imply there's "more to know" than this view
dbg_fmt.finish_non_exhaustive()
}
}
dbg_fmt.field_with("tokens", |field| field.debug_list().entries(tokens).finish());
dbg_fmt.field("approx_token_stream_pos", &self.num_bump_calls);
DebugParser { parser: self, lookahead }
// some fields are interesting for certain values, as they relate to macro parsing
if let Some(subparser) = self.subparser_name {
dbg_fmt.field("subparser_name", &subparser);
}
if let Recovery::Forbidden = self.recovery {
dbg_fmt.field("recovery", &self.recovery);
}
// imply there's "more to know" than this view
dbg_fmt.finish_non_exhaustive()
})
}
pub fn clear_expected_token_types(&mut self) {

View file

@ -1791,7 +1791,7 @@ pub(crate) struct UnusedAssign {
pub(crate) struct UnusedAssignSuggestion {
pub pre: &'static str,
#[suggestion_part(code = "{pre}mut ")]
pub ty_span: Span,
pub ty_span: Option<Span>,
#[suggestion_part(code = "")]
pub ty_ref_span: Span,
#[suggestion_part(code = "*")]

View file

@ -1620,24 +1620,28 @@ impl<'tcx> Liveness<'_, 'tcx> {
&& let item = self.ir.tcx.hir_owner_node(item_id)
&& let Some(fn_decl) = item.fn_decl()
&& let hir::PatKind::Binding(hir::BindingMode::MUT, _hir_id, ident, _) = pat.kind
&& let Some((ty_span, pre)) = fn_decl
&& let Some((lt, mut_ty)) = fn_decl
.inputs
.iter()
.filter_map(|ty| {
if ty.span == *ty_span
&& let hir::TyKind::Ref(lt, mut_ty) = ty.kind
{
// `&'name Ty` -> `&'name mut Ty` or `&Ty` -> `&mut Ty`
Some((
mut_ty.ty.span.shrink_to_lo(),
if lt.ident.span.lo() == lt.ident.span.hi() { "" } else { " " },
))
Some((lt, mut_ty))
} else {
None
}
})
.next()
{
let ty_span = if mut_ty.mutbl.is_mut() {
// Leave `&'name mut Ty` and `&mut Ty` as they are (#136028).
None
} else {
// `&'name Ty` -> `&'name mut Ty` or `&Ty` -> `&mut Ty`
Some(mut_ty.ty.span.shrink_to_lo())
};
let pre = if lt.ident.span.lo() == lt.ident.span.hi() { "" } else { " " };
Some(errors::UnusedAssignSuggestion {
ty_span,
pre,

View file

@ -38,9 +38,17 @@ where
}
let size = arg.layout.size;
if size.bits() <= 128 && arg.layout.is_single_vector_element(cx, size) {
arg.cast_to(Reg { kind: RegKind::Vector, size });
return;
if size.bits() <= 128 {
if let BackendRepr::Vector { .. } = arg.layout.backend_repr {
// pass non-wrapped vector types using `PassMode::Direct`
return;
}
if arg.layout.is_single_vector_element(cx, size) {
// pass non-transparant wrappers around a vector as `PassMode::Cast`
arg.cast_to(Reg { kind: RegKind::Vector, size });
return;
}
}
if !arg.layout.is_aggregate() && size.bits() <= 64 {
arg.extend_integer_width_to(64);

View file

@ -353,6 +353,7 @@ pub fn iter_10k(b: &mut Bencher) {
}
#[bench]
#[cfg_attr(target_os = "emscripten", ignore)] // hits an OOM
pub fn iter_1m(b: &mut Bencher) {
bench_iter(b, 1_000, 1_000_000);
}

View file

@ -366,14 +366,25 @@ rotate!(rotate_medium_half, gen_random, 9158, 9158 / 2);
rotate!(rotate_medium_half_plus_one, gen_random, 9158, 9158 / 2 + 1);
// Intended to use more RAM than the machine has cache
#[cfg(not(target_os = "emscripten"))] // hits an OOM
rotate!(rotate_huge_by1, gen_random, 5 * 1024 * 1024, 1);
#[cfg(not(target_os = "emscripten"))] // hits an OOM
rotate!(rotate_huge_by9199_u64, gen_random, 5 * 1024 * 1024, 9199);
#[cfg(not(target_os = "emscripten"))] // hits an OOM
rotate!(rotate_huge_by9199_bytes, gen_random_bytes, 5 * 1024 * 1024, 9199);
#[cfg(not(target_os = "emscripten"))] // hits an OOM
rotate!(rotate_huge_by9199_strings, gen_strings, 5 * 1024 * 1024, 9199);
#[cfg(not(target_os = "emscripten"))] // hits an OOM
rotate!(rotate_huge_by9199_big, gen_big_random, 5 * 1024 * 1024, 9199);
#[cfg(not(target_os = "emscripten"))] // hits an OOM
rotate!(rotate_huge_by1234577_u64, gen_random, 5 * 1024 * 1024, 1234577);
#[cfg(not(target_os = "emscripten"))] // hits an OOM
rotate!(rotate_huge_by1234577_bytes, gen_random_bytes, 5 * 1024 * 1024, 1234577);
#[cfg(not(target_os = "emscripten"))] // hits an OOM
rotate!(rotate_huge_by1234577_strings, gen_strings, 5 * 1024 * 1024, 1234577);
#[cfg(not(target_os = "emscripten"))] // hits an OOM
rotate!(rotate_huge_by1234577_big, gen_big_random, 5 * 1024 * 1024, 1234577);
#[cfg(not(target_os = "emscripten"))] // hits an OOM
rotate!(rotate_huge_half, gen_random, 5 * 1024 * 1024, 5 * 1024 * 1024 / 2);
#[cfg(not(target_os = "emscripten"))] // hits an OOM
rotate!(rotate_huge_half_plus_one, gen_random, 5 * 1024 * 1024, 5 * 1024 * 1024 / 2 + 1);

View file

@ -547,6 +547,11 @@ fn bench_in_place_collect_droppable(b: &mut Bencher) {
})
}
// node.js gives out of memory error to use with length 1_100_000
#[cfg(target_os = "emscripten")]
const LEN: usize = 4096;
#[cfg(not(target_os = "emscripten"))]
const LEN: usize = 16384;
#[bench]

View file

@ -502,9 +502,7 @@ fn test_retain_catch_unwind() {
// even if the order might not be correct.
//
// Destructors must be called exactly once per element.
// FIXME: re-enable emscripten once it can unwind again
#[test]
#[cfg(not(target_os = "emscripten"))]
#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")]
fn panic_safe() {
use std::cmp;

View file

@ -94,9 +94,6 @@ fn test_rng() -> rand_xorshift::XorShiftRng {
rand::SeedableRng::from_seed(seed)
}
// FIXME: Instantiated functions with i128 in the signature is not supported in Emscripten.
// See https://github.com/kripken/emscripten-fastcomp/issues/169
#[cfg(not(target_os = "emscripten"))]
#[test]
fn test_boxed_hasher() {
let ordinary_hash = hash(&5u32);

View file

@ -1414,7 +1414,6 @@ fn test_box_slice_clone() {
#[test]
#[allow(unused_must_use)] // here, we care about the side effects of `.clone()`
#[cfg_attr(target_os = "emscripten", ignore)]
#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")]
fn test_box_slice_clone_panics() {
use std::sync::Arc;

View file

@ -11,7 +11,14 @@ use crate::sort::{Sort, known_good_stable_sort, patterns};
#[cfg(miri)]
const TEST_LENGTHS: &[usize] = &[2, 3, 4, 7, 10, 15, 20, 24, 33, 50, 100, 171, 300];
#[cfg(not(miri))]
// node.js gives out of memory error to use with length 1_100_000
#[cfg(all(not(miri), target_os = "emscripten"))]
const TEST_LENGTHS: &[usize] = &[
2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 16, 17, 20, 24, 30, 32, 33, 35, 50, 100, 200, 500, 1_000,
2_048, 5_000, 10_000, 100_000,
];
#[cfg(all(not(miri), not(target_os = "emscripten")))]
const TEST_LENGTHS: &[usize] = &[
2, 3, 4, 5, 6, 7, 8, 9, 10, 15, 16, 17, 20, 24, 30, 32, 33, 35, 50, 100, 200, 500, 1_000,
2_048, 5_000, 10_000, 100_000, 1_100_000,

View file

@ -128,6 +128,7 @@ fn try_unwrap() {
}
#[test]
#[cfg_attr(any(target_os = "emscripten", target_os = "wasi"), ignore)] // no threads
fn into_inner() {
for _ in 0..100
// ^ Increase chances of hitting potential race conditions

View file

@ -1587,9 +1587,7 @@ fn extract_if_complex() {
}
}
// FIXME: re-enable emscripten once it can unwind again
#[test]
#[cfg(not(target_os = "emscripten"))]
#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")]
fn extract_if_consumed_panic() {
use std::rc::Rc;
@ -1640,9 +1638,7 @@ fn extract_if_consumed_panic() {
}
}
// FIXME: Re-enable emscripten once it can catch panics
#[test]
#[cfg(not(target_os = "emscripten"))]
#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")]
fn extract_if_unconsumed_panic() {
use std::rc::Rc;

View file

@ -141,9 +141,6 @@ fn test_custom_state() {
// const { assert!(hash(&Custom { hash: 6 }) == 6) };
}
// FIXME: Instantiated functions with i128 in the signature is not supported in Emscripten.
// See https://github.com/kripken/emscripten-fastcomp/issues/169
#[cfg(not(target_os = "emscripten"))]
#[test]
fn test_indirect_hasher() {
let mut hasher = MyHasher { hash: 0 };

View file

@ -84,9 +84,6 @@ where
F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit<u8>]) -> Option<(&'a [u8], i16)>,
G: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit<u8>]) -> (&'a [u8], i16),
{
if cfg!(target_os = "emscripten") {
return; // using rng pulls in i128 support, which doesn't work
}
let mut rng = crate::test_rng();
let f32_range = Uniform::new(0x0000_0001u32, 0x7f80_0000);
iterate("f32_random_equivalence_test", k, n, f, g, |_| {
@ -100,9 +97,6 @@ where
F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit<u8>]) -> Option<(&'a [u8], i16)>,
G: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit<u8>]) -> (&'a [u8], i16),
{
if cfg!(target_os = "emscripten") {
return; // using rng pulls in i128 support, which doesn't work
}
let mut rng = crate::test_rng();
let f64_range = Uniform::new(0x0000_0000_0000_0001u64, 0x7ff0_0000_0000_0000);
iterate("f64_random_equivalence_test", k, n, f, g, |_| {

View file

@ -51,9 +51,7 @@ macro_rules! test_op {
};
}
test_op!(test_neg_defined, Neg::neg(0), 0, i8, i16, i32, i64, f32, f64);
#[cfg(not(target_os = "emscripten"))]
test_op!(test_neg_defined_128, Neg::neg(0), 0, i128);
test_op!(test_neg_defined, Neg::neg(0), 0, i8, i16, i32, i64, i128, f32, f64);
test_op!(test_not_defined_bool, Not::not(true), false, bool);
@ -69,17 +67,17 @@ macro_rules! test_arith_op {
i16,
i32,
i64,
i128,
isize,
u8,
u16,
u32,
u64,
u128,
usize,
f32,
f64
);
#[cfg(not(target_os = "emscripten"))]
impls_defined!($op, $method($lhs, $rhs), 0, i128, u128);
}
};
($fn_name:ident, $op:ident::$method:ident(&mut $lhs:literal, $rhs:literal)) => {
@ -93,17 +91,17 @@ macro_rules! test_arith_op {
i16,
i32,
i64,
i128,
isize,
u8,
u16,
u32,
u64,
u128,
usize,
f32,
f64
);
#[cfg(not(target_os = "emscripten"))]
impls_defined!($op, $method(&mut $lhs, $rhs), 0, i128, u128);
}
};
}
@ -131,15 +129,15 @@ macro_rules! test_bitop {
i16,
i32,
i64,
i128,
isize,
u8,
u16,
u32,
u64,
u128,
usize
);
#[cfg(not(target_os = "emscripten"))]
impls_defined!($op, $method(0, 0), 0, i128, u128);
impls_defined!($op, $method(false, false), false, bool);
}
};
@ -156,15 +154,15 @@ macro_rules! test_bitop_assign {
i16,
i32,
i64,
i128,
isize,
u8,
u16,
u32,
u64,
u128,
usize
);
#[cfg(not(target_os = "emscripten"))]
impls_defined!($op, $method(&mut 0, 0), 0, i128, u128);
impls_defined!($op, $method(&mut false, false), false, bool);
}
};
@ -182,9 +180,11 @@ macro_rules! test_shift_inner {
$(impl_defined!($op, $method(0,0), 0, $lt, $rt);)+
};
($op:ident::$method:ident, $lt:ty) => {
test_shift_inner!($op::$method, $lt, i8, i16, i32, i64, isize, u8, u16, u32, u64, usize);
#[cfg(not(target_os = "emscripten"))]
test_shift_inner!($op::$method, $lt, i128, u128);
test_shift_inner!(
$op::$method, $lt,
i8, i16, i32, i64, i128, isize,
u8, u16, u32, u64, u128, usize
);
};
}
@ -195,9 +195,11 @@ macro_rules! test_shift {
($test_name:ident, $op:ident::$method:ident) => {
#[test]
fn $test_name() {
test_shift!($op::$method, i8, i16, i32, i64, isize, u8, u16, u32, u64, usize);
#[cfg(not(target_os = "emscripten"))]
test_shift!($op::$method, i128, u128);
test_shift!(
$op::$method,
i8, i16, i32, i64, i128, isize,
u8, u16, u32, u64, u128, usize
);
}
};
}
@ -207,9 +209,11 @@ macro_rules! test_shift_assign_inner {
$(impl_defined!($op, $method(&mut 0,0), 0, $lt, $rt);)+
};
($op:ident::$method:ident, $lt:ty) => {
test_shift_assign_inner!($op::$method, $lt, i8, i16, i32, i64, isize, u8, u16, u32, u64, usize);
#[cfg(not(target_os = "emscripten"))]
test_shift_assign_inner!($op::$method, $lt, i128, u128);
test_shift_assign_inner!(
$op::$method, $lt,
i8, i16, i32, i64, i128, isize,
u8, u16, u32, u64, u128, usize
);
};
}
@ -220,9 +224,11 @@ macro_rules! test_shift_assign {
($test_name:ident, $op:ident::$method:ident) => {
#[test]
fn $test_name() {
test_shift_assign!($op::$method, i8, i16, i32, i64, isize, u8, u16, u32, u64, usize);
#[cfg(not(target_os = "emscripten"))]
test_shift_assign!($op::$method, i128, u128);
test_shift_assign!(
$op::$method,
i8, i16, i32, i64, i128, isize,
u8, u16, u32, u64, u128, usize
);
}
};
}

View file

@ -64,14 +64,12 @@ wrapping_test!(test_wrapping_i8, i8, i8::MIN, i8::MAX);
wrapping_test!(test_wrapping_i16, i16, i16::MIN, i16::MAX);
wrapping_test!(test_wrapping_i32, i32, i32::MIN, i32::MAX);
wrapping_test!(test_wrapping_i64, i64, i64::MIN, i64::MAX);
#[cfg(not(target_os = "emscripten"))]
wrapping_test!(test_wrapping_i128, i128, i128::MIN, i128::MAX);
wrapping_test!(test_wrapping_isize, isize, isize::MIN, isize::MAX);
wrapping_test!(test_wrapping_u8, u8, u8::MIN, u8::MAX);
wrapping_test!(test_wrapping_u16, u16, u16::MIN, u16::MAX);
wrapping_test!(test_wrapping_u32, u32, u32::MIN, u32::MAX);
wrapping_test!(test_wrapping_u64, u64, u64::MIN, u64::MAX);
#[cfg(not(target_os = "emscripten"))]
wrapping_test!(test_wrapping_u128, u128, u128::MIN, u128::MAX);
wrapping_test!(test_wrapping_usize, usize, usize::MIN, usize::MAX);

View file

@ -112,7 +112,6 @@ fn test_neg_zero() {
assert_eq!(Fp::Zero, neg_zero.classify());
}
#[cfg_attr(all(target_arch = "wasm32", target_os = "emscripten"), ignore)] // issue 42630
#[test]
fn test_one() {
let one: f64 = 1.0f64;
@ -165,7 +164,6 @@ fn test_is_finite() {
assert!((-109.2f64).is_finite());
}
#[cfg_attr(all(target_arch = "wasm32", target_os = "emscripten"), ignore)] // issue 42630
#[test]
fn test_is_normal() {
let nan: f64 = f64::NAN;
@ -183,7 +181,6 @@ fn test_is_normal() {
assert!(!1e-308f64.is_normal());
}
#[cfg_attr(all(target_arch = "wasm32", target_os = "emscripten"), ignore)] // issue 42630
#[test]
fn test_classify() {
let nan: f64 = f64::NAN;

View file

@ -126,6 +126,7 @@ mod io_benches {
use crate::io::prelude::*;
#[bench]
#[cfg_attr(target_os = "emscripten", ignore)] // no /dev
fn bench_copy_buf_reader(b: &mut Bencher) {
let mut file_in = File::open("/dev/zero").expect("opening /dev/zero failed");
// use dyn to avoid specializations unrelated to readbuf

View file

@ -7,7 +7,6 @@ use crate::mem::MaybeUninit;
use crate::ops::Deref;
#[test]
#[cfg_attr(target_os = "emscripten", ignore)]
fn read_until() {
let mut buf = Cursor::new(&b"12"[..]);
let mut v = Vec::new();
@ -359,7 +358,6 @@ fn chain_zero_length_read_is_not_eof() {
}
#[bench]
#[cfg_attr(target_os = "emscripten", ignore)]
#[cfg_attr(miri, ignore)] // Miri isn't fast...
fn bench_read_to_end(b: &mut test::Bencher) {
b.iter(|| {

View file

@ -1,6 +1,6 @@
//! This is an implementation of a global allocator on wasm targets when
//! emscripten is not in use. In that situation there's no actual runtime for us
//! to lean on for allocation, so instead we provide our own!
//! emscripten or wasi is not in use. In that situation there's no actual runtime
//! for us to lean on for allocation, so instead we provide our own!
//!
//! The wasm instruction set has two instructions for getting the current
//! amount of memory and growing the amount of memory. These instructions are the

View file

@ -1,8 +1,7 @@
//! System bindings for the wasm/web platform
//!
//! This module contains the facade (aka platform-specific) implementations of
//! OS level functionality for wasm. Note that this wasm is *not* the emscripten
//! wasm, so we have no runtime here.
//! OS level functionality for wasm.
//!
//! This is all super highly experimental and not actually intended for
//! wide/production use yet, it's still all in the experimental category. This

View file

@ -2,7 +2,7 @@
//!
//! This module contains the facade (aka platform-specific) implementations of
//! OS level functionality for wasm. Note that this wasm is *not* the emscripten
//! wasm, so we have no runtime here.
//! or wasi wasm, so we have no runtime here.
//!
//! This is all super highly experimental and not actually intended for
//! wide/production use yet, it's still all in the experimental category. This

View file

@ -1,7 +1,7 @@
#![feature(anonymous_pipe)]
fn main() {
#[cfg(all(not(miri), any(unix, windows)))]
#[cfg(all(not(miri), any(unix, windows), not(target_os = "emscripten")))]
{
use std::io::{Read, pipe};
use std::{env, process};

View file

@ -5,7 +5,8 @@ use std::{env, fs, process, str};
mod common;
#[test]
#[cfg_attr(any(miri, target_os = "wasi"), ignore)] // Process spawning not supported by Miri and wasi
// Process spawning not supported by Miri, Emscripten and wasi
#[cfg_attr(any(miri, target_os = "emscripten", target_os = "wasi"), ignore)]
fn issue_15149() {
// If we're the parent, copy our own binary to a new directory.
let my_path = env::current_exe().unwrap();

View file

@ -1,7 +1,7 @@
//! Module converting command-line arguments into test configuration.
use std::env;
use std::io::{self, IsTerminal};
use std::io::{self, IsTerminal, Write};
use std::path::PathBuf;
use super::options::{ColorConfig, Options, OutputFormat, RunIgnored};
@ -58,7 +58,7 @@ fn optgroups() -> getopts::Options {
.optflag("", "bench", "Run benchmarks instead of tests")
.optflag("", "list", "List all tests and benchmarks")
.optflag("h", "help", "Display this message")
.optopt("", "logfile", "Write logs to the specified file", "PATH")
.optopt("", "logfile", "Write logs to the specified file (deprecated)", "PATH")
.optflag(
"",
"nocapture",
@ -281,6 +281,10 @@ fn parse_opts_impl(matches: getopts::Matches) -> OptRes {
let options = Options::new().display_output(matches.opt_present("show-output"));
if logfile.is_some() {
let _ = write!(io::stderr(), "warning: `--logfile` is deprecated");
}
let test_opts = TestOpts {
list,
filters,

View file

@ -133,9 +133,7 @@ fn ignored_tests_result_in_ignored() {
assert_eq!(result, TrIgnored);
}
// FIXME: Re-enable emscripten once it can catch panics again (introduced by #65251)
#[test]
#[cfg(not(target_os = "emscripten"))]
#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")]
fn test_should_panic() {
fn f() -> Result<(), String> {
@ -164,9 +162,7 @@ fn test_should_panic() {
assert_eq!(result, TrOk);
}
// FIXME: Re-enable emscripten once it can catch panics again (introduced by #65251)
#[test]
#[cfg(not(target_os = "emscripten"))]
#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")]
fn test_should_panic_good_message() {
fn f() -> Result<(), String> {
@ -195,9 +191,7 @@ fn test_should_panic_good_message() {
assert_eq!(result, TrOk);
}
// FIXME: Re-enable emscripten once it can catch panics again (introduced by #65251)
#[test]
#[cfg(not(target_os = "emscripten"))]
#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")]
fn test_should_panic_bad_message() {
use crate::tests::TrFailedMsg;
@ -231,9 +225,7 @@ fn test_should_panic_bad_message() {
assert_eq!(result, TrFailedMsg(failed_msg.to_string()));
}
// FIXME: Re-enable emscripten once it can catch panics again (introduced by #65251)
#[test]
#[cfg(not(target_os = "emscripten"))]
#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")]
fn test_should_panic_non_string_message_type() {
use std::any::TypeId;
@ -272,9 +264,7 @@ fn test_should_panic_non_string_message_type() {
assert_eq!(result, TrFailedMsg(failed_msg));
}
// FIXME: Re-enable emscripten once it can catch panics again (introduced by #65251)
#[test]
#[cfg(not(target_os = "emscripten"))]
#[cfg_attr(not(panic = "unwind"), ignore = "test requires unwinding support")]
fn test_should_panic_but_succeeds() {
let should_panic_variants = [ShouldPanic::Yes, ShouldPanic::YesWithMessage("error message")];

View file

@ -118,7 +118,7 @@ This target is not extensively tested in CI for the rust-lang/rust repository. I
can be tested locally, for example, with:
```sh
./x.py test --target wasm32-unknown-emscripten --skip src/tools/linkchecker
EMCC_CFLAGS="-s MAXIMUM_MEMORY=2GB" ./x.py test --target wasm32-unknown-emscripten --skip src/tools/linkchecker
```
To run these tests, both `emcc` and `node` need to be in your `$PATH`. You can

View file

@ -268,6 +268,8 @@ Controls the format of the output. Valid options:
Writes the results of the tests to the given file.
This option is deprecated.
#### `--report-time`
⚠️ 🚧 This option is [unstable](#unstable-options), and requires the `-Z

143
tests/codegen/s390x-simd.rs Normal file
View file

@ -0,0 +1,143 @@
//! test that s390x vector types are passed using `PassMode::Direct`
//! see also https://github.com/rust-lang/rust/issues/135744
//@ add-core-stubs
//@ compile-flags: --target s390x-unknown-linux-gnu -O
//@ needs-llvm-components: systemz
#![crate_type = "rlib"]
#![feature(no_core, asm_experimental_arch)]
#![feature(s390x_target_feature, simd_ffi, link_llvm_intrinsics, repr_simd)]
#![no_core]
extern crate minicore;
use minicore::*;
#[repr(simd)]
struct i8x16([i8; 16]);
#[repr(simd)]
struct i16x8([i16; 8]);
#[repr(simd)]
struct i32x4([i32; 4]);
#[repr(simd)]
struct i64x2([i64; 2]);
#[repr(simd)]
struct f32x4([f32; 4]);
#[repr(simd)]
struct f64x2([f64; 2]);
#[allow(improper_ctypes)]
extern "C" {
#[link_name = "llvm.smax.v16i8"]
fn vmxb(a: i8x16, b: i8x16) -> i8x16;
#[link_name = "llvm.smax.v8i16"]
fn vmxh(a: i16x8, b: i16x8) -> i16x8;
#[link_name = "llvm.smax.v4i32"]
fn vmxf(a: i32x4, b: i32x4) -> i32x4;
#[link_name = "llvm.smax.v2i64"]
fn vmxg(a: i64x2, b: i64x2) -> i64x2;
}
// CHECK-LABEL: define <16 x i8> @max_i8x16
// CHECK-SAME: <16 x i8> %a, <16 x i8> %b
// CHECK: call <16 x i8> @llvm.smax.v16i8(<16 x i8> %a, <16 x i8> %b)
#[no_mangle]
#[target_feature(enable = "vector")]
pub unsafe extern "C" fn max_i8x16(a: i8x16, b: i8x16) -> i8x16 {
vmxb(a, b)
}
// CHECK-LABEL: define <8 x i16> @max_i16x8
// CHECK-SAME: <8 x i16> %a, <8 x i16> %b
// CHECK: call <8 x i16> @llvm.smax.v8i16(<8 x i16> %a, <8 x i16> %b)
#[no_mangle]
#[target_feature(enable = "vector")]
pub unsafe extern "C" fn max_i16x8(a: i16x8, b: i16x8) -> i16x8 {
vmxh(a, b)
}
// CHECK-LABEL: define <4 x i32> @max_i32x4
// CHECK-SAME: <4 x i32> %a, <4 x i32> %b
// CHECK: call <4 x i32> @llvm.smax.v4i32(<4 x i32> %a, <4 x i32> %b)
#[no_mangle]
#[target_feature(enable = "vector")]
pub unsafe extern "C" fn max_i32x4(a: i32x4, b: i32x4) -> i32x4 {
vmxf(a, b)
}
// CHECK-LABEL: define <2 x i64> @max_i64x2
// CHECK-SAME: <2 x i64> %a, <2 x i64> %b
// CHECK: call <2 x i64> @llvm.smax.v2i64(<2 x i64> %a, <2 x i64> %b)
#[no_mangle]
#[target_feature(enable = "vector")]
pub unsafe extern "C" fn max_i64x2(a: i64x2, b: i64x2) -> i64x2 {
vmxg(a, b)
}
// CHECK-LABEL: define <4 x float> @choose_f32x4
// CHECK-SAME: <4 x float> %a, <4 x float> %b
#[no_mangle]
#[target_feature(enable = "vector")]
pub unsafe extern "C" fn choose_f32x4(a: f32x4, b: f32x4, c: bool) -> f32x4 {
if c { a } else { b }
}
// CHECK-LABEL: define <2 x double> @choose_f64x2
// CHECK-SAME: <2 x double> %a, <2 x double> %b
#[no_mangle]
#[target_feature(enable = "vector")]
pub unsafe extern "C" fn choose_f64x2(a: f64x2, b: f64x2, c: bool) -> f64x2 {
if c { a } else { b }
}
#[repr(C)]
struct Wrapper<T>(T);
#[no_mangle]
#[inline(never)]
#[target_feature(enable = "vector")]
pub unsafe extern "C" fn max_wrapper_i8x16(a: Wrapper<i8x16>, b: Wrapper<i8x16>) -> Wrapper<i8x16> {
// CHECK-LABEL: max_wrapper_i8x16
// CHECK-SAME: sret([16 x i8])
// CHECK-SAME: <16 x i8>
// CHECK-SAME: <16 x i8>
// CHECK: call <16 x i8> @llvm.smax.v16i8
// CHECK-SAME: <16 x i8>
// CHECK-SAME: <16 x i8>
Wrapper(vmxb(a.0, b.0))
}
#[no_mangle]
#[inline(never)]
#[target_feature(enable = "vector")]
pub unsafe extern "C" fn max_wrapper_i64x2(a: Wrapper<i64x2>, b: Wrapper<i64x2>) -> Wrapper<i64x2> {
// CHECK-LABEL: max_wrapper_i64x2
// CHECK-SAME: sret([16 x i8])
// CHECK-SAME: <16 x i8>
// CHECK-SAME: <16 x i8>
// CHECK: call <2 x i64> @llvm.smax.v2i64
// CHECK-SAME: <2 x i64>
// CHECK-SAME: <2 x i64>
Wrapper(vmxg(a.0, b.0))
}
#[no_mangle]
#[inline(never)]
#[target_feature(enable = "vector")]
pub unsafe extern "C" fn choose_wrapper_f64x2(
a: Wrapper<f64x2>,
b: Wrapper<f64x2>,
c: bool,
) -> Wrapper<f64x2> {
// CHECK-LABEL: choose_wrapper_f64x2
// CHECK-SAME: sret([16 x i8])
// CHECK-SAME: <16 x i8>
// CHECK-SAME: <16 x i8>
Wrapper(choose_f64x2(a.0, b.0, c))
}
// CHECK: declare <2 x i64> @llvm.smax.v2i64(<2 x i64>, <2 x i64>)

View file

@ -1,17 +1,26 @@
//@ run-rustfix
#![deny(unused_assignments, unused_variables)]
#![allow(unused_mut)]
struct Object;
fn change_object(object: &mut Object) { //~ HELP you might have meant to mutate
let object2 = Object;
*object = object2; //~ ERROR mismatched types
let object2 = Object;
*object = object2; //~ ERROR mismatched types
}
fn change_object2(object: &mut Object) { //~ ERROR variable `object` is assigned to, but never used
//~^ HELP you might have meant to mutate
let object2 = Object;
*object = object2;
//~^ ERROR `object2` does not live long enough
//~| ERROR value assigned to `object` is never read
}
fn change_object3(object: &mut Object) { //~ ERROR variable `object` is assigned to, but never used
//~^ HELP you might have meant to mutate
let object2 = Object;
let mut object2 = Object; //~ HELP consider changing this to be mutable
*object = object2;
//~^ ERROR `object2` does not live long enough
//~^ ERROR cannot borrow `object2` as mutable
//~| ERROR value assigned to `object` is never read
}
@ -19,4 +28,5 @@ fn main() {
let mut object = Object;
change_object(&mut object);
change_object2(&mut object);
change_object3(&mut object);
}

View file

@ -1,17 +1,26 @@
//@ run-rustfix
#![deny(unused_assignments, unused_variables)]
#![allow(unused_mut)]
struct Object;
fn change_object(mut object: &Object) { //~ HELP you might have meant to mutate
let object2 = Object;
object = object2; //~ ERROR mismatched types
let object2 = Object;
object = object2; //~ ERROR mismatched types
}
fn change_object2(mut object: &Object) { //~ ERROR variable `object` is assigned to, but never used
//~^ HELP you might have meant to mutate
let object2 = Object;
object = &object2;
//~^ ERROR `object2` does not live long enough
//~| ERROR value assigned to `object` is never read
}
fn change_object3(mut object: &mut Object) { //~ ERROR variable `object` is assigned to, but never used
//~^ HELP you might have meant to mutate
let object2 = Object;
object = &object2;
//~^ ERROR `object2` does not live long enough
let object2 = Object; //~ HELP consider changing this to be mutable
object = &mut object2;
//~^ ERROR cannot borrow `object2` as mutable
//~| ERROR value assigned to `object` is never read
}
@ -19,4 +28,5 @@ fn main() {
let mut object = Object;
change_object(&mut object);
change_object2(&mut object);
change_object3(&mut object);
}

View file

@ -1,24 +1,24 @@
error[E0308]: mismatched types
--> $DIR/mut-arg-of-borrowed-type-meant-to-be-arg-of-mut-borrow.rs:7:14
--> $DIR/mut-arg-of-borrowed-type-meant-to-be-arg-of-mut-borrow.rs:8:13
|
LL | fn change_object(mut object: &Object) {
| ------- expected due to this parameter type
LL | let object2 = Object;
LL | object = object2;
| ^^^^^^^ expected `&Object`, found `Object`
LL | let object2 = Object;
LL | object = object2;
| ^^^^^^^ expected `&Object`, found `Object`
|
help: you might have meant to mutate the pointed at value being passed in, instead of changing the reference in the local binding
|
LL ~ fn change_object(object: &mut Object) {
LL | let object2 = Object;
LL ~ *object = object2;
LL | let object2 = Object;
LL ~ *object = object2;
|
error: value assigned to `object` is never read
--> $DIR/mut-arg-of-borrowed-type-meant-to-be-arg-of-mut-borrow.rs:13:5
--> $DIR/mut-arg-of-borrowed-type-meant-to-be-arg-of-mut-borrow.rs:14:4
|
LL | object = &object2;
| ^^^^^^
LL | object = &object2;
| ^^^^^^
|
note: the lint level is defined here
--> $DIR/mut-arg-of-borrowed-type-meant-to-be-arg-of-mut-borrow.rs:2:9
@ -29,12 +29,12 @@ help: you might have meant to mutate the pointed at value being passed in, inste
|
LL ~ fn change_object2(object: &mut Object) {
LL |
LL | let object2 = Object;
LL ~ *object = object2;
LL | let object2 = Object;
LL ~ *object = object2;
|
error: variable `object` is assigned to, but never used
--> $DIR/mut-arg-of-borrowed-type-meant-to-be-arg-of-mut-borrow.rs:10:23
--> $DIR/mut-arg-of-borrowed-type-meant-to-be-arg-of-mut-borrow.rs:11:23
|
LL | fn change_object2(mut object: &Object) {
| ^^^^^^
@ -47,23 +47,56 @@ LL | #![deny(unused_assignments, unused_variables)]
| ^^^^^^^^^^^^^^^^
error[E0597]: `object2` does not live long enough
--> $DIR/mut-arg-of-borrowed-type-meant-to-be-arg-of-mut-borrow.rs:13:14
--> $DIR/mut-arg-of-borrowed-type-meant-to-be-arg-of-mut-borrow.rs:14:13
|
LL | fn change_object2(mut object: &Object) {
| - let's call the lifetime of this reference `'1`
LL |
LL | let object2 = Object;
| ------- binding `object2` declared here
LL | object = &object2;
| ---------^^^^^^^^
| | |
| | borrowed value does not live long enough
| assignment requires that `object2` is borrowed for `'1`
LL | let object2 = Object;
| ------- binding `object2` declared here
LL | object = &object2;
| ---------^^^^^^^^
| | |
| | borrowed value does not live long enough
| assignment requires that `object2` is borrowed for `'1`
...
LL | }
| - `object2` dropped here while still borrowed
error: aborting due to 4 previous errors
error: value assigned to `object` is never read
--> $DIR/mut-arg-of-borrowed-type-meant-to-be-arg-of-mut-borrow.rs:22:5
|
LL | object = &mut object2;
| ^^^^^^
|
help: you might have meant to mutate the pointed at value being passed in, instead of changing the reference in the local binding
|
LL ~ fn change_object3(object: &mut Object) {
LL |
LL | let object2 = Object;
LL ~ *object = object2;
|
Some errors have detailed explanations: E0308, E0597.
error: variable `object` is assigned to, but never used
--> $DIR/mut-arg-of-borrowed-type-meant-to-be-arg-of-mut-borrow.rs:19:23
|
LL | fn change_object3(mut object: &mut Object) {
| ^^^^^^
|
= note: consider using `_object` instead
error[E0596]: cannot borrow `object2` as mutable, as it is not declared as mutable
--> $DIR/mut-arg-of-borrowed-type-meant-to-be-arg-of-mut-borrow.rs:22:14
|
LL | object = &mut object2;
| ^^^^^^^^^^^^ cannot borrow as mutable
|
help: consider changing this to be mutable
|
LL | let mut object2 = Object;
| +++
error: aborting due to 7 previous errors
Some errors have detailed explanations: E0308, E0596, E0597.
For more information about an error, try `rustc --explain E0308`.

View file

@ -0,0 +1,28 @@
//@ only-wasm32
//@ revisions: v0_1_0 v0_2_87 v0_2_88 v0_3_0 v1_0_0
//@[v0_1_0] check-fail
//@[v0_1_0] rustc-env:CARGO_PKG_VERSION_MAJOR=0
//@[v0_1_0] rustc-env:CARGO_PKG_VERSION_MINOR=1
//@[v0_1_0] rustc-env:CARGO_PKG_VERSION_PATCH=0
//@[v0_2_87] check-fail
//@[v0_2_87] rustc-env:CARGO_PKG_VERSION_MAJOR=0
//@[v0_2_87] rustc-env:CARGO_PKG_VERSION_MINOR=2
//@[v0_2_87] rustc-env:CARGO_PKG_VERSION_PATCH=87
//@[v0_2_88] check-pass
//@[v0_2_88] rustc-env:CARGO_PKG_VERSION_MAJOR=0
//@[v0_2_88] rustc-env:CARGO_PKG_VERSION_MINOR=2
//@[v0_2_88] rustc-env:CARGO_PKG_VERSION_PATCH=88
//@[v0_3_0] check-pass
//@[v0_3_0] rustc-env:CARGO_PKG_VERSION_MAJOR=0
//@[v0_3_0] rustc-env:CARGO_PKG_VERSION_MINOR=3
//@[v0_3_0] rustc-env:CARGO_PKG_VERSION_PATCH=0
//@[v1_0_0] check-pass
//@[v1_0_0] rustc-env:CARGO_PKG_VERSION_MAJOR=1
//@[v1_0_0] rustc-env:CARGO_PKG_VERSION_MINOR=0
//@[v1_0_0] rustc-env:CARGO_PKG_VERSION_PATCH=0
#![crate_name = "wasm_bindgen"]
//[v0_1_0]~^ ERROR: older versions of the `wasm-bindgen` crate
//[v0_2_87]~^^ ERROR: older versions of the `wasm-bindgen` crate
fn main() {}

View file

@ -0,0 +1,8 @@
error: older versions of the `wasm-bindgen` crate are incompatible with current versions of Rust; please update to `wasm-bindgen` v0.2.88
--> $DIR/wasm-bindgen-broken-error.rs:24:1
|
LL | #![crate_name = "wasm_bindgen"]
| ^
error: aborting due to 1 previous error

View file

@ -0,0 +1,8 @@
error: older versions of the `wasm-bindgen` crate are incompatible with current versions of Rust; please update to `wasm-bindgen` v0.2.88
--> $DIR/wasm-bindgen-broken-error.rs:24:1
|
LL | #![crate_name = "wasm_bindgen"]
| ^
error: aborting due to 1 previous error