Merge from rustc

This commit is contained in:
The Miri Cronjob Bot 2025-05-19 05:02:00 +00:00
commit 1bb2d7352a
104 changed files with 2204 additions and 888 deletions

View file

@ -738,7 +738,7 @@ dependencies = [
"tracing-subscriber",
"unified-diff",
"walkdir",
"windows 0.59.0",
"windows",
]
[[package]]
@ -1587,7 +1587,7 @@ dependencies = [
"js-sys",
"log",
"wasm-bindgen",
"windows-core 0.61.0",
"windows-core",
]
[[package]]
@ -3493,7 +3493,7 @@ dependencies = [
"thorin-dwp",
"tracing",
"wasm-encoder 0.219.2",
"windows 0.59.0",
"windows",
]
[[package]]
@ -3552,7 +3552,7 @@ dependencies = [
"tempfile",
"thin-vec",
"tracing",
"windows 0.59.0",
"windows",
]
[[package]]
@ -3615,7 +3615,7 @@ dependencies = [
"shlex",
"stable_mir",
"tracing",
"windows 0.59.0",
"windows",
]
[[package]]
@ -3670,7 +3670,7 @@ dependencies = [
"termcolor",
"termize",
"tracing",
"windows 0.59.0",
"windows",
]
[[package]]
@ -4415,7 +4415,7 @@ dependencies = [
"smallvec",
"termize",
"tracing",
"windows 0.59.0",
"windows",
]
[[package]]
@ -5102,7 +5102,7 @@ dependencies = [
"libc",
"objc2-core-foundation",
"objc2-io-kit",
"windows 0.61.1",
"windows",
]
[[package]]
@ -6002,16 +6002,6 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows"
version = "0.59.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7f919aee0a93304be7f62e8e5027811bbba96bcb1de84d6618be56e43f8a32a1"
dependencies = [
"windows-core 0.59.0",
"windows-targets 0.53.0",
]
[[package]]
name = "windows"
version = "0.61.1"
@ -6019,7 +6009,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c5ee8f3d025738cb02bad7868bbb5f8a6327501e870bf51f1b455b0a2454a419"
dependencies = [
"windows-collections",
"windows-core 0.61.0",
"windows-core",
"windows-future",
"windows-link",
"windows-numerics",
@ -6042,20 +6032,7 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8"
dependencies = [
"windows-core 0.61.0",
]
[[package]]
name = "windows-core"
version = "0.59.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "810ce18ed2112484b0d4e15d022e5f598113e220c53e373fb31e67e21670c1ce"
dependencies = [
"windows-implement 0.59.0",
"windows-interface",
"windows-result",
"windows-strings 0.3.1",
"windows-targets 0.53.0",
"windows-core",
]
[[package]]
@ -6064,11 +6041,11 @@ version = "0.61.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980"
dependencies = [
"windows-implement 0.60.0",
"windows-implement",
"windows-interface",
"windows-link",
"windows-result",
"windows-strings 0.4.0",
"windows-strings",
]
[[package]]
@ -6077,21 +6054,10 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a1d6bbefcb7b60acd19828e1bc965da6fcf18a7e39490c5f8be71e54a19ba32"
dependencies = [
"windows-core 0.61.0",
"windows-core",
"windows-link",
]
[[package]]
name = "windows-implement"
version = "0.59.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "83577b051e2f49a058c308f17f273b570a6a758386fc291b5f6a934dd84e48c1"
dependencies = [
"proc-macro2",
"quote",
"syn 2.0.101",
]
[[package]]
name = "windows-implement"
version = "0.60.0"
@ -6126,7 +6092,7 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1"
dependencies = [
"windows-core 0.61.0",
"windows-core",
"windows-link",
]
@ -6139,15 +6105,6 @@ dependencies = [
"windows-link",
]
[[package]]
name = "windows-strings"
version = "0.3.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "87fa48cc5d406560701792be122a10132491cff9d0aeb23583cc2dcafc847319"
dependencies = [
"windows-link",
]
[[package]]
name = "windows-strings"
version = "0.4.0"

View file

@ -610,7 +610,7 @@ impl Pat {
/// Walk top-down and call `it` in each place where a pattern occurs
/// starting with the root pattern `walk` is called on. If `it` returns
/// false then we will descend no further but siblings will be processed.
pub fn walk(&self, it: &mut impl FnMut(&Pat) -> bool) {
pub fn walk<'ast>(&'ast self, it: &mut impl FnMut(&'ast Pat) -> bool) {
if !it(self) {
return;
}

View file

@ -18,7 +18,31 @@ use {rustc_ast as ast, rustc_parse_format as parse};
use crate::errors;
use crate::util::{ExprToSpannedString, expr_to_spanned_string};
pub struct AsmArgs {
/// An argument to one of the `asm!` macros. The argument is syntactically valid, but is otherwise
/// not validated at all.
pub struct AsmArg {
pub kind: AsmArgKind,
pub span: Span,
}
pub enum AsmArgKind {
Template(P<ast::Expr>),
Operand(Option<Symbol>, ast::InlineAsmOperand),
Options(Vec<AsmOption>),
ClobberAbi(Vec<(Symbol, Span)>),
}
pub struct AsmOption {
pub symbol: Symbol,
pub span: Span,
// A bitset, with only the bit for this option's symbol set.
pub options: ast::InlineAsmOptions,
// Used when suggesting to remove an option.
pub span_with_comma: Span,
}
/// Validated assembly arguments, ready for macro expansion.
struct ValidatedAsmArgs {
pub templates: Vec<P<ast::Expr>>,
pub operands: Vec<(ast::InlineAsmOperand, Span)>,
named_args: FxIndexMap<Symbol, usize>,
@ -59,41 +83,95 @@ fn eat_operand_keyword<'a>(
}
}
fn parse_args<'a>(
ecx: &ExtCtxt<'a>,
sp: Span,
tts: TokenStream,
fn parse_asm_operand<'a>(
p: &mut Parser<'a>,
asm_macro: AsmMacro,
) -> PResult<'a, AsmArgs> {
let mut p = ecx.new_parser_from_tts(tts);
parse_asm_args(&mut p, sp, asm_macro)
) -> PResult<'a, Option<ast::InlineAsmOperand>> {
let dcx = p.dcx();
Ok(Some(if eat_operand_keyword(p, exp!(In), asm_macro)? {
let reg = parse_reg(p)?;
if p.eat_keyword(exp!(Underscore)) {
let err = dcx.create_err(errors::AsmUnderscoreInput { span: p.token.span });
return Err(err);
}
let expr = p.parse_expr()?;
ast::InlineAsmOperand::In { reg, expr }
} else if eat_operand_keyword(p, exp!(Out), asm_macro)? {
let reg = parse_reg(p)?;
let expr = if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) };
ast::InlineAsmOperand::Out { reg, expr, late: false }
} else if eat_operand_keyword(p, exp!(Lateout), asm_macro)? {
let reg = parse_reg(p)?;
let expr = if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) };
ast::InlineAsmOperand::Out { reg, expr, late: true }
} else if eat_operand_keyword(p, exp!(Inout), asm_macro)? {
let reg = parse_reg(p)?;
if p.eat_keyword(exp!(Underscore)) {
let err = dcx.create_err(errors::AsmUnderscoreInput { span: p.token.span });
return Err(err);
}
let expr = p.parse_expr()?;
if p.eat(exp!(FatArrow)) {
let out_expr =
if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) };
ast::InlineAsmOperand::SplitInOut { reg, in_expr: expr, out_expr, late: false }
} else {
ast::InlineAsmOperand::InOut { reg, expr, late: false }
}
} else if eat_operand_keyword(p, exp!(Inlateout), asm_macro)? {
let reg = parse_reg(p)?;
if p.eat_keyword(exp!(Underscore)) {
let err = dcx.create_err(errors::AsmUnderscoreInput { span: p.token.span });
return Err(err);
}
let expr = p.parse_expr()?;
if p.eat(exp!(FatArrow)) {
let out_expr =
if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) };
ast::InlineAsmOperand::SplitInOut { reg, in_expr: expr, out_expr, late: true }
} else {
ast::InlineAsmOperand::InOut { reg, expr, late: true }
}
} else if eat_operand_keyword(p, exp!(Label), asm_macro)? {
let block = p.parse_block()?;
ast::InlineAsmOperand::Label { block }
} else if p.eat_keyword(exp!(Const)) {
let anon_const = p.parse_expr_anon_const()?;
ast::InlineAsmOperand::Const { anon_const }
} else if p.eat_keyword(exp!(Sym)) {
let expr = p.parse_expr()?;
let ast::ExprKind::Path(qself, path) = &expr.kind else {
let err = dcx.create_err(errors::AsmSymNoPath { span: expr.span });
return Err(err);
};
let sym =
ast::InlineAsmSym { id: ast::DUMMY_NODE_ID, qself: qself.clone(), path: path.clone() };
ast::InlineAsmOperand::Sym { sym }
} else {
return Ok(None);
}))
}
// Primarily public for rustfmt consumption.
// Internal consumers should continue to leverage `expand_asm`/`expand__global_asm`
// Public for rustfmt.
pub fn parse_asm_args<'a>(
p: &mut Parser<'a>,
sp: Span,
asm_macro: AsmMacro,
) -> PResult<'a, AsmArgs> {
) -> PResult<'a, Vec<AsmArg>> {
let dcx = p.dcx();
if p.token == token::Eof {
return Err(dcx.create_err(errors::AsmRequiresTemplate { span: sp }));
}
let mut args = Vec::new();
let first_template = p.parse_expr()?;
let mut args = AsmArgs {
templates: vec![first_template],
operands: vec![],
named_args: Default::default(),
reg_args: Default::default(),
clobber_abis: Vec::new(),
options: ast::InlineAsmOptions::empty(),
options_spans: vec![],
};
args.push(AsmArg { span: first_template.span, kind: AsmArgKind::Template(first_template) });
let mut allow_templates = true;
while p.token != token::Eof {
if !p.eat(exp!(Comma)) {
if allow_templates {
@ -104,27 +182,39 @@ pub fn parse_asm_args<'a>(
return Err(p.expect(exp!(Comma)).err().unwrap());
}
}
// Accept trailing commas.
if p.token == token::Eof {
break;
} // accept trailing commas
// Parse clobber_abi
if p.eat_keyword(exp!(ClobberAbi)) {
parse_clobber_abi(p, &mut args)?;
allow_templates = false;
continue;
}
// Parse options
if p.eat_keyword(exp!(Options)) {
parse_options(p, &mut args, asm_macro)?;
allow_templates = false;
continue;
}
let span_start = p.token.span;
// Parse operand names
// Parse `clobber_abi`.
if p.eat_keyword(exp!(ClobberAbi)) {
allow_templates = false;
args.push(AsmArg {
kind: AsmArgKind::ClobberAbi(parse_clobber_abi(p)?),
span: span_start.to(p.prev_token.span),
});
continue;
}
// Parse `options`.
if p.eat_keyword(exp!(Options)) {
allow_templates = false;
args.push(AsmArg {
kind: AsmArgKind::Options(parse_options(p, asm_macro)?),
span: span_start.to(p.prev_token.span),
});
continue;
}
// Parse operand names.
let name = if p.token.is_ident() && p.look_ahead(1, |t| *t == token::Eq) {
let (ident, _) = p.token.ident().unwrap();
p.bump();
@ -135,69 +225,13 @@ pub fn parse_asm_args<'a>(
None
};
let mut explicit_reg = false;
let op = if eat_operand_keyword(p, exp!(In), asm_macro)? {
let reg = parse_reg(p, &mut explicit_reg)?;
if p.eat_keyword(exp!(Underscore)) {
let err = dcx.create_err(errors::AsmUnderscoreInput { span: p.token.span });
return Err(err);
}
let expr = p.parse_expr()?;
ast::InlineAsmOperand::In { reg, expr }
} else if eat_operand_keyword(p, exp!(Out), asm_macro)? {
let reg = parse_reg(p, &mut explicit_reg)?;
let expr = if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) };
ast::InlineAsmOperand::Out { reg, expr, late: false }
} else if eat_operand_keyword(p, exp!(Lateout), asm_macro)? {
let reg = parse_reg(p, &mut explicit_reg)?;
let expr = if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) };
ast::InlineAsmOperand::Out { reg, expr, late: true }
} else if eat_operand_keyword(p, exp!(Inout), asm_macro)? {
let reg = parse_reg(p, &mut explicit_reg)?;
if p.eat_keyword(exp!(Underscore)) {
let err = dcx.create_err(errors::AsmUnderscoreInput { span: p.token.span });
return Err(err);
}
let expr = p.parse_expr()?;
if p.eat(exp!(FatArrow)) {
let out_expr =
if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) };
ast::InlineAsmOperand::SplitInOut { reg, in_expr: expr, out_expr, late: false }
} else {
ast::InlineAsmOperand::InOut { reg, expr, late: false }
}
} else if eat_operand_keyword(p, exp!(Inlateout), asm_macro)? {
let reg = parse_reg(p, &mut explicit_reg)?;
if p.eat_keyword(exp!(Underscore)) {
let err = dcx.create_err(errors::AsmUnderscoreInput { span: p.token.span });
return Err(err);
}
let expr = p.parse_expr()?;
if p.eat(exp!(FatArrow)) {
let out_expr =
if p.eat_keyword(exp!(Underscore)) { None } else { Some(p.parse_expr()?) };
ast::InlineAsmOperand::SplitInOut { reg, in_expr: expr, out_expr, late: true }
} else {
ast::InlineAsmOperand::InOut { reg, expr, late: true }
}
} else if eat_operand_keyword(p, exp!(Label), asm_macro)? {
let block = p.parse_block()?;
ast::InlineAsmOperand::Label { block }
} else if p.eat_keyword(exp!(Const)) {
let anon_const = p.parse_expr_anon_const()?;
ast::InlineAsmOperand::Const { anon_const }
} else if p.eat_keyword(exp!(Sym)) {
let expr = p.parse_expr()?;
let ast::ExprKind::Path(qself, path) = &expr.kind else {
let err = dcx.create_err(errors::AsmSymNoPath { span: expr.span });
return Err(err);
};
let sym = ast::InlineAsmSym {
id: ast::DUMMY_NODE_ID,
qself: qself.clone(),
path: path.clone(),
};
ast::InlineAsmOperand::Sym { sym }
if let Some(op) = parse_asm_operand(p, asm_macro)? {
allow_templates = false;
args.push(AsmArg {
span: span_start.to(p.prev_token.span),
kind: AsmArgKind::Operand(name, op),
});
} else if allow_templates {
let template = p.parse_expr()?;
// If it can't possibly expand to a string, provide diagnostics here to include other
@ -217,55 +251,164 @@ pub fn parse_asm_args<'a>(
return Err(err);
}
}
args.templates.push(template);
continue;
args.push(AsmArg { span: template.span, kind: AsmArgKind::Template(template) });
} else {
p.unexpected_any()?
};
allow_templates = false;
let span = span_start.to(p.prev_token.span);
let slot = args.operands.len();
args.operands.push((op, span));
// Validate the order of named, positional & explicit register operands and
// clobber_abi/options. We do this at the end once we have the full span
// of the argument available.
if explicit_reg {
if name.is_some() {
dcx.emit_err(errors::AsmExplicitRegisterName { span });
}
args.reg_args.insert(slot);
} else if let Some(name) = name {
if let Some(&prev) = args.named_args.get(&name) {
dcx.emit_err(errors::AsmDuplicateArg { span, name, prev: args.operands[prev].1 });
continue;
}
args.named_args.insert(name, slot);
} else if !args.named_args.is_empty() || !args.reg_args.is_empty() {
let named = args.named_args.values().map(|p| args.operands[*p].1).collect();
let explicit = args.reg_args.iter().map(|p| args.operands[p].1).collect();
dcx.emit_err(errors::AsmPositionalAfter { span, named, explicit });
}
}
if args.options.contains(ast::InlineAsmOptions::NOMEM)
&& args.options.contains(ast::InlineAsmOptions::READONLY)
Ok(args)
}
fn parse_args<'a>(
ecx: &ExtCtxt<'a>,
sp: Span,
tts: TokenStream,
asm_macro: AsmMacro,
) -> PResult<'a, ValidatedAsmArgs> {
let args = parse_asm_args(&mut ecx.new_parser_from_tts(tts), sp, asm_macro)?;
validate_asm_args(ecx, asm_macro, args)
}
fn validate_asm_args<'a>(
ecx: &ExtCtxt<'a>,
asm_macro: AsmMacro,
args: Vec<AsmArg>,
) -> PResult<'a, ValidatedAsmArgs> {
let dcx = ecx.dcx();
let mut validated = ValidatedAsmArgs {
templates: vec![],
operands: vec![],
named_args: Default::default(),
reg_args: Default::default(),
clobber_abis: Vec::new(),
options: ast::InlineAsmOptions::empty(),
options_spans: vec![],
};
let mut allow_templates = true;
for arg in args {
match arg.kind {
AsmArgKind::Template(template) => {
// The error for the first template is delayed.
if !allow_templates {
match template.kind {
ast::ExprKind::Lit(token_lit)
if matches!(
token_lit.kind,
token::LitKind::Str | token::LitKind::StrRaw(_)
) => {}
ast::ExprKind::MacCall(..) => {}
_ => {
let err = dcx.create_err(errors::AsmExpectedOther {
span: template.span,
is_inline_asm: matches!(asm_macro, AsmMacro::Asm),
});
return Err(err);
}
}
}
validated.templates.push(template);
}
AsmArgKind::Operand(name, op) => {
allow_templates = false;
let explicit_reg = matches!(op.reg(), Some(ast::InlineAsmRegOrRegClass::Reg(_)));
let span = arg.span;
let slot = validated.operands.len();
validated.operands.push((op, span));
// Validate the order of named, positional & explicit register operands and
// clobber_abi/options. We do this at the end once we have the full span
// of the argument available.
if explicit_reg {
if name.is_some() {
dcx.emit_err(errors::AsmExplicitRegisterName { span });
}
validated.reg_args.insert(slot);
} else if let Some(name) = name {
if let Some(&prev) = validated.named_args.get(&name) {
dcx.emit_err(errors::AsmDuplicateArg {
span,
name,
prev: validated.operands[prev].1,
});
continue;
}
validated.named_args.insert(name, slot);
} else if !validated.named_args.is_empty() || !validated.reg_args.is_empty() {
let named =
validated.named_args.values().map(|p| validated.operands[*p].1).collect();
let explicit =
validated.reg_args.iter().map(|p| validated.operands[p].1).collect();
dcx.emit_err(errors::AsmPositionalAfter { span, named, explicit });
}
}
AsmArgKind::Options(new_options) => {
allow_templates = false;
for asm_option in new_options {
let AsmOption { span, symbol, span_with_comma, options } = asm_option;
if !asm_macro.is_supported_option(options) {
// Tool-only output.
dcx.emit_err(errors::AsmUnsupportedOption {
span,
symbol,
span_with_comma,
macro_name: asm_macro.macro_name(),
});
} else if validated.options.contains(options) {
// Tool-only output.
dcx.emit_err(errors::AsmOptAlreadyprovided {
span,
symbol,
span_with_comma,
});
} else {
validated.options |= asm_option.options;
}
}
validated.options_spans.push(arg.span);
}
AsmArgKind::ClobberAbi(new_abis) => {
allow_templates = false;
match &new_abis[..] {
// This should have errored above during parsing.
[] => unreachable!(),
[(abi, _span)] => validated.clobber_abis.push((*abi, arg.span)),
_ => validated.clobber_abis.extend(new_abis),
}
}
}
}
if validated.options.contains(ast::InlineAsmOptions::NOMEM)
&& validated.options.contains(ast::InlineAsmOptions::READONLY)
{
let spans = args.options_spans.clone();
let spans = validated.options_spans.clone();
dcx.emit_err(errors::AsmMutuallyExclusive { spans, opt1: "nomem", opt2: "readonly" });
}
if args.options.contains(ast::InlineAsmOptions::PURE)
&& args.options.contains(ast::InlineAsmOptions::NORETURN)
if validated.options.contains(ast::InlineAsmOptions::PURE)
&& validated.options.contains(ast::InlineAsmOptions::NORETURN)
{
let spans = args.options_spans.clone();
let spans = validated.options_spans.clone();
dcx.emit_err(errors::AsmMutuallyExclusive { spans, opt1: "pure", opt2: "noreturn" });
}
if args.options.contains(ast::InlineAsmOptions::PURE)
&& !args.options.intersects(ast::InlineAsmOptions::NOMEM | ast::InlineAsmOptions::READONLY)
if validated.options.contains(ast::InlineAsmOptions::PURE)
&& !validated
.options
.intersects(ast::InlineAsmOptions::NOMEM | ast::InlineAsmOptions::READONLY)
{
let spans = args.options_spans.clone();
let spans = validated.options_spans.clone();
dcx.emit_err(errors::AsmPureCombine { spans });
}
@ -273,7 +416,7 @@ pub fn parse_asm_args<'a>(
let mut outputs_sp = vec![];
let mut regclass_outputs = vec![];
let mut labels_sp = vec![];
for (op, op_sp) in &args.operands {
for (op, op_sp) in &validated.operands {
match op {
ast::InlineAsmOperand::Out { reg, expr, .. }
| ast::InlineAsmOperand::SplitInOut { reg, out_expr: expr, .. } => {
@ -296,10 +439,10 @@ pub fn parse_asm_args<'a>(
_ => {}
}
}
if args.options.contains(ast::InlineAsmOptions::PURE) && !have_real_output {
dcx.emit_err(errors::AsmPureNoOutput { spans: args.options_spans.clone() });
if validated.options.contains(ast::InlineAsmOptions::PURE) && !have_real_output {
dcx.emit_err(errors::AsmPureNoOutput { spans: validated.options_spans.clone() });
}
if args.options.contains(ast::InlineAsmOptions::NORETURN)
if validated.options.contains(ast::InlineAsmOptions::NORETURN)
&& !outputs_sp.is_empty()
&& labels_sp.is_empty()
{
@ -307,15 +450,15 @@ pub fn parse_asm_args<'a>(
// Bail out now since this is likely to confuse MIR
return Err(err);
}
if args.options.contains(ast::InlineAsmOptions::MAY_UNWIND) && !labels_sp.is_empty() {
if validated.options.contains(ast::InlineAsmOptions::MAY_UNWIND) && !labels_sp.is_empty() {
dcx.emit_err(errors::AsmMayUnwind { labels_sp });
}
if !args.clobber_abis.is_empty() {
if !validated.clobber_abis.is_empty() {
match asm_macro {
AsmMacro::GlobalAsm | AsmMacro::NakedAsm => {
let err = dcx.create_err(errors::AsmUnsupportedClobberAbi {
spans: args.clobber_abis.iter().map(|(_, span)| *span).collect(),
spans: validated.clobber_abis.iter().map(|(_, span)| *span).collect(),
macro_name: asm_macro.macro_name(),
});
@ -326,71 +469,21 @@ pub fn parse_asm_args<'a>(
if !regclass_outputs.is_empty() {
dcx.emit_err(errors::AsmClobberNoReg {
spans: regclass_outputs,
clobbers: args.clobber_abis.iter().map(|(_, span)| *span).collect(),
clobbers: validated.clobber_abis.iter().map(|(_, span)| *span).collect(),
});
}
}
}
}
Ok(args)
Ok(validated)
}
/// Report a duplicate option error.
///
/// This function must be called immediately after the option token is parsed.
/// Otherwise, the suggestion will be incorrect.
fn err_duplicate_option(p: &Parser<'_>, symbol: Symbol, span: Span) {
// Tool-only output
let full_span = if p.token == token::Comma { span.to(p.token.span) } else { span };
p.dcx().emit_err(errors::AsmOptAlreadyprovided { span, symbol, full_span });
}
/// Report an invalid option error.
///
/// This function must be called immediately after the option token is parsed.
/// Otherwise, the suggestion will be incorrect.
fn err_unsupported_option(p: &Parser<'_>, asm_macro: AsmMacro, symbol: Symbol, span: Span) {
// Tool-only output
let full_span = if p.token == token::Comma { span.to(p.token.span) } else { span };
p.dcx().emit_err(errors::AsmUnsupportedOption {
span,
symbol,
full_span,
macro_name: asm_macro.macro_name(),
});
}
/// Try to set the provided option in the provided `AsmArgs`.
/// If it is already set, report a duplicate option error.
///
/// This function must be called immediately after the option token is parsed.
/// Otherwise, the error will not point to the correct spot.
fn try_set_option<'a>(
p: &Parser<'a>,
args: &mut AsmArgs,
asm_macro: AsmMacro,
symbol: Symbol,
option: ast::InlineAsmOptions,
) {
if !asm_macro.is_supported_option(option) {
err_unsupported_option(p, asm_macro, symbol, p.prev_token.span);
} else if args.options.contains(option) {
err_duplicate_option(p, symbol, p.prev_token.span);
} else {
args.options |= option;
}
}
fn parse_options<'a>(
p: &mut Parser<'a>,
args: &mut AsmArgs,
asm_macro: AsmMacro,
) -> PResult<'a, ()> {
let span_start = p.prev_token.span;
fn parse_options<'a>(p: &mut Parser<'a>, asm_macro: AsmMacro) -> PResult<'a, Vec<AsmOption>> {
p.expect(exp!(OpenParen))?;
let mut asm_options = Vec::new();
while !p.eat(exp!(CloseParen)) {
const OPTIONS: [(ExpKeywordPair, ast::InlineAsmOptions); ast::InlineAsmOptions::COUNT] = [
(exp!(Pure), ast::InlineAsmOptions::PURE),
@ -405,38 +498,38 @@ fn parse_options<'a>(
];
'blk: {
for (exp, option) in OPTIONS {
let kw_matched = if asm_macro.is_supported_option(option) {
for (exp, options) in OPTIONS {
// Gives a more accurate list of expected next tokens.
let kw_matched = if asm_macro.is_supported_option(options) {
p.eat_keyword(exp)
} else {
p.eat_keyword_noexpect(exp.kw)
};
if kw_matched {
try_set_option(p, args, asm_macro, exp.kw, option);
let span = p.prev_token.span;
let span_with_comma =
if p.token == token::Comma { span.to(p.token.span) } else { span };
asm_options.push(AsmOption { symbol: exp.kw, span, options, span_with_comma });
break 'blk;
}
}
return p.unexpected();
return p.unexpected_any();
}
// Allow trailing commas
// Allow trailing commas.
if p.eat(exp!(CloseParen)) {
break;
}
p.expect(exp!(Comma))?;
}
let new_span = span_start.to(p.prev_token.span);
args.options_spans.push(new_span);
Ok(())
Ok(asm_options)
}
fn parse_clobber_abi<'a>(p: &mut Parser<'a>, args: &mut AsmArgs) -> PResult<'a, ()> {
let span_start = p.prev_token.span;
fn parse_clobber_abi<'a>(p: &mut Parser<'a>) -> PResult<'a, Vec<(Symbol, Span)>> {
p.expect(exp!(OpenParen))?;
if p.eat(exp!(CloseParen)) {
@ -462,31 +555,14 @@ fn parse_clobber_abi<'a>(p: &mut Parser<'a>, args: &mut AsmArgs) -> PResult<'a,
p.expect(exp!(Comma))?;
}
let full_span = span_start.to(p.prev_token.span);
match &new_abis[..] {
// should have errored above during parsing
[] => unreachable!(),
[(abi, _span)] => args.clobber_abis.push((*abi, full_span)),
abis => {
for (abi, span) in abis {
args.clobber_abis.push((*abi, *span));
}
}
}
Ok(())
Ok(new_abis)
}
fn parse_reg<'a>(
p: &mut Parser<'a>,
explicit_reg: &mut bool,
) -> PResult<'a, ast::InlineAsmRegOrRegClass> {
fn parse_reg<'a>(p: &mut Parser<'a>) -> PResult<'a, ast::InlineAsmRegOrRegClass> {
p.expect(exp!(OpenParen))?;
let result = match p.token.uninterpolate().kind {
token::Ident(name, IdentIsRaw::No) => ast::InlineAsmRegOrRegClass::RegClass(name),
token::Literal(token::Lit { kind: token::LitKind::Str, symbol, suffix: _ }) => {
*explicit_reg = true;
ast::InlineAsmRegOrRegClass::Reg(symbol)
}
_ => {
@ -503,7 +579,7 @@ fn parse_reg<'a>(
fn expand_preparsed_asm(
ecx: &mut ExtCtxt<'_>,
asm_macro: AsmMacro,
args: AsmArgs,
args: ValidatedAsmArgs,
) -> ExpandResult<Result<ast::InlineAsm, ErrorGuaranteed>, ()> {
let mut template = vec![];
// Register operands are implicitly used since they are not allowed to be

View file

@ -910,7 +910,7 @@ pub(crate) struct AsmOptAlreadyprovided {
pub(crate) span: Span,
pub(crate) symbol: Symbol,
#[suggestion(code = "", applicability = "machine-applicable", style = "tool-only")]
pub(crate) full_span: Span,
pub(crate) span_with_comma: Span,
}
#[derive(Diagnostic)]
@ -921,7 +921,7 @@ pub(crate) struct AsmUnsupportedOption {
pub(crate) span: Span,
pub(crate) symbol: Symbol,
#[suggestion(code = "", applicability = "machine-applicable", style = "tool-only")]
pub(crate) full_span: Span,
pub(crate) span_with_comma: Span,
pub(crate) macro_name: &'static str,
}

View file

@ -58,5 +58,5 @@ default-features = false
features = ["read_core", "elf", "macho", "pe", "xcoff", "unaligned", "archive", "write", "wasm"]
[target.'cfg(windows)'.dependencies.windows]
version = "0.59.0"
version = "0.61.0"
features = ["Win32_Globalization"]

View file

@ -38,7 +38,7 @@ features = ["nightly"] # for may_dangle
version = "0.12"
[target.'cfg(windows)'.dependencies.windows]
version = "0.59.0"
version = "0.61.0"
features = [
"Win32_Foundation",
"Win32_Storage_FileSystem",

View file

@ -60,7 +60,7 @@ libc = "0.2"
# tidy-alphabetical-end
[target.'cfg(windows)'.dependencies.windows]
version = "0.59.0"
version = "0.61.0"
features = [
"Win32_System_Diagnostics_Debug",
]

View file

@ -33,7 +33,7 @@ tracing = "0.1"
# tidy-alphabetical-end
[target.'cfg(windows)'.dependencies.windows]
version = "0.59.0"
version = "0.61.0"
features = [
"Win32_Foundation",
"Win32_Security",

View file

@ -82,6 +82,8 @@ declare_features! (
(accepted, attr_literals, "1.30.0", Some(34981)),
/// Allows overloading augmented assignment operations like `a += b`.
(accepted, augmented_assignments, "1.8.0", Some(28235)),
/// Allows using `avx512*` target features.
(accepted, avx512_target_feature, "CURRENT_RUSTC_VERSION", Some(44839)),
/// Allows mixing bind-by-move in patterns and references to those identifiers in guards.
(accepted, bind_by_move_pattern_guards, "1.39.0", Some(15287)),
/// Allows bindings in the subpattern of a binding pattern.

View file

@ -318,7 +318,6 @@ declare_features! (
(unstable, aarch64_ver_target_feature, "1.27.0", Some(44839)),
(unstable, apx_target_feature, "1.88.0", Some(139284)),
(unstable, arm_target_feature, "1.27.0", Some(44839)),
(unstable, avx512_target_feature, "1.27.0", Some(44839)),
(unstable, bpf_target_feature, "1.54.0", Some(44839)),
(unstable, csky_target_feature, "1.73.0", Some(44839)),
(unstable, ermsb_target_feature, "1.49.0", Some(44839)),

View file

@ -218,7 +218,12 @@ impl<'a, 'tcx> Visitor<'tcx> for GatherLocalsVisitor<'a, 'tcx> {
);
}
let old_outermost_fn_param_pat = self.outermost_fn_param_pat.take();
intravisit::walk_pat(self, p);
if let PatKind::Guard(subpat, _) = p.kind {
// We'll visit the guard when checking it. Don't gather its locals twice.
self.visit_pat(subpat);
} else {
intravisit::walk_pat(self, p);
}
self.outermost_fn_param_pat = old_outermost_fn_param_pat;
}

View file

@ -98,6 +98,14 @@ impl<'tcx> InferCtxt<'tcx> {
sub_region: Region<'tcx>,
cause: &ObligationCause<'tcx>,
) {
// `is_global` means the type has no params, infer, placeholder, or non-`'static`
// free regions. If the type has none of these things, then we can skip registering
// this outlives obligation since it has no components which affect lifetime
// checking in an interesting way.
if sup_type.is_global() {
return;
}
debug!(?sup_type, ?sub_region, ?cause);
let origin = SubregionOrigin::from_obligation_cause(cause, || {
infer::RelateParamBound(

View file

@ -255,6 +255,7 @@ fn main() {
} else if target.contains("haiku")
|| target.contains("darwin")
|| (is_crossed && (target.contains("dragonfly") || target.contains("solaris")))
|| target.contains("cygwin")
{
println!("cargo:rustc-link-lib=z");
} else if target.contains("netbsd") {

View file

@ -121,6 +121,7 @@ use crate::ty;
use crate::ty::codec::{TyDecoder, TyEncoder};
pub use crate::ty::diagnostics::*;
use crate::ty::fast_reject::SimplifiedType;
use crate::ty::layout::LayoutError;
use crate::ty::util::Discr;
use crate::ty::walk::TypeWalker;
@ -1878,6 +1879,11 @@ impl<'tcx> TyCtxt<'tcx> {
self.def_kind(trait_def_id) == DefKind::TraitAlias
}
/// Arena-alloc of LayoutError for coroutine layout
fn layout_error(self, err: LayoutError<'tcx>) -> &'tcx LayoutError<'tcx> {
self.arena.alloc(err)
}
/// Returns layout of a non-async-drop coroutine. Layout might be unavailable if the
/// coroutine is tainted by errors.
///
@ -1886,12 +1892,14 @@ impl<'tcx> TyCtxt<'tcx> {
fn ordinary_coroutine_layout(
self,
def_id: DefId,
coroutine_kind_ty: Ty<'tcx>,
) -> Option<&'tcx CoroutineLayout<'tcx>> {
args: GenericArgsRef<'tcx>,
) -> Result<&'tcx CoroutineLayout<'tcx>, &'tcx LayoutError<'tcx>> {
let coroutine_kind_ty = args.as_coroutine().kind_ty();
let mir = self.optimized_mir(def_id);
let ty = || Ty::new_coroutine(self, def_id, args);
// Regular coroutine
if coroutine_kind_ty.is_unit() {
mir.coroutine_layout_raw()
mir.coroutine_layout_raw().ok_or_else(|| self.layout_error(LayoutError::Unknown(ty())))
} else {
// If we have a `Coroutine` that comes from an coroutine-closure,
// then it may be a by-move or by-ref body.
@ -1905,6 +1913,7 @@ impl<'tcx> TyCtxt<'tcx> {
// a by-ref coroutine.
if identity_kind_ty == coroutine_kind_ty {
mir.coroutine_layout_raw()
.ok_or_else(|| self.layout_error(LayoutError::Unknown(ty())))
} else {
assert_matches!(coroutine_kind_ty.to_opt_closure_kind(), Some(ClosureKind::FnOnce));
assert_matches!(
@ -1913,6 +1922,7 @@ impl<'tcx> TyCtxt<'tcx> {
);
self.optimized_mir(self.coroutine_by_move_body_def_id(def_id))
.coroutine_layout_raw()
.ok_or_else(|| self.layout_error(LayoutError::Unknown(ty())))
}
}
}
@ -1924,12 +1934,15 @@ impl<'tcx> TyCtxt<'tcx> {
self,
def_id: DefId,
args: GenericArgsRef<'tcx>,
) -> Option<&'tcx CoroutineLayout<'tcx>> {
) -> Result<&'tcx CoroutineLayout<'tcx>, &'tcx LayoutError<'tcx>> {
let ty = || Ty::new_coroutine(self, def_id, args);
if args[0].has_placeholders() || args[0].has_non_region_param() {
return None;
return Err(self.layout_error(LayoutError::TooGeneric(ty())));
}
let instance = InstanceKind::AsyncDropGlue(def_id, Ty::new_coroutine(self, def_id, args));
self.mir_shims(instance).coroutine_layout_raw()
self.mir_shims(instance)
.coroutine_layout_raw()
.ok_or_else(|| self.layout_error(LayoutError::Unknown(ty())))
}
/// Returns layout of a coroutine. Layout might be unavailable if the
@ -1938,7 +1951,7 @@ impl<'tcx> TyCtxt<'tcx> {
self,
def_id: DefId,
args: GenericArgsRef<'tcx>,
) -> Option<&'tcx CoroutineLayout<'tcx>> {
) -> Result<&'tcx CoroutineLayout<'tcx>, &'tcx LayoutError<'tcx>> {
if self.is_async_drop_in_place_coroutine(def_id) {
// layout of `async_drop_in_place<T>::{closure}` in case,
// when T is a coroutine, contains this internal coroutine's ptr in upvars
@ -1960,12 +1973,12 @@ impl<'tcx> TyCtxt<'tcx> {
variant_source_info,
storage_conflicts: BitMatrix::new(0, 0),
};
return Some(self.arena.alloc(proxy_layout));
return Ok(self.arena.alloc(proxy_layout));
} else {
self.async_drop_coroutine_layout(def_id, args)
}
} else {
self.ordinary_coroutine_layout(def_id, args.as_coroutine().kind_ty())
self.ordinary_coroutine_layout(def_id, args)
}
}

View file

@ -836,6 +836,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
#[instrument(level = "trace", skip(self), ret)]
fn simplify_rvalue(
&mut self,
lhs: &Place<'tcx>,
rvalue: &mut Rvalue<'tcx>,
location: Location,
) -> Option<VnIndex> {
@ -855,7 +856,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
Value::Repeat(op, amount)
}
Rvalue::NullaryOp(op, ty) => Value::NullaryOp(op, ty),
Rvalue::Aggregate(..) => return self.simplify_aggregate(rvalue, location),
Rvalue::Aggregate(..) => return self.simplify_aggregate(lhs, rvalue, location),
Rvalue::Ref(_, borrow_kind, ref mut place) => {
self.simplify_place_projection(place, location);
return Some(self.new_pointer(*place, AddressKind::Ref(borrow_kind)));
@ -943,6 +944,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
fn simplify_aggregate_to_copy(
&mut self,
lhs: &Place<'tcx>,
rvalue: &mut Rvalue<'tcx>,
location: Location,
fields: &[VnIndex],
@ -982,12 +984,16 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
// Allow introducing places with non-constant offsets, as those are still better than
// reconstructing an aggregate.
if let Some(place) = self.try_as_place(copy_from_local_value, location, true) {
if rvalue.ty(self.local_decls, self.tcx) == place.ty(self.local_decls, self.tcx).ty {
if let Some(place) = self.try_as_place(copy_from_local_value, location, true)
&& rvalue.ty(self.local_decls, self.tcx) == place.ty(self.local_decls, self.tcx).ty
{
// Avoid creating `*a = copy (*b)`, as they might be aliases resulting in overlapping assignments.
// FIXME: This also avoids any kind of projection, not just derefs. We can add allowed projections.
if lhs.as_local().is_some() {
self.reused_locals.insert(place.local);
*rvalue = Rvalue::Use(Operand::Copy(place));
return Some(copy_from_local_value);
}
return Some(copy_from_local_value);
}
None
@ -995,6 +1001,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
fn simplify_aggregate(
&mut self,
lhs: &Place<'tcx>,
rvalue: &mut Rvalue<'tcx>,
location: Location,
) -> Option<VnIndex> {
@ -1090,7 +1097,7 @@ impl<'body, 'tcx> VnState<'body, 'tcx> {
if let AggregateTy::Def(_, _) = ty
&& let Some(value) =
self.simplify_aggregate_to_copy(rvalue, location, &fields, variant_index)
self.simplify_aggregate_to_copy(lhs, rvalue, location, &fields, variant_index)
{
return Some(value);
}
@ -1765,7 +1772,7 @@ impl<'tcx> MutVisitor<'tcx> for VnState<'_, 'tcx> {
if let StatementKind::Assign(box (ref mut lhs, ref mut rvalue)) = stmt.kind {
self.simplify_place_projection(lhs, location);
let value = self.simplify_rvalue(rvalue, location);
let value = self.simplify_rvalue(lhs, rvalue, location);
let value = if let Some(local) = lhs.as_local()
&& self.ssa.is_ssa(local)
// FIXME(#112651) `rvalue` may have a subtype to `local`. We can only mark

View file

@ -752,7 +752,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
let layout = if def_id == self.caller_body.source.def_id() {
self.caller_body
.coroutine_layout_raw()
.or_else(|| self.tcx.coroutine_layout(def_id, args))
.or_else(|| self.tcx.coroutine_layout(def_id, args).ok())
} else if self.tcx.needs_coroutine_by_move_body_def_id(def_id)
&& let ty::ClosureKind::FnOnce =
args.as_coroutine().kind_ty().to_opt_closure_kind().unwrap()
@ -762,7 +762,7 @@ impl<'a, 'tcx> Visitor<'tcx> for TypeChecker<'a, 'tcx> {
// Same if this is the by-move body of a coroutine-closure.
self.caller_body.coroutine_layout_raw()
} else {
self.tcx.coroutine_layout(def_id, args)
self.tcx.coroutine_layout(def_id, args).ok()
};
let Some(layout) = layout else {

View file

@ -6,7 +6,6 @@
#![allow(rustc::diagnostic_outside_of_impl)]
#![allow(rustc::untranslatable_diagnostic)]
#![allow(unused_crate_dependencies)]
#![cfg_attr(all(feature = "rustc", bootstrap), feature(let_chains))]
// tidy-alphabetical-end
pub mod constructor;

View file

@ -2493,7 +2493,7 @@ impl<'ra, 'tcx> Resolver<'ra, 'tcx> {
let Res::Def(DefKind::Macro(MacroKind::Bang), _) = binding.res() else {
return None;
};
let module_name = crate_module.kind.name().unwrap_or(kw::Empty);
let module_name = crate_module.kind.name().unwrap_or(kw::Crate);
let import_snippet = match import.kind {
ImportKind::Single { source, target, .. } if source != target => {
format!("{source} as {target}")

View file

@ -111,6 +111,19 @@ enum PatBoundCtx {
Or,
}
/// Tracks bindings resolved within a pattern. This serves two purposes:
///
/// - This tracks when identifiers are bound multiple times within a pattern. In a product context,
/// this is an error. In an or-pattern, this lets us reuse the same resolution for each instance.
/// See `fresh_binding` and `resolve_pattern_inner` for more information.
///
/// - The guard expression of a guard pattern may use bindings from within the guard pattern, but
/// not from elsewhere in the pattern containing it. This allows us to isolate the bindings in the
/// subpattern to construct the scope for the guard.
///
/// Each identifier must map to at most one distinct [`Res`].
type PatternBindings = SmallVec<[(PatBoundCtx, FxIndexMap<Ident, Res>); 1]>;
/// Does this the item (from the item rib scope) allow generic parameters?
#[derive(Copy, Clone, Debug)]
pub(crate) enum HasGenericParams {
@ -786,7 +799,14 @@ impl<'ra: 'ast, 'ast, 'tcx> Visitor<'ast> for LateResolutionVisitor<'_, 'ast, 'r
fn visit_pat(&mut self, p: &'ast Pat) {
let prev = self.diag_metadata.current_pat;
self.diag_metadata.current_pat = Some(p);
visit::walk_pat(self, p);
if let PatKind::Guard(subpat, _) = &p.kind {
// We walk the guard expression in `resolve_pattern_inner`. Don't resolve it twice.
self.visit_pat(subpat);
} else {
visit::walk_pat(self, p);
}
self.diag_metadata.current_pat = prev;
}
fn visit_local(&mut self, local: &'ast Local) {
@ -2297,7 +2317,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
fn resolve_fn_params(
&mut self,
has_self: bool,
inputs: impl Iterator<Item = (Option<&'ast Pat>, &'ast Ty)>,
inputs: impl Iterator<Item = (Option<&'ast Pat>, &'ast Ty)> + Clone,
) -> Result<LifetimeRes, (Vec<MissingLifetime>, Vec<ElisionFnParameter>)> {
enum Elision {
/// We have not found any candidate.
@ -2319,15 +2339,20 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
let mut parameter_info = Vec::new();
let mut all_candidates = Vec::new();
// Resolve and apply bindings first so diagnostics can see if they're used in types.
let mut bindings = smallvec![(PatBoundCtx::Product, Default::default())];
for (index, (pat, ty)) in inputs.enumerate() {
debug!(?pat, ?ty);
for (pat, _) in inputs.clone() {
debug!("resolving bindings in pat = {pat:?}");
self.with_lifetime_rib(LifetimeRibKind::Elided(LifetimeRes::Infer), |this| {
if let Some(pat) = pat {
this.resolve_pattern(pat, PatternSource::FnParam, &mut bindings);
}
});
}
self.apply_pattern_bindings(bindings);
for (index, (pat, ty)) in inputs.enumerate() {
debug!("resolving type for pat = {pat:?}, ty = {ty:?}");
// Record elision candidates only for this parameter.
debug_assert_matches!(self.lifetime_elision_candidates, None);
self.lifetime_elision_candidates = Some(Default::default());
@ -3615,16 +3640,10 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
self.visit_path(&delegation.path, delegation.id);
let Some(body) = &delegation.body else { return };
self.with_rib(ValueNS, RibKind::FnOrCoroutine, |this| {
// `PatBoundCtx` is not necessary in this context
let mut bindings = smallvec![(PatBoundCtx::Product, Default::default())];
let span = delegation.path.segments.last().unwrap().ident.span;
this.fresh_binding(
Ident::new(kw::SelfLower, span),
delegation.id,
PatternSource::FnParam,
&mut bindings,
);
let ident = Ident::new(kw::SelfLower, span.normalize_to_macro_rules());
let res = Res::Local(delegation.id);
this.innermost_rib_bindings(ValueNS).insert(ident, res);
this.visit_block(body);
});
}
@ -3635,6 +3654,7 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
for Param { pat, .. } in params {
this.resolve_pattern(pat, PatternSource::FnParam, &mut bindings);
}
this.apply_pattern_bindings(bindings);
});
for Param { ty, .. } in params {
self.visit_ty(ty);
@ -3851,13 +3871,32 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
fn resolve_pattern_top(&mut self, pat: &'ast Pat, pat_src: PatternSource) {
let mut bindings = smallvec![(PatBoundCtx::Product, Default::default())];
self.resolve_pattern(pat, pat_src, &mut bindings);
self.apply_pattern_bindings(bindings);
}
/// Apply the bindings from a pattern to the innermost rib of the current scope.
fn apply_pattern_bindings(&mut self, mut pat_bindings: PatternBindings) {
let rib_bindings = self.innermost_rib_bindings(ValueNS);
let Some((_, pat_bindings)) = pat_bindings.pop() else {
bug!("tried applying nonexistent bindings from pattern");
};
if rib_bindings.is_empty() {
// Often, such as for match arms, the bindings are introduced into a new rib.
// In this case, we can move the bindings over directly.
*rib_bindings = pat_bindings;
} else {
rib_bindings.extend(pat_bindings);
}
}
/// Resolve bindings in a pattern. `apply_pattern_bindings` must be called after to introduce
/// the bindings into scope.
fn resolve_pattern(
&mut self,
pat: &'ast Pat,
pat_src: PatternSource,
bindings: &mut SmallVec<[(PatBoundCtx, FxHashSet<Ident>); 1]>,
bindings: &mut PatternBindings,
) {
// We walk the pattern before declaring the pattern's inner bindings,
// so that we avoid resolving a literal expression to a binding defined
@ -3890,9 +3929,9 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
#[tracing::instrument(skip(self, bindings), level = "debug")]
fn resolve_pattern_inner(
&mut self,
pat: &Pat,
pat: &'ast Pat,
pat_src: PatternSource,
bindings: &mut SmallVec<[(PatBoundCtx, FxHashSet<Ident>); 1]>,
bindings: &mut PatternBindings,
) {
// Visit all direct subpatterns of this pattern.
pat.walk(&mut |pat| {
@ -3950,6 +3989,31 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
// Prevent visiting `ps` as we've already done so above.
return false;
}
PatKind::Guard(ref subpat, ref guard) => {
// Add a new set of bindings to the stack to collect bindings in `subpat`.
bindings.push((PatBoundCtx::Product, Default::default()));
// Resolving `subpat` adds bindings onto the newly-pushed context. After, the
// total number of contexts on the stack should be the same as before.
let binding_ctx_stack_len = bindings.len();
self.resolve_pattern_inner(subpat, pat_src, bindings);
assert_eq!(bindings.len(), binding_ctx_stack_len);
// These bindings, but none from the surrounding pattern, are visible in the
// guard; put them in scope and resolve `guard`.
let subpat_bindings = bindings.pop().unwrap().1;
self.with_rib(ValueNS, RibKind::Normal, |this| {
*this.innermost_rib_bindings(ValueNS) = subpat_bindings.clone();
this.resolve_expr(guard, None);
});
// Propagate the subpattern's bindings upwards.
// FIXME(guard_patterns): For `if let` guards, we'll also need to get the
// bindings introduced by the guard from its rib and propagate them upwards.
// This will require checking the identifiers for overlaps with `bindings`, like
// what `fresh_binding` does (ideally sharing its logic). To keep them separate
// from `subpat_bindings`, we can introduce a fresh rib for the guard.
bindings.last_mut().unwrap().1.extend(subpat_bindings);
// Prevent visiting `subpat` as we've already done so above.
return false;
}
_ => {}
}
true
@ -3988,20 +4052,17 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
ident: Ident,
pat_id: NodeId,
pat_src: PatternSource,
bindings: &mut SmallVec<[(PatBoundCtx, FxHashSet<Ident>); 1]>,
bindings: &mut PatternBindings,
) -> Res {
// Add the binding to the local ribs, if it doesn't already exist in the bindings map.
// Add the binding to the bindings map, if it doesn't already exist.
// (We must not add it if it's in the bindings map because that breaks the assumptions
// later passes make about or-patterns.)
let ident = ident.normalize_to_macro_rules();
let mut bound_iter = bindings.iter().filter(|(_, set)| set.contains(&ident));
// Already bound in a product pattern? e.g. `(a, a)` which is not allowed.
let already_bound_and = bound_iter.clone().any(|(ctx, _)| *ctx == PatBoundCtx::Product);
// Already bound in an or-pattern? e.g. `V1(a) | V2(a)`.
// This is *required* for consistency which is checked later.
let already_bound_or = bound_iter.any(|(ctx, _)| *ctx == PatBoundCtx::Or);
let already_bound_and = bindings
.iter()
.any(|(ctx, map)| *ctx == PatBoundCtx::Product && map.contains_key(&ident));
if already_bound_and {
// Overlap in a product pattern somewhere; report an error.
use ResolutionError::*;
@ -4014,19 +4075,23 @@ impl<'a, 'ast, 'ra: 'ast, 'tcx> LateResolutionVisitor<'a, 'ast, 'ra, 'tcx> {
self.report_error(ident.span, error(ident));
}
// Record as bound.
bindings.last_mut().unwrap().1.insert(ident);
if already_bound_or {
// Already bound in an or-pattern? e.g. `V1(a) | V2(a)`.
// This is *required* for consistency which is checked later.
let already_bound_or = bindings
.iter()
.find_map(|(ctx, map)| if *ctx == PatBoundCtx::Or { map.get(&ident) } else { None });
let res = if let Some(&res) = already_bound_or {
// `Variant1(a) | Variant2(a)`, ok
// Reuse definition from the first `a`.
self.innermost_rib_bindings(ValueNS)[&ident]
} else {
// A completely fresh binding is added to the set.
let res = Res::Local(pat_id);
self.innermost_rib_bindings(ValueNS).insert(ident, res);
res
}
} else {
// A completely fresh binding is added to the map.
Res::Local(pat_id)
};
// Record as bound.
bindings.last_mut().unwrap().1.insert(ident, res);
res
}
fn innermost_rib_bindings(&mut self, ns: Namespace) -> &mut FxIndexMap<Ident, Res> {

View file

@ -33,7 +33,7 @@ libc = "0.2"
# tidy-alphabetical-end
[target.'cfg(windows)'.dependencies.windows]
version = "0.59.0"
version = "0.61.0"
features = [
"Win32_Foundation",
"Win32_System_LibraryLoader",

View file

@ -78,10 +78,16 @@ fn current_dll_path() -> Result<PathBuf, String> {
if libc::dladdr(addr, &mut info) == 0 {
return Err("dladdr failed".into());
}
if info.dli_fname.is_null() {
return Err("dladdr returned null pointer".into());
}
let bytes = CStr::from_ptr(info.dli_fname).to_bytes();
#[cfg(target_os = "cygwin")]
let fname_ptr = info.dli_fname.as_ptr();
#[cfg(not(target_os = "cygwin"))]
let fname_ptr = {
if info.dli_fname.is_null() {
return Err("dladdr returned null pointer".into());
}
info.dli_fname
};
let bytes = CStr::from_ptr(fname_ptr).to_bytes();
let os = OsStr::from_bytes(bytes);
Ok(PathBuf::from(os))
}

View file

@ -1,4 +1,4 @@
use crate::spec::{FramePointer, LinkerFlavor, Lld, Target, TargetMetadata, base};
use crate::spec::{FramePointer, Target, TargetMetadata, base};
pub(crate) fn target() -> Target {
let mut base = base::windows_msvc::opts();
@ -11,11 +11,6 @@ pub(crate) fn target() -> Target {
// and other services. It must point to the previous {x29, x30} pair on the stack."
base.frame_pointer = FramePointer::NonLeaf;
// MSVC emits a warning about code that may trip "Cortex-A53 MPCore processor bug #843419" (see
// https://developer.arm.com/documentation/epm048406/latest) which is sometimes emitted by LLVM.
// Since Arm64 Windows 10+ isn't supported on that processor, it's safe to disable the warning.
base.add_pre_link_args(LinkerFlavor::Msvc(Lld::No), &["/arm64hazardfree"]);
Target {
llvm_target: "aarch64-pc-windows-msvc".into(),
metadata: TargetMetadata {

View file

@ -416,25 +416,25 @@ static X86_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[
),
("avx10.2", Unstable(sym::avx10_target_feature), &["avx10.1"]),
("avx2", Stable, &["avx"]),
("avx512bf16", Unstable(sym::avx512_target_feature), &["avx512bw"]),
("avx512bitalg", Unstable(sym::avx512_target_feature), &["avx512bw"]),
("avx512bw", Unstable(sym::avx512_target_feature), &["avx512f"]),
("avx512cd", Unstable(sym::avx512_target_feature), &["avx512f"]),
("avx512dq", Unstable(sym::avx512_target_feature), &["avx512f"]),
("avx512f", Unstable(sym::avx512_target_feature), &["avx2", "fma", "f16c"]),
("avx512fp16", Unstable(sym::avx512_target_feature), &["avx512bw"]),
("avx512ifma", Unstable(sym::avx512_target_feature), &["avx512f"]),
("avx512vbmi", Unstable(sym::avx512_target_feature), &["avx512bw"]),
("avx512vbmi2", Unstable(sym::avx512_target_feature), &["avx512bw"]),
("avx512vl", Unstable(sym::avx512_target_feature), &["avx512f"]),
("avx512vnni", Unstable(sym::avx512_target_feature), &["avx512f"]),
("avx512vp2intersect", Unstable(sym::avx512_target_feature), &["avx512f"]),
("avx512vpopcntdq", Unstable(sym::avx512_target_feature), &["avx512f"]),
("avxifma", Unstable(sym::avx512_target_feature), &["avx2"]),
("avxneconvert", Unstable(sym::avx512_target_feature), &["avx2"]),
("avxvnni", Unstable(sym::avx512_target_feature), &["avx2"]),
("avxvnniint16", Unstable(sym::avx512_target_feature), &["avx2"]),
("avxvnniint8", Unstable(sym::avx512_target_feature), &["avx2"]),
("avx512bf16", Stable, &["avx512bw"]),
("avx512bitalg", Stable, &["avx512bw"]),
("avx512bw", Stable, &["avx512f"]),
("avx512cd", Stable, &["avx512f"]),
("avx512dq", Stable, &["avx512f"]),
("avx512f", Stable, &["avx2", "fma", "f16c"]),
("avx512fp16", Stable, &["avx512bw"]),
("avx512ifma", Stable, &["avx512f"]),
("avx512vbmi", Stable, &["avx512bw"]),
("avx512vbmi2", Stable, &["avx512bw"]),
("avx512vl", Stable, &["avx512f"]),
("avx512vnni", Stable, &["avx512f"]),
("avx512vp2intersect", Stable, &["avx512f"]),
("avx512vpopcntdq", Stable, &["avx512f"]),
("avxifma", Stable, &["avx2"]),
("avxneconvert", Stable, &["avx2"]),
("avxvnni", Stable, &["avx2"]),
("avxvnniint16", Stable, &["avx2"]),
("avxvnniint8", Stable, &["avx2"]),
("bmi1", Stable, &[]),
("bmi2", Stable, &[]),
("cmpxchg16b", Stable, &[]),
@ -442,7 +442,7 @@ static X86_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[
("f16c", Stable, &["avx"]),
("fma", Stable, &["avx"]),
("fxsr", Stable, &[]),
("gfni", Unstable(sym::avx512_target_feature), &["sse2"]),
("gfni", Stable, &["sse2"]),
("kl", Unstable(sym::keylocker_x86), &["sse2"]),
("lahfsahf", Unstable(sym::lahfsahf_target_feature), &[]),
("lzcnt", Stable, &[]),
@ -469,8 +469,8 @@ static X86_FEATURES: &[(&str, Stability, ImpliedFeatures)] = &[
("sse4a", Unstable(sym::sse4a_target_feature), &["sse3"]),
("ssse3", Stable, &["sse3"]),
("tbm", Unstable(sym::tbm_target_feature), &[]),
("vaes", Unstable(sym::avx512_target_feature), &["avx2", "aes"]),
("vpclmulqdq", Unstable(sym::avx512_target_feature), &["avx", "pclmulqdq"]),
("vaes", Stable, &["avx2", "aes"]),
("vpclmulqdq", Stable, &["avx", "pclmulqdq"]),
("widekl", Unstable(sym::keylocker_x86), &["kl"]),
("x87", Unstable(sym::x87_target_feature), &[]),
("xop", Unstable(sym::xop_target_feature), &[/*"fma4", */ "avx", "sse4a"]),

View file

@ -492,9 +492,7 @@ fn layout_of_uncached<'tcx>(
ty::Coroutine(def_id, args) => {
use rustc_middle::ty::layout::PrimitiveExt as _;
let Some(info) = tcx.coroutine_layout(def_id, args) else {
return Err(error(cx, LayoutError::Unknown(ty)));
};
let info = tcx.coroutine_layout(def_id, args)?;
let local_layouts = info
.field_tys

View file

@ -20,6 +20,8 @@ macro_rules! impl_general_format {
}
}
#[cfg(target_has_reliable_f16)]
impl_general_format! { f16 }
impl_general_format! { f32 f64 }
// Don't inline this so callers don't use the stack space this function
@ -231,6 +233,13 @@ macro_rules! floating {
floating! { f32 f64 }
#[cfg(target_has_reliable_f16)]
floating! { f16 }
// FIXME(f16_f128): A fallback is used when the backend+target does not support f16 well, in order
// to avoid ICEs.
#[cfg(not(target_has_reliable_f16))]
#[stable(feature = "rust1", since = "1.0.0")]
impl Debug for f16 {
#[inline]
@ -239,6 +248,33 @@ impl Debug for f16 {
}
}
#[cfg(not(target_has_reliable_f16))]
#[stable(feature = "rust1", since = "1.0.0")]
impl Display for f16 {
#[inline]
fn fmt(&self, fmt: &mut Formatter<'_>) -> Result {
Debug::fmt(self, fmt)
}
}
#[cfg(not(target_has_reliable_f16))]
#[stable(feature = "rust1", since = "1.0.0")]
impl LowerExp for f16 {
#[inline]
fn fmt(&self, fmt: &mut Formatter<'_>) -> Result {
Debug::fmt(self, fmt)
}
}
#[cfg(not(target_has_reliable_f16))]
#[stable(feature = "rust1", since = "1.0.0")]
impl UpperExp for f16 {
#[inline]
fn fmt(&self, fmt: &mut Formatter<'_>) -> Result {
Debug::fmt(self, fmt)
}
}
#[stable(feature = "rust1", since = "1.0.0")]
impl Debug for f128 {
#[inline]

View file

@ -101,6 +101,7 @@
#![feature(bstr)]
#![feature(bstr_internals)]
#![feature(cfg_match)]
#![feature(cfg_target_has_reliable_f16_f128)]
#![feature(const_carrying_mul_add)]
#![feature(const_eval_select)]
#![feature(core_intrinsics)]
@ -111,7 +112,6 @@
#![feature(is_ascii_octdigit)]
#![feature(lazy_get)]
#![feature(link_cfg)]
#![feature(non_null_from_ref)]
#![feature(offset_of_enum)]
#![feature(panic_internals)]
#![feature(ptr_alignment_type)]
@ -188,9 +188,9 @@
//
// Target features:
// tidy-alphabetical-start
#![cfg_attr(bootstrap, feature(avx512_target_feature))]
#![feature(aarch64_unstable_target_feature)]
#![feature(arm_target_feature)]
#![feature(avx512_target_feature)]
#![feature(hexagon_target_feature)]
#![feature(keylocker_x86)]
#![feature(loongarch_target_feature)]

View file

@ -45,7 +45,7 @@ macro_rules! int {
}
}
int!(u32, u64);
int!(u16, u32, u64);
/// A helper trait to avoid duplicating basically all the conversion code for IEEE floats.
///
@ -189,9 +189,14 @@ pub trait RawFloat:
/// Returns the mantissa, exponent and sign as integers.
///
/// That is, this returns `(m, p, s)` such that `s * m * 2^p` represents the original float.
/// For 0, the exponent will be `-(EXP_BIAS + SIG_BITS`, which is the
/// minimum subnormal power.
/// This returns `(m, p, s)` such that `s * m * 2^p` represents the original float. For 0, the
/// exponent will be `-(EXP_BIAS + SIG_BITS)`, which is the minimum subnormal power. For
/// infinity or NaN, the exponent will be `EXP_SAT - EXP_BIAS - SIG_BITS`.
///
/// If subnormal, the mantissa will be shifted one bit to the left. Otherwise, it is returned
/// with the explicit bit set but otherwise unshifted
///
/// `s` is only ever +/-1.
fn integer_decode(self) -> (u64, i16, i8) {
let bits = self.to_bits();
let sign: i8 = if bits >> (Self::BITS - 1) == Self::Int::ZERO { 1 } else { -1 };
@ -213,6 +218,50 @@ const fn pow2_to_pow10(a: i64) -> i64 {
res as i64
}
#[cfg(target_has_reliable_f16)]
impl RawFloat for f16 {
type Int = u16;
const INFINITY: Self = Self::INFINITY;
const NEG_INFINITY: Self = Self::NEG_INFINITY;
const NAN: Self = Self::NAN;
const NEG_NAN: Self = -Self::NAN;
const BITS: u32 = 16;
const SIG_TOTAL_BITS: u32 = Self::MANTISSA_DIGITS;
const EXP_MASK: Self::Int = Self::EXP_MASK;
const SIG_MASK: Self::Int = Self::MAN_MASK;
const MIN_EXPONENT_ROUND_TO_EVEN: i32 = -22;
const MAX_EXPONENT_ROUND_TO_EVEN: i32 = 5;
const SMALLEST_POWER_OF_TEN: i32 = -27;
#[inline]
fn from_u64(v: u64) -> Self {
debug_assert!(v <= Self::MAX_MANTISSA_FAST_PATH);
v as _
}
#[inline]
fn from_u64_bits(v: u64) -> Self {
Self::from_bits((v & 0xFFFF) as u16)
}
fn pow10_fast_path(exponent: usize) -> Self {
#[allow(clippy::use_self)]
const TABLE: [f16; 8] = [1e0, 1e1, 1e2, 1e3, 1e4, 0.0, 0.0, 0.];
TABLE[exponent & 7]
}
fn to_bits(self) -> Self::Int {
self.to_bits()
}
fn classify(self) -> FpCategory {
self.classify()
}
}
impl RawFloat for f32 {
type Int = u32;

View file

@ -171,9 +171,25 @@ macro_rules! from_str_float_impl {
}
};
}
#[cfg(target_has_reliable_f16)]
from_str_float_impl!(f16);
from_str_float_impl!(f32);
from_str_float_impl!(f64);
// FIXME(f16_f128): A fallback is used when the backend+target does not support f16 well, in order
// to avoid ICEs.
#[cfg(not(target_has_reliable_f16))]
impl FromStr for f16 {
type Err = ParseFloatError;
#[inline]
fn from_str(_src: &str) -> Result<Self, ParseFloatError> {
unimplemented!("requires target_has_reliable_f16")
}
}
/// An error which can be returned when parsing a float.
///
/// This error is used as the error type for the [`FromStr`] implementation

View file

@ -45,6 +45,13 @@ pub trait DecodableFloat: RawFloat + Copy {
fn min_pos_norm_value() -> Self;
}
#[cfg(target_has_reliable_f16)]
impl DecodableFloat for f16 {
fn min_pos_norm_value() -> Self {
f16::MIN_POSITIVE
}
}
impl DecodableFloat for f32 {
fn min_pos_norm_value() -> Self {
f32::MIN_POSITIVE

View file

@ -262,7 +262,8 @@ impl<T: ?Sized> NonNull<T> {
}
/// Converts a reference to a `NonNull` pointer.
#[unstable(feature = "non_null_from_ref", issue = "130823")]
#[stable(feature = "non_null_from_ref", since = "CURRENT_RUSTC_VERSION")]
#[rustc_const_stable(feature = "non_null_from_ref", since = "CURRENT_RUSTC_VERSION")]
#[inline]
pub const fn from_ref(r: &T) -> Self {
// SAFETY: A reference cannot be null.
@ -270,7 +271,8 @@ impl<T: ?Sized> NonNull<T> {
}
/// Converts a mutable reference to a `NonNull` pointer.
#[unstable(feature = "non_null_from_ref", issue = "130823")]
#[stable(feature = "non_null_from_ref", since = "CURRENT_RUSTC_VERSION")]
#[rustc_const_stable(feature = "non_null_from_ref", since = "CURRENT_RUSTC_VERSION")]
#[inline]
pub const fn from_mut(r: &mut T) -> Self {
// SAFETY: A mutable reference cannot be null.

View file

@ -7,6 +7,20 @@ const FPATHS_F32: &[FPath<f32>] =
const FPATHS_F64: &[FPath<f64>] =
&[((0, 0, false, false), Some(0.0)), ((0, 0, false, false), Some(0.0))];
// FIXME(f16_f128): enable on all targets once possible.
#[test]
#[cfg(target_has_reliable_f16)]
fn check_fast_path_f16() {
const FPATHS_F16: &[FPath<f16>] =
&[((0, 0, false, false), Some(0.0)), ((0, 0, false, false), Some(0.0))];
for ((exponent, mantissa, negative, many_digits), expected) in FPATHS_F16.iter().copied() {
let dec = Decimal { exponent, mantissa, negative, many_digits };
let actual = dec.try_fast_path::<f16>();
assert_eq!(actual, expected);
}
}
#[test]
fn check_fast_path_f32() {
for ((exponent, mantissa, negative, many_digits), expected) in FPATHS_F32.iter().copied() {

View file

@ -1,5 +1,24 @@
use core::num::dec2flt::float::RawFloat;
// FIXME(f16_f128): enable on all targets once possible.
#[test]
#[cfg(target_has_reliable_f16)]
fn test_f16_integer_decode() {
assert_eq!(3.14159265359f16.integer_decode(), (1608, -9, 1));
assert_eq!((-8573.5918555f16).integer_decode(), (1072, 3, -1));
#[cfg(not(miri))] // miri doesn't have powf16
assert_eq!(2f16.powf(14.0).integer_decode(), (1 << 10, 4, 1));
assert_eq!(0f16.integer_decode(), (0, -25, 1));
assert_eq!((-0f16).integer_decode(), (0, -25, -1));
assert_eq!(f16::INFINITY.integer_decode(), (1 << 10, 6, 1));
assert_eq!(f16::NEG_INFINITY.integer_decode(), (1 << 10, 6, -1));
// Ignore the "sign" (quiet / signalling flag) of NAN.
// It can vary between runtime operations and LLVM folding.
let (nan_m, nan_p, _nan_s) = f16::NAN.integer_decode();
assert_eq!((nan_m, nan_p), (1536, 6));
}
#[test]
fn test_f32_integer_decode() {
assert_eq!(3.14159265359f32.integer_decode(), (13176795, -22, 1));
@ -34,6 +53,27 @@ fn test_f64_integer_decode() {
/* Sanity checks of computed magic numbers */
// FIXME(f16_f128): enable on all targets once possible.
#[test]
#[cfg(target_has_reliable_f16)]
fn test_f16_consts() {
assert_eq!(<f16 as RawFloat>::INFINITY, f16::INFINITY);
assert_eq!(<f16 as RawFloat>::NEG_INFINITY, -f16::INFINITY);
assert_eq!(<f16 as RawFloat>::NAN.to_bits(), f16::NAN.to_bits());
assert_eq!(<f16 as RawFloat>::NEG_NAN.to_bits(), (-f16::NAN).to_bits());
assert_eq!(<f16 as RawFloat>::SIG_BITS, 10);
assert_eq!(<f16 as RawFloat>::MIN_EXPONENT_ROUND_TO_EVEN, -22);
assert_eq!(<f16 as RawFloat>::MAX_EXPONENT_ROUND_TO_EVEN, 5);
assert_eq!(<f16 as RawFloat>::MIN_EXPONENT_FAST_PATH, -4);
assert_eq!(<f16 as RawFloat>::MAX_EXPONENT_FAST_PATH, 4);
assert_eq!(<f16 as RawFloat>::MAX_EXPONENT_DISGUISED_FAST_PATH, 7);
assert_eq!(<f16 as RawFloat>::EXP_MIN, -14);
assert_eq!(<f16 as RawFloat>::EXP_SAT, 0x1f);
assert_eq!(<f16 as RawFloat>::SMALLEST_POWER_OF_TEN, -27);
assert_eq!(<f16 as RawFloat>::LARGEST_POWER_OF_TEN, 4);
assert_eq!(<f16 as RawFloat>::MAX_MANTISSA_FAST_PATH, 2048);
}
#[test]
fn test_f32_consts() {
assert_eq!(<f32 as RawFloat>::INFINITY, f32::INFINITY);

View file

@ -1,6 +1,12 @@
use core::num::dec2flt::float::RawFloat;
use core::num::dec2flt::lemire::compute_float;
#[cfg(target_has_reliable_f16)]
fn compute_float16(q: i64, w: u64) -> (i32, u64) {
let fp = compute_float::<f16>(q, w);
(fp.p_biased, fp.m)
}
fn compute_float32(q: i64, w: u64) -> (i32, u64) {
let fp = compute_float::<f32>(q, w);
(fp.p_biased, fp.m)
@ -11,23 +17,73 @@ fn compute_float64(q: i64, w: u64) -> (i32, u64) {
(fp.p_biased, fp.m)
}
// FIXME(f16_f128): enable on all targets once possible.
#[test]
#[cfg(target_has_reliable_f16)]
fn compute_float_f16_rounding() {
// The maximum integer that cna be converted to a `f16` without lost precision.
let val = 1 << 11;
let scale = 10_u64.pow(10);
// These test near-halfway cases for half-precision floats.
assert_eq!(compute_float16(0, val), (26, 0));
assert_eq!(compute_float16(0, val + 1), (26, 0));
assert_eq!(compute_float16(0, val + 2), (26, 1));
assert_eq!(compute_float16(0, val + 3), (26, 2));
assert_eq!(compute_float16(0, val + 4), (26, 2));
// For the next power up, the two nearest representable numbers are twice as far apart.
let val2 = 1 << 12;
assert_eq!(compute_float16(0, val2), (27, 0));
assert_eq!(compute_float16(0, val2 + 2), (27, 0));
assert_eq!(compute_float16(0, val2 + 4), (27, 1));
assert_eq!(compute_float16(0, val2 + 6), (27, 2));
assert_eq!(compute_float16(0, val2 + 8), (27, 2));
// These are examples of the above tests, with digits from the exponent shifted
// to the mantissa.
assert_eq!(compute_float16(-10, val * scale), (26, 0));
assert_eq!(compute_float16(-10, (val + 1) * scale), (26, 0));
assert_eq!(compute_float16(-10, (val + 2) * scale), (26, 1));
// Let's check the lines to see if anything is different in table...
assert_eq!(compute_float16(-10, (val + 3) * scale), (26, 2));
assert_eq!(compute_float16(-10, (val + 4) * scale), (26, 2));
// Check the rounding point between infinity and the next representable number down
assert_eq!(compute_float16(4, 6), (f16::INFINITE_POWER - 1, 851));
assert_eq!(compute_float16(4, 7), (f16::INFINITE_POWER, 0)); // infinity
assert_eq!(compute_float16(2, 655), (f16::INFINITE_POWER - 1, 1023));
}
#[test]
fn compute_float_f32_rounding() {
// These test near-halfway cases for single-precision floats.
assert_eq!(compute_float32(0, 16777216), (151, 0));
assert_eq!(compute_float32(0, 16777217), (151, 0));
assert_eq!(compute_float32(0, 16777218), (151, 1));
assert_eq!(compute_float32(0, 16777219), (151, 2));
assert_eq!(compute_float32(0, 16777220), (151, 2));
// the maximum integer that cna be converted to a `f32` without lost precision.
let val = 1 << 24;
let scale = 10_u64.pow(10);
// These are examples of the above tests, with
// digits from the exponent shifted to the mantissa.
assert_eq!(compute_float32(-10, 167772160000000000), (151, 0));
assert_eq!(compute_float32(-10, 167772170000000000), (151, 0));
assert_eq!(compute_float32(-10, 167772180000000000), (151, 1));
// These test near-halfway cases for single-precision floats.
assert_eq!(compute_float32(0, val), (151, 0));
assert_eq!(compute_float32(0, val + 1), (151, 0));
assert_eq!(compute_float32(0, val + 2), (151, 1));
assert_eq!(compute_float32(0, val + 3), (151, 2));
assert_eq!(compute_float32(0, val + 4), (151, 2));
// For the next power up, the two nearest representable numbers are twice as far apart.
let val2 = 1 << 25;
assert_eq!(compute_float32(0, val2), (152, 0));
assert_eq!(compute_float32(0, val2 + 2), (152, 0));
assert_eq!(compute_float32(0, val2 + 4), (152, 1));
assert_eq!(compute_float32(0, val2 + 6), (152, 2));
assert_eq!(compute_float32(0, val2 + 8), (152, 2));
// These are examples of the above tests, with digits from the exponent shifted
// to the mantissa.
assert_eq!(compute_float32(-10, val * scale), (151, 0));
assert_eq!(compute_float32(-10, (val + 1) * scale), (151, 0));
assert_eq!(compute_float32(-10, (val + 2) * scale), (151, 1));
// Let's check the lines to see if anything is different in table...
assert_eq!(compute_float32(-10, 167772190000000000), (151, 2));
assert_eq!(compute_float32(-10, 167772200000000000), (151, 2));
assert_eq!(compute_float32(-10, (val + 3) * scale), (151, 2));
assert_eq!(compute_float32(-10, (val + 4) * scale), (151, 2));
// Check the rounding point between infinity and the next representable number down
assert_eq!(compute_float32(38, 3), (f32::INFINITE_POWER - 1, 6402534));
@ -37,23 +93,38 @@ fn compute_float_f32_rounding() {
#[test]
fn compute_float_f64_rounding() {
// These test near-halfway cases for double-precision floats.
assert_eq!(compute_float64(0, 9007199254740992), (1076, 0));
assert_eq!(compute_float64(0, 9007199254740993), (1076, 0));
assert_eq!(compute_float64(0, 9007199254740994), (1076, 1));
assert_eq!(compute_float64(0, 9007199254740995), (1076, 2));
assert_eq!(compute_float64(0, 9007199254740996), (1076, 2));
assert_eq!(compute_float64(0, 18014398509481984), (1077, 0));
assert_eq!(compute_float64(0, 18014398509481986), (1077, 0));
assert_eq!(compute_float64(0, 18014398509481988), (1077, 1));
assert_eq!(compute_float64(0, 18014398509481990), (1077, 2));
assert_eq!(compute_float64(0, 18014398509481992), (1077, 2));
// The maximum integer that cna be converted to a `f64` without lost precision.
let val = 1 << 53;
let scale = 1000;
// These are examples of the above tests, with
// digits from the exponent shifted to the mantissa.
assert_eq!(compute_float64(-3, 9007199254740992000), (1076, 0));
assert_eq!(compute_float64(-3, 9007199254740993000), (1076, 0));
assert_eq!(compute_float64(-3, 9007199254740994000), (1076, 1));
assert_eq!(compute_float64(-3, 9007199254740995000), (1076, 2));
assert_eq!(compute_float64(-3, 9007199254740996000), (1076, 2));
// These test near-halfway cases for double-precision floats.
assert_eq!(compute_float64(0, val), (1076, 0));
assert_eq!(compute_float64(0, val + 1), (1076, 0));
assert_eq!(compute_float64(0, val + 2), (1076, 1));
assert_eq!(compute_float64(0, val + 3), (1076, 2));
assert_eq!(compute_float64(0, val + 4), (1076, 2));
// For the next power up, the two nearest representable numbers are twice as far apart.
let val2 = 1 << 54;
assert_eq!(compute_float64(0, val2), (1077, 0));
assert_eq!(compute_float64(0, val2 + 2), (1077, 0));
assert_eq!(compute_float64(0, val2 + 4), (1077, 1));
assert_eq!(compute_float64(0, val2 + 6), (1077, 2));
assert_eq!(compute_float64(0, val2 + 8), (1077, 2));
// These are examples of the above tests, with digits from the exponent shifted
// to the mantissa.
assert_eq!(compute_float64(-3, val * scale), (1076, 0));
assert_eq!(compute_float64(-3, (val + 1) * scale), (1076, 0));
assert_eq!(compute_float64(-3, (val + 2) * scale), (1076, 1));
assert_eq!(compute_float64(-3, (val + 3) * scale), (1076, 2));
assert_eq!(compute_float64(-3, (val + 4) * scale), (1076, 2));
// Check the rounding point between infinity and the next representable number down
assert_eq!(compute_float64(308, 1), (f64::INFINITE_POWER - 1, 506821272651936));
assert_eq!(compute_float64(308, 2), (f64::INFINITE_POWER, 0)); // infinity
assert_eq!(
compute_float64(292, 17976931348623157),
(f64::INFINITE_POWER - 1, 4503599627370495)
);
}

View file

@ -11,15 +11,23 @@ mod parse;
// Requires a *polymorphic literal*, i.e., one that can serve as f64 as well as f32.
macro_rules! test_literal {
($x: expr) => {{
#[cfg(target_has_reliable_f16)]
let x16: f16 = $x;
let x32: f32 = $x;
let x64: f64 = $x;
let inputs = &[stringify!($x).into(), format!("{:?}", x64), format!("{:e}", x64)];
for input in inputs {
assert_eq!(input.parse(), Ok(x64));
assert_eq!(input.parse(), Ok(x32));
assert_eq!(input.parse(), Ok(x64), "failed f64 {input}");
assert_eq!(input.parse(), Ok(x32), "failed f32 {input}");
#[cfg(target_has_reliable_f16)]
assert_eq!(input.parse(), Ok(x16), "failed f16 {input}");
let neg_input = format!("-{input}");
assert_eq!(neg_input.parse(), Ok(-x64));
assert_eq!(neg_input.parse(), Ok(-x32));
assert_eq!(neg_input.parse(), Ok(-x64), "failed f64 {neg_input}");
assert_eq!(neg_input.parse(), Ok(-x32), "failed f32 {neg_input}");
#[cfg(target_has_reliable_f16)]
assert_eq!(neg_input.parse(), Ok(-x16), "failed f16 {neg_input}");
}
}};
}
@ -84,48 +92,87 @@ fn fast_path_correct() {
test_literal!(1.448997445238699);
}
// FIXME(f16_f128): remove gates once tests work on all targets
#[test]
fn lonely_dot() {
#[cfg(target_has_reliable_f16)]
assert!(".".parse::<f16>().is_err());
assert!(".".parse::<f32>().is_err());
assert!(".".parse::<f64>().is_err());
}
#[test]
fn exponentiated_dot() {
#[cfg(target_has_reliable_f16)]
assert!(".e0".parse::<f16>().is_err());
assert!(".e0".parse::<f32>().is_err());
assert!(".e0".parse::<f64>().is_err());
}
#[test]
fn lonely_sign() {
assert!("+".parse::<f32>().is_err());
assert!("-".parse::<f64>().is_err());
#[cfg(target_has_reliable_f16)]
assert!("+".parse::<f16>().is_err());
assert!("-".parse::<f32>().is_err());
assert!("+".parse::<f64>().is_err());
}
#[test]
fn whitespace() {
#[cfg(target_has_reliable_f16)]
assert!("1.0 ".parse::<f16>().is_err());
assert!(" 1.0".parse::<f32>().is_err());
assert!("1.0 ".parse::<f64>().is_err());
}
#[test]
fn nan() {
#[cfg(target_has_reliable_f16)]
{
assert!("NaN".parse::<f16>().unwrap().is_nan());
assert!("-NaN".parse::<f16>().unwrap().is_nan());
}
assert!("NaN".parse::<f32>().unwrap().is_nan());
assert!("-NaN".parse::<f32>().unwrap().is_nan());
assert!("NaN".parse::<f64>().unwrap().is_nan());
assert!("-NaN".parse::<f64>().unwrap().is_nan());
}
#[test]
fn inf() {
assert_eq!("inf".parse(), Ok(f64::INFINITY));
assert_eq!("-inf".parse(), Ok(f64::NEG_INFINITY));
#[cfg(target_has_reliable_f16)]
{
assert_eq!("inf".parse(), Ok(f16::INFINITY));
assert_eq!("-inf".parse(), Ok(f16::NEG_INFINITY));
}
assert_eq!("inf".parse(), Ok(f32::INFINITY));
assert_eq!("-inf".parse(), Ok(f32::NEG_INFINITY));
assert_eq!("inf".parse(), Ok(f64::INFINITY));
assert_eq!("-inf".parse(), Ok(f64::NEG_INFINITY));
}
#[test]
fn massive_exponent() {
#[cfg(target_has_reliable_f16)]
{
let max = i16::MAX;
assert_eq!(format!("1e{max}000").parse(), Ok(f16::INFINITY));
assert_eq!(format!("1e-{max}000").parse(), Ok(0.0f16));
assert_eq!(format!("1e{max}000").parse(), Ok(f16::INFINITY));
}
let max = i32::MAX;
assert_eq!(format!("1e{max}000").parse(), Ok(f32::INFINITY));
assert_eq!(format!("1e-{max}000").parse(), Ok(0.0f32));
assert_eq!(format!("1e{max}000").parse(), Ok(f32::INFINITY));
let max = i64::MAX;
assert_eq!(format!("1e{max}000").parse(), Ok(f64::INFINITY));
assert_eq!(format!("1e-{max}000").parse(), Ok(0.0));
assert_eq!(format!("1e-{max}000").parse(), Ok(0.0f64));
assert_eq!(format!("1e{max}000").parse(), Ok(f64::INFINITY));
}

View file

@ -10,6 +10,9 @@ fn new_dec(e: i64, m: u64) -> Decimal {
fn missing_pieces() {
let permutations = &[".e", "1e", "e4", "e", ".12e", "321.e", "32.12e+", "12.32e-"];
for &s in permutations {
#[cfg(target_has_reliable_f16)]
assert_eq!(dec2flt::<f16>(s), Err(pfe_invalid()));
assert_eq!(dec2flt::<f32>(s), Err(pfe_invalid()));
assert_eq!(dec2flt::<f64>(s), Err(pfe_invalid()));
}
}
@ -17,15 +20,31 @@ fn missing_pieces() {
#[test]
fn invalid_chars() {
let invalid = "r,?<j";
let error = Err(pfe_invalid());
let valid_strings = &["123", "666.", ".1", "5e1", "7e-3", "0.0e+1"];
for c in invalid.chars() {
for s in valid_strings {
for i in 0..s.len() {
let mut input = String::new();
input.push_str(s);
input.insert(i, c);
assert!(dec2flt::<f64>(&input) == error, "did not reject invalid {:?}", input);
#[cfg(target_has_reliable_f16)]
assert_eq!(
dec2flt::<f16>(&input),
Err(pfe_invalid()),
"f16 did not reject invalid {input:?}",
);
assert_eq!(
dec2flt::<f32>(&input),
Err(pfe_invalid()),
"f32 did not reject invalid {input:?}",
);
assert_eq!(
dec2flt::<f64>(&input),
Err(pfe_invalid()),
"f64 did not reject invalid {input:?}",
);
}
}
}

View file

@ -16,7 +16,7 @@ mod random;
pub fn decode_finite<T: DecodableFloat>(v: T) -> Decoded {
match decode(v).1 {
FullDecoded::Finite(decoded) => decoded,
full_decoded => panic!("expected finite, got {full_decoded:?} instead"),
full_decoded => panic!("expected finite, got {full_decoded:?} instead for {v:?}"),
}
}
@ -75,6 +75,11 @@ macro_rules! try_fixed {
})
}
#[cfg(target_has_reliable_f16)]
fn ldexp_f16(a: f16, b: i32) -> f16 {
ldexp_f64(a as f64, b) as f16
}
fn ldexp_f32(a: f32, b: i32) -> f32 {
ldexp_f64(a as f64, b) as f32
}
@ -176,6 +181,13 @@ trait TestableFloat: DecodableFloat + fmt::Display {
fn ldexpi(f: i64, exp: isize) -> Self;
}
#[cfg(target_has_reliable_f16)]
impl TestableFloat for f16 {
fn ldexpi(f: i64, exp: isize) -> Self {
f as Self * (exp as Self).exp2()
}
}
impl TestableFloat for f32 {
fn ldexpi(f: i64, exp: isize) -> Self {
f as Self * (exp as Self).exp2()
@ -225,6 +237,76 @@ macro_rules! check_exact_one {
//
// [1] Vern Paxson, A Program for Testing IEEE Decimal-Binary Conversion
// ftp://ftp.ee.lbl.gov/testbase-report.ps.Z
// or https://www.icir.org/vern/papers/testbase-report.pdf
#[cfg(target_has_reliable_f16)]
pub fn f16_shortest_sanity_test<F>(mut f: F)
where
F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit<u8>]) -> (&'a [u8], i16),
{
// 0.0999145507813
// 0.0999755859375
// 0.100036621094
check_shortest!(f(0.1f16) => b"1", 0);
// 0.3330078125
// 0.333251953125 (1/3 in the default rounding)
// 0.33349609375
check_shortest!(f(1.0f16/3.0) => b"3333", 0);
// 10^1 * 0.3138671875
// 10^1 * 0.3140625
// 10^1 * 0.3142578125
check_shortest!(f(3.14f16) => b"314", 1);
// 10^18 * 0.31415916243714048
// 10^18 * 0.314159196796878848
// 10^18 * 0.314159231156617216
check_shortest!(f(3.1415e4f16) => b"3141", 5);
// regression test for decoders
// 10^2 * 0.31984375
// 10^2 * 0.32
// 10^2 * 0.3203125
check_shortest!(f(ldexp_f16(1.0, 5)) => b"32", 2);
// 10^5 * 0.65472
// 10^5 * 0.65504
// 10^5 * 0.65536
check_shortest!(f(f16::MAX) => b"655", 5);
// 10^-4 * 0.60975551605224609375
// 10^-4 * 0.6103515625
// 10^-4 * 0.61094760894775390625
check_shortest!(f(f16::MIN_POSITIVE) => b"6104", -4);
// 10^-9 * 0
// 10^-9 * 0.59604644775390625
// 10^-8 * 0.11920928955078125
let minf16 = ldexp_f16(1.0, -24);
check_shortest!(f(minf16) => b"6", -7);
}
#[cfg(target_has_reliable_f16)]
pub fn f16_exact_sanity_test<F>(mut f: F)
where
F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit<u8>], i16) -> (&'a [u8], i16),
{
let minf16 = ldexp_f16(1.0, -24);
check_exact!(f(0.1f16) => b"999755859375 ", -1);
check_exact!(f(0.5f16) => b"5 ", 0);
check_exact!(f(1.0f16/3.0) => b"333251953125 ", 0);
check_exact!(f(3.141f16) => b"3140625 ", 1);
check_exact!(f(3.141e4f16) => b"31408 ", 5);
check_exact!(f(f16::MAX) => b"65504 ", 5);
check_exact!(f(f16::MIN_POSITIVE) => b"6103515625 ", -4);
check_exact!(f(minf16) => b"59604644775390625", -7);
// FIXME(f16_f128): these should gain the check_exact_one tests like `f32` and `f64` have,
// but these values are not easy to generate. The algorithm from the Paxon paper [1] needs
// to be adapted to binary16.
}
pub fn f32_shortest_sanity_test<F>(mut f: F)
where
@ -553,23 +635,45 @@ where
assert_eq!(to_string(f, 1.9971e20, Minus, 1), "199710000000000000000.0");
assert_eq!(to_string(f, 1.9971e20, Minus, 8), "199710000000000000000.00000000");
assert_eq!(to_string(f, f32::MAX, Minus, 0), format!("34028235{:0>31}", ""));
assert_eq!(to_string(f, f32::MAX, Minus, 1), format!("34028235{:0>31}.0", ""));
assert_eq!(to_string(f, f32::MAX, Minus, 8), format!("34028235{:0>31}.00000000", ""));
#[cfg(target_has_reliable_f16)]
{
// f16
assert_eq!(to_string(f, f16::MAX, Minus, 0), "65500");
assert_eq!(to_string(f, f16::MAX, Minus, 1), "65500.0");
assert_eq!(to_string(f, f16::MAX, Minus, 8), "65500.00000000");
let minf32 = ldexp_f32(1.0, -149);
assert_eq!(to_string(f, minf32, Minus, 0), format!("0.{:0>44}1", ""));
assert_eq!(to_string(f, minf32, Minus, 45), format!("0.{:0>44}1", ""));
assert_eq!(to_string(f, minf32, Minus, 46), format!("0.{:0>44}10", ""));
let minf16 = ldexp_f16(1.0, -24);
assert_eq!(to_string(f, minf16, Minus, 0), "0.00000006");
assert_eq!(to_string(f, minf16, Minus, 8), "0.00000006");
assert_eq!(to_string(f, minf16, Minus, 9), "0.000000060");
}
assert_eq!(to_string(f, f64::MAX, Minus, 0), format!("17976931348623157{:0>292}", ""));
assert_eq!(to_string(f, f64::MAX, Minus, 1), format!("17976931348623157{:0>292}.0", ""));
assert_eq!(to_string(f, f64::MAX, Minus, 8), format!("17976931348623157{:0>292}.00000000", ""));
{
// f32
assert_eq!(to_string(f, f32::MAX, Minus, 0), format!("34028235{:0>31}", ""));
assert_eq!(to_string(f, f32::MAX, Minus, 1), format!("34028235{:0>31}.0", ""));
assert_eq!(to_string(f, f32::MAX, Minus, 8), format!("34028235{:0>31}.00000000", ""));
let minf64 = ldexp_f64(1.0, -1074);
assert_eq!(to_string(f, minf64, Minus, 0), format!("0.{:0>323}5", ""));
assert_eq!(to_string(f, minf64, Minus, 324), format!("0.{:0>323}5", ""));
assert_eq!(to_string(f, minf64, Minus, 325), format!("0.{:0>323}50", ""));
let minf32 = ldexp_f32(1.0, -149);
assert_eq!(to_string(f, minf32, Minus, 0), format!("0.{:0>44}1", ""));
assert_eq!(to_string(f, minf32, Minus, 45), format!("0.{:0>44}1", ""));
assert_eq!(to_string(f, minf32, Minus, 46), format!("0.{:0>44}10", ""));
}
{
// f64
assert_eq!(to_string(f, f64::MAX, Minus, 0), format!("17976931348623157{:0>292}", ""));
assert_eq!(to_string(f, f64::MAX, Minus, 1), format!("17976931348623157{:0>292}.0", ""));
assert_eq!(
to_string(f, f64::MAX, Minus, 8),
format!("17976931348623157{:0>292}.00000000", "")
);
let minf64 = ldexp_f64(1.0, -1074);
assert_eq!(to_string(f, minf64, Minus, 0), format!("0.{:0>323}5", ""));
assert_eq!(to_string(f, minf64, Minus, 324), format!("0.{:0>323}5", ""));
assert_eq!(to_string(f, minf64, Minus, 325), format!("0.{:0>323}50", ""));
}
if cfg!(miri) {
// Miri is too slow
@ -655,27 +759,45 @@ where
assert_eq!(to_string(f, 1.0e23, Minus, (23, 24), false), "100000000000000000000000");
assert_eq!(to_string(f, 1.0e23, Minus, (24, 25), false), "1e23");
assert_eq!(to_string(f, f32::MAX, Minus, (-4, 16), false), "3.4028235e38");
assert_eq!(to_string(f, f32::MAX, Minus, (-39, 38), false), "3.4028235e38");
assert_eq!(to_string(f, f32::MAX, Minus, (-38, 39), false), format!("34028235{:0>31}", ""));
#[cfg(target_has_reliable_f16)]
{
// f16
assert_eq!(to_string(f, f16::MAX, Minus, (-2, 2), false), "6.55e4");
assert_eq!(to_string(f, f16::MAX, Minus, (-4, 4), false), "6.55e4");
assert_eq!(to_string(f, f16::MAX, Minus, (-5, 5), false), "65500");
let minf32 = ldexp_f32(1.0, -149);
assert_eq!(to_string(f, minf32, Minus, (-4, 16), false), "1e-45");
assert_eq!(to_string(f, minf32, Minus, (-44, 45), false), "1e-45");
assert_eq!(to_string(f, minf32, Minus, (-45, 44), false), format!("0.{:0>44}1", ""));
let minf16 = ldexp_f16(1.0, -24);
assert_eq!(to_string(f, minf16, Minus, (-2, 2), false), "6e-8");
assert_eq!(to_string(f, minf16, Minus, (-7, 7), false), "6e-8");
assert_eq!(to_string(f, minf16, Minus, (-8, 8), false), "0.00000006");
}
assert_eq!(to_string(f, f64::MAX, Minus, (-4, 16), false), "1.7976931348623157e308");
assert_eq!(
to_string(f, f64::MAX, Minus, (-308, 309), false),
format!("17976931348623157{:0>292}", "")
);
assert_eq!(to_string(f, f64::MAX, Minus, (-309, 308), false), "1.7976931348623157e308");
{
// f32
assert_eq!(to_string(f, f32::MAX, Minus, (-4, 16), false), "3.4028235e38");
assert_eq!(to_string(f, f32::MAX, Minus, (-39, 38), false), "3.4028235e38");
assert_eq!(to_string(f, f32::MAX, Minus, (-38, 39), false), format!("34028235{:0>31}", ""));
let minf64 = ldexp_f64(1.0, -1074);
assert_eq!(to_string(f, minf64, Minus, (-4, 16), false), "5e-324");
assert_eq!(to_string(f, minf64, Minus, (-324, 323), false), format!("0.{:0>323}5", ""));
assert_eq!(to_string(f, minf64, Minus, (-323, 324), false), "5e-324");
let minf32 = ldexp_f32(1.0, -149);
assert_eq!(to_string(f, minf32, Minus, (-4, 16), false), "1e-45");
assert_eq!(to_string(f, minf32, Minus, (-44, 45), false), "1e-45");
assert_eq!(to_string(f, minf32, Minus, (-45, 44), false), format!("0.{:0>44}1", ""));
}
{
// f64
assert_eq!(to_string(f, f64::MAX, Minus, (-4, 16), false), "1.7976931348623157e308");
assert_eq!(
to_string(f, f64::MAX, Minus, (-308, 309), false),
format!("17976931348623157{:0>292}", "")
);
assert_eq!(to_string(f, f64::MAX, Minus, (-309, 308), false), "1.7976931348623157e308");
let minf64 = ldexp_f64(1.0, -1074);
assert_eq!(to_string(f, minf64, Minus, (-4, 16), false), "5e-324");
assert_eq!(to_string(f, minf64, Minus, (-324, 323), false), format!("0.{:0>323}5", ""));
assert_eq!(to_string(f, minf64, Minus, (-323, 324), false), "5e-324");
}
assert_eq!(to_string(f, 1.1, Minus, (i16::MIN, i16::MAX), false), "1.1");
}
@ -791,6 +913,26 @@ where
"9.999999999999999547481118258862586856139387236908078193664550781250000e-7"
);
#[cfg(target_has_reliable_f16)]
{
assert_eq!(to_string(f, f16::MAX, Minus, 1, false), "7e4");
assert_eq!(to_string(f, f16::MAX, Minus, 2, false), "6.6e4");
assert_eq!(to_string(f, f16::MAX, Minus, 4, false), "6.550e4");
assert_eq!(to_string(f, f16::MAX, Minus, 5, false), "6.5504e4");
assert_eq!(to_string(f, f16::MAX, Minus, 6, false), "6.55040e4");
assert_eq!(to_string(f, f16::MAX, Minus, 16, false), "6.550400000000000e4");
let minf16 = ldexp_f16(1.0, -24);
assert_eq!(to_string(f, minf16, Minus, 1, false), "6e-8");
assert_eq!(to_string(f, minf16, Minus, 2, false), "6.0e-8");
assert_eq!(to_string(f, minf16, Minus, 4, false), "5.960e-8");
assert_eq!(to_string(f, minf16, Minus, 8, false), "5.9604645e-8");
assert_eq!(to_string(f, minf16, Minus, 16, false), "5.960464477539062e-8");
assert_eq!(to_string(f, minf16, Minus, 17, false), "5.9604644775390625e-8");
assert_eq!(to_string(f, minf16, Minus, 18, false), "5.96046447753906250e-8");
assert_eq!(to_string(f, minf16, Minus, 24, false), "5.96046447753906250000000e-8");
}
assert_eq!(to_string(f, f32::MAX, Minus, 1, false), "3e38");
assert_eq!(to_string(f, f32::MAX, Minus, 2, false), "3.4e38");
assert_eq!(to_string(f, f32::MAX, Minus, 4, false), "3.403e38");
@ -1069,6 +1211,13 @@ where
"0.000000999999999999999954748111825886258685613938723690807819366455078125000"
);
#[cfg(target_has_reliable_f16)]
{
assert_eq!(to_string(f, f16::MAX, Minus, 0), "65504");
assert_eq!(to_string(f, f16::MAX, Minus, 1), "65504.0");
assert_eq!(to_string(f, f16::MAX, Minus, 2), "65504.00");
}
assert_eq!(to_string(f, f32::MAX, Minus, 0), "340282346638528859811704183484516925440");
assert_eq!(to_string(f, f32::MAX, Minus, 1), "340282346638528859811704183484516925440.0");
assert_eq!(to_string(f, f32::MAX, Minus, 2), "340282346638528859811704183484516925440.00");
@ -1078,6 +1227,21 @@ where
return;
}
#[cfg(target_has_reliable_f16)]
{
let minf16 = ldexp_f16(1.0, -24);
assert_eq!(to_string(f, minf16, Minus, 0), "0");
assert_eq!(to_string(f, minf16, Minus, 1), "0.0");
assert_eq!(to_string(f, minf16, Minus, 2), "0.00");
assert_eq!(to_string(f, minf16, Minus, 4), "0.0000");
assert_eq!(to_string(f, minf16, Minus, 8), "0.00000006");
assert_eq!(to_string(f, minf16, Minus, 10), "0.0000000596");
assert_eq!(to_string(f, minf16, Minus, 15), "0.000000059604645");
assert_eq!(to_string(f, minf16, Minus, 20), "0.00000005960464477539");
assert_eq!(to_string(f, minf16, Minus, 24), "0.000000059604644775390625");
assert_eq!(to_string(f, minf16, Minus, 32), "0.00000005960464477539062500000000");
}
let minf32 = ldexp_f32(1.0, -149);
assert_eq!(to_string(f, minf32, Minus, 0), "0");
assert_eq!(to_string(f, minf32, Minus, 1), "0.0");

View file

@ -79,6 +79,20 @@ where
(npassed, nignored)
}
#[cfg(target_has_reliable_f16)]
pub fn f16_random_equivalence_test<F, G>(f: F, g: G, k: usize, n: usize)
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),
{
let mut rng = crate::test_rng();
let f16_range = Uniform::new(0x0001u16, 0x7c00).unwrap();
iterate("f16_random_equivalence_test", k, n, f, g, |_| {
let x = f16::from_bits(f16_range.sample(&mut rng));
decode_finite(x)
});
}
pub fn f32_random_equivalence_test<F, G>(f: F, g: G, k: usize, n: usize)
where
F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit<u8>]) -> Option<(&'a [u8], i16)>,
@ -105,6 +119,24 @@ where
});
}
#[cfg(target_has_reliable_f16)]
pub fn f16_exhaustive_equivalence_test<F, G>(f: F, g: G, k: usize)
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),
{
// Unlike the other float types, `f16` is small enough that these exhaustive tests
// can run in less than a second so we don't need to ignore it.
// iterate from 0x0001 to 0x7bff, i.e., all finite ranges
let (npassed, nignored) =
iterate("f16_exhaustive_equivalence_test", k, 0x7bff, f, g, |i: usize| {
let x = f16::from_bits(i as u16 + 1);
decode_finite(x)
});
assert_eq!((npassed, nignored), (29735, 2008));
}
pub fn f32_exhaustive_equivalence_test<F, G>(f: F, g: G, k: usize)
where
F: for<'a> FnMut(&Decoded, &'a mut [MaybeUninit<u8>]) -> Option<(&'a [u8], i16)>,
@ -133,6 +165,17 @@ fn shortest_random_equivalence_test() {
f64_random_equivalence_test(format_shortest_opt, fallback, MAX_SIG_DIGITS, n);
f32_random_equivalence_test(format_shortest_opt, fallback, MAX_SIG_DIGITS, n);
#[cfg(target_has_reliable_f16)]
f16_random_equivalence_test(format_shortest_opt, fallback, MAX_SIG_DIGITS, n);
}
#[test]
#[cfg_attr(miri, ignore)] // Miri is to slow
#[cfg(target_has_reliable_f16)]
fn shortest_f16_exhaustive_equivalence_test() {
// see the f32 version
use core::num::flt2dec::strategy::dragon::format_shortest as fallback;
f16_exhaustive_equivalence_test(format_shortest_opt, fallback, MAX_SIG_DIGITS);
}
#[test]
@ -158,6 +201,23 @@ fn shortest_f64_hard_random_equivalence_test() {
f64_random_equivalence_test(format_shortest_opt, fallback, MAX_SIG_DIGITS, 100_000_000);
}
#[test]
#[cfg(target_has_reliable_f16)]
fn exact_f16_random_equivalence_test() {
use core::num::flt2dec::strategy::dragon::format_exact as fallback;
// Miri is too slow
let n = if cfg!(miri) { 3 } else { 1_000 };
for k in 1..21 {
f16_random_equivalence_test(
|d, buf| format_exact_opt(d, buf, i16::MIN),
|d, buf| fallback(d, buf, i16::MIN),
k,
n,
);
}
}
#[test]
fn exact_f32_random_equivalence_test() {
use core::num::flt2dec::strategy::dragon::format_exact as fallback;

View file

@ -18,6 +18,8 @@ fn test_mul_pow10() {
fn shortest_sanity_test() {
f64_shortest_sanity_test(format_shortest);
f32_shortest_sanity_test(format_shortest);
#[cfg(target_has_reliable_f16)]
f16_shortest_sanity_test(format_shortest);
more_shortest_sanity_test(format_shortest);
}
@ -41,6 +43,9 @@ fn exact_sanity_test() {
f64_exact_sanity_test(format_exact);
}
f32_exact_sanity_test(format_exact);
#[cfg(target_has_reliable_f16)]
f16_exact_sanity_test(format_exact);
}
#[test]

View file

@ -38,6 +38,8 @@ fn test_max_pow10_no_more_than() {
fn shortest_sanity_test() {
f64_shortest_sanity_test(format_shortest);
f32_shortest_sanity_test(format_shortest);
#[cfg(target_has_reliable_f16)]
f16_shortest_sanity_test(format_shortest);
more_shortest_sanity_test(format_shortest);
}
@ -50,6 +52,8 @@ fn exact_sanity_test() {
f64_exact_sanity_test(format_exact);
}
f32_exact_sanity_test(format_exact);
#[cfg(target_has_reliable_f16)]
f16_exact_sanity_test(format_exact);
}
#[test]

View file

@ -732,157 +732,157 @@ assume_usize_width! {
}
macro_rules! test_float {
($modname: ident, $fty: ty, $inf: expr, $neginf: expr, $nan: expr, $min: expr, $max: expr, $min_pos: expr, $max_exp:expr) => {
($modname: ident, $fassert: ident, $fty: ty, $inf: expr, $neginf: expr, $nan: expr, $min: expr, $max: expr, $min_pos: expr, $max_exp:expr) => {
mod $modname {
#[test]
fn min() {
assert_eq!((0.0 as $fty).min(0.0), 0.0);
assert!((0.0 as $fty).min(0.0).is_sign_positive());
assert_eq!((-0.0 as $fty).min(-0.0), -0.0);
assert!((-0.0 as $fty).min(-0.0).is_sign_negative());
assert_eq!((9.0 as $fty).min(9.0), 9.0);
assert_eq!((-9.0 as $fty).min(0.0), -9.0);
assert_eq!((0.0 as $fty).min(9.0), 0.0);
assert!((0.0 as $fty).min(9.0).is_sign_positive());
assert_eq!((-0.0 as $fty).min(9.0), -0.0);
assert!((-0.0 as $fty).min(9.0).is_sign_negative());
assert_eq!((-0.0 as $fty).min(-9.0), -9.0);
assert_eq!(($inf as $fty).min(9.0), 9.0);
assert_eq!((9.0 as $fty).min($inf), 9.0);
assert_eq!(($inf as $fty).min(-9.0), -9.0);
assert_eq!((-9.0 as $fty).min($inf), -9.0);
assert_eq!(($neginf as $fty).min(9.0), $neginf);
assert_eq!((9.0 as $fty).min($neginf), $neginf);
assert_eq!(($neginf as $fty).min(-9.0), $neginf);
assert_eq!((-9.0 as $fty).min($neginf), $neginf);
assert_eq!(($nan as $fty).min(9.0), 9.0);
assert_eq!(($nan as $fty).min(-9.0), -9.0);
assert_eq!((9.0 as $fty).min($nan), 9.0);
assert_eq!((-9.0 as $fty).min($nan), -9.0);
assert!(($nan as $fty).min($nan).is_nan());
$fassert!((0.0 as $fty).min(0.0), 0.0);
$fassert!((0.0 as $fty).min(0.0).is_sign_positive());
$fassert!((-0.0 as $fty).min(-0.0), -0.0);
$fassert!((-0.0 as $fty).min(-0.0).is_sign_negative());
$fassert!((9.0 as $fty).min(9.0), 9.0);
$fassert!((-9.0 as $fty).min(0.0), -9.0);
$fassert!((0.0 as $fty).min(9.0), 0.0);
$fassert!((0.0 as $fty).min(9.0).is_sign_positive());
$fassert!((-0.0 as $fty).min(9.0), -0.0);
$fassert!((-0.0 as $fty).min(9.0).is_sign_negative());
$fassert!((-0.0 as $fty).min(-9.0), -9.0);
$fassert!(($inf as $fty).min(9.0), 9.0);
$fassert!((9.0 as $fty).min($inf), 9.0);
$fassert!(($inf as $fty).min(-9.0), -9.0);
$fassert!((-9.0 as $fty).min($inf), -9.0);
$fassert!(($neginf as $fty).min(9.0), $neginf);
$fassert!((9.0 as $fty).min($neginf), $neginf);
$fassert!(($neginf as $fty).min(-9.0), $neginf);
$fassert!((-9.0 as $fty).min($neginf), $neginf);
$fassert!(($nan as $fty).min(9.0), 9.0);
$fassert!(($nan as $fty).min(-9.0), -9.0);
$fassert!((9.0 as $fty).min($nan), 9.0);
$fassert!((-9.0 as $fty).min($nan), -9.0);
$fassert!(($nan as $fty).min($nan).is_nan());
}
#[test]
fn max() {
assert_eq!((0.0 as $fty).max(0.0), 0.0);
assert!((0.0 as $fty).max(0.0).is_sign_positive());
assert_eq!((-0.0 as $fty).max(-0.0), -0.0);
assert!((-0.0 as $fty).max(-0.0).is_sign_negative());
assert_eq!((9.0 as $fty).max(9.0), 9.0);
assert_eq!((-9.0 as $fty).max(0.0), 0.0);
assert!((-9.0 as $fty).max(0.0).is_sign_positive());
assert_eq!((-9.0 as $fty).max(-0.0), -0.0);
assert!((-9.0 as $fty).max(-0.0).is_sign_negative());
assert_eq!((0.0 as $fty).max(9.0), 9.0);
assert_eq!((0.0 as $fty).max(-9.0), 0.0);
assert!((0.0 as $fty).max(-9.0).is_sign_positive());
assert_eq!((-0.0 as $fty).max(-9.0), -0.0);
assert!((-0.0 as $fty).max(-9.0).is_sign_negative());
assert_eq!(($inf as $fty).max(9.0), $inf);
assert_eq!((9.0 as $fty).max($inf), $inf);
assert_eq!(($inf as $fty).max(-9.0), $inf);
assert_eq!((-9.0 as $fty).max($inf), $inf);
assert_eq!(($neginf as $fty).max(9.0), 9.0);
assert_eq!((9.0 as $fty).max($neginf), 9.0);
assert_eq!(($neginf as $fty).max(-9.0), -9.0);
assert_eq!((-9.0 as $fty).max($neginf), -9.0);
assert_eq!(($nan as $fty).max(9.0), 9.0);
assert_eq!(($nan as $fty).max(-9.0), -9.0);
assert_eq!((9.0 as $fty).max($nan), 9.0);
assert_eq!((-9.0 as $fty).max($nan), -9.0);
assert!(($nan as $fty).max($nan).is_nan());
$fassert!((0.0 as $fty).max(0.0), 0.0);
$fassert!((0.0 as $fty).max(0.0).is_sign_positive());
$fassert!((-0.0 as $fty).max(-0.0), -0.0);
$fassert!((-0.0 as $fty).max(-0.0).is_sign_negative());
$fassert!((9.0 as $fty).max(9.0), 9.0);
$fassert!((-9.0 as $fty).max(0.0), 0.0);
$fassert!((-9.0 as $fty).max(0.0).is_sign_positive());
$fassert!((-9.0 as $fty).max(-0.0), -0.0);
$fassert!((-9.0 as $fty).max(-0.0).is_sign_negative());
$fassert!((0.0 as $fty).max(9.0), 9.0);
$fassert!((0.0 as $fty).max(-9.0), 0.0);
$fassert!((0.0 as $fty).max(-9.0).is_sign_positive());
$fassert!((-0.0 as $fty).max(-9.0), -0.0);
$fassert!((-0.0 as $fty).max(-9.0).is_sign_negative());
$fassert!(($inf as $fty).max(9.0), $inf);
$fassert!((9.0 as $fty).max($inf), $inf);
$fassert!(($inf as $fty).max(-9.0), $inf);
$fassert!((-9.0 as $fty).max($inf), $inf);
$fassert!(($neginf as $fty).max(9.0), 9.0);
$fassert!((9.0 as $fty).max($neginf), 9.0);
$fassert!(($neginf as $fty).max(-9.0), -9.0);
$fassert!((-9.0 as $fty).max($neginf), -9.0);
$fassert!(($nan as $fty).max(9.0), 9.0);
$fassert!(($nan as $fty).max(-9.0), -9.0);
$fassert!((9.0 as $fty).max($nan), 9.0);
$fassert!((-9.0 as $fty).max($nan), -9.0);
$fassert!(($nan as $fty).max($nan).is_nan());
}
#[test]
fn minimum() {
assert_eq!((0.0 as $fty).minimum(0.0), 0.0);
assert!((0.0 as $fty).minimum(0.0).is_sign_positive());
assert_eq!((-0.0 as $fty).minimum(0.0), -0.0);
assert!((-0.0 as $fty).minimum(0.0).is_sign_negative());
assert_eq!((-0.0 as $fty).minimum(-0.0), -0.0);
assert!((-0.0 as $fty).minimum(-0.0).is_sign_negative());
assert_eq!((9.0 as $fty).minimum(9.0), 9.0);
assert_eq!((-9.0 as $fty).minimum(0.0), -9.0);
assert_eq!((0.0 as $fty).minimum(9.0), 0.0);
assert!((0.0 as $fty).minimum(9.0).is_sign_positive());
assert_eq!((-0.0 as $fty).minimum(9.0), -0.0);
assert!((-0.0 as $fty).minimum(9.0).is_sign_negative());
assert_eq!((-0.0 as $fty).minimum(-9.0), -9.0);
assert_eq!(($inf as $fty).minimum(9.0), 9.0);
assert_eq!((9.0 as $fty).minimum($inf), 9.0);
assert_eq!(($inf as $fty).minimum(-9.0), -9.0);
assert_eq!((-9.0 as $fty).minimum($inf), -9.0);
assert_eq!(($neginf as $fty).minimum(9.0), $neginf);
assert_eq!((9.0 as $fty).minimum($neginf), $neginf);
assert_eq!(($neginf as $fty).minimum(-9.0), $neginf);
assert_eq!((-9.0 as $fty).minimum($neginf), $neginf);
assert!(($nan as $fty).minimum(9.0).is_nan());
assert!(($nan as $fty).minimum(-9.0).is_nan());
assert!((9.0 as $fty).minimum($nan).is_nan());
assert!((-9.0 as $fty).minimum($nan).is_nan());
assert!(($nan as $fty).minimum($nan).is_nan());
$fassert!((0.0 as $fty).minimum(0.0), 0.0);
$fassert!((0.0 as $fty).minimum(0.0).is_sign_positive());
$fassert!((-0.0 as $fty).minimum(0.0), -0.0);
$fassert!((-0.0 as $fty).minimum(0.0).is_sign_negative());
$fassert!((-0.0 as $fty).minimum(-0.0), -0.0);
$fassert!((-0.0 as $fty).minimum(-0.0).is_sign_negative());
$fassert!((9.0 as $fty).minimum(9.0), 9.0);
$fassert!((-9.0 as $fty).minimum(0.0), -9.0);
$fassert!((0.0 as $fty).minimum(9.0), 0.0);
$fassert!((0.0 as $fty).minimum(9.0).is_sign_positive());
$fassert!((-0.0 as $fty).minimum(9.0), -0.0);
$fassert!((-0.0 as $fty).minimum(9.0).is_sign_negative());
$fassert!((-0.0 as $fty).minimum(-9.0), -9.0);
$fassert!(($inf as $fty).minimum(9.0), 9.0);
$fassert!((9.0 as $fty).minimum($inf), 9.0);
$fassert!(($inf as $fty).minimum(-9.0), -9.0);
$fassert!((-9.0 as $fty).minimum($inf), -9.0);
$fassert!(($neginf as $fty).minimum(9.0), $neginf);
$fassert!((9.0 as $fty).minimum($neginf), $neginf);
$fassert!(($neginf as $fty).minimum(-9.0), $neginf);
$fassert!((-9.0 as $fty).minimum($neginf), $neginf);
$fassert!(($nan as $fty).minimum(9.0).is_nan());
$fassert!(($nan as $fty).minimum(-9.0).is_nan());
$fassert!((9.0 as $fty).minimum($nan).is_nan());
$fassert!((-9.0 as $fty).minimum($nan).is_nan());
$fassert!(($nan as $fty).minimum($nan).is_nan());
}
#[test]
fn maximum() {
assert_eq!((0.0 as $fty).maximum(0.0), 0.0);
assert!((0.0 as $fty).maximum(0.0).is_sign_positive());
assert_eq!((-0.0 as $fty).maximum(0.0), 0.0);
assert!((-0.0 as $fty).maximum(0.0).is_sign_positive());
assert_eq!((-0.0 as $fty).maximum(-0.0), -0.0);
assert!((-0.0 as $fty).maximum(-0.0).is_sign_negative());
assert_eq!((9.0 as $fty).maximum(9.0), 9.0);
assert_eq!((-9.0 as $fty).maximum(0.0), 0.0);
assert!((-9.0 as $fty).maximum(0.0).is_sign_positive());
assert_eq!((-9.0 as $fty).maximum(-0.0), -0.0);
assert!((-9.0 as $fty).maximum(-0.0).is_sign_negative());
assert_eq!((0.0 as $fty).maximum(9.0), 9.0);
assert_eq!((0.0 as $fty).maximum(-9.0), 0.0);
assert!((0.0 as $fty).maximum(-9.0).is_sign_positive());
assert_eq!((-0.0 as $fty).maximum(-9.0), -0.0);
assert!((-0.0 as $fty).maximum(-9.0).is_sign_negative());
assert_eq!(($inf as $fty).maximum(9.0), $inf);
assert_eq!((9.0 as $fty).maximum($inf), $inf);
assert_eq!(($inf as $fty).maximum(-9.0), $inf);
assert_eq!((-9.0 as $fty).maximum($inf), $inf);
assert_eq!(($neginf as $fty).maximum(9.0), 9.0);
assert_eq!((9.0 as $fty).maximum($neginf), 9.0);
assert_eq!(($neginf as $fty).maximum(-9.0), -9.0);
assert_eq!((-9.0 as $fty).maximum($neginf), -9.0);
assert!(($nan as $fty).maximum(9.0).is_nan());
assert!(($nan as $fty).maximum(-9.0).is_nan());
assert!((9.0 as $fty).maximum($nan).is_nan());
assert!((-9.0 as $fty).maximum($nan).is_nan());
assert!(($nan as $fty).maximum($nan).is_nan());
$fassert!((0.0 as $fty).maximum(0.0), 0.0);
$fassert!((0.0 as $fty).maximum(0.0).is_sign_positive());
$fassert!((-0.0 as $fty).maximum(0.0), 0.0);
$fassert!((-0.0 as $fty).maximum(0.0).is_sign_positive());
$fassert!((-0.0 as $fty).maximum(-0.0), -0.0);
$fassert!((-0.0 as $fty).maximum(-0.0).is_sign_negative());
$fassert!((9.0 as $fty).maximum(9.0), 9.0);
$fassert!((-9.0 as $fty).maximum(0.0), 0.0);
$fassert!((-9.0 as $fty).maximum(0.0).is_sign_positive());
$fassert!((-9.0 as $fty).maximum(-0.0), -0.0);
$fassert!((-9.0 as $fty).maximum(-0.0).is_sign_negative());
$fassert!((0.0 as $fty).maximum(9.0), 9.0);
$fassert!((0.0 as $fty).maximum(-9.0), 0.0);
$fassert!((0.0 as $fty).maximum(-9.0).is_sign_positive());
$fassert!((-0.0 as $fty).maximum(-9.0), -0.0);
$fassert!((-0.0 as $fty).maximum(-9.0).is_sign_negative());
$fassert!(($inf as $fty).maximum(9.0), $inf);
$fassert!((9.0 as $fty).maximum($inf), $inf);
$fassert!(($inf as $fty).maximum(-9.0), $inf);
$fassert!((-9.0 as $fty).maximum($inf), $inf);
$fassert!(($neginf as $fty).maximum(9.0), 9.0);
$fassert!((9.0 as $fty).maximum($neginf), 9.0);
$fassert!(($neginf as $fty).maximum(-9.0), -9.0);
$fassert!((-9.0 as $fty).maximum($neginf), -9.0);
$fassert!(($nan as $fty).maximum(9.0).is_nan());
$fassert!(($nan as $fty).maximum(-9.0).is_nan());
$fassert!((9.0 as $fty).maximum($nan).is_nan());
$fassert!((-9.0 as $fty).maximum($nan).is_nan());
$fassert!(($nan as $fty).maximum($nan).is_nan());
}
#[test]
fn midpoint() {
assert_eq!((0.5 as $fty).midpoint(0.5), 0.5);
assert_eq!((0.5 as $fty).midpoint(2.5), 1.5);
assert_eq!((3.0 as $fty).midpoint(4.0), 3.5);
assert_eq!((-3.0 as $fty).midpoint(4.0), 0.5);
assert_eq!((3.0 as $fty).midpoint(-4.0), -0.5);
assert_eq!((-3.0 as $fty).midpoint(-4.0), -3.5);
assert_eq!((0.0 as $fty).midpoint(0.0), 0.0);
assert_eq!((-0.0 as $fty).midpoint(-0.0), -0.0);
assert_eq!((-5.0 as $fty).midpoint(5.0), 0.0);
assert_eq!(($max as $fty).midpoint($min), 0.0);
assert_eq!(($min as $fty).midpoint($max), -0.0);
assert_eq!(($max as $fty).midpoint($min_pos), $max / 2.);
assert_eq!((-$max as $fty).midpoint($min_pos), -$max / 2.);
assert_eq!(($max as $fty).midpoint(-$min_pos), $max / 2.);
assert_eq!((-$max as $fty).midpoint(-$min_pos), -$max / 2.);
assert_eq!(($min_pos as $fty).midpoint($max), $max / 2.);
assert_eq!(($min_pos as $fty).midpoint(-$max), -$max / 2.);
assert_eq!((-$min_pos as $fty).midpoint($max), $max / 2.);
assert_eq!((-$min_pos as $fty).midpoint(-$max), -$max / 2.);
assert_eq!(($max as $fty).midpoint($max), $max);
assert_eq!(($min_pos as $fty).midpoint($min_pos), $min_pos);
assert_eq!((-$min_pos as $fty).midpoint(-$min_pos), -$min_pos);
assert_eq!(($max as $fty).midpoint(5.0), $max / 2.0 + 2.5);
assert_eq!(($max as $fty).midpoint(-5.0), $max / 2.0 - 2.5);
assert_eq!(($inf as $fty).midpoint($inf), $inf);
assert_eq!(($neginf as $fty).midpoint($neginf), $neginf);
assert!(($nan as $fty).midpoint(1.0).is_nan());
assert!((1.0 as $fty).midpoint($nan).is_nan());
assert!(($nan as $fty).midpoint($nan).is_nan());
$fassert!((0.5 as $fty).midpoint(0.5), 0.5);
$fassert!((0.5 as $fty).midpoint(2.5), 1.5);
$fassert!((3.0 as $fty).midpoint(4.0), 3.5);
$fassert!((-3.0 as $fty).midpoint(4.0), 0.5);
$fassert!((3.0 as $fty).midpoint(-4.0), -0.5);
$fassert!((-3.0 as $fty).midpoint(-4.0), -3.5);
$fassert!((0.0 as $fty).midpoint(0.0), 0.0);
$fassert!((-0.0 as $fty).midpoint(-0.0), -0.0);
$fassert!((-5.0 as $fty).midpoint(5.0), 0.0);
$fassert!(($max as $fty).midpoint($min), 0.0);
$fassert!(($min as $fty).midpoint($max), -0.0);
$fassert!(($max as $fty).midpoint($min_pos), $max / 2.);
$fassert!((-$max as $fty).midpoint($min_pos), -$max / 2.);
$fassert!(($max as $fty).midpoint(-$min_pos), $max / 2.);
$fassert!((-$max as $fty).midpoint(-$min_pos), -$max / 2.);
$fassert!(($min_pos as $fty).midpoint($max), $max / 2.);
$fassert!(($min_pos as $fty).midpoint(-$max), -$max / 2.);
$fassert!((-$min_pos as $fty).midpoint($max), $max / 2.);
$fassert!((-$min_pos as $fty).midpoint(-$max), -$max / 2.);
$fassert!(($max as $fty).midpoint($max), $max);
$fassert!(($min_pos as $fty).midpoint($min_pos), $min_pos);
$fassert!((-$min_pos as $fty).midpoint(-$min_pos), -$min_pos);
$fassert!(($max as $fty).midpoint(5.0), $max / 2.0 + 2.5);
$fassert!(($max as $fty).midpoint(-5.0), $max / 2.0 - 2.5);
$fassert!(($inf as $fty).midpoint($inf), $inf);
$fassert!(($neginf as $fty).midpoint($neginf), $neginf);
$fassert!(($nan as $fty).midpoint(1.0).is_nan());
$fassert!((1.0 as $fty).midpoint($nan).is_nan());
$fassert!(($nan as $fty).midpoint($nan).is_nan());
// test if large differences in magnitude are still correctly computed.
// NOTE: that because of how small x and y are, x + y can never overflow
@ -907,19 +907,19 @@ macro_rules! test_float {
}
#[test]
fn rem_euclid() {
let a: $fty = 42.0;
assert!($inf.rem_euclid(a).is_nan());
assert_eq!(a.rem_euclid($inf), a);
assert!(a.rem_euclid($nan).is_nan());
// FIXME: Use $fassert when rem_euclid becomes const
assert!($inf.rem_euclid((42.0 as $fty)).is_nan());
assert_eq!((42.0 as $fty).rem_euclid($inf), (42.0 as $fty));
assert!((42.0 as $fty).rem_euclid($nan).is_nan());
assert!($inf.rem_euclid($inf).is_nan());
assert!($inf.rem_euclid($nan).is_nan());
assert!($nan.rem_euclid($inf).is_nan());
}
#[test]
fn div_euclid() {
let a: $fty = 42.0;
assert_eq!(a.div_euclid($inf), 0.0);
assert!(a.div_euclid($nan).is_nan());
// FIXME: Use $fassert when div_euclid becomes const
assert_eq!((42.0 as $fty).div_euclid($inf), 0.0);
assert!((42.0 as $fty).div_euclid($nan).is_nan());
assert!($inf.div_euclid($inf).is_nan());
assert!($inf.div_euclid($nan).is_nan());
assert!($nan.div_euclid($inf).is_nan());
@ -928,8 +928,41 @@ macro_rules! test_float {
};
}
// Custom assert macro that distribute between assert! and assert_eq! in a non-const context
macro_rules! float_assert {
($b:expr) => {
assert!($b);
};
($left:expr, $right:expr) => {
assert_eq!($left, $right);
};
}
// Custom assert macro that only uses assert! in a const context
macro_rules! float_const_assert {
($b:expr) => {
assert!(const { $b });
};
($left:expr, $right:expr) => {
assert!(const { $left == $right });
};
}
test_float!(
f32,
float_assert,
f32,
f32::INFINITY,
f32::NEG_INFINITY,
f32::NAN,
f32::MIN,
f32::MAX,
f32::MIN_POSITIVE,
f32::MAX_EXP
);
test_float!(
f32_const,
float_const_assert,
f32,
f32::INFINITY,
f32::NEG_INFINITY,
@ -941,6 +974,19 @@ test_float!(
);
test_float!(
f64,
float_assert,
f64,
f64::INFINITY,
f64::NEG_INFINITY,
f64::NAN,
f64::MIN,
f64::MAX,
f64::MIN_POSITIVE,
f64::MAX_EXP
);
test_float!(
f64_const,
float_const_assert,
f64,
f64::INFINITY,
f64::NEG_INFINITY,

View file

@ -305,7 +305,6 @@
#![feature(iter_advance_by)]
#![feature(iter_next_chunk)]
#![feature(lang_items)]
#![feature(let_chains)]
#![feature(link_cfg)]
#![feature(linkage)]
#![feature(macro_metavar_expr_concat)]

View file

@ -25,15 +25,36 @@ impl Drop for Handler {
}
}
#[cfg(any(
target_os = "linux",
target_os = "freebsd",
target_os = "hurd",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "solaris",
target_os = "illumos",
#[cfg(all(
not(miri),
any(
target_os = "linux",
target_os = "freebsd",
target_os = "hurd",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "solaris",
target_os = "illumos",
),
))]
mod thread_info;
// miri doesn't model signals nor stack overflows and this code has some
// synchronization properties that we don't want to expose to user code,
// hence we disable it on miri.
#[cfg(all(
not(miri),
any(
target_os = "linux",
target_os = "freebsd",
target_os = "hurd",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "solaris",
target_os = "illumos",
)
))]
mod imp {
use libc::{
@ -46,22 +67,13 @@ mod imp {
use libc::{mmap64, mprotect, munmap};
use super::Handler;
use crate::cell::Cell;
use super::thread_info::{delete_current_info, set_current_info, with_current_info};
use crate::ops::Range;
use crate::sync::OnceLock;
use crate::sync::atomic::{Atomic, AtomicBool, AtomicPtr, AtomicUsize, Ordering};
use crate::sys::pal::unix::os;
use crate::{io, mem, ptr, thread};
// We use a TLS variable to store the address of the guard page. While TLS
// variables are not guaranteed to be signal-safe, this works out in practice
// since we make sure to write to the variable before the signal stack is
// installed, thereby ensuring that the variable is always allocated when
// the signal handler is called.
thread_local! {
// FIXME: use `Range` once that implements `Copy`.
static GUARD: Cell<(usize, usize)> = const { Cell::new((0, 0)) };
}
use crate::thread::with_current_name;
use crate::{io, mem, panic, ptr};
// Signal handler for the SIGSEGV and SIGBUS handlers. We've got guard pages
// (unmapped pages) at the end of every thread's stack, so if a thread ends
@ -93,29 +105,35 @@ mod imp {
info: *mut libc::siginfo_t,
_data: *mut libc::c_void,
) {
let (start, end) = GUARD.get();
// SAFETY: this pointer is provided by the system and will always point to a valid `siginfo_t`.
let addr = unsafe { (*info).si_addr().addr() };
let fault_addr = unsafe { (*info).si_addr().addr() };
// If the faulting address is within the guard page, then we print a
// message saying so and abort.
if start <= addr && addr < end {
thread::with_current_name(|name| {
let name = name.unwrap_or("<unknown>");
rtprintpanic!("\nthread '{name}' has overflowed its stack\n");
});
rtabort!("stack overflow");
} else {
// Unregister ourselves by reverting back to the default behavior.
// SAFETY: assuming all platforms define struct sigaction as "zero-initializable"
let mut action: sigaction = unsafe { mem::zeroed() };
action.sa_sigaction = SIG_DFL;
// SAFETY: pray this is a well-behaved POSIX implementation of fn sigaction
unsafe { sigaction(signum, &action, ptr::null_mut()) };
// See comment above for why this function returns.
// `with_current_info` expects that the process aborts after it is
// called. If the signal was not caused by a memory access, this might
// not be true. We detect this by noticing that the `si_addr` field is
// zero if the signal is synthetic.
if fault_addr != 0 {
with_current_info(|thread_info| {
// If the faulting address is within the guard page, then we print a
// message saying so and abort.
if let Some(thread_info) = thread_info
&& thread_info.guard_page_range.contains(&fault_addr)
{
let name = thread_info.thread_name.as_deref().unwrap_or("<unknown>");
rtprintpanic!("\nthread '{name}' has overflowed its stack\n");
rtabort!("stack overflow");
}
})
}
// Unregister ourselves by reverting back to the default behavior.
// SAFETY: assuming all platforms define struct sigaction as "zero-initializable"
let mut action: sigaction = unsafe { mem::zeroed() };
action.sa_sigaction = SIG_DFL;
// SAFETY: pray this is a well-behaved POSIX implementation of fn sigaction
unsafe { sigaction(signum, &action, ptr::null_mut()) };
// See comment above for why this function returns.
}
static PAGE_SIZE: Atomic<usize> = AtomicUsize::new(0);
@ -128,9 +146,7 @@ mod imp {
pub unsafe fn init() {
PAGE_SIZE.store(os::page_size(), Ordering::Relaxed);
// Always write to GUARD to ensure the TLS variable is allocated.
let guard = unsafe { install_main_guard().unwrap_or(0..0) };
GUARD.set((guard.start, guard.end));
let mut guard_page_range = unsafe { install_main_guard() };
// SAFETY: assuming all platforms define struct sigaction as "zero-initializable"
let mut action: sigaction = unsafe { mem::zeroed() };
@ -145,7 +161,13 @@ mod imp {
let handler = unsafe { make_handler(true) };
MAIN_ALTSTACK.store(handler.data, Ordering::Relaxed);
mem::forget(handler);
if let Some(guard_page_range) = guard_page_range.take() {
let thread_name = with_current_name(|name| name.map(Box::from));
set_current_info(guard_page_range, thread_name);
}
}
action.sa_flags = SA_SIGINFO | SA_ONSTACK;
action.sa_sigaction = signal_handler as sighandler_t;
// SAFETY: only overriding signals if the default is set
@ -214,9 +236,10 @@ mod imp {
}
if !main_thread {
// Always write to GUARD to ensure the TLS variable is allocated.
let guard = unsafe { current_guard() }.unwrap_or(0..0);
GUARD.set((guard.start, guard.end));
if let Some(guard_page_range) = unsafe { current_guard() } {
let thread_name = with_current_name(|name| name.map(Box::from));
set_current_info(guard_page_range, thread_name);
}
}
// SAFETY: assuming stack_t is zero-initializable
@ -261,6 +284,8 @@ mod imp {
// a mapping that started one page earlier, so walk back a page and unmap from there.
unsafe { munmap(data.sub(page_size), sigstack_size + page_size) };
}
delete_current_info();
}
/// Modern kernels on modern hardware can have dynamic signal stack sizes.
@ -590,17 +615,20 @@ mod imp {
// usually have fewer qualms about forwards compatibility, since the runtime
// is shipped with the OS):
// <https://github.com/apple/swift/blob/swift-5.10-RELEASE/stdlib/public/runtime/CrashHandlerMacOS.cpp>
#[cfg(not(any(
target_os = "linux",
target_os = "freebsd",
target_os = "hurd",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "solaris",
target_os = "illumos",
target_os = "cygwin",
)))]
#[cfg(any(
miri,
not(any(
target_os = "linux",
target_os = "freebsd",
target_os = "hurd",
target_os = "macos",
target_os = "netbsd",
target_os = "openbsd",
target_os = "solaris",
target_os = "illumos",
target_os = "cygwin",
))
))]
mod imp {
pub unsafe fn init() {}

View file

@ -0,0 +1,129 @@
//! TLS, but async-signal-safe.
//!
//! Unfortunately, because thread local storage isn't async-signal-safe, we
//! cannot soundly use it in our stack overflow handler. While this works
//! without problems on most platforms, it can lead to undefined behaviour
//! on others (such as GNU/Linux). Luckily, the POSIX specification documents
//! two thread-specific values that can be accessed in asynchronous signal
//! handlers: the value of `pthread_self()` and the address of `errno`. As
//! `pthread_t` is an opaque platform-specific type, we use the address of
//! `errno` here. As it is thread-specific and does not change over the
//! lifetime of a thread, we can use `&errno` as a key for a `BTreeMap`
//! that stores thread-specific data.
//!
//! Concurrent access to this map is synchronized by two locks an outer
//! [`Mutex`] and an inner spin lock that also remembers the identity of
//! the lock owner:
//! * The spin lock is the primary means of synchronization: since it only
//! uses native atomics, it can be soundly used inside the signal handle
//! as opposed to [`Mutex`], which might not be async-signal-safe.
//! * The [`Mutex`] prevents busy-waiting in the setup logic, as all accesses
//! there are performed with the [`Mutex`] held, which makes the spin-lock
//! redundant in the common case.
//! * Finally, by using the `errno` address as the locked value of the spin
//! lock, we can detect cases where a SIGSEGV occurred while the thread
//! info is being modified.
use crate::collections::BTreeMap;
use crate::hint::spin_loop;
use crate::ops::Range;
use crate::sync::Mutex;
use crate::sync::atomic::{AtomicUsize, Ordering};
use crate::sys::os::errno_location;
pub struct ThreadInfo {
pub guard_page_range: Range<usize>,
pub thread_name: Option<Box<str>>,
}
static LOCK: Mutex<()> = Mutex::new(());
static SPIN_LOCK: AtomicUsize = AtomicUsize::new(0);
// This uses a `BTreeMap` instead of a hashmap since it supports constant
// initialization and automatically reduces the amount of memory used when
// items are removed.
static mut THREAD_INFO: BTreeMap<usize, ThreadInfo> = BTreeMap::new();
struct UnlockOnDrop;
impl Drop for UnlockOnDrop {
fn drop(&mut self) {
SPIN_LOCK.store(0, Ordering::Release);
}
}
/// Get the current thread's information, if available.
///
/// Calling this function might freeze other threads if they attempt to modify
/// their thread information. Thus, the caller should ensure that the process
/// is aborted shortly after this function is called.
///
/// This function is guaranteed to be async-signal-safe if `f` is too.
pub fn with_current_info<R>(f: impl FnOnce(Option<&ThreadInfo>) -> R) -> R {
let this = errno_location().addr();
let mut attempt = 0;
let _guard = loop {
// If we are just spinning endlessly, it's very likely that the thread
// modifying the thread info map has a lower priority than us and will
// not continue until we stop running. Just give up in that case.
if attempt == 10_000_000 {
rtprintpanic!("deadlock in SIGSEGV handler");
return f(None);
}
match SPIN_LOCK.compare_exchange(0, this, Ordering::Acquire, Ordering::Relaxed) {
Ok(_) => break UnlockOnDrop,
Err(owner) if owner == this => {
rtabort!("a thread received SIGSEGV while modifying its stack overflow information")
}
// Spin until the lock can be acquired there is nothing better to
// do. This is unfortunately a priority hole, but a stack overflow
// is a fatal error anyway.
Err(_) => {
spin_loop();
attempt += 1;
}
}
};
// SAFETY: we own the spin lock, so `THREAD_INFO` cannot not be aliased.
let thread_info = unsafe { &*(&raw const THREAD_INFO) };
f(thread_info.get(&this))
}
fn spin_lock_in_setup(this: usize) -> UnlockOnDrop {
loop {
match SPIN_LOCK.compare_exchange(0, this, Ordering::Acquire, Ordering::Relaxed) {
Ok(_) => return UnlockOnDrop,
Err(owner) if owner == this => {
unreachable!("the thread info setup logic isn't recursive")
}
// This function is always called with the outer lock held,
// meaning the only time locking can fail is if another thread has
// encountered a stack overflow. Since that will abort the process,
// we just stop the current thread until that time. We use `pause`
// instead of spinning to avoid priority inversion.
// SAFETY: this doesn't have any safety preconditions.
Err(_) => drop(unsafe { libc::pause() }),
}
}
}
pub fn set_current_info(guard_page_range: Range<usize>, thread_name: Option<Box<str>>) {
let this = errno_location().addr();
let _lock_guard = LOCK.lock();
let _spin_guard = spin_lock_in_setup(this);
// SAFETY: we own the spin lock, so `THREAD_INFO` cannot be aliased.
let thread_info = unsafe { &mut *(&raw mut THREAD_INFO) };
thread_info.insert(this, ThreadInfo { guard_page_range, thread_name });
}
pub fn delete_current_info() {
let this = errno_location().addr();
let _lock_guard = LOCK.lock();
let _spin_guard = spin_lock_in_setup(this);
// SAFETY: we own the spin lock, so `THREAD_INFO` cannot not be aliased.
let thread_info = unsafe { &mut *(&raw mut THREAD_INFO) };
thread_info.remove(&this);
}

@ -1 +1 @@
Subproject commit f1c1839c0deb985a9f98cbd6b38a6d43f2df6157
Subproject commit 1dfaa4db2479753a46a3e90f2c3c89d89d0b21f1

View file

@ -64,7 +64,7 @@ dependencies = [
"tracing-subscriber",
"tracing-tree",
"walkdir",
"windows 0.57.0",
"windows",
"xz2",
]
@ -703,7 +703,7 @@ dependencies = [
"ntapi",
"objc2-core-foundation",
"objc2-io-kit",
"windows 0.61.1",
"windows",
]
[[package]]
@ -916,16 +916,6 @@ version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows"
version = "0.57.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "12342cb4d8e3b046f3d80effd474a7a02447231330ef77d71daa6fbc40681143"
dependencies = [
"windows-core 0.57.0",
"windows-targets",
]
[[package]]
name = "windows"
version = "0.61.1"
@ -933,7 +923,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "c5ee8f3d025738cb02bad7868bbb5f8a6327501e870bf51f1b455b0a2454a419"
dependencies = [
"windows-collections",
"windows-core 0.61.0",
"windows-core",
"windows-future",
"windows-link",
"windows-numerics",
@ -945,19 +935,7 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "3beeceb5e5cfd9eb1d76b381630e82c4241ccd0d27f1a39ed41b2760b255c5e8"
dependencies = [
"windows-core 0.61.0",
]
[[package]]
name = "windows-core"
version = "0.57.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d2ed2439a290666cd67ecce2b0ffaad89c2a56b976b736e6ece670297897832d"
dependencies = [
"windows-implement 0.57.0",
"windows-interface 0.57.0",
"windows-result 0.1.2",
"windows-targets",
"windows-core",
]
[[package]]
@ -966,10 +944,10 @@ version = "0.61.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "4763c1de310c86d75a878046489e2e5ba02c649d185f21c67d4cf8a56d098980"
dependencies = [
"windows-implement 0.60.0",
"windows-interface 0.59.1",
"windows-implement",
"windows-interface",
"windows-link",
"windows-result 0.3.2",
"windows-result",
"windows-strings",
]
@ -979,21 +957,10 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "7a1d6bbefcb7b60acd19828e1bc965da6fcf18a7e39490c5f8be71e54a19ba32"
dependencies = [
"windows-core 0.61.0",
"windows-core",
"windows-link",
]
[[package]]
name = "windows-implement"
version = "0.57.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9107ddc059d5b6fbfbffdfa7a7fe3e22a226def0b2608f72e9d552763d3e1ad7"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "windows-implement"
version = "0.60.0"
@ -1005,17 +972,6 @@ dependencies = [
"syn",
]
[[package]]
name = "windows-interface"
version = "0.57.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "29bee4b38ea3cde66011baa44dba677c432a78593e202392d1e9070cf2a7fca7"
dependencies = [
"proc-macro2",
"quote",
"syn",
]
[[package]]
name = "windows-interface"
version = "0.59.1"
@ -1039,19 +995,10 @@ version = "0.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9150af68066c4c5c07ddc0ce30421554771e528bde427614c61038bc2c92c2b1"
dependencies = [
"windows-core 0.61.0",
"windows-core",
"windows-link",
]
[[package]]
name = "windows-result"
version = "0.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5e383302e8ec8515204254685643de10811af0ed97ea37210dc26fb0032647f8"
dependencies = [
"windows-targets",
]
[[package]]
name = "windows-result"
version = "0.3.2"

View file

@ -70,7 +70,7 @@ tracing-tree = { version = "0.4.0", optional = true }
version = "1.0.0"
[target.'cfg(windows)'.dependencies.windows]
version = "0.57"
version = "0.61"
features = [
"Win32_Foundation",
"Win32_Security",

View file

@ -342,7 +342,7 @@ fn format_rusage_data(child: Child) -> Option<String> {
use windows::Win32::System::Threading::GetProcessTimes;
use windows::Win32::System::Time::FileTimeToSystemTime;
let handle = HANDLE(child.as_raw_handle() as isize);
let handle = HANDLE(child.as_raw_handle());
let mut user_filetime = Default::default();
let mut user_time = Default::default();

View file

@ -3554,7 +3554,7 @@ impl Step for TestFloatParse {
builder.ensure(tool::TestFloatParse { host: self.host });
// Run any unit tests in the crate
let cargo_test = tool::prepare_tool_cargo(
let mut cargo_test = tool::prepare_tool_cargo(
builder,
compiler,
Mode::ToolStd,
@ -3564,6 +3564,7 @@ impl Step for TestFloatParse {
SourceType::InTree,
&[],
);
cargo_test.allow_features(tool::TestFloatParse::ALLOW_FEATURES);
run_cargo_test(cargo_test, &[], &[], crate_name, bootstrap_host, builder);
@ -3578,6 +3579,7 @@ impl Step for TestFloatParse {
SourceType::InTree,
&[],
);
cargo_run.allow_features(tool::TestFloatParse::ALLOW_FEATURES);
if !matches!(env::var("FLOAT_PARSE_TESTS_NO_SKIP_HUGE").as_deref(), Ok("1") | Ok("true")) {
cargo_run.args(["--", "--skip-huge"]);

View file

@ -1259,6 +1259,10 @@ pub struct TestFloatParse {
pub host: TargetSelection,
}
impl TestFloatParse {
pub const ALLOW_FEATURES: &'static str = "f16,cfg_target_has_reliable_f16_f128";
}
impl Step for TestFloatParse {
type Output = ToolBuildResult;
const ONLY_HOSTS: bool = true;
@ -1280,7 +1284,7 @@ impl Step for TestFloatParse {
path: "src/etc/test-float-parse",
source_type: SourceType::InTree,
extra_features: Vec::new(),
allow_features: "",
allow_features: Self::ALLOW_FEATURES,
cargo_args: Vec::new(),
artifact_kind: ToolArtifactKind::Binary,
})

View file

@ -1388,7 +1388,7 @@ impl<'a> Builder<'a> {
// Windows doesn't need dylib path munging because the dlls for the
// compiler live next to the compiler and the system will find them
// automatically.
if cfg!(windows) {
if cfg!(any(windows, target_os = "cygwin")) {
return;
}

View file

@ -130,7 +130,7 @@ pub fn is_debug_info(name: &str) -> bool {
/// Returns the corresponding relative library directory that the compiler's
/// dylibs will be found in.
pub fn libdir(target: TargetSelection) -> &'static str {
if target.is_windows() { "bin" } else { "lib" }
if target.is_windows() || target.contains("cygwin") { "bin" } else { "lib" }
}
/// Adds a list of lookup paths to `cmd`'s dynamic library lookup path.

View file

@ -66,7 +66,6 @@ mod for_windows {
// Enable the Windows Error Reporting dialog which msys disables,
// so we can JIT debug rustc
let mode = SetErrorMode(THREAD_ERROR_MODE::default());
let mode = THREAD_ERROR_MODE(mode);
SetErrorMode(mode & !SEM_NOGPFAULTERRORBOX);
// Create a new job object for us to use

View file

@ -20,7 +20,7 @@ use std::str::FromStr;
/// Returns the environment variable which the dynamic library lookup path
/// resides in for this platform.
pub fn dylib_path_var() -> &'static str {
if cfg!(target_os = "windows") {
if cfg!(any(target_os = "windows", target_os = "cygwin")) {
"PATH"
} else if cfg!(target_vendor = "apple") {
"DYLD_LIBRARY_PATH"

View file

@ -1,9 +1,9 @@
# Rust for Linux notification group
**Github Label:** [O-rfl] <br>
**Github Label:** [A-rust-for-linux] <br>
**Ping command:** `@rustbot ping rfl`
[O-rfl]: https://github.com/rust-lang/rust/labels/O-rfl
[A-rust-for-linux]: https://github.com/rust-lang/rust/labels/A-rust-for-linux
This list will be used to notify [Rust for Linux (RfL)][rfl] maintainers
when the compiler or the standard library changes in a way that would

View file

@ -6,6 +6,8 @@ title = "The rustc book"
[output.html]
git-repository-url = "https://github.com/rust-lang/rust/tree/master/src/doc/rustc"
edit-url-template = "https://github.com/rust-lang/rust/edit/master/src/doc/rustc/{path}"
additional-css = ["theme/pagetoc.css"]
additional-js = ["theme/pagetoc.js"]
[output.html.search]
use-boolean-and = true

View file

@ -0,0 +1,84 @@
/* Inspired by https://github.com/JorelAli/mdBook-pagetoc/tree/98ee241 (under WTFPL) */
:root {
--toc-width: 270px;
--center-content-toc-shift: calc(-1 * var(--toc-width) / 2);
}
.nav-chapters {
/* adjust width of buttons that bring to the previous or the next page */
min-width: 50px;
}
@media only screen {
@media (max-width: 1179px) {
.sidebar-hidden #sidetoc {
display: none;
}
}
@media (max-width: 1439px) {
.sidebar-visible #sidetoc {
display: none;
}
}
@media (1180px <= width <= 1439px) {
.sidebar-hidden main {
position: relative;
left: var(--center-content-toc-shift);
}
}
@media (1440px <= width <= 1700px) {
.sidebar-visible main {
position: relative;
left: var(--center-content-toc-shift);
}
}
#sidetoc {
margin-left: calc(100% + 20px);
}
#pagetoc {
position: fixed;
/* adjust TOC width */
width: var(--toc-width);
height: calc(100vh - var(--menu-bar-height) - 0.67em * 4);
overflow: auto;
}
#pagetoc a {
border-left: 1px solid var(--sidebar-bg);
color: var(--sidebar-fg) !important;
display: block;
padding-bottom: 5px;
padding-top: 5px;
padding-left: 10px;
text-align: left;
text-decoration: none;
}
#pagetoc a:hover,
#pagetoc a.active {
background: var(--sidebar-bg);
color: var(--sidebar-active) !important;
}
#pagetoc .active {
background: var(--sidebar-bg);
color: var(--sidebar-active);
}
#pagetoc .pagetoc-H2 {
padding-left: 20px;
}
#pagetoc .pagetoc-H3 {
padding-left: 40px;
}
#pagetoc .pagetoc-H4 {
padding-left: 60px;
}
}
@media print {
#sidetoc {
display: none;
}
}

View file

@ -0,0 +1,104 @@
// Inspired by https://github.com/JorelAli/mdBook-pagetoc/tree/98ee241 (under WTFPL)
let activeHref = location.href;
function updatePageToc(elem = undefined) {
let selectedPageTocElem = elem;
const pagetoc = document.getElementById("pagetoc");
function getRect(element) {
return element.getBoundingClientRect();
}
function overflowTop(container, element) {
return getRect(container).top - getRect(element).top;
}
function overflowBottom(container, element) {
return getRect(container).bottom - getRect(element).bottom;
}
// We've not selected a heading to highlight, and the URL needs updating
// so we need to find a heading based on the URL
if (selectedPageTocElem === undefined && location.href !== activeHref) {
activeHref = location.href;
for (const pageTocElement of pagetoc.children) {
if (pageTocElement.href === activeHref) {
selectedPageTocElem = pageTocElement;
}
}
}
// We still don't have a selected heading, let's try and find the most
// suitable heading based on the scroll position
if (selectedPageTocElem === undefined) {
const margin = window.innerHeight / 3;
const headers = document.getElementsByClassName("header");
for (let i = 0; i < headers.length; i++) {
const header = headers[i];
if (selectedPageTocElem === undefined && getRect(header).top >= 0) {
if (getRect(header).top < margin) {
selectedPageTocElem = header;
} else {
selectedPageTocElem = headers[Math.max(0, i - 1)];
}
}
// a very long last section's heading is over the screen
if (selectedPageTocElem === undefined && i === headers.length - 1) {
selectedPageTocElem = header;
}
}
}
// Remove the active flag from all pagetoc elements
for (const pageTocElement of pagetoc.children) {
pageTocElement.classList.remove("active");
}
// If we have a selected heading, set it to active and scroll to it
if (selectedPageTocElem !== undefined) {
for (const pageTocElement of pagetoc.children) {
if (selectedPageTocElem.href.localeCompare(pageTocElement.href) === 0) {
pageTocElement.classList.add("active");
if (overflowTop(pagetoc, pageTocElement) > 0) {
pagetoc.scrollTop = pageTocElement.offsetTop;
}
if (overflowBottom(pagetoc, pageTocElement) < 0) {
pagetoc.scrollTop -= overflowBottom(pagetoc, pageTocElement);
}
}
}
}
}
if (document.getElementById("sidetoc") === null &&
document.getElementsByClassName("header").length > 0) {
// The sidetoc element doesn't exist yet, let's create it
// Create the empty sidetoc and pagetoc elements
const sidetoc = document.createElement("div");
const pagetoc = document.createElement("div");
sidetoc.id = "sidetoc";
pagetoc.id = "pagetoc";
sidetoc.appendChild(pagetoc);
// And append them to the current DOM
const main = document.querySelector('main');
main.insertBefore(sidetoc, main.firstChild);
// Populate sidebar on load
window.addEventListener("load", () => {
for (const header of document.getElementsByClassName("header")) {
const link = document.createElement("a");
link.innerHTML = header.innerHTML;
link.href = header.hash;
link.classList.add("pagetoc-" + header.parentElement.tagName);
document.getElementById("pagetoc").appendChild(link);
link.onclick = () => updatePageToc(link);
}
updatePageToc();
});
// Update page table of contents selected heading on scroll
window.addEventListener("scroll", () => updatePageToc());
}

View file

@ -13,3 +13,10 @@ rayon = "1"
[lib]
name = "test_float_parse"
[lints.rust.unexpected_cfgs]
level = "warn"
check-cfg = [
# Internal features aren't marked known config by default
'cfg(target_has_reliable_f16)',
]

View file

@ -1,4 +1,3 @@
use std::cmp::min;
use std::fmt::Write;
use std::ops::RangeInclusive;
@ -83,7 +82,13 @@ where
}
fn new() -> Self {
Self { iter: F::Int::ZERO..=min(F::Int::ONE << 22, F::MAN_BITS.try_into().unwrap()) }
let upper_lim = if F::MAN_BITS >= 22 {
F::Int::ONE << 22
} else {
(F::Int::ONE << F::MAN_BITS) - F::Int::ONE
};
Self { iter: F::Int::ZERO..=upper_lim }
}
fn write_string(s: &mut String, ctx: Self::WriteCtx) {

View file

@ -1,3 +1,7 @@
#![feature(f16)]
#![feature(cfg_target_has_reliable_f16_f128)]
#![expect(internal_features)] // reliable_f16_f128
mod traits;
mod ui;
mod validate;
@ -114,6 +118,9 @@ pub fn register_tests(cfg: &Config) -> Vec<TestInfo> {
let mut tests = Vec::new();
// Register normal generators for all floats.
#[cfg(target_has_reliable_f16)]
register_float::<f16>(&mut tests, cfg);
register_float::<f32>(&mut tests, cfg);
register_float::<f64>(&mut tests, cfg);

View file

@ -98,7 +98,7 @@ macro_rules! impl_int {
}
}
impl_int!(u32, i32; u64, i64);
impl_int!(u16, i16; u32, i32; u64, i64);
/// Floating point types.
pub trait Float:
@ -170,6 +170,9 @@ macro_rules! impl_float {
impl_float!(f32, u32; f64, u64);
#[cfg(target_has_reliable_f16)]
impl_float!(f16, u16);
/// A test generator. Should provide an iterator that produces unique patterns to parse.
///
/// The iterator needs to provide a `WriteCtx` (could be anything), which is then used to

View file

@ -11,7 +11,6 @@
#![feature(if_let_guard)]
#![feature(impl_trait_in_assoc_type)]
#![feature(iter_intersperse)]
#![feature(let_chains)]
#![feature(never_type)]
#![feature(round_char_boundary)]
#![feature(test)]

@ -1 +1 @@
Subproject commit 8448283b4bd34ea00d76fd4f18ec730b549d6e1d
Subproject commit c1118fdbb3024157df7f4cfe765f2b0b4339e8a2

View file

@ -37,7 +37,7 @@ libc = "0.2"
miow = "0.6"
[target.'cfg(windows)'.dependencies.windows]
version = "0.59.0"
version = "0.61.0"
features = [
"Win32_Foundation",
"Win32_System_Diagnostics_Debug",

View file

@ -117,7 +117,12 @@ trait EvalContextExtPriv<'tcx>: crate::MiriInterpCxExt<'tcx> {
let mut info = std::mem::MaybeUninit::<libc::Dl_info>::uninit();
unsafe {
if libc::dladdr(*func.deref() as *const _, info.as_mut_ptr()) != 0 {
if std::ffi::CStr::from_ptr(info.assume_init().dli_fname).to_str().unwrap()
let info = info.assume_init();
#[cfg(target_os = "cygwin")]
let fname_ptr = info.dli_fname.as_ptr();
#[cfg(not(target_os = "cygwin"))]
let fname_ptr = info.dli_fname;
if std::ffi::CStr::from_ptr(fname_ptr).to_str().unwrap()
!= _lib_path.to_str().unwrap()
{
return None;

View file

@ -2,7 +2,7 @@
//@only-target: x86_64 i686
//@compile-flags: -C target-feature=+aes,+vaes,+avx512f
#![feature(avx512_target_feature, stdarch_x86_avx512)]
#![feature(stdarch_x86_avx512)]
use core::mem::transmute;
#[cfg(target_arch = "x86")]

View file

@ -2,7 +2,6 @@
//@only-target: x86_64 i686
//@compile-flags: -C target-feature=+avx512f,+avx512vl,+avx512bitalg,+avx512vpopcntdq
#![feature(avx512_target_feature)]
#![feature(stdarch_x86_avx512)]
#[cfg(target_arch = "x86")]

View file

@ -6,7 +6,6 @@
// be interpreted as integers; signedness does not make sense for them, but
// __mXXXi happens to be defined in terms of signed integers.
#![allow(overflowing_literals)]
#![feature(avx512_target_feature)]
#![feature(stdarch_x86_avx512)]
#[cfg(target_arch = "x86")]

View file

@ -8,7 +8,6 @@
// be interpreted as integers; signedness does not make sense for them, but
// __mXXXi happens to be defined in terms of signed integers.
#![allow(overflowing_literals)]
#![feature(avx512_target_feature)]
#![feature(stdarch_x86_avx512)]
#[cfg(target_arch = "x86")]

View file

@ -1,10 +1,10 @@
use rustc_ast::ast;
use rustc_builtin_macros::asm::{AsmArgs, parse_asm_args};
use rustc_builtin_macros::asm::{AsmArg, parse_asm_args};
use crate::rewrite::RewriteContext;
#[allow(dead_code)]
pub(crate) fn parse_asm(context: &RewriteContext<'_>, mac: &ast::MacCall) -> Option<AsmArgs> {
pub(crate) fn parse_asm(context: &RewriteContext<'_>, mac: &ast::MacCall) -> Option<Vec<AsmArg>> {
let ts = mac.args.tokens.clone();
let mut parser = super::build_parser(context, ts);
parse_asm_args(&mut parser, mac.span(), ast::AsmMacro::Asm).ok()

View file

@ -429,10 +429,13 @@ const PERMITTED_RUSTC_DEPENDENCIES: &[&str] = &[
"winapi-util",
"winapi-x86_64-pc-windows-gnu",
"windows",
"windows-collections",
"windows-core",
"windows-future",
"windows-implement",
"windows-interface",
"windows-link",
"windows-numerics",
"windows-result",
"windows-strings",
"windows-sys",

View file

@ -0,0 +1,18 @@
- // MIR for `overlapping` before GVN
+ // MIR for `overlapping` after GVN
fn overlapping(_1: Adt) -> () {
let mut _0: ();
let mut _2: *mut Adt;
let mut _3: u32;
let mut _4: &Adt;
bb0: {
_2 = &raw mut _1;
_4 = &(*_2);
_3 = copy (((*_4) as variant#1).0: u32);
(*_2) = Adt::Some(copy _3);
return;
}
}

View file

@ -0,0 +1,36 @@
//@ test-mir-pass: GVN
#![feature(custom_mir, core_intrinsics)]
// Check that we do not create overlapping assignments.
use std::intrinsics::mir::*;
// EMIT_MIR gvn_overlapping.overlapping.GVN.diff
#[custom_mir(dialect = "runtime")]
fn overlapping(_17: Adt) {
// CHECK-LABEL: fn overlapping(
// CHECK: let mut [[PTR:.*]]: *mut Adt;
// CHECK: (*[[PTR]]) = Adt::Some(copy {{.*}});
mir! {
let _33: *mut Adt;
let _48: u32;
let _73: &Adt;
{
_33 = core::ptr::addr_of_mut!(_17);
_73 = &(*_33);
_48 = Field(Variant((*_73), 1), 0);
(*_33) = Adt::Some(_48);
Return()
}
}
}
fn main() {
overlapping(Adt::Some(0));
}
enum Adt {
None,
Some(u32),
}

View file

@ -6,7 +6,7 @@ use run_make_support::{rustc, source_root};
fn main() {
rustc()
.edition("2021")
.edition("2024")
.arg("-Dwarnings")
.crate_type("rlib")
.input(source_root().join("library/core/src/lib.rs"))

View file

@ -11,6 +11,13 @@
//@ needs-dynamic-linking
//@ only-nightly (requires unstable rustc flag)
// This test trips a check in the MSVC linker for an outdated processor:
// "LNK1322: cannot avoid potential ARM hazard (Cortex-A53 MPCore processor bug #843419)"
// Until MSVC removes this check:
// https://developercommunity.microsoft.com/t/Remove-checking-for-and-fixing-Cortex-A/10905134
// we'll need to disable this test on Arm64 Windows.
//@ ignore-aarch64-pc-windows-msvc
#![deny(warnings)]
use run_make_support::{dynamic_lib_name, rfs, rust_lib_name, rustc};

View file

@ -7,8 +7,6 @@
//@ run-pass
//@ needs-subprocess
#![feature(avx512_target_feature)]
#![allow(overflowing_literals)]
#![allow(unused_variables)]

View file

@ -2,7 +2,6 @@
//@ build-fail
//@ compile-flags: -C target-feature=-avx
#![feature(avx512_target_feature)]
#![feature(portable_simd)]
#![feature(simd_ffi)]
#![allow(improper_ctypes_definitions)]

View file

@ -1,5 +1,5 @@
error: this function call uses SIMD vector type `std::arch::x86_64::__m256` which (with the chosen ABI) requires the `avx` target feature, which is not enabled in the caller
--> $DIR/simd-abi-checks-avx.rs:60:11
--> $DIR/simd-abi-checks-avx.rs:59:11
|
LL | f(g());
| ^^^ function called here
@ -7,7 +7,7 @@ LL | f(g());
= help: consider enabling it globally (`-C target-feature=+avx`) or locally (`#[target_feature(enable="avx")]`)
error: this function call uses SIMD vector type `std::arch::x86_64::__m256` which (with the chosen ABI) requires the `avx` target feature, which is not enabled in the caller
--> $DIR/simd-abi-checks-avx.rs:60:9
--> $DIR/simd-abi-checks-avx.rs:59:9
|
LL | f(g());
| ^^^^^^ function called here
@ -15,7 +15,7 @@ LL | f(g());
= help: consider enabling it globally (`-C target-feature=+avx`) or locally (`#[target_feature(enable="avx")]`)
error: this function call uses SIMD vector type `std::arch::x86_64::__m256` which (with the chosen ABI) requires the `avx` target feature, which is not enabled in the caller
--> $DIR/simd-abi-checks-avx.rs:66:14
--> $DIR/simd-abi-checks-avx.rs:65:14
|
LL | gavx(favx());
| ^^^^^^ function called here
@ -23,7 +23,7 @@ LL | gavx(favx());
= help: consider enabling it globally (`-C target-feature=+avx`) or locally (`#[target_feature(enable="avx")]`)
error: this function call uses SIMD vector type `std::arch::x86_64::__m256` which (with the chosen ABI) requires the `avx` target feature, which is not enabled in the caller
--> $DIR/simd-abi-checks-avx.rs:66:9
--> $DIR/simd-abi-checks-avx.rs:65:9
|
LL | gavx(favx());
| ^^^^^^^^^^^^ function called here
@ -31,7 +31,7 @@ LL | gavx(favx());
= help: consider enabling it globally (`-C target-feature=+avx`) or locally (`#[target_feature(enable="avx")]`)
error: this function call uses SIMD vector type `std::arch::x86_64::__m256` which (with the chosen ABI) requires the `avx` target feature, which is not enabled in the caller
--> $DIR/simd-abi-checks-avx.rs:76:19
--> $DIR/simd-abi-checks-avx.rs:75:19
|
LL | w(Wrapper(g()));
| ^^^ function called here
@ -39,7 +39,7 @@ LL | w(Wrapper(g()));
= help: consider enabling it globally (`-C target-feature=+avx`) or locally (`#[target_feature(enable="avx")]`)
error: this function call uses SIMD vector type `Wrapper` which (with the chosen ABI) requires the `avx` target feature, which is not enabled in the caller
--> $DIR/simd-abi-checks-avx.rs:76:9
--> $DIR/simd-abi-checks-avx.rs:75:9
|
LL | w(Wrapper(g()));
| ^^^^^^^^^^^^^^^ function called here
@ -47,7 +47,7 @@ LL | w(Wrapper(g()));
= help: consider enabling it globally (`-C target-feature=+avx`) or locally (`#[target_feature(enable="avx")]`)
error: this function call uses SIMD vector type `std::arch::x86_64::__m256` which (with the chosen ABI) requires the `avx` target feature, which is not enabled in the caller
--> $DIR/simd-abi-checks-avx.rs:90:9
--> $DIR/simd-abi-checks-avx.rs:89:9
|
LL | some_extern();
| ^^^^^^^^^^^^^ function called here
@ -55,7 +55,7 @@ LL | some_extern();
= help: consider enabling it globally (`-C target-feature=+avx`) or locally (`#[target_feature(enable="avx")]`)
error: this function definition uses SIMD vector type `std::arch::x86_64::__m256` which (with the chosen ABI) requires the `avx` target feature, which is not enabled
--> $DIR/simd-abi-checks-avx.rs:25:1
--> $DIR/simd-abi-checks-avx.rs:24:1
|
LL | unsafe extern "C" fn g() -> __m256 {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here
@ -63,7 +63,7 @@ LL | unsafe extern "C" fn g() -> __m256 {
= help: consider enabling it globally (`-C target-feature=+avx`) or locally (`#[target_feature(enable="avx")]`)
error: this function definition uses SIMD vector type `std::arch::x86_64::__m256` which (with the chosen ABI) requires the `avx` target feature, which is not enabled
--> $DIR/simd-abi-checks-avx.rs:20:1
--> $DIR/simd-abi-checks-avx.rs:19:1
|
LL | unsafe extern "C" fn f(_: __m256) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here
@ -71,7 +71,7 @@ LL | unsafe extern "C" fn f(_: __m256) {
= help: consider enabling it globally (`-C target-feature=+avx`) or locally (`#[target_feature(enable="avx")]`)
error: this function definition uses SIMD vector type `Wrapper` which (with the chosen ABI) requires the `avx` target feature, which is not enabled
--> $DIR/simd-abi-checks-avx.rs:15:1
--> $DIR/simd-abi-checks-avx.rs:14:1
|
LL | unsafe extern "C" fn w(_: Wrapper) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ function defined here
@ -79,7 +79,7 @@ LL | unsafe extern "C" fn w(_: Wrapper) {
= help: consider enabling it globally (`-C target-feature=+avx`) or locally (`#[target_feature(enable="avx")]`)
error: this function call uses SIMD vector type `std::arch::x86_64::__m256` which (with the chosen ABI) requires the `avx` target feature, which is not enabled in the caller
--> $DIR/simd-abi-checks-avx.rs:54:8
--> $DIR/simd-abi-checks-avx.rs:53:8
|
LL | || g()
| ^^^ function called here
@ -87,7 +87,7 @@ LL | || g()
= help: consider enabling it globally (`-C target-feature=+avx`) or locally (`#[target_feature(enable="avx")]`)
note: the above error was encountered while instantiating `fn in_closure::{closure#0}`
--> $DIR/simd-abi-checks-avx.rs:82:9
--> $DIR/simd-abi-checks-avx.rs:81:9
|
LL | in_closure()();
| ^^^^^^^^^^^^^^

View file

@ -96,10 +96,8 @@ global_asm!("", options(FOO));
//~^ ERROR expected one of
global_asm!("", options(nomem FOO));
//~^ ERROR expected one of
//~| ERROR the `nomem` option cannot be used with `global_asm!`
global_asm!("", options(nomem, FOO));
//~^ ERROR expected one of
//~| ERROR the `nomem` option cannot be used with `global_asm!`
global_asm!("{}", options(), const FOO);
global_asm!("", clobber_abi(FOO));
//~^ ERROR expected string literal

View file

@ -218,68 +218,56 @@ error: expected one of `)`, `att_syntax`, or `raw`, found `FOO`
LL | global_asm!("", options(FOO));
| ^^^ expected one of `)`, `att_syntax`, or `raw`
error: the `nomem` option cannot be used with `global_asm!`
--> $DIR/parse-error.rs:97:25
|
LL | global_asm!("", options(nomem FOO));
| ^^^^^ the `nomem` option is not meaningful for global-scoped inline assembly
error: expected one of `)` or `,`, found `FOO`
--> $DIR/parse-error.rs:97:31
|
LL | global_asm!("", options(nomem FOO));
| ^^^ expected one of `)` or `,`
error: the `nomem` option cannot be used with `global_asm!`
--> $DIR/parse-error.rs:100:25
|
LL | global_asm!("", options(nomem, FOO));
| ^^^^^ the `nomem` option is not meaningful for global-scoped inline assembly
error: expected one of `)`, `att_syntax`, or `raw`, found `FOO`
--> $DIR/parse-error.rs:100:32
--> $DIR/parse-error.rs:99:32
|
LL | global_asm!("", options(nomem, FOO));
| ^^^ expected one of `)`, `att_syntax`, or `raw`
error: expected string literal
--> $DIR/parse-error.rs:104:29
--> $DIR/parse-error.rs:102:29
|
LL | global_asm!("", clobber_abi(FOO));
| ^^^ not a string literal
error: expected one of `)` or `,`, found `FOO`
--> $DIR/parse-error.rs:106:33
--> $DIR/parse-error.rs:104:33
|
LL | global_asm!("", clobber_abi("C" FOO));
| ^^^ expected one of `)` or `,`
error: expected string literal
--> $DIR/parse-error.rs:108:34
--> $DIR/parse-error.rs:106:34
|
LL | global_asm!("", clobber_abi("C", FOO));
| ^^^ not a string literal
error: `clobber_abi` cannot be used with `global_asm!`
--> $DIR/parse-error.rs:110:19
--> $DIR/parse-error.rs:108:19
|
LL | global_asm!("{}", clobber_abi("C"), const FOO);
| ^^^^^^^^^^^^^^^^
error: `clobber_abi` cannot be used with `global_asm!`
--> $DIR/parse-error.rs:112:28
--> $DIR/parse-error.rs:110:28
|
LL | global_asm!("", options(), clobber_abi("C"));
| ^^^^^^^^^^^^^^^^
error: `clobber_abi` cannot be used with `global_asm!`
--> $DIR/parse-error.rs:114:30
--> $DIR/parse-error.rs:112:30
|
LL | global_asm!("{}", options(), clobber_abi("C"), const FOO);
| ^^^^^^^^^^^^^^^^
error: duplicate argument named `a`
--> $DIR/parse-error.rs:116:35
--> $DIR/parse-error.rs:114:35
|
LL | global_asm!("{a}", a = const FOO, a = const BAR);
| ------------- ^^^^^^^^^^^^^ duplicate argument
@ -287,7 +275,7 @@ LL | global_asm!("{a}", a = const FOO, a = const BAR);
| previously here
error: argument never used
--> $DIR/parse-error.rs:116:35
--> $DIR/parse-error.rs:114:35
|
LL | global_asm!("{a}", a = const FOO, a = const BAR);
| ^^^^^^^^^^^^^ argument never used
@ -295,19 +283,19 @@ LL | global_asm!("{a}", a = const FOO, a = const BAR);
= help: if this argument is intentionally unused, consider using it in an asm comment: `"/* {1} */"`
error: expected one of `clobber_abi`, `const`, `options`, or `sym`, found `""`
--> $DIR/parse-error.rs:119:28
--> $DIR/parse-error.rs:117:28
|
LL | global_asm!("", options(), "");
| ^^ expected one of `clobber_abi`, `const`, `options`, or `sym`
error: expected one of `clobber_abi`, `const`, `options`, or `sym`, found `"{}"`
--> $DIR/parse-error.rs:121:30
--> $DIR/parse-error.rs:119:30
|
LL | global_asm!("{}", const FOO, "{}", const FOO);
| ^^^^ expected one of `clobber_abi`, `const`, `options`, or `sym`
error: asm template must be a string literal
--> $DIR/parse-error.rs:123:13
--> $DIR/parse-error.rs:121:13
|
LL | global_asm!(format!("{{{}}}", 0), const FOO);
| ^^^^^^^^^^^^^^^^^^^^
@ -315,7 +303,7 @@ LL | global_asm!(format!("{{{}}}", 0), const FOO);
= note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info)
error: asm template must be a string literal
--> $DIR/parse-error.rs:125:20
--> $DIR/parse-error.rs:123:20
|
LL | global_asm!("{1}", format!("{{{}}}", 0), const FOO, const BAR);
| ^^^^^^^^^^^^^^^^^^^^
@ -418,6 +406,6 @@ LL - let mut bar = 0;
LL + const bar: /* Type */ = 0;
|
error: aborting due to 59 previous errors
error: aborting due to 57 previous errors
For more information about this error, try `rustc --explain E0435`.

View file

@ -113,11 +113,9 @@ global_asm!("", options(FOO));
global_asm!("", options(FOO,));
//~^ ERROR expected one of `)`, `att_syntax`, or `raw`, found `FOO`
global_asm!("", options(nomem FOO));
//~^ ERROR the `nomem` option cannot be used with `global_asm!`
//~| ERROR expected one of `)` or `,`, found `FOO`
//~^ ERROR expected one of `)` or `,`, found `FOO`
global_asm!("", options(nomem, FOO));
//~^ ERROR the `nomem` option cannot be used with `global_asm!`
//~| ERROR expected one of `)`, `att_syntax`, or `raw`, found `FOO`
//~^ ERROR expected one of `)`, `att_syntax`, or `raw`, found `FOO`
global_asm!("{}", options(), const FOO);
global_asm!("", clobber_abi(FOO));
//~^ ERROR expected string literal

View file

@ -270,74 +270,62 @@ error: expected one of `)`, `att_syntax`, or `raw`, found `FOO`
LL | global_asm!("", options(FOO,));
| ^^^ expected one of `)`, `att_syntax`, or `raw`
error: the `nomem` option cannot be used with `global_asm!`
--> $DIR/parse-error.rs:115:25
|
LL | global_asm!("", options(nomem FOO));
| ^^^^^ the `nomem` option is not meaningful for global-scoped inline assembly
error: expected one of `)` or `,`, found `FOO`
--> $DIR/parse-error.rs:115:31
|
LL | global_asm!("", options(nomem FOO));
| ^^^ expected one of `)` or `,`
error: the `nomem` option cannot be used with `global_asm!`
--> $DIR/parse-error.rs:118:25
|
LL | global_asm!("", options(nomem, FOO));
| ^^^^^ the `nomem` option is not meaningful for global-scoped inline assembly
error: expected one of `)`, `att_syntax`, or `raw`, found `FOO`
--> $DIR/parse-error.rs:118:32
--> $DIR/parse-error.rs:117:32
|
LL | global_asm!("", options(nomem, FOO));
| ^^^ expected one of `)`, `att_syntax`, or `raw`
error: expected string literal
--> $DIR/parse-error.rs:122:29
--> $DIR/parse-error.rs:120:29
|
LL | global_asm!("", clobber_abi(FOO));
| ^^^ not a string literal
error: expected one of `)` or `,`, found `FOO`
--> $DIR/parse-error.rs:124:33
--> $DIR/parse-error.rs:122:33
|
LL | global_asm!("", clobber_abi("C" FOO));
| ^^^ expected one of `)` or `,`
error: expected string literal
--> $DIR/parse-error.rs:126:34
--> $DIR/parse-error.rs:124:34
|
LL | global_asm!("", clobber_abi("C", FOO));
| ^^^ not a string literal
error: `clobber_abi` cannot be used with `global_asm!`
--> $DIR/parse-error.rs:128:19
--> $DIR/parse-error.rs:126:19
|
LL | global_asm!("{}", clobber_abi("C"), const FOO);
| ^^^^^^^^^^^^^^^^
error: `clobber_abi` cannot be used with `global_asm!`
--> $DIR/parse-error.rs:130:28
--> $DIR/parse-error.rs:128:28
|
LL | global_asm!("", options(), clobber_abi("C"));
| ^^^^^^^^^^^^^^^^
error: `clobber_abi` cannot be used with `global_asm!`
--> $DIR/parse-error.rs:132:30
--> $DIR/parse-error.rs:130:30
|
LL | global_asm!("{}", options(), clobber_abi("C"), const FOO);
| ^^^^^^^^^^^^^^^^
error: `clobber_abi` cannot be used with `global_asm!`
--> $DIR/parse-error.rs:134:17
--> $DIR/parse-error.rs:132:17
|
LL | global_asm!("", clobber_abi("C"), clobber_abi("C"));
| ^^^^^^^^^^^^^^^^ ^^^^^^^^^^^^^^^^
error: duplicate argument named `a`
--> $DIR/parse-error.rs:136:35
--> $DIR/parse-error.rs:134:35
|
LL | global_asm!("{a}", a = const FOO, a = const BAR);
| ------------- ^^^^^^^^^^^^^ duplicate argument
@ -345,7 +333,7 @@ LL | global_asm!("{a}", a = const FOO, a = const BAR);
| previously here
error: argument never used
--> $DIR/parse-error.rs:136:35
--> $DIR/parse-error.rs:134:35
|
LL | global_asm!("{a}", a = const FOO, a = const BAR);
| ^^^^^^^^^^^^^ argument never used
@ -353,19 +341,19 @@ LL | global_asm!("{a}", a = const FOO, a = const BAR);
= help: if this argument is intentionally unused, consider using it in an asm comment: `"/* {1} */"`
error: expected one of `clobber_abi`, `const`, `options`, or `sym`, found `""`
--> $DIR/parse-error.rs:139:28
--> $DIR/parse-error.rs:137:28
|
LL | global_asm!("", options(), "");
| ^^ expected one of `clobber_abi`, `const`, `options`, or `sym`
error: expected one of `clobber_abi`, `const`, `options`, or `sym`, found `"{}"`
--> $DIR/parse-error.rs:141:30
--> $DIR/parse-error.rs:139:30
|
LL | global_asm!("{}", const FOO, "{}", const FOO);
| ^^^^ expected one of `clobber_abi`, `const`, `options`, or `sym`
error: asm template must be a string literal
--> $DIR/parse-error.rs:143:13
--> $DIR/parse-error.rs:141:13
|
LL | global_asm!(format!("{{{}}}", 0), const FOO);
| ^^^^^^^^^^^^^^^^^^^^
@ -373,7 +361,7 @@ LL | global_asm!(format!("{{{}}}", 0), const FOO);
= note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info)
error: asm template must be a string literal
--> $DIR/parse-error.rs:145:20
--> $DIR/parse-error.rs:143:20
|
LL | global_asm!("{1}", format!("{{{}}}", 0), const FOO, const BAR);
| ^^^^^^^^^^^^^^^^^^^^
@ -381,37 +369,37 @@ LL | global_asm!("{1}", format!("{{{}}}", 0), const FOO, const BAR);
= note: this error originates in the macro `format` (in Nightly builds, run with -Z macro-backtrace for more info)
error: the `in` operand cannot be used with `global_asm!`
--> $DIR/parse-error.rs:148:19
--> $DIR/parse-error.rs:146:19
|
LL | global_asm!("{}", in(reg));
| ^^ the `in` operand is not meaningful for global-scoped inline assembly, remove it
error: the `out` operand cannot be used with `global_asm!`
--> $DIR/parse-error.rs:150:19
--> $DIR/parse-error.rs:148:19
|
LL | global_asm!("{}", out(reg));
| ^^^ the `out` operand is not meaningful for global-scoped inline assembly, remove it
error: the `lateout` operand cannot be used with `global_asm!`
--> $DIR/parse-error.rs:152:19
--> $DIR/parse-error.rs:150:19
|
LL | global_asm!("{}", lateout(reg));
| ^^^^^^^ the `lateout` operand is not meaningful for global-scoped inline assembly, remove it
error: the `inout` operand cannot be used with `global_asm!`
--> $DIR/parse-error.rs:154:19
--> $DIR/parse-error.rs:152:19
|
LL | global_asm!("{}", inout(reg));
| ^^^^^ the `inout` operand is not meaningful for global-scoped inline assembly, remove it
error: the `inlateout` operand cannot be used with `global_asm!`
--> $DIR/parse-error.rs:156:19
--> $DIR/parse-error.rs:154:19
|
LL | global_asm!("{}", inlateout(reg));
| ^^^^^^^^^ the `inlateout` operand is not meaningful for global-scoped inline assembly, remove it
error: the `label` operand cannot be used with `global_asm!`
--> $DIR/parse-error.rs:158:19
--> $DIR/parse-error.rs:156:19
|
LL | global_asm!("{}", label(reg));
| ^^^^^ the `label` operand is not meaningful for global-scoped inline assembly, remove it
@ -476,6 +464,6 @@ LL - let mut bar = 0;
LL + const bar: /* Type */ = 0;
|
error: aborting due to 72 previous errors
error: aborting due to 70 previous errors
For more information about this error, try `rustc --explain E0435`.

View file

@ -2,7 +2,6 @@
//@ only-x86_64
//@ compile-flags: --crate-type=lib -C target-cpu=skylake
#![feature(avx512_target_feature)]
#![feature(stdarch_x86_avx512)]
use std::arch::x86_64::*;

View file

@ -2,8 +2,6 @@
// Set the base cpu explicitly, in case the default has been changed.
//@ compile-flags: -C target-cpu=x86-64
#![feature(avx512_target_feature)]
use std::arch::asm;
#[target_feature(enable = "avx")]

View file

@ -1,23 +1,23 @@
error: register class `ymm_reg` requires the `avx` target feature
--> $DIR/target-feature-attr.rs:20:40
--> $DIR/target-feature-attr.rs:18:40
|
LL | asm!("vaddps {2:y}, {0:y}, {1:y}", in(ymm_reg) x, in(ymm_reg) y, lateout(ymm_reg) x);
| ^^^^^^^^^^^^^
error: register class `ymm_reg` requires the `avx` target feature
--> $DIR/target-feature-attr.rs:20:55
--> $DIR/target-feature-attr.rs:18:55
|
LL | asm!("vaddps {2:y}, {0:y}, {1:y}", in(ymm_reg) x, in(ymm_reg) y, lateout(ymm_reg) x);
| ^^^^^^^^^^^^^
error: register class `ymm_reg` requires the `avx` target feature
--> $DIR/target-feature-attr.rs:20:70
--> $DIR/target-feature-attr.rs:18:70
|
LL | asm!("vaddps {2:y}, {0:y}, {1:y}", in(ymm_reg) x, in(ymm_reg) y, lateout(ymm_reg) x);
| ^^^^^^^^^^^^^^^^^^
error: register class `kreg` requires at least one of the following target features: avx512bw, avx512f
--> $DIR/target-feature-attr.rs:35:23
--> $DIR/target-feature-attr.rs:33:23
|
LL | asm!("/* {0} */", in(kreg) x);
| ^^^^^^^^^^

View file

@ -22,7 +22,6 @@ fn other_guards_dont() {
let ((x if guard(x)) | x) = 0;
//~^ ERROR: guard patterns are experimental
//~| ERROR: cannot find value `x`
if let (x if guard(x)) = 0 {}
//~^ ERROR: guard patterns are experimental
@ -37,7 +36,6 @@ fn other_guards_dont() {
fn even_as_function_parameters(((x if guard(x), _) | (_, x)): (i32, i32)) {}
//~^ ERROR: guard patterns are experimental
//~| ERROR: cannot find value `x`
fn guard<T>(x: T) -> bool {
unimplemented!()

View file

@ -10,24 +10,6 @@ LL - (0 if guard(0)) => {},
LL + 0 if guard(0) => {},
|
error[E0425]: cannot find value `x` in this scope
--> $DIR/feature-gate-guard-patterns.rs:23:22
|
LL | let ((x if guard(x)) | x) = 0;
| ^ not found in this scope
error[E0425]: cannot find value `x` in this scope
--> $DIR/feature-gate-guard-patterns.rs:38:45
|
LL | fn even_as_function_parameters(((x if guard(x), _) | (_, x)): (i32, i32)) {}
| ^
|
help: the binding `x` is available in a different scope in the same function
--> $DIR/feature-gate-guard-patterns.rs:23:11
|
LL | let ((x if guard(x)) | x) = 0;
| ^
error[E0658]: guard patterns are experimental
--> $DIR/feature-gate-guard-patterns.rs:18:15
|
@ -51,7 +33,7 @@ LL | let ((x if guard(x)) | x) = 0;
= help: consider using match arm guards
error[E0658]: guard patterns are experimental
--> $DIR/feature-gate-guard-patterns.rs:27:18
--> $DIR/feature-gate-guard-patterns.rs:26:18
|
LL | if let (x if guard(x)) = 0 {}
| ^^^^^^^^
@ -62,7 +44,7 @@ LL | if let (x if guard(x)) = 0 {}
= help: consider using match arm guards
error[E0658]: guard patterns are experimental
--> $DIR/feature-gate-guard-patterns.rs:30:21
--> $DIR/feature-gate-guard-patterns.rs:29:21
|
LL | while let (x if guard(x)) = 0 {}
| ^^^^^^^^
@ -73,7 +55,7 @@ LL | while let (x if guard(x)) = 0 {}
= help: consider using match arm guards
error[E0658]: guard patterns are experimental
--> $DIR/feature-gate-guard-patterns.rs:34:21
--> $DIR/feature-gate-guard-patterns.rs:33:21
|
LL | while let (x if guard(x)) = 0 {}
| ^^^^^^^^
@ -84,7 +66,7 @@ LL | while let (x if guard(x)) = 0 {}
= help: consider using match arm guards
error[E0658]: guard patterns are experimental
--> $DIR/feature-gate-guard-patterns.rs:38:39
--> $DIR/feature-gate-guard-patterns.rs:37:39
|
LL | fn even_as_function_parameters(((x if guard(x), _) | (_, x)): (i32, i32)) {}
| ^^^^^^^^
@ -94,7 +76,6 @@ LL | fn even_as_function_parameters(((x if guard(x), _) | (_, x)): (i32, i32)) {
= note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date
= help: consider using match arm guards
error: aborting due to 9 previous errors
error: aborting due to 7 previous errors
Some errors have detailed explanations: E0425, E0658.
For more information about an error, try `rustc --explain E0425`.
For more information about this error, try `rustc --explain E0658`.

View file

@ -11,7 +11,7 @@ mod m {
pub struct other_item;
}
use ::nu;
use crate::nu;
pub use self::p::{other_item as _};
//~^ ERROR unresolved import `self::p::nu` [E0432]
//~| HELP a macro with this name exists at the root of the crate

View file

@ -7,7 +7,7 @@ LL | pub use self::p::{nu, other_item as _};
= note: this could be because a macro annotated with `#[macro_export]` will be exported at the root of the crate instead of the module where it is defined
help: a macro with this name exists at the root of the crate
|
LL ~ use ::nu;
LL ~ use crate::nu;
LL ~ pub use self::p::{other_item as _};
|

View file

@ -1,4 +1,8 @@
//@ run-rustfix
//@ revisions: edition_2015 edition_2018
//@ [edition_2015] edition: 2015
//@ [edition_2018] edition: 2018
#![allow(unused, nonstandard_style)]
mod m {
#[macro_export]
@ -8,7 +12,7 @@ mod m {
pub struct other_item;
use ::nu;
use crate::nu;
pub use self::{other_item as _};
//~^ ERROR unresolved import `self::nu` [E0432]
//~| HELP a macro with this name exists at the root of the crate

View file

@ -1,5 +1,5 @@
error[E0432]: unresolved import `self::nu`
--> $DIR/issue-99695.rs:11:20
--> $DIR/issue-99695.rs:15:20
|
LL | pub use self::{nu, other_item as _};
| ^^ no `nu` in `m`
@ -7,7 +7,7 @@ LL | pub use self::{nu, other_item as _};
= note: this could be because a macro annotated with `#[macro_export]` will be exported at the root of the crate instead of the module where it is defined
help: a macro with this name exists at the root of the crate
|
LL ~ use ::nu;
LL ~ use crate::nu;
LL ~ pub use self::{other_item as _};
|

View file

@ -0,0 +1,21 @@
//@ run-rustfix
//@ revisions: edition_2015 edition_2018
//@ [edition_2015] edition: 2015
//@ [edition_2018] edition: 2018
#![allow(unused, nonstandard_style)]
mod m {
#[macro_export]
macro_rules! nu {
{} => {};
}
pub struct other_item;
use crate::nu;
pub use self::{other_item as _};
//~^ ERROR unresolved import `self::nu` [E0432]
//~| HELP a macro with this name exists at the root of the crate
}
fn main() {}

View file

@ -0,0 +1,16 @@
error[E0432]: unresolved import `self::nu`
--> $DIR/issue-99695.rs:15:20
|
LL | pub use self::{nu, other_item as _};
| ^^ no `nu` in `m`
|
= note: this could be because a macro annotated with `#[macro_export]` will be exported at the root of the crate instead of the module where it is defined
help: a macro with this name exists at the root of the crate
|
LL ~ use crate::nu;
LL ~ pub use self::{other_item as _};
|
error: aborting due to 1 previous error
For more information about this error, try `rustc --explain E0432`.

View file

@ -1,4 +1,8 @@
//@ run-rustfix
//@ revisions: edition_2015 edition_2018
//@ [edition_2015] edition: 2015
//@ [edition_2018] edition: 2018
#![allow(unused, nonstandard_style)]
mod m {
#[macro_export]

View file

@ -0,0 +1,81 @@
//! Test that guard patterns can see bindings already in scope and bindings introduced in their
//! subpattern, but no other bindings from the containing pattern. Also make sure bindings
//! introduced in guard patterns are visible in fn/arm/loop/etc bodies.
#![feature(guard_patterns)]
#![expect(incomplete_features)]
fn good_fn_item(((x if x) | x): bool) -> bool { x }
fn bad_fn_item_1(x: bool, ((y if x) | y): bool) {}
//~^ ERROR cannot find value `x` in this scope
fn bad_fn_item_2(((x if y) | x): bool, y: bool) {}
//~^ ERROR cannot find value `y` in this scope
fn main() {
let ((local if local) if local) = false;
match (true, true) {
(x if local, y if good_fn_item(y)) => x && y,
(x, y if x) => x && y,
//~^ ERROR cannot find value `x` in this scope
(x if y, y) => x && y,
//~^ ERROR cannot find value `y` in this scope
};
match (true,) {
(x @ y if x && y,) => x && y,
(x @ (y if y),) => x && y,
(x @ (y if x),) => x && y,
//~^ ERROR cannot find value `x` in this scope
};
match (Ok(true),) {
((Ok(x) | Err(x)) if good_fn_item(x),) => x,
((Ok(x) if local) | (Err(x) if good_fn_item(x)),) => x,
((Ok(x if x) if x) | (Err(x if x) if x) if x,) if x => x,
((Ok(x) if y) | (Err(y) if x),) => x && y,
//~^ ERROR variable `x` is not bound in all patterns
//~| ERROR variable `y` is not bound in all patterns
//~| ERROR cannot find value `x` in this scope
//~| ERROR cannot find value `y` in this scope
};
let (_ if nonexistent) = true;
//~^ ERROR cannot find value `nonexistent` in this scope
if let ((x, y if x) | (x if y, y)) = (true, true) { x && y; }
//~^ ERROR cannot find value `x` in this scope
//~| ERROR cannot find value `y` in this scope
while let ((x, y if x) | (x if y, y)) = (true, true) { x && y; }
//~^ ERROR cannot find value `x` in this scope
//~| ERROR cannot find value `y` in this scope
for ((x, y if x) | (x if y, y)) in [(true, true)] { x && y; }
//~^ ERROR cannot find value `x` in this scope
//~| ERROR cannot find value `y` in this scope
(|(x if x), (y if y)| x && y)(true, true);
(|(x if y), (y if x)| x && y)(true, true);
//~^ ERROR cannot find value `x` in this scope
//~| ERROR cannot find value `y` in this scope
// FIXME(guard_patterns): mismatched bindings are not yet allowed
match Some(0) {
Some(x if x > 0) | None => {}
//~^ ERROR variable `x` is not bound in all patterns
}
}
/// Make sure shadowing is handled properly. In particular, if a pattern shadows an identifier,
/// a guard pattern's guard should still see the original binding if the shadowing binding isn't in
/// its subpattern.
fn test_shadowing(local: bool) -> u8 {
match (0, 0) {
// The `local` binding here shadows the `bool` definition, so we get a type error.
//~v ERROR mismatched types
local if local => 0,
// The guards here should see the `bool` definition of `local`, not the new `u8` binding.
// The body should see the new binding.
(local, _ if local) => local,
(_ if local, local) => local,
}
}

View file

@ -0,0 +1,133 @@
error[E0408]: variable `y` is not bound in all patterns
--> $DIR/name-resolution.rs:37:10
|
LL | ((Ok(x) if y) | (Err(y) if x),) => x && y,
| ^^^^^^^^^^^^ - variable not in all patterns
| |
| pattern doesn't bind `y`
error[E0408]: variable `x` is not bound in all patterns
--> $DIR/name-resolution.rs:37:25
|
LL | ((Ok(x) if y) | (Err(y) if x),) => x && y,
| - ^^^^^^^^^^^^^ pattern doesn't bind `x`
| |
| variable not in all patterns
error[E0408]: variable `x` is not bound in all patterns
--> $DIR/name-resolution.rs:63:28
|
LL | Some(x if x > 0) | None => {}
| - ^^^^ pattern doesn't bind `x`
| |
| variable not in all patterns
error[E0425]: cannot find value `x` in this scope
--> $DIR/name-resolution.rs:10:34
|
LL | fn bad_fn_item_1(x: bool, ((y if x) | y): bool) {}
| ^ help: a local variable with a similar name exists: `y`
error[E0425]: cannot find value `y` in this scope
--> $DIR/name-resolution.rs:12:25
|
LL | fn bad_fn_item_2(((x if y) | x): bool, y: bool) {}
| ^ help: a local variable with a similar name exists: `x`
error[E0425]: cannot find value `x` in this scope
--> $DIR/name-resolution.rs:20:18
|
LL | (x, y if x) => x && y,
| ^ help: a local variable with a similar name exists: `y`
error[E0425]: cannot find value `y` in this scope
--> $DIR/name-resolution.rs:22:15
|
LL | (x if y, y) => x && y,
| ^ help: a local variable with a similar name exists: `x`
error[E0425]: cannot find value `x` in this scope
--> $DIR/name-resolution.rs:29:20
|
LL | (x @ (y if x),) => x && y,
| ^ help: a local variable with a similar name exists: `y`
error[E0425]: cannot find value `y` in this scope
--> $DIR/name-resolution.rs:37:20
|
LL | ((Ok(x) if y) | (Err(y) if x),) => x && y,
| ^ help: a local variable with a similar name exists: `x`
error[E0425]: cannot find value `x` in this scope
--> $DIR/name-resolution.rs:37:36
|
LL | ((Ok(x) if y) | (Err(y) if x),) => x && y,
| ^ help: a local variable with a similar name exists: `y`
error[E0425]: cannot find value `nonexistent` in this scope
--> $DIR/name-resolution.rs:44:15
|
LL | let (_ if nonexistent) = true;
| ^^^^^^^^^^^ not found in this scope
error[E0425]: cannot find value `x` in this scope
--> $DIR/name-resolution.rs:46:22
|
LL | if let ((x, y if x) | (x if y, y)) = (true, true) { x && y; }
| ^ help: a local variable with a similar name exists: `y`
error[E0425]: cannot find value `y` in this scope
--> $DIR/name-resolution.rs:46:33
|
LL | if let ((x, y if x) | (x if y, y)) = (true, true) { x && y; }
| ^ help: a local variable with a similar name exists: `x`
error[E0425]: cannot find value `x` in this scope
--> $DIR/name-resolution.rs:49:25
|
LL | while let ((x, y if x) | (x if y, y)) = (true, true) { x && y; }
| ^ help: a local variable with a similar name exists: `y`
error[E0425]: cannot find value `y` in this scope
--> $DIR/name-resolution.rs:49:36
|
LL | while let ((x, y if x) | (x if y, y)) = (true, true) { x && y; }
| ^ help: a local variable with a similar name exists: `x`
error[E0425]: cannot find value `x` in this scope
--> $DIR/name-resolution.rs:52:19
|
LL | for ((x, y if x) | (x if y, y)) in [(true, true)] { x && y; }
| ^ help: a local variable with a similar name exists: `y`
error[E0425]: cannot find value `y` in this scope
--> $DIR/name-resolution.rs:52:30
|
LL | for ((x, y if x) | (x if y, y)) in [(true, true)] { x && y; }
| ^ help: a local variable with a similar name exists: `x`
error[E0425]: cannot find value `y` in this scope
--> $DIR/name-resolution.rs:57:13
|
LL | (|(x if y), (y if x)| x && y)(true, true);
| ^ help: a local variable with a similar name exists: `x`
error[E0425]: cannot find value `x` in this scope
--> $DIR/name-resolution.rs:57:23
|
LL | (|(x if y), (y if x)| x && y)(true, true);
| ^ help: a local variable with a similar name exists: `y`
error[E0308]: mismatched types
--> $DIR/name-resolution.rs:75:18
|
LL | local if local => 0,
| ^^^^^ expected `bool`, found `({integer}, {integer})`
|
= note: expected type `bool`
found tuple `({integer}, {integer})`
error: aborting due to 20 previous errors
Some errors have detailed explanations: E0308, E0408, E0425.
For more information about an error, try `rustc --explain E0308`.

View file

@ -0,0 +1,15 @@
//@ check-pass
//! Test that `GatherLocalsVisitor` only visits expressions in guard patterns when checking the
//! expressions, and not a second time when visiting the pattern. If locals are declared inside the
//! the guard expression, it would ICE if visited twice ("evaluated expression more than once").
#![feature(guard_patterns)]
#![expect(incomplete_features)]
fn main() {
match (0,) {
// FIXME(guard_patterns): liveness lints don't work yet; this will ICE without the `_`.
(_ if { let _x = false; _x },) => {}
_ => {}
}
}

View file

@ -7,7 +7,6 @@
//@ ignore-fuchsia must translate zircon signal to SIGILL, FIXME (#58590)
#![feature(repr_simd, target_feature, cfg_target_feature)]
#![feature(avx512_target_feature)]
use std::process::{Command, ExitStatus};
use std::env;

View file

@ -1,5 +1,5 @@
#![feature(avx512_target_feature)]
#![feature(x87_target_feature)]
#[inline]
#[target_feature(enable = "avx512ifma")]
#[target_feature(enable = "x87")]
pub unsafe fn foo() {}

View file

@ -2,7 +2,6 @@
//
// gate-test-sse4a_target_feature
// gate-test-powerpc_target_feature
// gate-test-avx512_target_feature
// gate-test-tbm_target_feature
// gate-test-arm_target_feature
// gate-test-hexagon_target_feature
@ -27,7 +26,7 @@
// gate-test-x87_target_feature
// gate-test-m68k_target_feature
#[target_feature(enable = "avx512bw")]
#[target_feature(enable = "x87")]
//~^ ERROR: currently unstable
unsafe fn foo() {}

Some files were not shown because too many files have changed in this diff Show more