diff --git a/Cargo.lock b/Cargo.lock index f59dcdcf28d69..c1b3e5e6f42ca 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -2815,9 +2815,9 @@ dependencies = [ [[package]] name = "psm" -version = "0.1.23" +version = "0.1.24" source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "aa37f80ca58604976033fae9515a8a2989fc13797d953f7c04fb8fa36a11f205" +checksum = "200b9ff220857e53e184257720a14553b2f4aa02577d2ed9842d45d4b9654810" dependencies = [ "cc", ] diff --git a/compiler/rustc_ast/src/ast.rs b/compiler/rustc_ast/src/ast.rs index 2f55a9eaedab3..69ba78282f96f 100644 --- a/compiler/rustc_ast/src/ast.rs +++ b/compiler/rustc_ast/src/ast.rs @@ -627,9 +627,11 @@ impl Pat { | PatKind::Or(s) => s.iter().for_each(|p| p.walk(it)), // Trivial wrappers over inner patterns. - PatKind::Box(s) | PatKind::Deref(s) | PatKind::Ref(s, _) | PatKind::Paren(s) => { - s.walk(it) - } + PatKind::Box(s) + | PatKind::Deref(s) + | PatKind::Ref(s, _) + | PatKind::Paren(s) + | PatKind::Guard(s, _) => s.walk(it), // These patterns do not contain subpatterns, skip. PatKind::Wild @@ -839,6 +841,9 @@ pub enum PatKind { // A never pattern `!`. Never, + /// A guard pattern (e.g., `x if guard(x)`). + Guard(P, P), + /// Parentheses in patterns used for grouping (i.e., `(PAT)`). Paren(P), diff --git a/compiler/rustc_ast/src/mut_visit.rs b/compiler/rustc_ast/src/mut_visit.rs index 622c260868ed3..3a4a8ce266e9c 100644 --- a/compiler/rustc_ast/src/mut_visit.rs +++ b/compiler/rustc_ast/src/mut_visit.rs @@ -1525,6 +1525,10 @@ pub fn walk_pat(vis: &mut T, pat: &mut P) { visit_opt(e2, |e| vis.visit_expr(e)); vis.visit_span(span); } + PatKind::Guard(p, e) => { + vis.visit_pat(p); + vis.visit_expr(e); + } PatKind::Tuple(elems) | PatKind::Slice(elems) | PatKind::Or(elems) => { visit_thin_vec(elems, |elem| vis.visit_pat(elem)) } diff --git a/compiler/rustc_ast/src/visit.rs b/compiler/rustc_ast/src/visit.rs index 2f6998783fa31..0b000c8cef8a3 100644 --- a/compiler/rustc_ast/src/visit.rs +++ b/compiler/rustc_ast/src/visit.rs @@ -682,6 +682,10 @@ pub fn walk_pat<'a, V: Visitor<'a>>(visitor: &mut V, pattern: &'a Pat) -> V::Res visit_opt!(visitor, visit_expr, lower_bound); visit_opt!(visitor, visit_expr, upper_bound); } + PatKind::Guard(subpattern, guard_condition) => { + try_visit!(visitor.visit_pat(subpattern)); + try_visit!(visitor.visit_expr(guard_condition)); + } PatKind::Wild | PatKind::Rest | PatKind::Never => {} PatKind::Err(_guar) => {} PatKind::Tuple(elems) | PatKind::Slice(elems) | PatKind::Or(elems) => { diff --git a/compiler/rustc_ast_lowering/src/pat.rs b/compiler/rustc_ast_lowering/src/pat.rs index ace7bfb5c73f2..c4bae084a3f8c 100644 --- a/compiler/rustc_ast_lowering/src/pat.rs +++ b/compiler/rustc_ast_lowering/src/pat.rs @@ -114,6 +114,8 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> { self.lower_range_end(end, e2.is_some()), ); } + // FIXME(guard_patterns): lower pattern guards to HIR + PatKind::Guard(inner, _) => pattern = inner, PatKind::Slice(pats) => break self.lower_pat_slice(pats), PatKind::Rest => { // If we reach here the `..` pattern is not semantically allowed. diff --git a/compiler/rustc_ast_passes/src/feature_gate.rs b/compiler/rustc_ast_passes/src/feature_gate.rs index 893115160815f..390a575a186ba 100644 --- a/compiler/rustc_ast_passes/src/feature_gate.rs +++ b/compiler/rustc_ast_passes/src/feature_gate.rs @@ -556,6 +556,7 @@ pub fn check_crate(krate: &ast::Crate, sess: &Session, features: &Features) { gate_all!(builtin_syntax, "`builtin #` syntax is unstable"); gate_all!(explicit_tail_calls, "`become` expression is experimental"); gate_all!(generic_const_items, "generic const items are experimental"); + gate_all!(guard_patterns, "guard patterns are experimental", "consider using match arm guards"); gate_all!(fn_delegation, "functions delegation is not yet fully implemented"); gate_all!(postfix_match, "postfix match is experimental"); gate_all!(mut_ref, "mutable by-reference bindings are experimental"); diff --git a/compiler/rustc_ast_pretty/src/pprust/state.rs b/compiler/rustc_ast_pretty/src/pprust/state.rs index 479677b0a5ad5..49e4a559e7382 100644 --- a/compiler/rustc_ast_pretty/src/pprust/state.rs +++ b/compiler/rustc_ast_pretty/src/pprust/state.rs @@ -1709,6 +1709,14 @@ impl<'a> State<'a> { self.print_expr(e, FixupContext::default()); } } + PatKind::Guard(subpat, condition) => { + self.popen(); + self.print_pat(subpat); + self.space(); + self.word_space("if"); + self.print_expr(condition, FixupContext::default()); + self.pclose(); + } PatKind::Slice(elts) => { self.word("["); self.commasep(Inconsistent, elts, |s, p| s.print_pat(p)); diff --git a/compiler/rustc_codegen_llvm/src/intrinsic.rs b/compiler/rustc_codegen_llvm/src/intrinsic.rs index d8b055137b359..c38c5d4c6442c 100644 --- a/compiler/rustc_codegen_llvm/src/intrinsic.rs +++ b/compiler/rustc_codegen_llvm/src/intrinsic.rs @@ -352,84 +352,84 @@ impl<'ll, 'tcx> IntrinsicCallBuilderMethods<'tcx> for Builder<'_, 'll, 'tcx> { | sym::saturating_add | sym::saturating_sub => { let ty = arg_tys[0]; - match int_type_width_signed(ty, self) { - Some((width, signed)) => match name { - sym::ctlz | sym::cttz => { - let y = self.const_bool(false); - let ret = self.call_intrinsic(&format!("llvm.{name}.i{width}"), &[ - args[0].immediate(), - y, - ]); - - self.intcast(ret, llret_ty, false) - } - sym::ctlz_nonzero => { - let y = self.const_bool(true); - let llvm_name = &format!("llvm.ctlz.i{width}"); - let ret = self.call_intrinsic(llvm_name, &[args[0].immediate(), y]); - self.intcast(ret, llret_ty, false) - } - sym::cttz_nonzero => { - let y = self.const_bool(true); - let llvm_name = &format!("llvm.cttz.i{width}"); - let ret = self.call_intrinsic(llvm_name, &[args[0].immediate(), y]); - self.intcast(ret, llret_ty, false) - } - sym::ctpop => { - let ret = self.call_intrinsic(&format!("llvm.ctpop.i{width}"), &[args - [0] - .immediate()]); - self.intcast(ret, llret_ty, false) - } - sym::bswap => { - if width == 8 { - args[0].immediate() // byte swap a u8/i8 is just a no-op - } else { - self.call_intrinsic(&format!("llvm.bswap.i{width}"), &[ - args[0].immediate() - ]) - } - } - sym::bitreverse => self - .call_intrinsic(&format!("llvm.bitreverse.i{width}"), &[ + if !ty.is_integral() { + tcx.dcx().emit_err(InvalidMonomorphization::BasicIntegerType { + span, + name, + ty, + }); + return Ok(()); + } + let (size, signed) = ty.int_size_and_signed(self.tcx); + let width = size.bits(); + match name { + sym::ctlz | sym::cttz => { + let y = self.const_bool(false); + let ret = self.call_intrinsic(&format!("llvm.{name}.i{width}"), &[ + args[0].immediate(), + y, + ]); + + self.intcast(ret, llret_ty, false) + } + sym::ctlz_nonzero => { + let y = self.const_bool(true); + let llvm_name = &format!("llvm.ctlz.i{width}"); + let ret = self.call_intrinsic(llvm_name, &[args[0].immediate(), y]); + self.intcast(ret, llret_ty, false) + } + sym::cttz_nonzero => { + let y = self.const_bool(true); + let llvm_name = &format!("llvm.cttz.i{width}"); + let ret = self.call_intrinsic(llvm_name, &[args[0].immediate(), y]); + self.intcast(ret, llret_ty, false) + } + sym::ctpop => { + let ret = self.call_intrinsic(&format!("llvm.ctpop.i{width}"), &[ + args[0].immediate() + ]); + self.intcast(ret, llret_ty, false) + } + sym::bswap => { + if width == 8 { + args[0].immediate() // byte swap a u8/i8 is just a no-op + } else { + self.call_intrinsic(&format!("llvm.bswap.i{width}"), &[ args[0].immediate() - ]), - sym::rotate_left | sym::rotate_right => { - let is_left = name == sym::rotate_left; - let val = args[0].immediate(); - let raw_shift = args[1].immediate(); - // rotate = funnel shift with first two args the same - let llvm_name = - &format!("llvm.fsh{}.i{}", if is_left { 'l' } else { 'r' }, width); - - // llvm expects shift to be the same type as the values, but rust - // always uses `u32`. - let raw_shift = self.intcast(raw_shift, self.val_ty(val), false); - - self.call_intrinsic(llvm_name, &[val, val, raw_shift]) + ]) } - sym::saturating_add | sym::saturating_sub => { - let is_add = name == sym::saturating_add; - let lhs = args[0].immediate(); - let rhs = args[1].immediate(); - let llvm_name = &format!( - "llvm.{}{}.sat.i{}", - if signed { 's' } else { 'u' }, - if is_add { "add" } else { "sub" }, - width - ); - self.call_intrinsic(llvm_name, &[lhs, rhs]) - } - _ => bug!(), - }, - None => { - tcx.dcx().emit_err(InvalidMonomorphization::BasicIntegerType { - span, - name, - ty, - }); - return Ok(()); } + sym::bitreverse => self + .call_intrinsic(&format!("llvm.bitreverse.i{width}"), &[ + args[0].immediate() + ]), + sym::rotate_left | sym::rotate_right => { + let is_left = name == sym::rotate_left; + let val = args[0].immediate(); + let raw_shift = args[1].immediate(); + // rotate = funnel shift with first two args the same + let llvm_name = + &format!("llvm.fsh{}.i{}", if is_left { 'l' } else { 'r' }, width); + + // llvm expects shift to be the same type as the values, but rust + // always uses `u32`. + let raw_shift = self.intcast(raw_shift, self.val_ty(val), false); + + self.call_intrinsic(llvm_name, &[val, val, raw_shift]) + } + sym::saturating_add | sym::saturating_sub => { + let is_add = name == sym::saturating_add; + let lhs = args[0].immediate(); + let rhs = args[1].immediate(); + let llvm_name = &format!( + "llvm.{}{}.sat.i{}", + if signed { 's' } else { 'u' }, + if is_add { "add" } else { "sub" }, + width + ); + self.call_intrinsic(llvm_name, &[lhs, rhs]) + } + _ => bug!(), } } @@ -2531,19 +2531,3 @@ fn generic_simd_intrinsic<'ll, 'tcx>( span_bug!(span, "unknown SIMD intrinsic"); } - -// Returns the width of an int Ty, and if it's signed or not -// Returns None if the type is not an integer -// FIXME: there’s multiple of this functions, investigate using some of the already existing -// stuffs. -fn int_type_width_signed(ty: Ty<'_>, cx: &CodegenCx<'_, '_>) -> Option<(u64, bool)> { - match ty.kind() { - ty::Int(t) => { - Some((t.bit_width().unwrap_or(u64::from(cx.tcx.sess.target.pointer_width)), true)) - } - ty::Uint(t) => { - Some((t.bit_width().unwrap_or(u64::from(cx.tcx.sess.target.pointer_width)), false)) - } - _ => None, - } -} diff --git a/compiler/rustc_error_codes/src/error_codes/E0751.md b/compiler/rustc_error_codes/src/error_codes/E0751.md index 8794f7868f302..825809b229aa4 100644 --- a/compiler/rustc_error_codes/src/error_codes/E0751.md +++ b/compiler/rustc_error_codes/src/error_codes/E0751.md @@ -9,4 +9,4 @@ impl !MyTrait for i32 { } // error! ``` Negative implementations are a promise that the trait will never be implemented -for the given types. Therefore, both cannot exists at the same time. +for the given types. Therefore, both cannot exist at the same time. diff --git a/compiler/rustc_expand/src/expand.rs b/compiler/rustc_expand/src/expand.rs index e5500c8bba1ce..6a6496f982702 100644 --- a/compiler/rustc_expand/src/expand.rs +++ b/compiler/rustc_expand/src/expand.rs @@ -990,7 +990,7 @@ pub fn parse_ast_fragment<'a>( } } AstFragmentKind::Ty => AstFragment::Ty(this.parse_ty()?), - AstFragmentKind::Pat => AstFragment::Pat(this.parse_pat_allow_top_alt( + AstFragmentKind::Pat => AstFragment::Pat(this.parse_pat_allow_top_guard( None, RecoverComma::No, RecoverColon::Yes, diff --git a/compiler/rustc_feature/src/unstable.rs b/compiler/rustc_feature/src/unstable.rs index abc7200699c66..bf26b5d25d2ba 100644 --- a/compiler/rustc_feature/src/unstable.rs +++ b/compiler/rustc_feature/src/unstable.rs @@ -505,6 +505,8 @@ declare_features! ( (incomplete, generic_const_items, "1.73.0", Some(113521)), /// Allows registering static items globally, possibly across crates, to iterate over at runtime. (unstable, global_registration, "1.80.0", Some(125119)), + /// Allows using guards in patterns. + (incomplete, guard_patterns, "CURRENT_RUSTC_VERSION", Some(129967)), /// Allows using `..=X` as a patterns in slices. (unstable, half_open_range_patterns_in_slices, "1.66.0", Some(67264)), /// Allows `if let` guard in match arms. diff --git a/compiler/rustc_lint/src/unused.rs b/compiler/rustc_lint/src/unused.rs index b775cd3740911..9cad5d98562b6 100644 --- a/compiler/rustc_lint/src/unused.rs +++ b/compiler/rustc_lint/src/unused.rs @@ -1235,7 +1235,7 @@ impl EarlyLintPass for UnusedParens { self.check_unused_parens_pat(cx, &f.pat, false, false, keep_space); }, // Avoid linting on `i @ (p0 | .. | pn)` and `box (p0 | .. | pn)`, #64106. - Ident(.., Some(p)) | Box(p) | Deref(p) => self.check_unused_parens_pat(cx, p, true, false, keep_space), + Ident(.., Some(p)) | Box(p) | Deref(p) | Guard(p, _) => self.check_unused_parens_pat(cx, p, true, false, keep_space), // Avoid linting on `&(mut x)` as `&mut x` has a different meaning, #55342. // Also avoid linting on `& mut? (p0 | .. | pn)`, #64106. Ref(p, m) => self.check_unused_parens_pat(cx, p, true, *m == Mutability::Not, keep_space), diff --git a/compiler/rustc_parse/src/parser/expr.rs b/compiler/rustc_parse/src/parser/expr.rs index 8d16d44b0a26a..3a9e9b480ecf2 100644 --- a/compiler/rustc_parse/src/parser/expr.rs +++ b/compiler/rustc_parse/src/parser/expr.rs @@ -2631,7 +2631,7 @@ impl<'a> Parser<'a> { }; self.bump(); // Eat `let` token let lo = self.prev_token.span; - let pat = self.parse_pat_allow_top_alt( + let pat = self.parse_pat_no_top_guard( None, RecoverComma::Yes, RecoverColon::Yes, @@ -2778,7 +2778,7 @@ impl<'a> Parser<'a> { }; // Try to parse the pattern `for ($PAT) in $EXPR`. let pat = match ( - self.parse_pat_allow_top_alt( + self.parse_pat_allow_top_guard( None, RecoverComma::Yes, RecoverColon::Yes, @@ -3241,7 +3241,7 @@ impl<'a> Parser<'a> { // then we should recover. let mut snapshot = this.create_snapshot_for_diagnostic(); let pattern_follows = snapshot - .parse_pat_allow_top_alt( + .parse_pat_no_top_guard( None, RecoverComma::Yes, RecoverColon::Yes, @@ -3315,43 +3315,37 @@ impl<'a> Parser<'a> { fn parse_match_arm_pat_and_guard(&mut self) -> PResult<'a, (P, Option>)> { if self.token == token::OpenDelim(Delimiter::Parenthesis) { - // Detect and recover from `($pat if $cond) => $arm`. let left = self.token.span; - match self.parse_pat_allow_top_alt( + let pat = self.parse_pat_no_top_guard( None, RecoverComma::Yes, RecoverColon::Yes, CommaRecoveryMode::EitherTupleOrPipe, - ) { - Ok(pat) => Ok((pat, self.parse_match_arm_guard()?)), - Err(err) - if let prev_sp = self.prev_token.span - && let true = self.eat_keyword(kw::If) => - { - // We know for certain we've found `($pat if` so far. - let mut cond = match self.parse_match_guard_condition() { - Ok(cond) => cond, - Err(cond_err) => { - cond_err.cancel(); - return Err(err); - } - }; - err.cancel(); - CondChecker::new(self).visit_expr(&mut cond); - self.eat_to_tokens(&[&token::CloseDelim(Delimiter::Parenthesis)]); - self.expect(&token::CloseDelim(Delimiter::Parenthesis))?; - let right = self.prev_token.span; - self.dcx().emit_err(errors::ParenthesesInMatchPat { - span: vec![left, right], - sugg: errors::ParenthesesInMatchPatSugg { left, right }, - }); - Ok((self.mk_pat(left.to(prev_sp), ast::PatKind::Wild), Some(cond))) - } - Err(err) => Err(err), + )?; + if let ast::PatKind::Paren(subpat) = &pat.kind + && let ast::PatKind::Guard(..) = &subpat.kind + { + // Detect and recover from `($pat if $cond) => $arm`. + // FIXME(guard_patterns): convert this to a normal guard instead + let span = pat.span; + let ast::PatKind::Paren(subpat) = pat.into_inner().kind else { unreachable!() }; + let ast::PatKind::Guard(_, mut cond) = subpat.into_inner().kind else { + unreachable!() + }; + self.psess.gated_spans.ungate_last(sym::guard_patterns, cond.span); + CondChecker::new(self).visit_expr(&mut cond); + let right = self.prev_token.span; + self.dcx().emit_err(errors::ParenthesesInMatchPat { + span: vec![left, right], + sugg: errors::ParenthesesInMatchPatSugg { left, right }, + }); + Ok((self.mk_pat(span, ast::PatKind::Wild), Some(cond))) + } else { + Ok((pat, self.parse_match_arm_guard()?)) } } else { // Regular parser flow: - let pat = self.parse_pat_allow_top_alt( + let pat = self.parse_pat_no_top_guard( None, RecoverComma::Yes, RecoverColon::Yes, diff --git a/compiler/rustc_parse/src/parser/nonterminal.rs b/compiler/rustc_parse/src/parser/nonterminal.rs index 8fb6f85d0dd8f..752a52b382b38 100644 --- a/compiler/rustc_parse/src/parser/nonterminal.rs +++ b/compiler/rustc_parse/src/parser/nonterminal.rs @@ -174,7 +174,7 @@ impl<'a> Parser<'a> { NonterminalKind::Pat(pat_kind) => { NtPat(self.collect_tokens_no_attrs(|this| match pat_kind { PatParam { .. } => this.parse_pat_no_top_alt(None, None), - PatWithOr => this.parse_pat_allow_top_alt( + PatWithOr => this.parse_pat_no_top_guard( None, RecoverComma::No, RecoverColon::No, diff --git a/compiler/rustc_parse/src/parser/pat.rs b/compiler/rustc_parse/src/parser/pat.rs index e08b925f00893..4cda887a02bdb 100644 --- a/compiler/rustc_parse/src/parser/pat.rs +++ b/compiler/rustc_parse/src/parser/pat.rs @@ -99,9 +99,34 @@ pub enum PatternLocation { impl<'a> Parser<'a> { /// Parses a pattern. /// - /// Corresponds to `pat` in RFC 2535 and does not admit or-patterns - /// at the top level. Used when parsing the parameters of lambda expressions, - /// functions, function pointers, and `pat` macro fragments. + /// Corresponds to `Pattern` in RFC 3637 and admits guard patterns at the top level. + /// Used when parsing patterns in all cases where neither `PatternNoTopGuard` nor + /// `PatternNoTopAlt` (see below) are used. + pub fn parse_pat_allow_top_guard( + &mut self, + expected: Option, + rc: RecoverComma, + ra: RecoverColon, + rt: CommaRecoveryMode, + ) -> PResult<'a, P> { + let pat = self.parse_pat_no_top_guard(expected, rc, ra, rt)?; + + if self.eat_keyword(kw::If) { + let cond = self.parse_expr()?; + // Feature-gate guard patterns + self.psess.gated_spans.gate(sym::guard_patterns, cond.span); + let span = pat.span.to(cond.span); + Ok(self.mk_pat(span, PatKind::Guard(pat, cond))) + } else { + Ok(pat) + } + } + + /// Parses a pattern. + /// + /// Corresponds to `PatternNoTopAlt` in RFC 3637 and does not admit or-patterns + /// or guard patterns at the top level. Used when parsing the parameters of lambda + /// expressions, functions, function pointers, and `pat_param` macro fragments. pub fn parse_pat_no_top_alt( &mut self, expected: Option, @@ -112,25 +137,26 @@ impl<'a> Parser<'a> { /// Parses a pattern. /// - /// Corresponds to `top_pat` in RFC 2535 and allows or-pattern at the top level. - /// Used for parsing patterns in all cases when `pat` is not used. + /// Corresponds to `PatternNoTopGuard` in RFC 3637 and allows or-patterns, but not + /// guard patterns, at the top level. Used for parsing patterns in `pat` fragments (until + /// the next edition) and `let`, `if let`, and `while let` expressions. /// /// Note that after the FCP in , /// a leading vert is allowed in nested or-patterns, too. This allows us to /// simplify the grammar somewhat. - pub fn parse_pat_allow_top_alt( + pub fn parse_pat_no_top_guard( &mut self, expected: Option, rc: RecoverComma, ra: RecoverColon, rt: CommaRecoveryMode, ) -> PResult<'a, P> { - self.parse_pat_allow_top_alt_inner(expected, rc, ra, rt, None).map(|(pat, _)| pat) + self.parse_pat_no_top_guard_inner(expected, rc, ra, rt, None).map(|(pat, _)| pat) } /// Returns the pattern and a bool indicating whether we recovered from a trailing vert (true = /// recovered). - fn parse_pat_allow_top_alt_inner( + fn parse_pat_no_top_guard_inner( &mut self, expected: Option, rc: RecoverComma, @@ -231,7 +257,7 @@ impl<'a> Parser<'a> { // We use `parse_pat_allow_top_alt` regardless of whether we actually want top-level // or-patterns so that we can detect when a user tries to use it. This allows us to print a // better error message. - let (pat, trailing_vert) = self.parse_pat_allow_top_alt_inner( + let (pat, trailing_vert) = self.parse_pat_no_top_guard_inner( expected, rc, RecoverColon::No, @@ -696,7 +722,7 @@ impl<'a> Parser<'a> { } else if self.check(&token::OpenDelim(Delimiter::Bracket)) { // Parse `[pat, pat,...]` as a slice pattern. let (pats, _) = self.parse_delim_comma_seq(Delimiter::Bracket, |p| { - p.parse_pat_allow_top_alt( + p.parse_pat_allow_top_guard( None, RecoverComma::No, RecoverColon::No, @@ -944,7 +970,7 @@ impl<'a> Parser<'a> { let open_paren = self.token.span; let (fields, trailing_comma) = self.parse_paren_comma_seq(|p| { - p.parse_pat_allow_top_alt( + p.parse_pat_allow_top_guard( None, RecoverComma::No, RecoverColon::No, @@ -1361,7 +1387,7 @@ impl<'a> Parser<'a> { path: Path, ) -> PResult<'a, PatKind> { let (fields, _) = self.parse_paren_comma_seq(|p| { - p.parse_pat_allow_top_alt( + p.parse_pat_allow_top_guard( None, RecoverComma::No, RecoverColon::No, @@ -1396,7 +1422,7 @@ impl<'a> Parser<'a> { self.parse_builtin(|self_, _lo, ident| { Ok(match ident.name { // builtin#deref(PAT) - sym::deref => Some(ast::PatKind::Deref(self_.parse_pat_allow_top_alt( + sym::deref => Some(ast::PatKind::Deref(self_.parse_pat_allow_top_guard( None, RecoverComma::Yes, RecoverColon::Yes, @@ -1671,7 +1697,7 @@ impl<'a> Parser<'a> { // Parsing a pattern of the form `fieldname: pat`. let fieldname = self.parse_field_name()?; self.bump(); - let pat = self.parse_pat_allow_top_alt( + let pat = self.parse_pat_allow_top_guard( None, RecoverComma::No, RecoverColon::No, diff --git a/compiler/rustc_parse/src/parser/path.rs b/compiler/rustc_parse/src/parser/path.rs index 2f19a9b6b20b4..6a7029a8f1c82 100644 --- a/compiler/rustc_parse/src/parser/path.rs +++ b/compiler/rustc_parse/src/parser/path.rs @@ -469,7 +469,7 @@ impl<'a> Parser<'a> { PathStyle::Pat if let Ok(_) = self .parse_paren_comma_seq(|p| { - p.parse_pat_allow_top_alt( + p.parse_pat_allow_top_guard( None, RecoverComma::No, RecoverColon::No, diff --git a/compiler/rustc_passes/src/input_stats.rs b/compiler/rustc_passes/src/input_stats.rs index b8f66a2b2ec34..f2a37307ceeb8 100644 --- a/compiler/rustc_passes/src/input_stats.rs +++ b/compiler/rustc_passes/src/input_stats.rs @@ -555,6 +555,7 @@ impl<'v> ast_visit::Visitor<'v> for StatCollector<'v> { Slice, Rest, Never, + Guard, Paren, MacCall, Err diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index a7dc20a874b4c..818d4afffc66b 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -999,6 +999,7 @@ symbols! { global_registration, globs, gt, + guard_patterns, half_open_range_patterns, half_open_range_patterns_in_slices, hash, diff --git a/library/core/src/intrinsics/mod.rs b/library/core/src/intrinsics/mod.rs index 802b571c51067..3e53c0497cc78 100644 --- a/library/core/src/intrinsics/mod.rs +++ b/library/core/src/intrinsics/mod.rs @@ -3577,34 +3577,44 @@ pub const fn discriminant_value(_v: &T) -> ::Discrimin unimplemented!() } -extern "rust-intrinsic" { - /// Rust's "try catch" construct for unwinding. Invokes the function pointer `try_fn` with the - /// data pointer `data`, and calls `catch_fn` if unwinding occurs while `try_fn` runs. - /// - /// `catch_fn` must not unwind. - /// - /// The third argument is a function called if an unwind occurs (both Rust `panic` and foreign - /// unwinds). This function takes the data pointer and a pointer to the target- and - /// runtime-specific exception object that was caught. - /// - /// Note that in the case of a foreign unwinding operation, the exception object data may not be - /// safely usable from Rust, and should not be directly exposed via the standard library. To - /// prevent unsafe access, the library implementation may either abort the process or present an - /// opaque error type to the user. - /// - /// For more information, see the compiler's source, as well as the documentation for the stable - /// version of this intrinsic, `std::panic::catch_unwind`. - #[rustc_nounwind] - pub fn catch_unwind(try_fn: fn(*mut u8), data: *mut u8, catch_fn: fn(*mut u8, *mut u8)) -> i32; - - /// Emits a `nontemporal` store, which gives a hint to the CPU that the data should not be held - /// in cache. Except for performance, this is fully equivalent to `ptr.write(val)`. - /// - /// Not all architectures provide such an operation. For instance, x86 does not: while `MOVNT` - /// exists, that operation is *not* equivalent to `ptr.write(val)` (`MOVNT` writes can be reordered - /// in ways that are not allowed for regular writes). - #[rustc_nounwind] - pub fn nontemporal_store(ptr: *mut T, val: T); +/// Rust's "try catch" construct for unwinding. Invokes the function pointer `try_fn` with the +/// data pointer `data`, and calls `catch_fn` if unwinding occurs while `try_fn` runs. +/// +/// `catch_fn` must not unwind. +/// +/// The third argument is a function called if an unwind occurs (both Rust `panic` and foreign +/// unwinds). This function takes the data pointer and a pointer to the target- and +/// runtime-specific exception object that was caught. +/// +/// Note that in the case of a foreign unwinding operation, the exception object data may not be +/// safely usable from Rust, and should not be directly exposed via the standard library. To +/// prevent unsafe access, the library implementation may either abort the process or present an +/// opaque error type to the user. +/// +/// For more information, see the compiler's source, as well as the documentation for the stable +/// version of this intrinsic, `std::panic::catch_unwind`. +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn catch_unwind( + _try_fn: fn(*mut u8), + _data: *mut u8, + _catch_fn: fn(*mut u8, *mut u8), +) -> i32 { + unreachable!() +} + +/// Emits a `nontemporal` store, which gives a hint to the CPU that the data should not be held +/// in cache. Except for performance, this is fully equivalent to `ptr.write(val)`. +/// +/// Not all architectures provide such an operation. For instance, x86 does not: while `MOVNT` +/// exists, that operation is *not* equivalent to `ptr.write(val)` (`MOVNT` writes can be reordered +/// in ways that are not allowed for regular writes). +#[rustc_intrinsic] +#[rustc_intrinsic_must_be_overridden] +#[rustc_nounwind] +pub unsafe fn nontemporal_store(_ptr: *mut T, _val: T) { + unreachable!() } /// See documentation of `<*const T>::offset_from` for details. diff --git a/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs b/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs index 9d26bf930a1a9..50a97579df779 100644 --- a/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs +++ b/src/tools/clippy/clippy_lints/src/unnested_or_patterns.rs @@ -234,7 +234,7 @@ fn transform_with_focus_on_idx(alternatives: &mut ThinVec>, focus_idx: us // In the case of only two patterns, replacement adds net characters. | Ref(_, Mutability::Not) // Dealt with elsewhere. - | Or(_) | Paren(_) | Deref(_) => false, + | Or(_) | Paren(_) | Deref(_) | Guard(..) => false, // Transform `box x | ... | box y` into `box (x | y)`. // // The cases below until `Slice(...)` deal with *singleton* products. diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index 84269fd44a1e0..7b11bf3b1219c 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -22,7 +22,7 @@ use crate::common::{ UI_STDERR, UI_STDOUT, UI_SVG, UI_WINDOWS_SVG, Ui, expected_output_path, incremental_dir, output_base_dir, output_base_name, output_testname_unique, }; -use crate::compute_diff::{write_diff, write_filtered_diff}; +use crate::compute_diff::{DiffLine, make_diff, write_diff, write_filtered_diff}; use crate::errors::{self, Error, ErrorKind}; use crate::header::TestProps; use crate::read2::{Truncated, read2_abbreviated}; @@ -2295,17 +2295,31 @@ impl<'test> TestCx<'test> { match output_kind { TestOutput::Compile => { if !self.props.dont_check_compiler_stdout { - errors += - self.compare_output(stdout_kind, &normalized_stdout, &expected_stdout); + errors += self.compare_output( + stdout_kind, + &normalized_stdout, + &proc_res.stdout, + &expected_stdout, + ); } if !self.props.dont_check_compiler_stderr { - errors += - self.compare_output(stderr_kind, &normalized_stderr, &expected_stderr); + errors += self.compare_output( + stderr_kind, + &normalized_stderr, + &stderr, + &expected_stderr, + ); } } TestOutput::Run => { - errors += self.compare_output(stdout_kind, &normalized_stdout, &expected_stdout); - errors += self.compare_output(stderr_kind, &normalized_stderr, &expected_stderr); + errors += self.compare_output( + stdout_kind, + &normalized_stdout, + &proc_res.stdout, + &expected_stdout, + ); + errors += + self.compare_output(stderr_kind, &normalized_stderr, &stderr, &expected_stderr); } } errors @@ -2533,7 +2547,13 @@ impl<'test> TestCx<'test> { } } - fn compare_output(&self, stream: &str, actual: &str, expected: &str) -> usize { + fn compare_output( + &self, + stream: &str, + actual: &str, + actual_unnormalized: &str, + expected: &str, + ) -> usize { let are_different = match (self.force_color_svg(), expected.find('\n'), actual.find('\n')) { // FIXME: We ignore the first line of SVG files // because the width parameter is non-deterministic. @@ -2590,28 +2610,14 @@ impl<'test> TestCx<'test> { if expected.is_empty() { println!("normalized {}:\n{}\n", stream, actual); } else { - println!("diff of {stream}:\n"); - if let Some(diff_command) = self.config.diff_command.as_deref() { - let mut args = diff_command.split_whitespace(); - let name = args.next().unwrap(); - match Command::new(name) - .args(args) - .args([&expected_path, &actual_path]) - .output() - { - Err(err) => { - self.fatal(&format!( - "failed to call custom diff command `{diff_command}`: {err}" - )); - } - Ok(output) => { - let output = String::from_utf8_lossy(&output.stdout); - print!("{output}"); - } - } - } else { - print!("{}", write_diff(expected, actual, 3)); - } + self.show_diff( + stream, + &expected_path, + &actual_path, + expected, + actual, + actual_unnormalized, + ); } } else { // Delete non-revision .stderr/.stdout file if revisions are used. @@ -2633,6 +2639,76 @@ impl<'test> TestCx<'test> { if self.config.bless { 0 } else { 1 } } + /// Returns whether to show the full stderr/stdout. + fn show_diff( + &self, + stream: &str, + expected_path: &Path, + actual_path: &Path, + expected: &str, + actual: &str, + actual_unnormalized: &str, + ) { + eprintln!("diff of {stream}:\n"); + if let Some(diff_command) = self.config.diff_command.as_deref() { + let mut args = diff_command.split_whitespace(); + let name = args.next().unwrap(); + match Command::new(name).args(args).args([expected_path, actual_path]).output() { + Err(err) => { + self.fatal(&format!( + "failed to call custom diff command `{diff_command}`: {err}" + )); + } + Ok(output) => { + let output = String::from_utf8_lossy(&output.stdout); + eprint!("{output}"); + } + } + } else { + eprint!("{}", write_diff(expected, actual, 3)); + } + + // NOTE: argument order is important, we need `actual` to be on the left so the line number match up when we compare it to `actual_unnormalized` below. + let diff_results = make_diff(actual, expected, 0); + + let (mut mismatches_normalized, mut mismatch_line_nos) = (String::new(), vec![]); + for hunk in diff_results { + let mut line_no = hunk.line_number; + for line in hunk.lines { + // NOTE: `Expected` is actually correct here, the argument order is reversed so our line numbers match up + if let DiffLine::Expected(normalized) = line { + mismatches_normalized += &normalized; + mismatches_normalized += "\n"; + mismatch_line_nos.push(line_no); + line_no += 1; + } + } + } + let mut mismatches_unnormalized = String::new(); + let diff_normalized = make_diff(actual, actual_unnormalized, 0); + for hunk in diff_normalized { + if mismatch_line_nos.contains(&hunk.line_number) { + for line in hunk.lines { + if let DiffLine::Resulting(unnormalized) = line { + mismatches_unnormalized += &unnormalized; + mismatches_unnormalized += "\n"; + } + } + } + } + + let normalized_diff = make_diff(&mismatches_normalized, &mismatches_unnormalized, 0); + // HACK: instead of checking if each hunk is empty, this only checks if the whole input is empty. we should be smarter about this so we don't treat added or removed output as normalized. + if !normalized_diff.is_empty() + && !mismatches_unnormalized.is_empty() + && !mismatches_normalized.is_empty() + { + eprintln!("Note: some mismatched output was normalized before being compared"); + // FIXME: respect diff_command + eprint!("{}", write_diff(&mismatches_unnormalized, &mismatches_normalized, 0)); + } + } + fn check_and_prune_duplicate_outputs( &self, proc_res: &ProcRes, diff --git a/src/tools/compiletest/src/runtest/coverage.rs b/src/tools/compiletest/src/runtest/coverage.rs index 961a160298631..030ca5ebb2474 100644 --- a/src/tools/compiletest/src/runtest/coverage.rs +++ b/src/tools/compiletest/src/runtest/coverage.rs @@ -39,8 +39,12 @@ impl<'test> TestCx<'test> { let expected_coverage_dump = self.load_expected_output(kind); let actual_coverage_dump = self.normalize_output(&proc_res.stdout, &[]); - let coverage_dump_errors = - self.compare_output(kind, &actual_coverage_dump, &expected_coverage_dump); + let coverage_dump_errors = self.compare_output( + kind, + &actual_coverage_dump, + &proc_res.stdout, + &expected_coverage_dump, + ); if coverage_dump_errors > 0 { self.fatal_proc_rec( @@ -135,8 +139,12 @@ impl<'test> TestCx<'test> { self.fatal_proc_rec(&err, &proc_res); }); - let coverage_errors = - self.compare_output(kind, &normalized_actual_coverage, &expected_coverage); + let coverage_errors = self.compare_output( + kind, + &normalized_actual_coverage, + &proc_res.stdout, + &expected_coverage, + ); if coverage_errors > 0 { self.fatal_proc_rec( diff --git a/src/tools/compiletest/src/runtest/ui.rs b/src/tools/compiletest/src/runtest/ui.rs index bb747c6802926..172b1e32aad3b 100644 --- a/src/tools/compiletest/src/runtest/ui.rs +++ b/src/tools/compiletest/src/runtest/ui.rs @@ -100,7 +100,7 @@ impl TestCx<'_> { ) }); - errors += self.compare_output("fixed", &fixed_code, &expected_fixed); + errors += self.compare_output("fixed", &fixed_code, &fixed_code, &expected_fixed); } else if !expected_fixed.is_empty() { panic!( "the `//@ run-rustfix` directive wasn't found but a `*.fixed` \ diff --git a/src/tools/rustfmt/src/patterns.rs b/src/tools/rustfmt/src/patterns.rs index 6fe2d4a8520e3..7b4730eadc854 100644 --- a/src/tools/rustfmt/src/patterns.rs +++ b/src/tools/rustfmt/src/patterns.rs @@ -48,7 +48,8 @@ fn is_short_pattern_inner(pat: &ast::Pat) -> bool { | ast::PatKind::MacCall(..) | ast::PatKind::Slice(..) | ast::PatKind::Path(..) - | ast::PatKind::Range(..) => false, + | ast::PatKind::Range(..) + | ast::PatKind::Guard(..) => false, ast::PatKind::Tuple(ref subpats) => subpats.len() <= 1, ast::PatKind::TupleStruct(_, ref path, ref subpats) => { path.segments.len() <= 1 && subpats.len() <= 1 @@ -338,8 +339,9 @@ impl Rewrite for Pat { .max_width_error(shape.width, self.span)?, ) .map(|inner_pat| format!("({})", inner_pat)), - PatKind::Err(_) => Err(RewriteError::Unknown), + PatKind::Guard(..) => Ok(context.snippet(self.span).to_string()), PatKind::Deref(_) => Err(RewriteError::Unknown), + PatKind::Err(_) => Err(RewriteError::Unknown), } } } diff --git a/src/tools/rustfmt/tests/target/guard_patterns.rs b/src/tools/rustfmt/tests/target/guard_patterns.rs new file mode 100644 index 0000000000000..2e4667b916cda --- /dev/null +++ b/src/tools/rustfmt/tests/target/guard_patterns.rs @@ -0,0 +1,12 @@ +#![feature(guard_patterns)] + +fn main() { + match user.subscription_plan() { + (Plan::Regular if user.credit() >= 100) | (Plan::Premium if user.credit() >= 80) => { + // Complete the transaction. + } + _ => { + // The user doesn't have enough credit, return an error message. + } + } +} diff --git a/tests/ui/assign-imm-local-twice.rs b/tests/ui/assign-imm-local-twice.rs deleted file mode 100644 index b2dfeb564d9f9..0000000000000 --- a/tests/ui/assign-imm-local-twice.rs +++ /dev/null @@ -1,13 +0,0 @@ -fn test() { - let v: isize; - //~^ HELP consider making this binding mutable - //~| SUGGESTION mut - v = 1; //~ NOTE first assignment - println!("v={}", v); - v = 2; //~ ERROR cannot assign twice to immutable variable - //~| NOTE cannot assign twice to immutable - println!("v={}", v); -} - -fn main() { -} diff --git a/tests/ui/assoc-lang-items.rs b/tests/ui/assoc-lang-items.rs deleted file mode 100644 index 23453d201a72f..0000000000000 --- a/tests/ui/assoc-lang-items.rs +++ /dev/null @@ -1,21 +0,0 @@ -#![feature(lang_items)] - -trait Foo { - #[lang = "dummy_lang_item_1"] //~ ERROR definition - fn foo() {} - - #[lang = "dummy_lang_item_2"] //~ ERROR definition - fn bar(); - - #[lang = "dummy_lang_item_3"] //~ ERROR definition - type MyType; -} - -struct Bar; - -impl Bar { - #[lang = "dummy_lang_item_4"] //~ ERROR definition - fn test() {} -} - -fn main() {} diff --git a/tests/ui/assoc-oddities-3.rs b/tests/ui/assoc-oddities-3.rs deleted file mode 100644 index ffde2ccf78635..0000000000000 --- a/tests/ui/assoc-oddities-3.rs +++ /dev/null @@ -1,13 +0,0 @@ -//@ run-pass - -fn that_odd_parse(c: bool, n: usize) -> u32 { - let x = 2; - let a = [1, 2, 3, 4]; - let b = [5, 6, 7, 7]; - x + if c { a } else { b }[n] -} - -fn main() { - assert_eq!(4, that_odd_parse(true, 1)); - assert_eq!(8, that_odd_parse(false, 1)); -} diff --git a/tests/ui/atomic-from-mut-not-available.rs b/tests/ui/atomic-from-mut-not-available.rs deleted file mode 100644 index 8326187838a20..0000000000000 --- a/tests/ui/atomic-from-mut-not-available.rs +++ /dev/null @@ -1,7 +0,0 @@ -//@ only-x86 -//@ only-linux - -fn main() { - core::sync::atomic::AtomicU64::from_mut(&mut 0u64); - //~^ ERROR: no function or associated item named `from_mut` found for struct `AtomicU64` -} diff --git a/tests/ui/borrowck/assign-imm-local-twice.fixed b/tests/ui/borrowck/assign-imm-local-twice.fixed new file mode 100644 index 0000000000000..dca994141bb8b --- /dev/null +++ b/tests/ui/borrowck/assign-imm-local-twice.fixed @@ -0,0 +1,21 @@ +//! Check that we do not allow assigning twice to an immutable variable. This test also checks a +//! few pieces of borrowck diagnostics: +//! +//! - A multipart borrowck diagnostics that points out the first assignment to an immutable +//! variable, alongside violating assignments that follow subsequently. +//! - A suggestion diagnostics to make the immutable binding mutable. + +//@ run-rustfix + +fn main() { + let mut v: isize; + //~^ HELP consider making this binding mutable + //~| SUGGESTION mut + v = 1; + //~^ NOTE first assignment + println!("v={}", v); + v = 2; + //~^ ERROR cannot assign twice to immutable variable + //~| NOTE cannot assign twice to immutable + println!("v={}", v); +} diff --git a/tests/ui/borrowck/assign-imm-local-twice.rs b/tests/ui/borrowck/assign-imm-local-twice.rs new file mode 100644 index 0000000000000..43437fa69fa84 --- /dev/null +++ b/tests/ui/borrowck/assign-imm-local-twice.rs @@ -0,0 +1,21 @@ +//! Check that we do not allow assigning twice to an immutable variable. This test also checks a +//! few pieces of borrowck diagnostics: +//! +//! - A multipart borrowck diagnostics that points out the first assignment to an immutable +//! variable, alongside violating assignments that follow subsequently. +//! - A suggestion diagnostics to make the immutable binding mutable. + +//@ run-rustfix + +fn main() { + let v: isize; + //~^ HELP consider making this binding mutable + //~| SUGGESTION mut + v = 1; + //~^ NOTE first assignment + println!("v={}", v); + v = 2; + //~^ ERROR cannot assign twice to immutable variable + //~| NOTE cannot assign twice to immutable + println!("v={}", v); +} diff --git a/tests/ui/assign-imm-local-twice.stderr b/tests/ui/borrowck/assign-imm-local-twice.stderr similarity index 84% rename from tests/ui/assign-imm-local-twice.stderr rename to tests/ui/borrowck/assign-imm-local-twice.stderr index fda3aa3de1b4b..0a2138d0b9625 100644 --- a/tests/ui/assign-imm-local-twice.stderr +++ b/tests/ui/borrowck/assign-imm-local-twice.stderr @@ -1,9 +1,9 @@ error[E0384]: cannot assign twice to immutable variable `v` - --> $DIR/assign-imm-local-twice.rs:7:5 + --> $DIR/assign-imm-local-twice.rs:17:5 | LL | v = 1; | ----- first assignment to `v` -LL | println!("v={}", v); +... LL | v = 2; | ^^^^^ cannot assign twice to immutable variable | diff --git a/tests/ui/assign-assign.rs b/tests/ui/codegen/assign-expr-unit-type.rs similarity index 53% rename from tests/ui/assign-assign.rs rename to tests/ui/codegen/assign-expr-unit-type.rs index 002393f5bffbb..b4268b5e09a3c 100644 --- a/tests/ui/assign-assign.rs +++ b/tests/ui/codegen/assign-expr-unit-type.rs @@ -1,5 +1,11 @@ +//! Regression test for [Using the result of an assignment expression results in an LLVM assert +//! #483][issue-483]. This test checks that assignment expressions produce a unit type, and is +//! properly lowered to LLVM IR such that it does not trigger an LLVM assertion. This test was added +//! *really* early, back in 2011. +//! +//! [issue-483]: https://github.com/rust-lang/rust/issues/483 + //@ run-pass -// Issue 483 - Assignment expressions result in nil fn test_assign() { let mut x: isize; @@ -27,4 +33,7 @@ fn test_assign_op() { assert_eq!(z, ()); } -pub fn main() { test_assign(); test_assign_op(); } +pub fn main() { + test_assign(); + test_assign_op(); +} diff --git a/tests/ui/feature-gates/feature-gate-guard-patterns.rs b/tests/ui/feature-gates/feature-gate-guard-patterns.rs new file mode 100644 index 0000000000000..929e8ef3181f9 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-guard-patterns.rs @@ -0,0 +1,46 @@ +fn match_guards_still_work() { + match 0 { + 0 if guard(0) => {}, + _ => {}, + } +} + +fn other_guards_dont() { + match 0 { + (0 if guard(0)) => {}, + //~^ ERROR unexpected parentheses surrounding `match` arm pattern + _ => {}, + } + + match 0 { + (0 if guard(0)) | 1 => {}, + //~^ ERROR: guard patterns are experimental + _ => {}, + } + + 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 + //~| WARN: irrefutable + + while let (x if guard(x)) = 0 {} + //~^ ERROR: guard patterns are experimental + //~| WARN: irrefutable + + #[cfg(FALSE)] + while let (x if guard(x)) = 0 {} + //~^ ERROR: guard patterns are experimental +} + +fn even_as_function_parameters(((x if guard(x), _) | (_, x)): (i32, i32)) {} +//~^ ERROR: guard patterns are experimental +//~| ERROR: cannot find value `x` + +fn guard(x: T) -> bool { + unimplemented!() +} + +fn main() {} diff --git a/tests/ui/feature-gates/feature-gate-guard-patterns.stderr b/tests/ui/feature-gates/feature-gate-guard-patterns.stderr new file mode 100644 index 0000000000000..0613b5c95a413 --- /dev/null +++ b/tests/ui/feature-gates/feature-gate-guard-patterns.stderr @@ -0,0 +1,119 @@ +error: unexpected parentheses surrounding `match` arm pattern + --> $DIR/feature-gate-guard-patterns.rs:10:9 + | +LL | (0 if guard(0)) => {}, + | ^ ^ + | +help: remove parentheses surrounding the pattern + | +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:21: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:21:11 + | +LL | let ((x if guard(x)) | x) = 0; + | ^ + +error[E0658]: guard patterns are experimental + --> $DIR/feature-gate-guard-patterns.rs:16:15 + | +LL | (0 if guard(0)) | 1 => {}, + | ^^^^^^^^ + | + = note: see issue #129967 for more information + = help: add `#![feature(guard_patterns)]` to the crate attributes to enable + = 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[E0658]: guard patterns are experimental + --> $DIR/feature-gate-guard-patterns.rs:21:16 + | +LL | let ((x if guard(x)) | x) = 0; + | ^^^^^^^^ + | + = note: see issue #129967 for more information + = help: add `#![feature(guard_patterns)]` to the crate attributes to enable + = 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[E0658]: guard patterns are experimental + --> $DIR/feature-gate-guard-patterns.rs:25:18 + | +LL | if let (x if guard(x)) = 0 {} + | ^^^^^^^^ + | + = note: see issue #129967 for more information + = help: add `#![feature(guard_patterns)]` to the crate attributes to enable + = 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[E0658]: guard patterns are experimental + --> $DIR/feature-gate-guard-patterns.rs:29:21 + | +LL | while let (x if guard(x)) = 0 {} + | ^^^^^^^^ + | + = note: see issue #129967 for more information + = help: add `#![feature(guard_patterns)]` to the crate attributes to enable + = 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[E0658]: guard patterns are experimental + --> $DIR/feature-gate-guard-patterns.rs:34:21 + | +LL | while let (x if guard(x)) = 0 {} + | ^^^^^^^^ + | + = note: see issue #129967 for more information + = help: add `#![feature(guard_patterns)]` to the crate attributes to enable + = 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[E0658]: guard patterns are experimental + --> $DIR/feature-gate-guard-patterns.rs:38:39 + | +LL | fn even_as_function_parameters(((x if guard(x), _) | (_, x)): (i32, i32)) {} + | ^^^^^^^^ + | + = note: see issue #129967 for more information + = help: add `#![feature(guard_patterns)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + = help: consider using match arm guards + +warning: irrefutable `if let` pattern + --> $DIR/feature-gate-guard-patterns.rs:25:8 + | +LL | if let (x if guard(x)) = 0 {} + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this pattern will always match, so the `if let` is useless + = help: consider replacing the `if let` with a `let` + = note: `#[warn(irrefutable_let_patterns)]` on by default + +warning: irrefutable `while let` pattern + --> $DIR/feature-gate-guard-patterns.rs:29:11 + | +LL | while let (x if guard(x)) = 0 {} + | ^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: this pattern will always match, so the loop will never exit + = help: consider instead using a `loop { ... }` with a `let` inside it + +error: aborting due to 9 previous errors; 2 warnings emitted + +Some errors have detailed explanations: E0425, E0658. +For more information about an error, try `rustc --explain E0425`. diff --git a/tests/ui/lang-items/assoc-lang-items.rs b/tests/ui/lang-items/assoc-lang-items.rs new file mode 100644 index 0000000000000..460d3ed232665 --- /dev/null +++ b/tests/ui/lang-items/assoc-lang-items.rs @@ -0,0 +1,35 @@ +//! Check that associated items can be marked as lang items, so that they don't have to be looked up +//! by name or by definition order indirectly. +//! +//! This test is not *quite* high-fidelity: it checks that you can use lang items on associated +//! items by looking at the error message *as a proxy*. That is, the error message is about +//! undefined lang items and not invalid attribute target, indicating that it has reached lang item +//! machinery (which is relying on knowing the implementation detail). However, it's annoying to +//! write a full-fidelity test for this, so I think this is acceptable even though it's not *great*. +//! +//! This was implemented in to help with +//! , which is itself relevant for e.g. `Fn::Output` +//! or `Future::Output` or specific use cases like [Use `T`'s discriminant type in +//! `mem::Discriminant` instead of `u64`](https://github.com/rust-lang/rust/pull/70705). + +#![feature(lang_items)] + +trait Foo { + #[lang = "dummy_lang_item_1"] //~ ERROR definition + fn foo() {} + + #[lang = "dummy_lang_item_2"] //~ ERROR definition + fn bar(); + + #[lang = "dummy_lang_item_3"] //~ ERROR definition + type MyType; +} + +struct Bar; + +impl Bar { + #[lang = "dummy_lang_item_4"] //~ ERROR definition + fn test() {} +} + +fn main() {} diff --git a/tests/ui/assoc-lang-items.stderr b/tests/ui/lang-items/assoc-lang-items.stderr similarity index 86% rename from tests/ui/assoc-lang-items.stderr rename to tests/ui/lang-items/assoc-lang-items.stderr index 59aec8e3fdc6e..7e61fea449be8 100644 --- a/tests/ui/assoc-lang-items.stderr +++ b/tests/ui/lang-items/assoc-lang-items.stderr @@ -1,23 +1,23 @@ error[E0522]: definition of an unknown lang item: `dummy_lang_item_1` - --> $DIR/assoc-lang-items.rs:4:5 + --> $DIR/assoc-lang-items.rs:18:5 | LL | #[lang = "dummy_lang_item_1"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ definition of unknown lang item `dummy_lang_item_1` error[E0522]: definition of an unknown lang item: `dummy_lang_item_2` - --> $DIR/assoc-lang-items.rs:7:5 + --> $DIR/assoc-lang-items.rs:21:5 | LL | #[lang = "dummy_lang_item_2"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ definition of unknown lang item `dummy_lang_item_2` error[E0522]: definition of an unknown lang item: `dummy_lang_item_3` - --> $DIR/assoc-lang-items.rs:10:5 + --> $DIR/assoc-lang-items.rs:24:5 | LL | #[lang = "dummy_lang_item_3"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ definition of unknown lang item `dummy_lang_item_3` error[E0522]: definition of an unknown lang item: `dummy_lang_item_4` - --> $DIR/assoc-lang-items.rs:17:5 + --> $DIR/assoc-lang-items.rs:31:5 | LL | #[lang = "dummy_lang_item_4"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ definition of unknown lang item `dummy_lang_item_4` diff --git a/tests/ui/parser/assoc/assoc-oddities-3.rs b/tests/ui/parser/assoc/assoc-oddities-3.rs new file mode 100644 index 0000000000000..1a41c4be02373 --- /dev/null +++ b/tests/ui/parser/assoc/assoc-oddities-3.rs @@ -0,0 +1,41 @@ +//! Check that braces has the expected precedence in relation to index op and some arithmetic +//! bin-ops involving nested braces. +//! +//! This is a regression test for [Wrapping expr in curly braces changes the operator precedence +//! #28777](https://github.com/rust-lang/rust/issues/28777), which was fixed by +//! . + +//@ run-pass + +fn that_odd_parse(c: bool, n: usize) -> u32 { + let x = 2; + let a = [1, 2, 3, 4]; + let b = [5, 6, 7, 7]; + x + if c { a } else { b }[n] +} + +/// See [Wrapping expr in curly braces changes the operator precedence +/// #28777](https://github.com/rust-lang/rust/issues/28777). This was fixed by +/// . #30375 added the `that_odd_parse` example above, +/// but that is not *quite* the same original example as reported in #28777, so we also include the +/// original example here. +fn check_issue_28777() { + // Before #30375 fixed the precedence... + + // ... `v1` evaluated to 9, indicating a parse of `(1 + 2) * 3`, while + let v1 = { 1 + { 2 } * { 3 } }; + + // `v2` evaluated to 7, indicating a parse of `1 + (2 * 3)`. + let v2 = 1 + { 2 } * { 3 }; + + // Check that both now evaluate to 7, as was fixed by #30375. + assert_eq!(v1, 7); + assert_eq!(v2, 7); +} + +fn main() { + assert_eq!(4, that_odd_parse(true, 1)); + assert_eq!(8, that_odd_parse(false, 1)); + + check_issue_28777(); +} diff --git a/tests/ui/parser/issues/issue-72373.rs b/tests/ui/parser/issues/issue-72373.rs index 4da6061c27fe8..ed88d53539bd4 100644 --- a/tests/ui/parser/issues/issue-72373.rs +++ b/tests/ui/parser/issues/issue-72373.rs @@ -3,7 +3,7 @@ fn foo(c: &[u32], n: u32) -> u32 { [h, ..] if h > n => 0, [h, ..] if h == n => 1, [h, ref ts..] => foo(c, n - h) + foo(ts, n), - //~^ ERROR expected one of `,`, `@`, `]`, or `|`, found `..` + //~^ ERROR expected one of `,`, `@`, `]`, `if`, or `|`, found `..` [] => 0, } } diff --git a/tests/ui/parser/issues/issue-72373.stderr b/tests/ui/parser/issues/issue-72373.stderr index c596c6abda553..d566d6f5fd138 100644 --- a/tests/ui/parser/issues/issue-72373.stderr +++ b/tests/ui/parser/issues/issue-72373.stderr @@ -1,8 +1,8 @@ -error: expected one of `,`, `@`, `]`, or `|`, found `..` +error: expected one of `,`, `@`, `]`, `if`, or `|`, found `..` --> $DIR/issue-72373.rs:5:19 | LL | [h, ref ts..] => foo(c, n - h) + foo(ts, n), - | ^^ expected one of `,`, `@`, `]`, or `|` + | ^^ expected one of `,`, `@`, `]`, `if`, or `|` | help: if you meant to bind the contents of the rest of the array pattern into `ts`, use `@` | diff --git a/tests/ui/parser/misspelled-keywords/ref.stderr b/tests/ui/parser/misspelled-keywords/ref.stderr index b8b52702314c8..398d9d6bb99b1 100644 --- a/tests/ui/parser/misspelled-keywords/ref.stderr +++ b/tests/ui/parser/misspelled-keywords/ref.stderr @@ -1,8 +1,8 @@ -error: expected one of `)`, `,`, `@`, or `|`, found `list` +error: expected one of `)`, `,`, `@`, `if`, or `|`, found `list` --> $DIR/ref.rs:4:19 | LL | Some(refe list) => println!("{list:?}"), - | ^^^^ expected one of `)`, `,`, `@`, or `|` + | ^^^^ expected one of `)`, `,`, `@`, `if`, or `|` | help: there is a keyword `ref` with a similar name | diff --git a/tests/ui/parser/pat-lt-bracket-7.rs b/tests/ui/parser/pat-lt-bracket-7.rs index 327aef5ad1570..abaeb4c83c05f 100644 --- a/tests/ui/parser/pat-lt-bracket-7.rs +++ b/tests/ui/parser/pat-lt-bracket-7.rs @@ -3,7 +3,7 @@ fn main() { let foo = core::iter::empty(); for Thing(x[]) in foo {} - //~^ ERROR: expected one of `)`, `,`, `@`, or `|`, found `[` + //~^ ERROR: expected one of `)`, `,`, `@`, `if`, or `|`, found `[` } const RECOVERY_WITNESS: () = 0; //~ ERROR mismatched types diff --git a/tests/ui/parser/pat-lt-bracket-7.stderr b/tests/ui/parser/pat-lt-bracket-7.stderr index 004dcfb2a7b2d..cc457a4e64e24 100644 --- a/tests/ui/parser/pat-lt-bracket-7.stderr +++ b/tests/ui/parser/pat-lt-bracket-7.stderr @@ -1,10 +1,10 @@ -error: expected one of `)`, `,`, `@`, or `|`, found `[` +error: expected one of `)`, `,`, `@`, `if`, or `|`, found `[` --> $DIR/pat-lt-bracket-7.rs:5:16 | LL | for Thing(x[]) in foo {} | ^ | | - | expected one of `)`, `,`, `@`, or `|` + | expected one of `)`, `,`, `@`, `if`, or `|` | help: missing `,` error[E0308]: mismatched types diff --git a/tests/ui/parser/recover/recover-pat-exprs.rs b/tests/ui/parser/recover/recover-pat-exprs.rs index e5e25df0c01cc..a78bb82828d3a 100644 --- a/tests/ui/parser/recover/recover-pat-exprs.rs +++ b/tests/ui/parser/recover/recover-pat-exprs.rs @@ -27,7 +27,7 @@ fn array_indexing() { { let x[0, 1, 2]; } //~ error: expected one of `:`, `;`, `=`, `@`, or `|`, found `[` { let x[0; 20]; } //~ error: expected one of `:`, `;`, `=`, `@`, or `|`, found `[` { let x[]; } //~ error: expected one of `:`, `;`, `=`, `@`, or `|`, found `[` - { let (x[]); } //~ error: expected one of `)`, `,`, `@`, or `|`, found `[` + { let (x[]); } //~ error: expected one of `)`, `,`, `@`, `if`, or `|`, found `[` //~^ missing `,` } @@ -95,12 +95,12 @@ fn main() { f?() => (), //~^ error: expected a pattern, found an expression (_ + 1) => (), - //~^ error: expected one of `)`, `,`, or `|`, found `+` + //~^ error: expected one of `)`, `,`, `if`, or `|`, found `+` } let 1 + 1 = 2; //~^ error: expected a pattern, found an expression let b = matches!(x, (x * x | x.f()) | x[0]); - //~^ error: expected one of `)`, `,`, `@`, or `|`, found `*` + //~^ error: expected one of `)`, `,`, `@`, `if`, or `|`, found `*` } diff --git a/tests/ui/parser/recover/recover-pat-exprs.stderr b/tests/ui/parser/recover/recover-pat-exprs.stderr index 041dfd647ad07..281eeced40242 100644 --- a/tests/ui/parser/recover/recover-pat-exprs.stderr +++ b/tests/ui/parser/recover/recover-pat-exprs.stderr @@ -213,13 +213,13 @@ error: expected one of `:`, `;`, `=`, `@`, or `|`, found `[` LL | { let x[]; } | ^ expected one of `:`, `;`, `=`, `@`, or `|` -error: expected one of `)`, `,`, `@`, or `|`, found `[` +error: expected one of `)`, `,`, `@`, `if`, or `|`, found `[` --> $DIR/recover-pat-exprs.rs:30:13 | LL | { let (x[]); } | ^ | | - | expected one of `)`, `,`, `@`, or `|` + | expected one of `)`, `,`, `@`, `if`, or `|` | help: missing `,` error: expected a pattern, found an expression @@ -611,11 +611,11 @@ LL | x.sqrt() @ .. => (), | = note: bindings are `x`, `mut x`, `ref x`, and `ref mut x` -error: expected one of `)`, `,`, or `|`, found `+` +error: expected one of `)`, `,`, `if`, or `|`, found `+` --> $DIR/recover-pat-exprs.rs:97:12 | LL | (_ + 1) => (), - | ^ expected one of `)`, `,`, or `|` + | ^ expected one of `)`, `,`, `if`, or `|` error: expected a pattern, found an expression --> $DIR/recover-pat-exprs.rs:81:9 @@ -772,11 +772,11 @@ LL | let 1 + 1 = 2; | = note: arbitrary expressions are not allowed in patterns: -error: expected one of `)`, `,`, `@`, or `|`, found `*` +error: expected one of `)`, `,`, `@`, `if`, or `|`, found `*` --> $DIR/recover-pat-exprs.rs:104:28 | LL | let b = matches!(x, (x * x | x.f()) | x[0]); - | ^ expected one of `)`, `,`, `@`, or `|` + | ^ expected one of `)`, `,`, `@`, `if`, or `|` --> $SRC_DIR/core/src/macros/mod.rs:LL:COL | = note: while parsing argument for this `pat` macro fragment diff --git a/tests/ui/parser/recover/recover-pat-wildcards.rs b/tests/ui/parser/recover/recover-pat-wildcards.rs index f506e2223d608..d4d28ce63587a 100644 --- a/tests/ui/parser/recover/recover-pat-wildcards.rs +++ b/tests/ui/parser/recover/recover-pat-wildcards.rs @@ -8,7 +8,7 @@ fn a() { fn b() { match 2 { - (_ % 4) => () //~ error: expected one of `)`, `,`, or `|`, found `%` + (_ % 4) => () //~ error: expected one of `)`, `,`, `if`, or `|`, found `%` } } @@ -40,7 +40,7 @@ fn f() { fn g() { match 7 { - (_ * 0)..5 => () //~ error: expected one of `)`, `,`, or `|`, found `*` + (_ * 0)..5 => () //~ error: expected one of `)`, `,`, `if`, or `|`, found `*` } } diff --git a/tests/ui/parser/recover/recover-pat-wildcards.stderr b/tests/ui/parser/recover/recover-pat-wildcards.stderr index 81a9920f6a243..f939e5133700f 100644 --- a/tests/ui/parser/recover/recover-pat-wildcards.stderr +++ b/tests/ui/parser/recover/recover-pat-wildcards.stderr @@ -4,11 +4,11 @@ error: expected one of `=>`, `if`, or `|`, found `+` LL | _ + 1 => () | ^ expected one of `=>`, `if`, or `|` -error: expected one of `)`, `,`, or `|`, found `%` +error: expected one of `)`, `,`, `if`, or `|`, found `%` --> $DIR/recover-pat-wildcards.rs:11:12 | LL | (_ % 4) => () - | ^ expected one of `)`, `,`, or `|` + | ^ expected one of `)`, `,`, `if`, or `|` error: expected one of `=>`, `if`, or `|`, found `.` --> $DIR/recover-pat-wildcards.rs:17:10 @@ -47,11 +47,11 @@ error: expected one of `=>`, `if`, or `|`, found reserved identifier `_` LL | 0..._ => () | ^ expected one of `=>`, `if`, or `|` -error: expected one of `)`, `,`, or `|`, found `*` +error: expected one of `)`, `,`, `if`, or `|`, found `*` --> $DIR/recover-pat-wildcards.rs:43:12 | LL | (_ * 0)..5 => () - | ^ expected one of `)`, `,`, or `|` + | ^ expected one of `)`, `,`, `if`, or `|` error: expected one of `=>`, `if`, or `|`, found `(` --> $DIR/recover-pat-wildcards.rs:49:11 diff --git a/tests/ui/pattern/bindings-after-at/nested-type-ascription-syntactically-invalid.stderr b/tests/ui/pattern/bindings-after-at/nested-type-ascription-syntactically-invalid.stderr index da8f4ca5f0cd7..6ce8f6d31a031 100644 --- a/tests/ui/pattern/bindings-after-at/nested-type-ascription-syntactically-invalid.stderr +++ b/tests/ui/pattern/bindings-after-at/nested-type-ascription-syntactically-invalid.stderr @@ -6,11 +6,11 @@ LL | let a: u8 @ b = 0; | | | while parsing the type for `a` -error: expected one of `)`, `,`, `@`, or `|`, found `:` +error: expected one of `)`, `,`, `@`, `if`, or `|`, found `:` --> $DIR/nested-type-ascription-syntactically-invalid.rs:24:15 | LL | let a @ (b: u8); - | ^ expected one of `)`, `,`, `@`, or `|` + | ^ expected one of `)`, `,`, `@`, `if`, or `|` | = note: type ascription syntax has been removed, see issue #101728 diff --git a/tests/ui/pattern/rfc-3637-guard-patterns/macro-rules.rs b/tests/ui/pattern/rfc-3637-guard-patterns/macro-rules.rs new file mode 100644 index 0000000000000..76681f45bb336 --- /dev/null +++ b/tests/ui/pattern/rfc-3637-guard-patterns/macro-rules.rs @@ -0,0 +1,20 @@ +//@ run-pass +//! Tests that the addition of guard patterns does not change the behavior of the `pat` macro +//! fragment. +#![feature(guard_patterns)] +#![allow(incomplete_features)] + +macro_rules! has_guard { + ($p:pat) => { + false + }; + ($p:pat if $e:expr) => { + true + }; +} + +fn main() { + assert_eq!(has_guard!(Some(_)), false); + assert_eq!(has_guard!(Some(_) if true), true); + assert_eq!(has_guard!((Some(_) if true)), false); +} diff --git a/tests/ui/stdlib-unit-tests/atomic-from-mut-not-available.alignment_matches.stderr b/tests/ui/stdlib-unit-tests/atomic-from-mut-not-available.alignment_matches.stderr new file mode 100644 index 0000000000000..109985c005286 --- /dev/null +++ b/tests/ui/stdlib-unit-tests/atomic-from-mut-not-available.alignment_matches.stderr @@ -0,0 +1,13 @@ +error[E0658]: use of unstable library feature `atomic_from_mut` + --> $DIR/atomic-from-mut-not-available.rs:24:5 + | +LL | core::sync::atomic::AtomicU64::from_mut(&mut 0u64); + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + | + = note: see issue #76314 for more information + = help: add `#![feature(atomic_from_mut)]` to the crate attributes to enable + = note: this compiler was built on YYYY-MM-DD; consider upgrading it if it is out of date + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/tests/ui/atomic-from-mut-not-available.stderr b/tests/ui/stdlib-unit-tests/atomic-from-mut-not-available.alignment_mismatch.stderr similarity index 94% rename from tests/ui/atomic-from-mut-not-available.stderr rename to tests/ui/stdlib-unit-tests/atomic-from-mut-not-available.alignment_mismatch.stderr index a4514524f48f5..47b17f9fcd7ad 100644 --- a/tests/ui/atomic-from-mut-not-available.stderr +++ b/tests/ui/stdlib-unit-tests/atomic-from-mut-not-available.alignment_mismatch.stderr @@ -1,5 +1,5 @@ error[E0599]: no function or associated item named `from_mut` found for struct `AtomicU64` in the current scope - --> $DIR/atomic-from-mut-not-available.rs:5:36 + --> $DIR/atomic-from-mut-not-available.rs:24:36 | LL | core::sync::atomic::AtomicU64::from_mut(&mut 0u64); | ^^^^^^^^ function or associated item not found in `AtomicU64` diff --git a/tests/ui/stdlib-unit-tests/atomic-from-mut-not-available.rs b/tests/ui/stdlib-unit-tests/atomic-from-mut-not-available.rs new file mode 100644 index 0000000000000..7e9de3570eb91 --- /dev/null +++ b/tests/ui/stdlib-unit-tests/atomic-from-mut-not-available.rs @@ -0,0 +1,27 @@ +//! This test exercises the combined effect of the `cfg(target_has_atomic_equal_alignment = "...")` +//! implementation in the compiler plus usage of said `cfg(target_has_atomic_equal_alignment)` in +//! `core` for the `Atomic64::from_mut` API. +//! +//! This test is a basic smoke test: that `AtomicU64::from_mut` is gated by +//! `#[cfg(target_has_atomic_equal_alignment = "8")]`, which is only available on platforms where +//! `AtomicU64` has the same alignment as `u64`. This is notably *not* satisfied by `x86_32`, where +//! they have differing alignments. Thus, `AtomicU64::from_mut` should *not* be available on +//! `x86_32` linux and should report assoc item not found, if the `cfg` is working correctly. +//! Conversely, `AtomicU64::from_mut` *should* be available on `x86_64` linux where the alignment +//! matches. + +//@ revisions: alignment_mismatch alignment_matches + +// This should fail on 32-bit x86 linux... +//@[alignment_mismatch] only-x86 +//@[alignment_mismatch] only-linux + +// ... but pass on 64-bit x86_64 linux. +//@[alignment_matches] only-x86_64 +//@[alignment_matches] only-linux + +fn main() { + core::sync::atomic::AtomicU64::from_mut(&mut 0u64); + //[alignment_mismatch]~^ ERROR no function or associated item named `from_mut` found for struct `AtomicU64` + //[alignment_matches]~^^ ERROR use of unstable library feature `atomic_from_mut` +}