diff --git a/compiler/rustc_ast/src/util/literal.rs b/compiler/rustc_ast/src/util/literal.rs index 231dd72af2c7d..9c18f55c03b4d 100644 --- a/compiler/rustc_ast/src/util/literal.rs +++ b/compiler/rustc_ast/src/util/literal.rs @@ -23,7 +23,7 @@ pub enum LitError { impl LitKind { /// Converts literal token into a semantic literal. - fn from_lit_token(lit: token::Lit) -> Result { + pub fn from_lit_token(lit: token::Lit) -> Result { let token::Lit { kind, symbol, suffix } = lit; if suffix.is_some() && !kind.may_have_suffix() { return Err(LitError::InvalidSuffix); diff --git a/compiler/rustc_ast_passes/src/ast_validation.rs b/compiler/rustc_ast_passes/src/ast_validation.rs index 0cf73178d679d..8f0b12cb4feef 100644 --- a/compiler/rustc_ast_passes/src/ast_validation.rs +++ b/compiler/rustc_ast_passes/src/ast_validation.rs @@ -64,8 +64,8 @@ struct AstValidator<'a> { /// certain positions. is_assoc_ty_bound_banned: bool, - /// Used to allow `let` expressions in certain syntactic locations. - is_let_allowed: bool, + /// See [ForbiddenLetReason] + forbidden_let_reason: Option, lint_buffer: &'a mut LintBuffer, } @@ -103,20 +103,28 @@ impl<'a> AstValidator<'a> { self.is_tilde_const_allowed = old; } - fn with_let_allowed(&mut self, allowed: bool, f: impl FnOnce(&mut Self, bool)) { - let old = mem::replace(&mut self.is_let_allowed, allowed); + fn with_let_management( + &mut self, + forbidden_let_reason: Option, + f: impl FnOnce(&mut Self, Option), + ) { + let old = mem::replace(&mut self.forbidden_let_reason, forbidden_let_reason); f(self, old); - self.is_let_allowed = old; + self.forbidden_let_reason = old; } /// Emits an error banning the `let` expression provided in the given location. - fn ban_let_expr(&self, expr: &'a Expr) { + fn ban_let_expr(&self, expr: &'a Expr, forbidden_let_reason: ForbiddenLetReason) { let sess = &self.session; if sess.opts.unstable_features.is_nightly_build() { - sess.struct_span_err(expr.span, "`let` expressions are not supported here") - .note("only supported directly in conditions of `if`- and `while`-expressions") - .note("as well as when nested within `&&` and parentheses in those conditions") - .emit(); + let err = "`let` expressions are not supported here"; + let mut diag = sess.struct_span_err(expr.span, err); + diag.note("only supported directly in conditions of `if` and `while` expressions"); + diag.note("as well as when nested within `&&` and parentheses in those conditions"); + if let ForbiddenLetReason::ForbiddenWithOr(span) = forbidden_let_reason { + diag.span_note(span, "`||` operators are not allowed in let chain expressions"); + } + diag.emit(); } else { sess.struct_span_err(expr.span, "expected expression, found statement (`let`)") .note("variable declaration using `let` is a statement") @@ -988,39 +996,48 @@ impl<'a> Visitor<'a> for AstValidator<'a> { } fn visit_expr(&mut self, expr: &'a Expr) { - self.with_let_allowed(false, |this, let_allowed| match &expr.kind { - ExprKind::If(cond, then, opt_else) => { - this.visit_block(then); - walk_list!(this, visit_expr, opt_else); - this.with_let_allowed(true, |this, _| this.visit_expr(cond)); - return; - } - ExprKind::Let(..) if !let_allowed => this.ban_let_expr(expr), - ExprKind::Match(expr, arms) => { - this.visit_expr(expr); - for arm in arms { - this.visit_expr(&arm.body); - this.visit_pat(&arm.pat); - walk_list!(this, visit_attribute, &arm.attrs); - if let Some(ref guard) = arm.guard { - if let ExprKind::Let(_, ref expr, _) = guard.kind { - this.with_let_allowed(true, |this, _| this.visit_expr(expr)); + self.with_let_management(Some(ForbiddenLetReason::GenericForbidden), |this, forbidden_let_reason| { + match &expr.kind { + ExprKind::Binary(Spanned { node: BinOpKind::Or, span }, lhs, rhs) => { + let forbidden_let_reason = Some(ForbiddenLetReason::ForbiddenWithOr(*span)); + this.with_let_management(forbidden_let_reason, |this, _| this.visit_expr(lhs)); + this.with_let_management(forbidden_let_reason, |this, _| this.visit_expr(rhs)); + } + ExprKind::If(cond, then, opt_else) => { + this.visit_block(then); + walk_list!(this, visit_expr, opt_else); + this.with_let_management(None, |this, _| this.visit_expr(cond)); + return; + } + ExprKind::Let(..) if let Some(elem) = forbidden_let_reason => { + this.ban_let_expr(expr, elem); + }, + ExprKind::Match(scrutinee, arms) => { + this.visit_expr(scrutinee); + for arm in arms { + this.visit_expr(&arm.body); + this.visit_pat(&arm.pat); + walk_list!(this, visit_attribute, &arm.attrs); + if let Some(guard) = &arm.guard && let ExprKind::Let(_, guard_expr, _) = &guard.kind { + this.with_let_management(None, |this, _| { + this.visit_expr(guard_expr) + }); return; } } } + ExprKind::Paren(_) | ExprKind::Binary(Spanned { node: BinOpKind::And, .. }, ..) => { + this.with_let_management(forbidden_let_reason, |this, _| visit::walk_expr(this, expr)); + return; + } + ExprKind::While(cond, then, opt_label) => { + walk_list!(this, visit_label, opt_label); + this.visit_block(then); + this.with_let_management(None, |this, _| this.visit_expr(cond)); + return; + } + _ => visit::walk_expr(this, expr), } - ExprKind::Paren(_) | ExprKind::Binary(Spanned { node: BinOpKind::And, .. }, ..) => { - this.with_let_allowed(let_allowed, |this, _| visit::walk_expr(this, expr)); - return; - } - ExprKind::While(cond, then, opt_label) => { - walk_list!(this, visit_label, opt_label); - this.visit_block(then); - this.with_let_allowed(true, |this, _| this.visit_expr(cond)); - return; - } - _ => visit::walk_expr(this, expr), }); } @@ -1772,10 +1789,19 @@ pub fn check_crate(session: &Session, krate: &Crate, lints: &mut LintBuffer) -> is_tilde_const_allowed: false, is_impl_trait_banned: false, is_assoc_ty_bound_banned: false, - is_let_allowed: false, + forbidden_let_reason: Some(ForbiddenLetReason::GenericForbidden), lint_buffer: lints, }; visit::walk_crate(&mut validator, krate); validator.has_proc_macro_decls } + +/// Used to forbid `let` expressions in certain syntactic locations. +#[derive(Clone, Copy)] +enum ForbiddenLetReason { + /// A let chain with the `||` operator + ForbiddenWithOr(Span), + /// `let` is not valid and the source environment is not important + GenericForbidden, +} diff --git a/compiler/rustc_ast_passes/src/lib.rs b/compiler/rustc_ast_passes/src/lib.rs index 876dd7f757c5a..9d52c32885db0 100644 --- a/compiler/rustc_ast_passes/src/lib.rs +++ b/compiler/rustc_ast_passes/src/lib.rs @@ -4,11 +4,13 @@ //! //! The crate also contains other misc AST visitors, e.g. `node_count` and `show_span`. -#![feature(iter_is_partitioned)] +#![allow(rustc::potential_query_instability)] #![feature(box_patterns)] +#![feature(if_let_guard)] +#![feature(iter_is_partitioned)] +#![feature(let_chains)] #![feature(let_else)] #![recursion_limit = "256"] -#![allow(rustc::potential_query_instability)] pub mod ast_validation; pub mod feature_gate; diff --git a/compiler/rustc_const_eval/src/interpret/eval_context.rs b/compiler/rustc_const_eval/src/interpret/eval_context.rs index 45ac3cd1f84a6..a8a57e6990ffd 100644 --- a/compiler/rustc_const_eval/src/interpret/eval_context.rs +++ b/compiler/rustc_const_eval/src/interpret/eval_context.rs @@ -164,7 +164,7 @@ pub enum StackPopCleanup { } /// State of a local variable including a memoized layout -#[derive(Clone, PartialEq, Eq, HashStable)] +#[derive(Clone, Debug, PartialEq, Eq, HashStable)] pub struct LocalState<'tcx, Tag: Provenance = AllocId> { pub value: LocalValue, /// Don't modify if `Some`, this is only used to prevent computing the layout twice @@ -714,6 +714,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { self.size_and_align_of(&mplace.meta, &mplace.layout) } + #[instrument(skip(self, body, return_place, return_to_block), level = "debug")] pub fn push_stack_frame( &mut self, instance: ty::Instance<'tcx>, @@ -721,6 +722,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { return_place: Option<&PlaceTy<'tcx, M::PointerTag>>, return_to_block: StackPopCleanup, ) -> InterpResult<'tcx> { + debug!("body: {:#?}", body); // first push a stack frame so we have access to the local substs let pre_frame = Frame { body, @@ -824,6 +826,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { /// `Drop` impls for any locals that have been initialized at this point. /// The cleanup block ends with a special `Resume` terminator, which will /// cause us to continue unwinding. + #[instrument(skip(self), level = "debug")] pub(super) fn pop_stack_frame(&mut self, unwinding: bool) -> InterpResult<'tcx> { info!( "popping stack frame ({})", @@ -876,6 +879,8 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { return Ok(()); } + debug!("locals: {:#?}", frame.locals); + // Cleanup: deallocate all locals that are backed by an allocation. for local in &frame.locals { self.deallocate_local(local.value)?; @@ -935,6 +940,7 @@ impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> { Ok(()) } + #[instrument(skip(self), level = "debug")] fn deallocate_local(&mut self, local: LocalValue) -> InterpResult<'tcx> { if let LocalValue::Live(Operand::Indirect(MemPlace { ptr, .. })) = local { // All locals have a backing allocation, even if the allocation is empty diff --git a/compiler/rustc_const_eval/src/interpret/intern.rs b/compiler/rustc_const_eval/src/interpret/intern.rs index bacb3f6ea7fec..fa8151a1f3c3d 100644 --- a/compiler/rustc_const_eval/src/interpret/intern.rs +++ b/compiler/rustc_const_eval/src/interpret/intern.rs @@ -359,6 +359,8 @@ pub fn intern_const_alloc_recursive< // pointers, ... So we can't intern them according to their type rules let mut todo: Vec<_> = leftover_allocations.iter().cloned().collect(); + debug!(?todo); + debug!("dead_alloc_map: {:#?}", ecx.memory.dead_alloc_map); while let Some(alloc_id) = todo.pop() { if let Some((_, mut alloc)) = ecx.memory.alloc_map.remove(&alloc_id) { // We can't call the `intern_shallow` method here, as its logic is tailored to safe diff --git a/compiler/rustc_const_eval/src/interpret/memory.rs b/compiler/rustc_const_eval/src/interpret/memory.rs index e100ebc4ccb28..5fe4672f17e5b 100644 --- a/compiler/rustc_const_eval/src/interpret/memory.rs +++ b/compiler/rustc_const_eval/src/interpret/memory.rs @@ -275,6 +275,7 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { Ok(new_ptr) } + #[instrument(skip(self), level = "debug")] pub fn deallocate( &mut self, ptr: Pointer>, @@ -305,6 +306,8 @@ impl<'mir, 'tcx, M: Machine<'mir, 'tcx>> Memory<'mir, 'tcx, M> { .into()); }; + debug!(?alloc); + if alloc.mutability == Mutability::Not { throw_ub_format!("deallocating immutable allocation {}", alloc_id); } diff --git a/compiler/rustc_expand/src/lib.rs b/compiler/rustc_expand/src/lib.rs index b024524aa2968..8a9efe01368e3 100644 --- a/compiler/rustc_expand/src/lib.rs +++ b/compiler/rustc_expand/src/lib.rs @@ -3,6 +3,7 @@ #![feature(crate_visibility_modifier)] #![feature(decl_macro)] #![feature(if_let_guard)] +#![feature(let_chains)] #![feature(let_else)] #![feature(proc_macro_diagnostic)] #![feature(proc_macro_internals)] diff --git a/compiler/rustc_expand/src/mbe.rs b/compiler/rustc_expand/src/mbe.rs index 5244ac36bba5d..3d4c77aba7339 100644 --- a/compiler/rustc_expand/src/mbe.rs +++ b/compiler/rustc_expand/src/mbe.rs @@ -6,17 +6,17 @@ crate mod macro_check; crate mod macro_parser; crate mod macro_rules; +crate mod metavar_expr; crate mod quoted; crate mod transcribe; +use metavar_expr::MetaVarExpr; use rustc_ast::token::{self, NonterminalKind, Token, TokenKind}; use rustc_ast::tokenstream::DelimSpan; - +use rustc_data_structures::sync::Lrc; use rustc_span::symbol::Ident; use rustc_span::Span; -use rustc_data_structures::sync::Lrc; - /// Contains the sub-token-trees of a "delimited" token tree, such as the contents of `(`. Note /// that the delimiter itself might be `NoDelim`. #[derive(Clone, PartialEq, Encodable, Decodable, Debug)] @@ -73,8 +73,8 @@ enum KleeneOp { ZeroOrOne, } -/// Similar to `tokenstream::TokenTree`, except that `$i`, `$i:ident`, and `$(...)` -/// are "first-class" token trees. Useful for parsing macros. +/// Similar to `tokenstream::TokenTree`, except that `$i`, `$i:ident`, `$(...)`, +/// and `${...}` are "first-class" token trees. Useful for parsing macros. #[derive(Debug, Clone, PartialEq, Encodable, Decodable)] enum TokenTree { Token(Token), @@ -85,6 +85,8 @@ enum TokenTree { MetaVar(Span, Ident), /// e.g., `$var:expr`. This is only used in the left hand side of MBE macros. MetaVarDecl(Span, Ident /* name to bind */, Option), + /// A meta-variable expression inside `${...}` + MetaVarExpr(DelimSpan, MetaVarExpr), } impl TokenTree { @@ -139,7 +141,9 @@ impl TokenTree { TokenTree::Token(Token { span, .. }) | TokenTree::MetaVar(span, _) | TokenTree::MetaVarDecl(span, _, _) => span, - TokenTree::Delimited(span, _) | TokenTree::Sequence(span, _) => span.entire(), + TokenTree::Delimited(span, _) + | TokenTree::MetaVarExpr(span, _) + | TokenTree::Sequence(span, _) => span.entire(), } } diff --git a/compiler/rustc_expand/src/mbe/macro_check.rs b/compiler/rustc_expand/src/mbe/macro_check.rs index 3497e5ad543a1..88e1169322093 100644 --- a/compiler/rustc_expand/src/mbe/macro_check.rs +++ b/compiler/rustc_expand/src/mbe/macro_check.rs @@ -278,6 +278,8 @@ fn check_binders( binders.insert(name, BinderInfo { span, ops: ops.into() }); } } + // `MetaVarExpr` can not appear in the LHS of a macro arm + TokenTree::MetaVarExpr(..) => {} TokenTree::Delimited(_, ref del) => { for tt in &del.tts { check_binders(sess, node_id, tt, macros, binders, ops, valid); @@ -335,6 +337,8 @@ fn check_occurrences( let name = MacroRulesNormalizedIdent::new(name); check_ops_is_prefix(sess, node_id, macros, binders, ops, span, name); } + // FIXME(c410-f3r) Check token (https://github.com/rust-lang/rust/issues/93902) + TokenTree::MetaVarExpr(..) => {} TokenTree::Delimited(_, ref del) => { check_nested_occurrences(sess, node_id, &del.tts, macros, binders, ops, valid); } diff --git a/compiler/rustc_expand/src/mbe/macro_parser.rs b/compiler/rustc_expand/src/mbe/macro_parser.rs index bb36dfd793d4a..04137086088dd 100644 --- a/compiler/rustc_expand/src/mbe/macro_parser.rs +++ b/compiler/rustc_expand/src/mbe/macro_parser.rs @@ -200,7 +200,7 @@ struct MatcherPos<'root, 'tt> { // This type is used a lot. Make sure it doesn't unintentionally get bigger. #[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))] -rustc_data_structures::static_assert_size!(MatcherPos<'_, '_>, 192); +rustc_data_structures::static_assert_size!(MatcherPos<'_, '_>, 240); impl<'root, 'tt> MatcherPos<'root, 'tt> { /// Generates the top-level matcher position in which the "dot" is before the first token of @@ -321,10 +321,13 @@ pub(super) fn count_names(ms: &[TokenTree]) -> usize { ms.iter().fold(0, |count, elt| { count + match *elt { - TokenTree::Sequence(_, ref seq) => seq.num_captures, TokenTree::Delimited(_, ref delim) => count_names(&delim.tts), TokenTree::MetaVar(..) => 0, TokenTree::MetaVarDecl(..) => 1, + // FIXME(c410-f3r) MetaVarExpr should be handled instead of being ignored + // https://github.com/rust-lang/rust/issues/9390 + TokenTree::MetaVarExpr(..) => 0, + TokenTree::Sequence(_, ref seq) => seq.num_captures, TokenTree::Token(..) => 0, } }) @@ -436,7 +439,9 @@ fn nameize>( } Occupied(..) => return Err((sp, format!("duplicated bind name: {}", bind_name))), }, - TokenTree::MetaVar(..) | TokenTree::Token(..) => (), + // FIXME(c410-f3r) MetaVar and MetaVarExpr should be handled instead of being ignored + // https://github.com/rust-lang/rust/issues/9390 + TokenTree::MetaVar(..) | TokenTree::MetaVarExpr(..) | TokenTree::Token(..) => {} } Ok(()) @@ -650,7 +655,7 @@ fn inner_parse_loop<'root, 'tt>( // rules. NOTE that this is not necessarily an error unless _all_ items in // `cur_items` end up doing this. There may still be some other matchers that do // end up working out. - TokenTree::Token(..) | TokenTree::MetaVar(..) => {} + TokenTree::Token(..) | TokenTree::MetaVar(..) | TokenTree::MetaVarExpr(..) => {} } } } diff --git a/compiler/rustc_expand/src/mbe/macro_rules.rs b/compiler/rustc_expand/src/mbe/macro_rules.rs index 62ea3aa76a46c..c3b1b34aa29b9 100644 --- a/compiler/rustc_expand/src/mbe/macro_rules.rs +++ b/compiler/rustc_expand/src/mbe/macro_rules.rs @@ -580,7 +580,10 @@ fn check_lhs_no_empty_seq(sess: &ParseSess, tts: &[mbe::TokenTree]) -> bool { use mbe::TokenTree; for tt in tts { match *tt { - TokenTree::Token(..) | TokenTree::MetaVar(..) | TokenTree::MetaVarDecl(..) => (), + TokenTree::Token(..) + | TokenTree::MetaVar(..) + | TokenTree::MetaVarDecl(..) + | TokenTree::MetaVarExpr(..) => (), TokenTree::Delimited(_, ref del) => { if !check_lhs_no_empty_seq(sess, &del.tts) { return false; @@ -669,7 +672,10 @@ impl FirstSets { let mut first = TokenSet::empty(); for tt in tts.iter().rev() { match *tt { - TokenTree::Token(..) | TokenTree::MetaVar(..) | TokenTree::MetaVarDecl(..) => { + TokenTree::Token(..) + | TokenTree::MetaVar(..) + | TokenTree::MetaVarDecl(..) + | TokenTree::MetaVarExpr(..) => { first.replace_with(tt.clone()); } TokenTree::Delimited(span, ref delimited) => { @@ -731,7 +737,10 @@ impl FirstSets { for tt in tts.iter() { assert!(first.maybe_empty); match *tt { - TokenTree::Token(..) | TokenTree::MetaVar(..) | TokenTree::MetaVarDecl(..) => { + TokenTree::Token(..) + | TokenTree::MetaVar(..) + | TokenTree::MetaVarDecl(..) + | TokenTree::MetaVarExpr(..) => { first.add_one(tt.clone()); return first; } @@ -907,7 +916,10 @@ fn check_matcher_core( // First, update `last` so that it corresponds to the set // of NT tokens that might end the sequence `... token`. match *token { - TokenTree::Token(..) | TokenTree::MetaVar(..) | TokenTree::MetaVarDecl(..) => { + TokenTree::Token(..) + | TokenTree::MetaVar(..) + | TokenTree::MetaVarDecl(..) + | TokenTree::MetaVarExpr(..) => { if token_can_be_followed_by_any(token) { // don't need to track tokens that work with any, last.replace_with_irrelevant(); diff --git a/compiler/rustc_expand/src/mbe/metavar_expr.rs b/compiler/rustc_expand/src/mbe/metavar_expr.rs new file mode 100644 index 0000000000000..6c5a755da6f40 --- /dev/null +++ b/compiler/rustc_expand/src/mbe/metavar_expr.rs @@ -0,0 +1,157 @@ +use rustc_ast::token; +use rustc_ast::tokenstream::{Cursor, TokenStream, TokenTree}; +use rustc_ast::{LitIntType, LitKind}; +use rustc_ast_pretty::pprust; +use rustc_errors::{Applicability, PResult}; +use rustc_session::parse::ParseSess; +use rustc_span::symbol::Ident; +use rustc_span::Span; + +/// A meta-variable expression, for expansions based on properties of meta-variables. +#[derive(Debug, Clone, PartialEq, Encodable, Decodable)] +crate enum MetaVarExpr { + /// The number of repetitions of an identifier, optionally limited to a number + /// of outer-most repetition depths. If the depth limit is `None` then the depth is unlimited. + Count(Ident, Option), + + /// Ignore a meta-variable for repetition without expansion. + Ignore(Ident), + + /// The index of the repetition at a particular depth, where 0 is the inner-most + /// repetition. The `usize` is the depth. + Index(usize), + + /// The length of the repetition at a particular depth, where 0 is the inner-most + /// repetition. The `usize` is the depth. + Length(usize), +} + +impl MetaVarExpr { + /// Attempt to parse a meta-variable expression from a token stream. + crate fn parse<'sess>( + input: &TokenStream, + outer_span: Span, + sess: &'sess ParseSess, + ) -> PResult<'sess, MetaVarExpr> { + let mut tts = input.trees(); + let ident = parse_ident(&mut tts, sess, outer_span)?; + let Some(TokenTree::Delimited(_, token::Paren, args)) = tts.next() else { + let msg = "meta-variable expression parameter must be wrapped in parentheses"; + return Err(sess.span_diagnostic.struct_span_err(ident.span, msg)); + }; + check_trailing_token(&mut tts, sess)?; + let mut iter = args.trees(); + let rslt = match &*ident.as_str() { + "count" => parse_count(&mut iter, sess, ident.span)?, + "ignore" => MetaVarExpr::Ignore(parse_ident(&mut iter, sess, ident.span)?), + "index" => MetaVarExpr::Index(parse_depth(&mut iter, sess, ident.span)?), + "length" => MetaVarExpr::Length(parse_depth(&mut iter, sess, ident.span)?), + _ => { + let err_msg = "unrecognized meta-variable expression"; + let mut err = sess.span_diagnostic.struct_span_err(ident.span, err_msg); + err.span_suggestion( + ident.span, + "supported expressions are count, ignore, index and length", + String::new(), + Applicability::MachineApplicable, + ); + return Err(err); + } + }; + check_trailing_token(&mut iter, sess)?; + Ok(rslt) + } + + crate fn ident(&self) -> Option<&Ident> { + match self { + MetaVarExpr::Count(ident, _) | MetaVarExpr::Ignore(ident) => Some(&ident), + MetaVarExpr::Index(..) | MetaVarExpr::Length(..) => None, + } + } +} + +// Checks if there are any remaining tokens. For example, `${ignore(ident ... a b c ...)}` +fn check_trailing_token<'sess>(iter: &mut Cursor, sess: &'sess ParseSess) -> PResult<'sess, ()> { + if let Some(tt) = iter.next() { + let mut diag = sess.span_diagnostic.struct_span_err( + tt.span(), + &format!("unexpected token: {}", pprust::tt_to_string(&tt)), + ); + diag.span_note(tt.span(), "meta-variable expression must not have trailing tokens"); + Err(diag) + } else { + Ok(()) + } +} + +/// Parse a meta-variable `count` expression: `count(ident[, depth])` +fn parse_count<'sess>( + iter: &mut Cursor, + sess: &'sess ParseSess, + span: Span, +) -> PResult<'sess, MetaVarExpr> { + let ident = parse_ident(iter, sess, span)?; + let depth = if try_eat_comma(iter) { Some(parse_depth(iter, sess, span)?) } else { None }; + Ok(MetaVarExpr::Count(ident, depth)) +} + +/// Parses the depth used by index(depth) and length(depth). +fn parse_depth<'sess>( + iter: &mut Cursor, + sess: &'sess ParseSess, + span: Span, +) -> PResult<'sess, usize> { + let Some(tt) = iter.next() else { return Ok(0) }; + let TokenTree::Token(token::Token { + kind: token::TokenKind::Literal(lit), .. + }) = tt else { + return Err(sess.span_diagnostic.struct_span_err( + span, + "meta-variable expression depth must be a literal" + )); + }; + if let Ok(lit_kind) = LitKind::from_lit_token(lit) + && let LitKind::Int(n_u128, LitIntType::Unsuffixed) = lit_kind + && let Ok(n_usize) = usize::try_from(n_u128) + { + Ok(n_usize) + } + else { + let msg = "only unsuffixes integer literals are supported in meta-variable expressions"; + Err(sess.span_diagnostic.struct_span_err(span, msg)) + } +} + +/// Parses an generic ident +fn parse_ident<'sess>( + iter: &mut Cursor, + sess: &'sess ParseSess, + span: Span, +) -> PResult<'sess, Ident> { + let err_fn = |msg| sess.span_diagnostic.struct_span_err(span, msg); + if let Some(tt) = iter.next() && let TokenTree::Token(token) = tt { + if let Some((elem, false)) = token.ident() { + return Ok(elem); + } + let token_str = pprust::token_to_string(&token); + let mut err = err_fn(&format!("expected identifier, found `{}`", &token_str)); + err.span_suggestion( + token.span, + &format!("try removing `{}`", &token_str), + String::new(), + Applicability::MaybeIncorrect, + ); + return Err(err); + } + Err(err_fn("expected identifier")) +} + +/// Tries to move the iterator forward returning `true` if there is a comma. If not, then the +/// iterator is not modified and the result is `false`. +fn try_eat_comma(iter: &mut Cursor) -> bool { + if let Some(TokenTree::Token(token::Token { kind: token::Comma, .. })) = iter.look_ahead(0) { + let _ = iter.next(); + return true; + } + false +} diff --git a/compiler/rustc_expand/src/mbe/quoted.rs b/compiler/rustc_expand/src/mbe/quoted.rs index dedc6c618b9a4..12c5dac9e0bf4 100644 --- a/compiler/rustc_expand/src/mbe/quoted.rs +++ b/compiler/rustc_expand/src/mbe/quoted.rs @@ -1,13 +1,13 @@ use crate::mbe::macro_parser; -use crate::mbe::{Delimited, KleeneOp, KleeneToken, SequenceRepetition, TokenTree}; +use crate::mbe::{Delimited, KleeneOp, KleeneToken, MetaVarExpr, SequenceRepetition, TokenTree}; use rustc_ast::token::{self, Token}; use rustc_ast::tokenstream; use rustc_ast::{NodeId, DUMMY_NODE_ID}; use rustc_ast_pretty::pprust; use rustc_feature::Features; -use rustc_session::parse::ParseSess; -use rustc_span::symbol::{kw, Ident}; +use rustc_session::parse::{feature_err, ParseSess}; +use rustc_span::symbol::{kw, sym, Ident}; use rustc_span::edition::Edition; use rustc_span::{Span, SyntaxContext}; @@ -25,22 +25,22 @@ const VALID_FRAGMENT_NAMES_MSG: &str = "valid fragment specifiers are \ /// # Parameters /// /// - `input`: a token stream to read from, the contents of which we are parsing. -/// - `expect_matchers`: `parse` can be used to parse either the "patterns" or the "body" of a -/// macro. Both take roughly the same form _except_ that in a pattern, metavars are declared with -/// their "matcher" type. For example `$var:expr` or `$id:ident`. In this example, `expr` and -/// `ident` are "matchers". They are not present in the body of a macro rule -- just in the -/// pattern, so we pass a parameter to indicate whether to expect them or not. +/// - `parsing_patterns`: `parse` can be used to parse either the "patterns" or the "body" of a +/// macro. Both take roughly the same form _except_ that: +/// - In a pattern, metavars are declared with their "matcher" type. For example `$var:expr` or +/// `$id:ident`. In this example, `expr` and `ident` are "matchers". They are not present in the +/// body of a macro rule -- just in the pattern. +/// - Metavariable expressions are only valid in the "body", not the "pattern". /// - `sess`: the parsing session. Any errors will be emitted to this session. /// - `node_id`: the NodeId of the macro we are parsing. /// - `features`: language features so we can do feature gating. -/// - `edition`: the edition of the crate defining the macro /// /// # Returns /// /// A collection of `self::TokenTree`. There may also be some errors emitted to `sess`. pub(super) fn parse( input: tokenstream::TokenStream, - expect_matchers: bool, + parsing_patterns: bool, sess: &ParseSess, node_id: NodeId, features: &Features, @@ -55,9 +55,9 @@ pub(super) fn parse( while let Some(tree) = trees.next() { // Given the parsed tree, if there is a metavar and we are expecting matchers, actually // parse out the matcher (i.e., in `$id:ident` this would parse the `:` and `ident`). - let tree = parse_tree(tree, &mut trees, expect_matchers, sess, node_id, features, edition); + let tree = parse_tree(tree, &mut trees, parsing_patterns, sess, node_id, features, edition); match tree { - TokenTree::MetaVar(start_sp, ident) if expect_matchers => { + TokenTree::MetaVar(start_sp, ident) if parsing_patterns => { let span = match trees.next() { Some(tokenstream::TokenTree::Token(Token { kind: token::Colon, span })) => { match trees.next() { @@ -118,6 +118,14 @@ pub(super) fn parse( result } +/// Asks for the `macro_metavar_expr` feature if it is not already declared +fn maybe_emit_macro_metavar_expr_feature(features: &Features, sess: &ParseSess, span: Span) { + if !features.macro_metavar_expr { + let msg = "meta-variable expressions are unstable"; + feature_err(&sess, sym::macro_metavar_expr, span, msg).emit(); + } +} + /// Takes a `tokenstream::TokenTree` and returns a `self::TokenTree`. Specifically, this takes a /// generic `TokenTree`, such as is used in the rest of the compiler, and returns a `TokenTree` /// for use in parsing a macro. @@ -129,14 +137,13 @@ pub(super) fn parse( /// - `tree`: the tree we wish to convert. /// - `outer_trees`: an iterator over trees. We may need to read more tokens from it in order to finish /// converting `tree` -/// - `expect_matchers`: same as for `parse` (see above). +/// - `parsing_patterns`: same as [parse]. /// - `sess`: the parsing session. Any errors will be emitted to this session. /// - `features`: language features so we can do feature gating. -/// - `edition` - the edition of the crate defining the macro fn parse_tree( tree: tokenstream::TokenTree, outer_trees: &mut impl Iterator, - expect_matchers: bool, + parsing_patterns: bool, sess: &ParseSess, node_id: NodeId, features: &Features, @@ -158,24 +165,57 @@ fn parse_tree( } match next { - // `tree` is followed by a delimited set of token trees. This indicates the beginning - // of a repetition sequence in the macro (e.g. `$(pat)*`). - Some(tokenstream::TokenTree::Delimited(span, delim, tts)) => { - // Must have `(` not `{` or `[` - if delim != token::Paren { - let tok = pprust::token_kind_to_string(&token::OpenDelim(delim)); - let msg = format!("expected `(`, found `{}`", tok); - sess.span_diagnostic.span_err(span.entire(), &msg); + // `tree` is followed by a delimited set of token trees. + Some(tokenstream::TokenTree::Delimited(delim_span, delim, tts)) => { + if parsing_patterns { + if delim != token::Paren { + span_dollar_dollar_or_metavar_in_the_lhs_err( + sess, + &Token { kind: token::OpenDelim(delim), span: delim_span.entire() }, + ); + } + } else { + match delim { + token::Brace => { + // The delimiter is `{`. This indicates the beginning + // of a meta-variable expression (e.g. `${count(ident)}`). + // Try to parse the meta-variable expression. + match MetaVarExpr::parse(&tts, delim_span.entire(), sess) { + Err(mut err) => { + err.emit(); + // Returns early the same read `$` to avoid spanning + // unrelated diagnostics that could be performed afterwards + return TokenTree::token(token::Dollar, span); + } + Ok(elem) => { + maybe_emit_macro_metavar_expr_feature( + features, + sess, + delim_span.entire(), + ); + return TokenTree::MetaVarExpr(delim_span, elem); + } + } + } + token::Paren => {} + _ => { + let tok = pprust::token_kind_to_string(&token::OpenDelim(delim)); + let msg = format!("expected `(` or `{{`, found `{}`", tok); + sess.span_diagnostic.span_err(delim_span.entire(), &msg); + } + } } - // Parse the contents of the sequence itself - let sequence = parse(tts, expect_matchers, sess, node_id, features, edition); + // If we didn't find a metavar expression above, then we must have a + // repetition sequence in the macro (e.g. `$(pat)*`). Parse the + // contents of the sequence itself + let sequence = parse(tts, parsing_patterns, sess, node_id, features, edition); // Get the Kleene operator and optional separator let (separator, kleene) = - parse_sep_and_kleene_op(&mut trees, span.entire(), sess); + parse_sep_and_kleene_op(&mut trees, delim_span.entire(), sess); // Count the number of captured "names" (i.e., named metavars) let name_captures = macro_parser::count_names(&sequence); TokenTree::Sequence( - span, + delim_span, Lrc::new(SequenceRepetition { tts: sequence, separator, @@ -197,7 +237,20 @@ fn parse_tree( } } - // `tree` is followed by a random token. This is an error. + // `tree` is followed by another `$`. This is an escaped `$`. + Some(tokenstream::TokenTree::Token(Token { kind: token::Dollar, span })) => { + if parsing_patterns { + span_dollar_dollar_or_metavar_in_the_lhs_err( + sess, + &Token { kind: token::Dollar, span }, + ); + } else { + maybe_emit_macro_metavar_expr_feature(features, sess, span); + } + TokenTree::token(token::Dollar, span) + } + + // `tree` is followed by some other token. This is an error. Some(tokenstream::TokenTree::Token(token)) => { let msg = format!( "expected identifier, found `{}`", @@ -221,7 +274,7 @@ fn parse_tree( span, Lrc::new(Delimited { delim, - tts: parse(tts, expect_matchers, sess, node_id, features, edition), + tts: parse(tts, parsing_patterns, sess, node_id, features, edition), }), ), } @@ -309,3 +362,15 @@ fn parse_sep_and_kleene_op( // Return a dummy (None, KleeneToken::new(KleeneOp::ZeroOrMore, span)) } + +// `$$` or a meta-variable is the lhs of a macro but shouldn't. +// +// For example, `macro_rules! foo { ( ${length()} ) => {} }` +fn span_dollar_dollar_or_metavar_in_the_lhs_err<'sess>(sess: &'sess ParseSess, token: &Token) { + sess.span_diagnostic + .span_err(token.span, &format!("unexpected token: {}", pprust::token_to_string(token))); + sess.span_diagnostic.span_note_without_error( + token.span, + "`$$` and meta-variable expressions are not allowed inside macro parameter definitions", + ); +} diff --git a/compiler/rustc_expand/src/mbe/transcribe.rs b/compiler/rustc_expand/src/mbe/transcribe.rs index 760dea77f9c2b..b8d8394754134 100644 --- a/compiler/rustc_expand/src/mbe/transcribe.rs +++ b/compiler/rustc_expand/src/mbe/transcribe.rs @@ -255,6 +255,11 @@ pub(super) fn transcribe<'a>( } } + // Replace meta-variable expressions with the result of their expansion. + mbe::TokenTree::MetaVarExpr(sp, expr) => { + transcribe_metavar_expr(cx, expr, interp, &repeats, &mut result, &sp)?; + } + // If we are entering a new delimiter, we push its contents to the `stack` to be // processed, and we push all of the currently produced results to the `result_stack`. // We will produce all of the results of the inside of the `Delimited` and then we will @@ -391,6 +396,28 @@ fn lockstep_iter_size( _ => LockstepIterSize::Unconstrained, } } + TokenTree::MetaVarExpr(_, ref expr) => { + let default_rslt = LockstepIterSize::Unconstrained; + let Some(ident) = expr.ident() else { return default_rslt; }; + let name = MacroRulesNormalizedIdent::new(ident.clone()); + match lookup_cur_matched(name, interpolations, repeats) { + Some(MatchedSeq(ref ads)) => { + default_rslt.with(LockstepIterSize::Constraint(ads.len(), name)) + } + _ => default_rslt, + } + } TokenTree::Token(..) => LockstepIterSize::Unconstrained, } } + +fn transcribe_metavar_expr<'a>( + _cx: &ExtCtxt<'a>, + _expr: mbe::MetaVarExpr, + _interp: &FxHashMap, + _repeats: &[(usize, usize)], + _result: &mut Vec, + _sp: &DelimSpan, +) -> PResult<'a, ()> { + Ok(()) +} diff --git a/compiler/rustc_feature/src/active.rs b/compiler/rustc_feature/src/active.rs index a69d28b184aed..a91f05587b277 100644 --- a/compiler/rustc_feature/src/active.rs +++ b/compiler/rustc_feature/src/active.rs @@ -426,6 +426,8 @@ declare_features! ( (active, link_cfg, "1.14.0", Some(37406), None), /// Allows using `reason` in lint attributes and the `#[expect(lint)]` lint check. (active, lint_reasons, "1.31.0", Some(54503), None), + /// Give access to additional metadata about declarative macro meta-variables. + (active, macro_metavar_expr, "1.61.0", Some(83527), None), /// Allows `#[marker]` on certain traits allowing overlapping implementations. (active, marker_trait_attr, "1.30.0", Some(29864), None), /// A minimal, sound subset of specialization intended to be used by the diff --git a/compiler/rustc_middle/src/mir/mod.rs b/compiler/rustc_middle/src/mir/mod.rs index aa061232d7e3c..dbc0c84695842 100644 --- a/compiler/rustc_middle/src/mir/mod.rs +++ b/compiler/rustc_middle/src/mir/mod.rs @@ -2554,7 +2554,14 @@ impl<'tcx> Constant<'tcx> { impl<'tcx> From> for ConstantKind<'tcx> { #[inline] fn from(ct: ty::Const<'tcx>) -> Self { - Self::Ty(ct) + match ct.val() { + ty::ConstKind::Value(cv) => { + // FIXME Once valtrees are introduced we need to convert those + // into `ConstValue` instances here + Self::Val(cv, ct.ty()) + } + _ => Self::Ty(ct), + } } } @@ -2635,6 +2642,27 @@ impl<'tcx> ConstantKind<'tcx> { Self::Val(val, _) => val.try_to_machine_usize(tcx), } } + + pub fn from_bool(tcx: TyCtxt<'tcx>, v: bool) -> Self { + let cv = ConstValue::from_bool(v); + Self::Val(cv, tcx.types.bool) + } + + pub fn from_zero_sized(ty: Ty<'tcx>) -> Self { + let cv = ConstValue::Scalar(Scalar::ZST); + Self::Val(cv, ty) + } + + pub fn from_usize(tcx: TyCtxt<'tcx>, n: u64) -> Self { + let ty = tcx.types.usize; + let size = tcx + .layout_of(ty::ParamEnv::empty().and(ty)) + .unwrap_or_else(|e| bug!("could not compute layout for {:?}: {:?}", ty, e)) + .size; + let cv = ConstValue::Scalar(Scalar::from_uint(n as u128, size)); + + Self::Val(cv, ty) + } } /// A collection of projections into user types. diff --git a/compiler/rustc_mir_build/src/build/cfg.rs b/compiler/rustc_mir_build/src/build/cfg.rs index f08c6405af17f..dca9a63120c32 100644 --- a/compiler/rustc_mir_build/src/build/cfg.rs +++ b/compiler/rustc_mir_build/src/build/cfg.rs @@ -2,7 +2,7 @@ use crate::build::CFG; use rustc_middle::mir::*; -use rustc_middle::ty::{self, TyCtxt}; +use rustc_middle::ty::TyCtxt; impl<'tcx> CFG<'tcx> { crate fn block_data(&self, blk: BasicBlock) -> &BasicBlockData<'tcx> { @@ -73,7 +73,7 @@ impl<'tcx> CFG<'tcx> { Rvalue::Use(Operand::Constant(Box::new(Constant { span: source_info.span, user_ty: None, - literal: ty::Const::zero_sized(tcx, tcx.types.unit).into(), + literal: ConstantKind::from_zero_sized(tcx.types.unit), }))), ); } diff --git a/compiler/rustc_mir_build/src/build/expr/into.rs b/compiler/rustc_mir_build/src/build/expr/into.rs index c706e6ef1d466..9c7c7203f47de 100644 --- a/compiler/rustc_mir_build/src/build/expr/into.rs +++ b/compiler/rustc_mir_build/src/build/expr/into.rs @@ -9,7 +9,7 @@ use rustc_hir as hir; use rustc_index::vec::Idx; use rustc_middle::mir::*; use rustc_middle::thir::*; -use rustc_middle::ty::{self, CanonicalUserTypeAnnotation}; +use rustc_middle::ty::CanonicalUserTypeAnnotation; use std::iter; impl<'a, 'tcx> Builder<'a, 'tcx> { @@ -107,7 +107,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { Constant { span: expr_span, user_ty: None, - literal: ty::Const::from_bool(this.tcx, true).into(), + literal: ConstantKind::from_bool(this.tcx, true), }, ); @@ -118,7 +118,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { Constant { span: expr_span, user_ty: None, - literal: ty::Const::from_bool(this.tcx, false).into(), + literal: ConstantKind::from_bool(this.tcx, false), }, ); @@ -183,8 +183,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { span: expr_span, user_ty: None, literal: match op { - LogicalOp::And => ty::Const::from_bool(this.tcx, false).into(), - LogicalOp::Or => ty::Const::from_bool(this.tcx, true).into(), + LogicalOp::And => ConstantKind::from_bool(this.tcx, false), + LogicalOp::Or => ConstantKind::from_bool(this.tcx, true), }, }, ); diff --git a/compiler/rustc_mir_build/src/build/misc.rs b/compiler/rustc_mir_build/src/build/misc.rs index fd5914460140a..3516eca1c1469 100644 --- a/compiler/rustc_mir_build/src/build/misc.rs +++ b/compiler/rustc_mir_build/src/build/misc.rs @@ -54,7 +54,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> { Constant { span: source_info.span, user_ty: None, - literal: ty::Const::from_usize(self.tcx, value).into(), + literal: ConstantKind::from_usize(self.tcx, value), }, ); temp diff --git a/compiler/rustc_mir_build/src/thir/cx/expr.rs b/compiler/rustc_mir_build/src/thir/cx/expr.rs index 5a7e1d88dd03f..dc11c76e07b23 100644 --- a/compiler/rustc_mir_build/src/thir/cx/expr.rs +++ b/compiler/rustc_mir_build/src/thir/cx/expr.rs @@ -569,6 +569,9 @@ impl<'tcx> Cx<'tcx> { hir::ExprKind::ConstBlock(ref anon_const) => { let anon_const_def_id = self.tcx.hir().local_def_id(anon_const.hir_id); + + // FIXME Do we want to use `from_inline_const` once valtrees + // are introduced? This would create `ValTree`s that will never be used... let value = ty::Const::from_inline_const(self.tcx, anon_const_def_id); ExprKind::ConstBlock { value } diff --git a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs index b80d2e52ee709..c94da838680e0 100644 --- a/compiler/rustc_mir_build/src/thir/pattern/check_match.rs +++ b/compiler/rustc_mir_build/src/thir/pattern/check_match.rs @@ -7,7 +7,8 @@ use super::{PatCtxt, PatternError}; use rustc_arena::TypedArena; use rustc_ast::Mutability; use rustc_errors::{ - error_code, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed, + error_code, pluralize, struct_span_err, Applicability, Diagnostic, DiagnosticBuilder, + ErrorGuaranteed, }; use rustc_hir as hir; use rustc_hir::def::*; @@ -20,7 +21,7 @@ use rustc_session::lint::builtin::{ }; use rustc_session::Session; use rustc_span::source_map::Spanned; -use rustc_span::{DesugaringKind, ExpnKind, MultiSpan, Span}; +use rustc_span::{BytePos, DesugaringKind, ExpnKind, MultiSpan, Span}; crate fn check_match(tcx: TyCtxt<'_>, def_id: DefId) { let body_id = match def_id.as_local() { @@ -241,6 +242,9 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> { } let joined_patterns = joined_uncovered_patterns(&cx, &witnesses); + + let mut bindings = vec![]; + let mut err = struct_span_err!( self.tcx.sess, pat.span, @@ -257,6 +261,16 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> { false } _ => { + pat.walk(&mut |pat: &hir::Pat<'_>| { + match pat.kind { + hir::PatKind::Binding(_, _, ident, _) => { + bindings.push(ident); + } + _ => {} + } + true + }); + err.span_label(pat.span, pattern_not_covered_label(&witnesses, &joined_patterns)); true } @@ -267,13 +281,71 @@ impl<'p, 'tcx> MatchVisitor<'_, 'p, 'tcx> { "`let` bindings require an \"irrefutable pattern\", like a `struct` or \ an `enum` with only one variant", ); - if let Ok(snippet) = self.tcx.sess.source_map().span_to_snippet(span) { - err.span_suggestion( - span, - "you might want to use `if let` to ignore the variant that isn't matched", - format!("if {} {{ /* */ }}", &snippet[..snippet.len() - 1]), + if self.tcx.sess.source_map().span_to_snippet(span).is_ok() { + let semi_span = span.shrink_to_hi().with_lo(span.hi() - BytePos(1)); + let start_span = span.shrink_to_lo(); + let end_span = semi_span.shrink_to_lo(); + err.multipart_suggestion( + &format!( + "you might want to use `if let` to ignore the variant{} that {} matched", + pluralize!(witnesses.len()), + match witnesses.len() { + 1 => "isn't", + _ => "aren't", + }, + ), + vec![ + match &bindings[..] { + [] => (start_span, "if ".to_string()), + [binding] => (start_span, format!("let {} = if ", binding)), + bindings => ( + start_span, + format!( + "let ({}) = if ", + bindings + .iter() + .map(|ident| ident.to_string()) + .collect::>() + .join(", ") + ), + ), + }, + match &bindings[..] { + [] => (semi_span, " { todo!() }".to_string()), + [binding] => { + (end_span, format!(" {{ {} }} else {{ todo!() }}", binding)) + } + bindings => ( + end_span, + format!( + " {{ ({}) }} else {{ todo!() }}", + bindings + .iter() + .map(|ident| ident.to_string()) + .collect::>() + .join(", ") + ), + ), + }, + ], Applicability::HasPlaceholders, ); + if !bindings.is_empty() && cx.tcx.sess.is_nightly_build() { + err.span_suggestion_verbose( + semi_span.shrink_to_lo(), + &format!( + "alternatively, on nightly, you might want to use \ + `#![feature(let_else)]` to handle the variant{} that {} matched", + pluralize!(witnesses.len()), + match witnesses.len() { + 1 => "isn't", + _ => "aren't", + }, + ), + " else { todo!() }".to_string(), + Applicability::HasPlaceholders, + ); + } } err.note( "for more information, visit \ diff --git a/compiler/rustc_session/src/config.rs b/compiler/rustc_session/src/config.rs index 221dc86c1d46d..1d36ff059de81 100644 --- a/compiler/rustc_session/src/config.rs +++ b/compiler/rustc_session/src/config.rs @@ -1072,6 +1072,7 @@ impl CrateCheckConfig { // NOTE: This should be kept in sync with `default_configuration` and // `fill_well_known_values` const WELL_KNOWN_NAMES: &[Symbol] = &[ + // rustc sym::unix, sym::windows, sym::target_os, @@ -1091,9 +1092,12 @@ impl CrateCheckConfig { sym::debug_assertions, sym::proc_macro, sym::test, + sym::feature, + // rustdoc sym::doc, sym::doctest, - sym::feature, + // miri + sym::miri, ]; // We only insert well-known names if `names()` was activated @@ -1128,13 +1132,14 @@ impl CrateCheckConfig { // No-values for name in [ + sym::doc, + sym::miri, sym::unix, - sym::windows, - sym::debug_assertions, - sym::proc_macro, sym::test, - sym::doc, sym::doctest, + sym::windows, + sym::proc_macro, + sym::debug_assertions, sym::target_thread_local, ] { self.values_valid.entry(name).or_default(); diff --git a/compiler/rustc_span/src/symbol.rs b/compiler/rustc_span/src/symbol.rs index 3f44292e03425..b042e801fe5c4 100644 --- a/compiler/rustc_span/src/symbol.rs +++ b/compiler/rustc_span/src/symbol.rs @@ -845,6 +845,7 @@ symbols! { macro_export, macro_lifetime_matcher, macro_literal_matcher, + macro_metavar_expr, macro_reexport, macro_use, macro_vis_matcher, @@ -891,6 +892,7 @@ symbols! { minnumf32, minnumf64, mips_target_feature, + miri, misc, mmx_reg, modifiers, diff --git a/compiler/rustc_trait_selection/src/traits/query/normalize.rs b/compiler/rustc_trait_selection/src/traits/query/normalize.rs index 6a2bd9ce1ea91..ed0ad5601aa40 100644 --- a/compiler/rustc_trait_selection/src/traits/query/normalize.rs +++ b/compiler/rustc_trait_selection/src/traits/query/normalize.rs @@ -334,6 +334,21 @@ impl<'cx, 'tcx> FallibleTypeFolder<'tcx> for QueryNormalizer<'cx, 'tcx> { &mut self, constant: mir::ConstantKind<'tcx>, ) -> Result, Self::Error> { - constant.try_super_fold_with(self) + let constant_kind = match constant { + mir::ConstantKind::Ty(c) => { + let const_folded = c.try_fold_with(self)?; + match const_folded.val() { + ty::ConstKind::Value(cv) => { + // FIXME With Valtrees we need to convert `cv: ValTree` + // to a `ConstValue` here. + mir::ConstantKind::Val(cv, const_folded.ty()) + } + _ => mir::ConstantKind::Ty(const_folded), + } + } + mir::ConstantKind::Val(_, _) => constant.try_super_fold_with(self)?, + }; + + Ok(constant_kind) } } diff --git a/library/alloc/src/collections/vec_deque/iter.rs b/library/alloc/src/collections/vec_deque/iter.rs index edadd666edce6..253fa0b561e95 100644 --- a/library/alloc/src/collections/vec_deque/iter.rs +++ b/library/alloc/src/collections/vec_deque/iter.rs @@ -1,5 +1,6 @@ use core::fmt; use core::iter::{FusedIterator, TrustedLen, TrustedRandomAccess, TrustedRandomAccessNoCoerce}; +use core::mem::MaybeUninit; use core::ops::Try; use super::{count, wrap_index, RingSlices}; @@ -12,7 +13,7 @@ use super::{count, wrap_index, RingSlices}; /// [`iter`]: super::VecDeque::iter #[stable(feature = "rust1", since = "1.0.0")] pub struct Iter<'a, T: 'a> { - pub(crate) ring: &'a [T], + pub(crate) ring: &'a [MaybeUninit], pub(crate) tail: usize, pub(crate) head: usize, } @@ -44,7 +45,10 @@ impl<'a, T> Iterator for Iter<'a, T> { } let tail = self.tail; self.tail = wrap_index(self.tail.wrapping_add(1), self.ring.len()); - unsafe { Some(self.ring.get_unchecked(tail)) } + // Safety: + // - `self.tail` in a ring buffer is always a valid index. + // - `self.head` and `self.tail` equality is checked above. + unsafe { Some(self.ring.get_unchecked(tail).assume_init_ref()) } } #[inline] @@ -58,8 +62,13 @@ impl<'a, T> Iterator for Iter<'a, T> { F: FnMut(Acc, Self::Item) -> Acc, { let (front, back) = RingSlices::ring_slices(self.ring, self.head, self.tail); - accum = front.iter().fold(accum, &mut f); - back.iter().fold(accum, &mut f) + // Safety: + // - `self.head` and `self.tail` in a ring buffer are always valid indices. + // - `RingSlices::ring_slices` guarantees that the slices split according to `self.head` and `self.tail` are initialized. + unsafe { + accum = MaybeUninit::slice_assume_init_ref(front).iter().fold(accum, &mut f); + MaybeUninit::slice_assume_init_ref(back).iter().fold(accum, &mut f) + } } fn try_fold(&mut self, init: B, mut f: F) -> R @@ -70,17 +79,19 @@ impl<'a, T> Iterator for Iter<'a, T> { { let (mut iter, final_res); if self.tail <= self.head { - // single slice self.ring[self.tail..self.head] - iter = self.ring[self.tail..self.head].iter(); + // Safety: single slice self.ring[self.tail..self.head] is initialized. + iter = unsafe { MaybeUninit::slice_assume_init_ref(&self.ring[self.tail..self.head]) } + .iter(); final_res = iter.try_fold(init, &mut f); } else { - // two slices: self.ring[self.tail..], self.ring[..self.head] + // Safety: two slices: self.ring[self.tail..], self.ring[..self.head] both are initialized. let (front, back) = self.ring.split_at(self.tail); - let mut back_iter = back.iter(); + + let mut back_iter = unsafe { MaybeUninit::slice_assume_init_ref(back).iter() }; let res = back_iter.try_fold(init, &mut f); let len = self.ring.len(); self.tail = (self.ring.len() - back_iter.len()) & (len - 1); - iter = front[..self.head].iter(); + iter = unsafe { MaybeUninit::slice_assume_init_ref(&front[..self.head]).iter() }; final_res = iter.try_fold(res?, &mut f); } self.tail = self.head - iter.len(); @@ -109,7 +120,7 @@ impl<'a, T> Iterator for Iter<'a, T> { // that is in bounds. unsafe { let idx = wrap_index(self.tail.wrapping_add(idx), self.ring.len()); - self.ring.get_unchecked(idx) + self.ring.get_unchecked(idx).assume_init_ref() } } } @@ -122,7 +133,10 @@ impl<'a, T> DoubleEndedIterator for Iter<'a, T> { return None; } self.head = wrap_index(self.head.wrapping_sub(1), self.ring.len()); - unsafe { Some(self.ring.get_unchecked(self.head)) } + // Safety: + // - `self.head` in a ring buffer is always a valid index. + // - `self.head` and `self.tail` equality is checked above. + unsafe { Some(self.ring.get_unchecked(self.head).assume_init_ref()) } } fn rfold(self, mut accum: Acc, mut f: F) -> Acc @@ -130,8 +144,13 @@ impl<'a, T> DoubleEndedIterator for Iter<'a, T> { F: FnMut(Acc, Self::Item) -> Acc, { let (front, back) = RingSlices::ring_slices(self.ring, self.head, self.tail); - accum = back.iter().rfold(accum, &mut f); - front.iter().rfold(accum, &mut f) + // Safety: + // - `self.head` and `self.tail` in a ring buffer are always valid indices. + // - `RingSlices::ring_slices` guarantees that the slices split according to `self.head` and `self.tail` are initialized. + unsafe { + accum = MaybeUninit::slice_assume_init_ref(back).iter().rfold(accum, &mut f); + MaybeUninit::slice_assume_init_ref(front).iter().rfold(accum, &mut f) + } } fn try_rfold(&mut self, init: B, mut f: F) -> R @@ -142,16 +161,20 @@ impl<'a, T> DoubleEndedIterator for Iter<'a, T> { { let (mut iter, final_res); if self.tail <= self.head { - // single slice self.ring[self.tail..self.head] - iter = self.ring[self.tail..self.head].iter(); + // Safety: single slice self.ring[self.tail..self.head] is initialized. + iter = unsafe { + MaybeUninit::slice_assume_init_ref(&self.ring[self.tail..self.head]).iter() + }; final_res = iter.try_rfold(init, &mut f); } else { - // two slices: self.ring[self.tail..], self.ring[..self.head] + // Safety: two slices: self.ring[self.tail..], self.ring[..self.head] both are initialized. let (front, back) = self.ring.split_at(self.tail); - let mut front_iter = front[..self.head].iter(); + + let mut front_iter = + unsafe { MaybeUninit::slice_assume_init_ref(&front[..self.head]).iter() }; let res = front_iter.try_rfold(init, &mut f); self.head = front_iter.len(); - iter = back.iter(); + iter = unsafe { MaybeUninit::slice_assume_init_ref(back).iter() }; final_res = iter.try_rfold(res?, &mut f); } self.head = self.tail + iter.len(); diff --git a/library/alloc/src/collections/vec_deque/mod.rs b/library/alloc/src/collections/vec_deque/mod.rs index 7139a0fb94d76..f27cd684067f9 100644 --- a/library/alloc/src/collections/vec_deque/mod.rs +++ b/library/alloc/src/collections/vec_deque/mod.rs @@ -12,7 +12,7 @@ use core::fmt; use core::hash::{Hash, Hasher}; use core::iter::{repeat_with, FromIterator}; use core::marker::PhantomData; -use core::mem::{self, ManuallyDrop}; +use core::mem::{self, ManuallyDrop, MaybeUninit}; use core::ops::{Index, IndexMut, Range, RangeBounds}; use core::ptr::{self, NonNull}; use core::slice; @@ -181,16 +181,28 @@ impl VecDeque { } } - /// Turn ptr into a slice + /// Turn ptr into a slice, since the elements of the backing buffer may be uninitialized, + /// we will return a slice of [`MaybeUninit`]. + /// + /// See [`MaybeUninit::zeroed`][zeroed] for examples of correct and + /// incorrect usage of this method. + /// + /// [zeroed]: mem::MaybeUninit::zeroed #[inline] - unsafe fn buffer_as_slice(&self) -> &[T] { - unsafe { slice::from_raw_parts(self.ptr(), self.cap()) } + unsafe fn buffer_as_slice(&self) -> &[MaybeUninit] { + unsafe { slice::from_raw_parts(self.ptr() as *mut MaybeUninit, self.cap()) } } - /// Turn ptr into a mut slice + /// Turn ptr into a mut slice, since the elements of the backing buffer may be uninitialized, + /// we will return a slice of [`MaybeUninit`]. + /// + /// See [`MaybeUninit::zeroed`][zeroed] for examples of correct and + /// incorrect usage of this method. + /// + /// [zeroed]: mem::MaybeUninit::zeroed #[inline] - unsafe fn buffer_as_mut_slice(&mut self) -> &mut [T] { - unsafe { slice::from_raw_parts_mut(self.ptr(), self.cap()) } + unsafe fn buffer_as_mut_slice(&mut self) -> &mut [MaybeUninit] { + unsafe { slice::from_raw_parts_mut(self.ptr() as *mut MaybeUninit, self.cap()) } } /// Moves an element out of the buffer @@ -1055,9 +1067,13 @@ impl VecDeque { #[inline] #[stable(feature = "deque_extras_15", since = "1.5.0")] pub fn as_slices(&self) -> (&[T], &[T]) { + // Safety: + // - `self.head` and `self.tail` in a ring buffer are always valid indices. + // - `RingSlices::ring_slices` guarantees that the slices split according to `self.head` and `self.tail` are initialized. unsafe { let buf = self.buffer_as_slice(); - RingSlices::ring_slices(buf, self.head, self.tail) + let (front, back) = RingSlices::ring_slices(buf, self.head, self.tail); + (MaybeUninit::slice_assume_init_ref(front), MaybeUninit::slice_assume_init_ref(back)) } } @@ -1089,11 +1105,15 @@ impl VecDeque { #[inline] #[stable(feature = "deque_extras_15", since = "1.5.0")] pub fn as_mut_slices(&mut self) -> (&mut [T], &mut [T]) { + // Safety: + // - `self.head` and `self.tail` in a ring buffer are always valid indices. + // - `RingSlices::ring_slices` guarantees that the slices split according to `self.head` and `self.tail` are initialized. unsafe { let head = self.head; let tail = self.tail; let buf = self.buffer_as_mut_slice(); - RingSlices::ring_slices(buf, head, tail) + let (front, back) = RingSlices::ring_slices(buf, head, tail); + (MaybeUninit::slice_assume_init_mut(front), MaybeUninit::slice_assume_init_mut(back)) } } @@ -2327,7 +2347,14 @@ impl VecDeque { if self.is_contiguous() { let tail = self.tail; let head = self.head; - return unsafe { RingSlices::ring_slices(self.buffer_as_mut_slice(), head, tail).0 }; + // Safety: + // - `self.head` and `self.tail` in a ring buffer are always valid indices. + // - `RingSlices::ring_slices` guarantees that the slices split according to `self.head` and `self.tail` are initialized. + return unsafe { + MaybeUninit::slice_assume_init_mut( + RingSlices::ring_slices(self.buffer_as_mut_slice(), head, tail).0, + ) + }; } let buf = self.buf.ptr(); @@ -2413,7 +2440,14 @@ impl VecDeque { let tail = self.tail; let head = self.head; - unsafe { RingSlices::ring_slices(self.buffer_as_mut_slice(), head, tail).0 } + // Safety: + // - `self.head` and `self.tail` in a ring buffer are always valid indices. + // - `RingSlices::ring_slices` guarantees that the slices split according to `self.head` and `self.tail` are initialized. + unsafe { + MaybeUninit::slice_assume_init_mut( + RingSlices::ring_slices(self.buffer_as_mut_slice(), head, tail).0, + ) + } } /// Rotates the double-ended queue `mid` places to the left. diff --git a/library/core/src/array/mod.rs b/library/core/src/array/mod.rs index ee79021ed536e..20dfbc6347c4f 100644 --- a/library/core/src/array/mod.rs +++ b/library/core/src/array/mod.rs @@ -276,9 +276,10 @@ impl<'a, T, const N: usize> IntoIterator for &'a mut [T; N] { } #[stable(feature = "index_trait_on_arrays", since = "1.50.0")] -impl Index for [T; N] +#[rustc_const_unstable(feature = "const_slice_index", issue = "none")] +impl const Index for [T; N] where - [T]: Index, + [T]: ~const Index, { type Output = <[T] as Index>::Output; @@ -289,9 +290,10 @@ where } #[stable(feature = "index_trait_on_arrays", since = "1.50.0")] -impl IndexMut for [T; N] +#[rustc_const_unstable(feature = "const_slice_index", issue = "none")] +impl const IndexMut for [T; N] where - [T]: IndexMut, + [T]: ~const IndexMut, { #[inline] fn index_mut(&mut self, index: I) -> &mut Self::Output { diff --git a/library/core/src/clone.rs b/library/core/src/clone.rs index 6f9579043c37d..1912694412b98 100644 --- a/library/core/src/clone.rs +++ b/library/core/src/clone.rs @@ -127,7 +127,11 @@ pub trait Clone: Sized { /// allocations. #[inline] #[stable(feature = "rust1", since = "1.0.0")] - fn clone_from(&mut self, source: &Self) { + #[default_method_body_is_const] + fn clone_from(&mut self, source: &Self) + where + Self: ~const Drop, + { *self = source.clone() } } @@ -178,7 +182,8 @@ mod impls { ($($t:ty)*) => { $( #[stable(feature = "rust1", since = "1.0.0")] - impl Clone for $t { + #[rustc_const_unstable(feature = "const_clone", issue = "91805")] + impl const Clone for $t { #[inline] fn clone(&self) -> Self { *self @@ -196,7 +201,8 @@ mod impls { } #[unstable(feature = "never_type", issue = "35121")] - impl Clone for ! { + #[rustc_const_unstable(feature = "const_clone", issue = "91805")] + impl const Clone for ! { #[inline] fn clone(&self) -> Self { *self @@ -204,7 +210,8 @@ mod impls { } #[stable(feature = "rust1", since = "1.0.0")] - impl Clone for *const T { + #[rustc_const_unstable(feature = "const_clone", issue = "91805")] + impl const Clone for *const T { #[inline] fn clone(&self) -> Self { *self @@ -212,7 +219,8 @@ mod impls { } #[stable(feature = "rust1", since = "1.0.0")] - impl Clone for *mut T { + #[rustc_const_unstable(feature = "const_clone", issue = "91805")] + impl const Clone for *mut T { #[inline] fn clone(&self) -> Self { *self @@ -221,7 +229,8 @@ mod impls { /// Shared references can be cloned, but mutable references *cannot*! #[stable(feature = "rust1", since = "1.0.0")] - impl Clone for &T { + #[rustc_const_unstable(feature = "const_clone", issue = "91805")] + impl const Clone for &T { #[inline] #[rustc_diagnostic_item = "noop_method_clone"] fn clone(&self) -> Self { diff --git a/library/core/src/convert/mod.rs b/library/core/src/convert/mod.rs index 0ceedf936333d..f5ea5f5ba50c0 100644 --- a/library/core/src/convert/mod.rs +++ b/library/core/src/convert/mod.rs @@ -300,7 +300,8 @@ pub trait Into: Sized { /// that encapsulate multiple error types. See the "Examples" section and [the book][book] for more /// details. /// -/// **Note: This trait must not fail**. If the conversion can fail, use [`TryFrom`]. +/// **Note: This trait must not fail**. The `From` trait is intended for perfect conversions. +/// If the conversion can fail or is not perfect, use [`TryFrom`]. /// /// # Generic Implementations /// @@ -690,7 +691,8 @@ impl AsMut for str { pub enum Infallible {} #[stable(feature = "convert_infallible", since = "1.34.0")] -impl Clone for Infallible { +#[rustc_const_unstable(feature = "const_clone", issue = "91805")] +impl const Clone for Infallible { fn clone(&self) -> Infallible { match *self {} } diff --git a/library/core/src/iter/traits/iterator.rs b/library/core/src/iter/traits/iterator.rs index f5c0a3b5cd849..b38df1c2d0228 100644 --- a/library/core/src/iter/traits/iterator.rs +++ b/library/core/src/iter/traits/iterator.rs @@ -1859,6 +1859,77 @@ pub trait Iterator { try_process(self, |i| i.collect()) } + /// Collects all the items from an iterator into a collection. + /// + /// This method consumes the iterator and adds all its items to the + /// passed collection. The collection is then returned, so the call chain + /// can be continued. + /// + /// This is useful when you already have a collection and wants to add + /// the iterator items to it. + /// + /// This method is a convenience method to call [Extend::extend](trait.Extend.html), + /// but instead of being called on a collection, it's called on an iterator. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(iter_collect_into)] + /// + /// let a = [1, 2, 3]; + /// let mut vec: Vec:: = vec![0, 1]; + /// + /// a.iter().map(|&x| x * 2).collect_into(&mut vec); + /// a.iter().map(|&x| x * 10).collect_into(&mut vec); + /// + /// assert_eq!(vec![0, 1, 2, 4, 6, 10, 20, 30], vec); + /// ``` + /// + /// `Vec` can have a manual set capacity to avoid reallocating it: + /// + /// ``` + /// #![feature(iter_collect_into)] + /// + /// let a = [1, 2, 3]; + /// let mut vec: Vec:: = Vec::with_capacity(6); + /// + /// a.iter().map(|&x| x * 2).collect_into(&mut vec); + /// a.iter().map(|&x| x * 10).collect_into(&mut vec); + /// + /// assert_eq!(6, vec.capacity()); + /// println!("{:?}", vec); + /// ``` + /// + /// The returned mutable reference can be used to continue the call chain: + /// + /// ``` + /// #![feature(iter_collect_into)] + /// + /// let a = [1, 2, 3]; + /// let mut vec: Vec:: = Vec::with_capacity(6); + /// + /// let count = a.iter().collect_into(&mut vec).iter().count(); + /// + /// assert_eq!(count, vec.len()); + /// println!("Vec len is {}", count); + /// + /// let count = a.iter().collect_into(&mut vec).iter().count(); + /// + /// assert_eq!(count, vec.len()); + /// println!("Vec len now is {}", count); + /// ``` + #[inline] + #[unstable(feature = "iter_collect_into", reason = "new API", issue = "94780")] + fn collect_into>(self, collection: &mut E) -> &mut E + where + Self: Sized, + { + collection.extend(self); + collection + } + /// Consumes an iterator, creating two collections from it. /// /// The predicate passed to `partition()` can return `true`, or `false`. diff --git a/library/core/src/lib.rs b/library/core/src/lib.rs index f436afbee448e..8834746612397 100644 --- a/library/core/src/lib.rs +++ b/library/core/src/lib.rs @@ -104,6 +104,7 @@ #![feature(const_caller_location)] #![feature(const_cell_into_inner)] #![feature(const_char_convert)] +#![feature(const_clone)] #![feature(const_discriminant)] #![feature(const_eval_select)] #![feature(const_float_bits_conv)] @@ -148,6 +149,8 @@ #![feature(variant_count)] #![feature(const_array_from_ref)] #![feature(const_slice_from_ref)] +#![feature(const_slice_index)] +#![feature(const_is_char_boundary)] // // Language features: #![feature(abi_unadjusted)] diff --git a/library/core/src/num/mod.rs b/library/core/src/num/mod.rs index 72105888f9447..07fd317e07486 100644 --- a/library/core/src/num/mod.rs +++ b/library/core/src/num/mod.rs @@ -809,7 +809,7 @@ impl u8 { ascii::escape_default(self) } - pub(crate) fn is_utf8_char_boundary(self) -> bool { + pub(crate) const fn is_utf8_char_boundary(self) -> bool { // This is bit magic equivalent to: b < 128 || b >= 192 (self as i8) >= -0x40 } diff --git a/library/core/src/ops/range.rs b/library/core/src/ops/range.rs index 1136722067874..5029e0560b892 100644 --- a/library/core/src/ops/range.rs +++ b/library/core/src/ops/range.rs @@ -446,7 +446,7 @@ impl RangeInclusive { /// Converts to an exclusive `Range` for `SliceIndex` implementations. /// The caller is responsible for dealing with `end == usize::MAX`. #[inline] - pub(crate) fn into_slice_range(self) -> Range { + pub(crate) const fn into_slice_range(self) -> Range { // If we're not exhausted, we want to simply slice `start..end + 1`. // If we are exhausted, then slicing with `end + 1..end + 1` gives us an // empty range that is still subject to bounds-checks for that endpoint. diff --git a/library/core/src/option.rs b/library/core/src/option.rs index 508837f63c3be..a6286f8d8d103 100644 --- a/library/core/src/option.rs +++ b/library/core/src/option.rs @@ -1857,7 +1857,11 @@ const fn expect_failed(msg: &str) -> ! { ///////////////////////////////////////////////////////////////////////////// #[stable(feature = "rust1", since = "1.0.0")] -impl Clone for Option { +#[rustc_const_unstable(feature = "const_clone", issue = "91805")] +impl const Clone for Option +where + T: ~const Clone + ~const Drop, +{ #[inline] fn clone(&self) -> Self { match self { diff --git a/library/core/src/ptr/const_ptr.rs b/library/core/src/ptr/const_ptr.rs index 485a5965f4cf7..ee544b4842e86 100644 --- a/library/core/src/ptr/const_ptr.rs +++ b/library/core/src/ptr/const_ptr.rs @@ -1032,10 +1032,11 @@ impl *const [T] { /// } /// ``` #[unstable(feature = "slice_ptr_get", issue = "74265")] + #[rustc_const_unstable(feature = "const_slice_index", issue = "none")] #[inline] - pub unsafe fn get_unchecked(self, index: I) -> *const I::Output + pub const unsafe fn get_unchecked(self, index: I) -> *const I::Output where - I: SliceIndex<[T]>, + I: ~const SliceIndex<[T]>, { // SAFETY: the caller ensures that `self` is dereferenceable and `index` in-bounds. unsafe { index.get_unchecked(self) } diff --git a/library/core/src/ptr/mut_ptr.rs b/library/core/src/ptr/mut_ptr.rs index 1412e836ebfc2..3374b48c88c6b 100644 --- a/library/core/src/ptr/mut_ptr.rs +++ b/library/core/src/ptr/mut_ptr.rs @@ -1302,10 +1302,11 @@ impl *mut [T] { /// } /// ``` #[unstable(feature = "slice_ptr_get", issue = "74265")] + #[rustc_const_unstable(feature = "const_slice_index", issue = "none")] #[inline(always)] - pub unsafe fn get_unchecked_mut(self, index: I) -> *mut I::Output + pub const unsafe fn get_unchecked_mut(self, index: I) -> *mut I::Output where - I: SliceIndex<[T]>, + I: ~const SliceIndex<[T]>, { // SAFETY: the caller ensures that `self` is dereferenceable and `index` in-bounds. unsafe { index.get_unchecked_mut(self) } diff --git a/library/core/src/ptr/non_null.rs b/library/core/src/ptr/non_null.rs index 0aa8e9960a8dd..c744ad5dd2deb 100644 --- a/library/core/src/ptr/non_null.rs +++ b/library/core/src/ptr/non_null.rs @@ -630,10 +630,11 @@ impl NonNull<[T]> { /// } /// ``` #[unstable(feature = "slice_ptr_get", issue = "74265")] + #[rustc_const_unstable(feature = "const_slice_index", issue = "none")] #[inline] - pub unsafe fn get_unchecked_mut(self, index: I) -> NonNull + pub const unsafe fn get_unchecked_mut(self, index: I) -> NonNull where - I: SliceIndex<[T]>, + I: ~const SliceIndex<[T]>, { // SAFETY: the caller ensures that `self` is dereferenceable and `index` in-bounds. // As a consequence, the resulting pointer cannot be null. @@ -642,7 +643,8 @@ impl NonNull<[T]> { } #[stable(feature = "nonnull", since = "1.25.0")] -impl Clone for NonNull { +#[rustc_const_unstable(feature = "const_clone", issue = "91805")] +impl const Clone for NonNull { #[inline] fn clone(&self) -> Self { *self diff --git a/library/core/src/ptr/unique.rs b/library/core/src/ptr/unique.rs index 661d111c99d52..cff68f64f78e0 100644 --- a/library/core/src/ptr/unique.rs +++ b/library/core/src/ptr/unique.rs @@ -146,7 +146,8 @@ impl Unique { } #[unstable(feature = "ptr_internals", issue = "none")] -impl Clone for Unique { +#[rustc_const_unstable(feature = "const_clone", issue = "91805")] +impl const Clone for Unique { #[inline] fn clone(&self) -> Self { *self diff --git a/library/core/src/result.rs b/library/core/src/result.rs index 1827860a39045..5a189f2b09811 100644 --- a/library/core/src/result.rs +++ b/library/core/src/result.rs @@ -1801,7 +1801,12 @@ fn unwrap_failed(_msg: &str, _error: &T) -> ! { ///////////////////////////////////////////////////////////////////////////// #[stable(feature = "rust1", since = "1.0.0")] -impl Clone for Result { +#[rustc_const_unstable(feature = "const_clone", issue = "91805")] +impl const Clone for Result +where + T: ~const Clone + ~const Drop, + E: ~const Clone + ~const Drop, +{ #[inline] fn clone(&self) -> Self { match self { diff --git a/library/core/src/slice/index.rs b/library/core/src/slice/index.rs index 0298bba8d329e..7e6fbbe353889 100644 --- a/library/core/src/slice/index.rs +++ b/library/core/src/slice/index.rs @@ -1,12 +1,14 @@ //! Indexing implementations for `[T]`. +use crate::intrinsics::const_eval_select; use crate::ops; use crate::ptr; #[stable(feature = "rust1", since = "1.0.0")] -impl ops::Index for [T] +#[rustc_const_unstable(feature = "const_slice_index", issue = "none")] +impl const ops::Index for [T] where - I: SliceIndex<[T]>, + I: ~const SliceIndex<[T]>, { type Output = I::Output; @@ -17,9 +19,10 @@ where } #[stable(feature = "rust1", since = "1.0.0")] -impl ops::IndexMut for [T] +#[rustc_const_unstable(feature = "const_slice_index", issue = "none")] +impl const ops::IndexMut for [T] where - I: SliceIndex<[T]>, + I: ~const SliceIndex<[T]>, { #[inline] fn index_mut(&mut self, index: I) -> &mut I::Output { @@ -31,31 +34,72 @@ where #[cfg_attr(feature = "panic_immediate_abort", inline)] #[cold] #[track_caller] -fn slice_start_index_len_fail(index: usize, len: usize) -> ! { +#[rustc_const_unstable(feature = "const_slice_index", issue = "none")] +const fn slice_start_index_len_fail(index: usize, len: usize) -> ! { + // SAFETY: we are just panicking here + unsafe { + const_eval_select( + (index, len), + slice_start_index_len_fail_ct, + slice_start_index_len_fail_rt, + ) + } +} + +// FIXME const-hack +fn slice_start_index_len_fail_rt(index: usize, len: usize) -> ! { panic!("range start index {} out of range for slice of length {}", index, len); } +const fn slice_start_index_len_fail_ct(_: usize, _: usize) -> ! { + panic!("slice start index is out of range for slice"); +} + #[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))] #[cfg_attr(feature = "panic_immediate_abort", inline)] #[cold] #[track_caller] -fn slice_end_index_len_fail(index: usize, len: usize) -> ! { +#[rustc_const_unstable(feature = "const_slice_index", issue = "none")] +const fn slice_end_index_len_fail(index: usize, len: usize) -> ! { + // SAFETY: we are just panicking here + unsafe { + const_eval_select((index, len), slice_end_index_len_fail_ct, slice_end_index_len_fail_rt) + } +} + +// FIXME const-hack +fn slice_end_index_len_fail_rt(index: usize, len: usize) -> ! { panic!("range end index {} out of range for slice of length {}", index, len); } +const fn slice_end_index_len_fail_ct(_: usize, _: usize) -> ! { + panic!("slice end index is out of range for slice"); +} + #[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))] #[cfg_attr(feature = "panic_immediate_abort", inline)] #[cold] #[track_caller] -fn slice_index_order_fail(index: usize, end: usize) -> ! { +#[rustc_const_unstable(feature = "const_slice_index", issue = "none")] +const fn slice_index_order_fail(index: usize, end: usize) -> ! { + // SAFETY: we are just panicking here + unsafe { const_eval_select((index, end), slice_index_order_fail_ct, slice_index_order_fail_rt) } +} + +// FIXME const-hack +fn slice_index_order_fail_rt(index: usize, end: usize) -> ! { panic!("slice index starts at {} but ends at {}", index, end); } +const fn slice_index_order_fail_ct(_: usize, _: usize) -> ! { + panic!("slice index start is larger than end"); +} + #[cfg_attr(not(feature = "panic_immediate_abort"), inline(never))] #[cfg_attr(feature = "panic_immediate_abort", inline)] #[cold] #[track_caller] -fn slice_start_index_overflow_fail() -> ! { +const fn slice_start_index_overflow_fail() -> ! { panic!("attempted to index slice from after maximum usize"); } @@ -63,7 +107,7 @@ fn slice_start_index_overflow_fail() -> ! { #[cfg_attr(feature = "panic_immediate_abort", inline)] #[cold] #[track_caller] -fn slice_end_index_overflow_fail() -> ! { +const fn slice_end_index_overflow_fail() -> ! { panic!("attempted to index slice up to maximum usize"); } @@ -153,7 +197,8 @@ pub unsafe trait SliceIndex: private_slice_index::Sealed { } #[stable(feature = "slice_get_slice_impls", since = "1.15.0")] -unsafe impl SliceIndex<[T]> for usize { +#[rustc_const_unstable(feature = "const_slice_index", issue = "none")] +unsafe impl const SliceIndex<[T]> for usize { type Output = T; #[inline] @@ -197,7 +242,8 @@ unsafe impl SliceIndex<[T]> for usize { } #[stable(feature = "slice_get_slice_impls", since = "1.15.0")] -unsafe impl SliceIndex<[T]> for ops::Range { +#[rustc_const_unstable(feature = "const_slice_index", issue = "none")] +unsafe impl const SliceIndex<[T]> for ops::Range { type Output = [T]; #[inline] @@ -261,7 +307,8 @@ unsafe impl SliceIndex<[T]> for ops::Range { } #[stable(feature = "slice_get_slice_impls", since = "1.15.0")] -unsafe impl SliceIndex<[T]> for ops::RangeTo { +#[rustc_const_unstable(feature = "const_slice_index", issue = "none")] +unsafe impl const SliceIndex<[T]> for ops::RangeTo { type Output = [T]; #[inline] @@ -298,7 +345,8 @@ unsafe impl SliceIndex<[T]> for ops::RangeTo { } #[stable(feature = "slice_get_slice_impls", since = "1.15.0")] -unsafe impl SliceIndex<[T]> for ops::RangeFrom { +#[rustc_const_unstable(feature = "const_slice_index", issue = "none")] +unsafe impl const SliceIndex<[T]> for ops::RangeFrom { type Output = [T]; #[inline] @@ -343,7 +391,8 @@ unsafe impl SliceIndex<[T]> for ops::RangeFrom { } #[stable(feature = "slice_get_slice_impls", since = "1.15.0")] -unsafe impl SliceIndex<[T]> for ops::RangeFull { +#[rustc_const_unstable(feature = "const_slice_index", issue = "none")] +unsafe impl const SliceIndex<[T]> for ops::RangeFull { type Output = [T]; #[inline] @@ -378,7 +427,8 @@ unsafe impl SliceIndex<[T]> for ops::RangeFull { } #[stable(feature = "inclusive_range", since = "1.26.0")] -unsafe impl SliceIndex<[T]> for ops::RangeInclusive { +#[rustc_const_unstable(feature = "const_slice_index", issue = "none")] +unsafe impl const SliceIndex<[T]> for ops::RangeInclusive { type Output = [T]; #[inline] @@ -421,7 +471,8 @@ unsafe impl SliceIndex<[T]> for ops::RangeInclusive { } #[stable(feature = "inclusive_range", since = "1.26.0")] -unsafe impl SliceIndex<[T]> for ops::RangeToInclusive { +#[rustc_const_unstable(feature = "const_slice_index", issue = "none")] +unsafe impl const SliceIndex<[T]> for ops::RangeToInclusive { type Output = [T]; #[inline] diff --git a/library/core/src/slice/mod.rs b/library/core/src/slice/mod.rs index 9467c7f54bac7..7311fe40e04d6 100644 --- a/library/core/src/slice/mod.rs +++ b/library/core/src/slice/mod.rs @@ -324,10 +324,11 @@ impl [T] { /// assert_eq!(None, v.get(0..4)); /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_unstable(feature = "const_slice_index", issue = "none")] #[inline] - pub fn get(&self, index: I) -> Option<&I::Output> + pub const fn get(&self, index: I) -> Option<&I::Output> where - I: SliceIndex, + I: ~const SliceIndex, { index.get(self) } @@ -348,10 +349,11 @@ impl [T] { /// assert_eq!(x, &[0, 42, 2]); /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_unstable(feature = "const_slice_index", issue = "none")] #[inline] - pub fn get_mut(&mut self, index: I) -> Option<&mut I::Output> + pub const fn get_mut(&mut self, index: I) -> Option<&mut I::Output> where - I: SliceIndex, + I: ~const SliceIndex, { index.get_mut(self) } @@ -379,10 +381,11 @@ impl [T] { /// } /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_unstable(feature = "const_slice_index", issue = "none")] #[inline] - pub unsafe fn get_unchecked(&self, index: I) -> &I::Output + pub const unsafe fn get_unchecked(&self, index: I) -> &I::Output where - I: SliceIndex, + I: ~const SliceIndex, { // SAFETY: the caller must uphold most of the safety requirements for `get_unchecked`; // the slice is dereferenceable because `self` is a safe reference. @@ -415,10 +418,11 @@ impl [T] { /// assert_eq!(x, &[1, 13, 4]); /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_unstable(feature = "const_slice_index", issue = "none")] #[inline] - pub unsafe fn get_unchecked_mut(&mut self, index: I) -> &mut I::Output + pub const unsafe fn get_unchecked_mut(&mut self, index: I) -> &mut I::Output where - I: SliceIndex, + I: ~const SliceIndex, { // SAFETY: the caller must uphold the safety requirements for `get_unchecked_mut`; // the slice is dereferenceable because `self` is a safe reference. diff --git a/library/core/src/str/mod.rs b/library/core/src/str/mod.rs index 09709dc3cf6df..f66bab999a98a 100644 --- a/library/core/src/str/mod.rs +++ b/library/core/src/str/mod.rs @@ -79,7 +79,23 @@ use iter::{MatchesInternal, SplitNInternal}; #[inline(never)] #[cold] #[track_caller] -fn slice_error_fail(s: &str, begin: usize, end: usize) -> ! { +#[rustc_allow_const_fn_unstable(const_eval_select)] +const fn slice_error_fail(s: &str, begin: usize, end: usize) -> ! { + // SAFETY: panics for both branches + unsafe { + crate::intrinsics::const_eval_select( + (s, begin, end), + slice_error_fail_ct, + slice_error_fail_rt, + ) + } +} + +const fn slice_error_fail_ct(_: &str, _: usize, _: usize) -> ! { + panic!("failed to slice string"); +} + +fn slice_error_fail_rt(s: &str, begin: usize, end: usize) -> ! { const MAX_DISPLAY_LENGTH: usize = 256; let trunc_len = s.floor_char_boundary(MAX_DISPLAY_LENGTH); let s_trunc = &s[..trunc_len]; @@ -189,8 +205,9 @@ impl str { /// ``` #[must_use] #[stable(feature = "is_char_boundary", since = "1.9.0")] + #[rustc_const_unstable(feature = "const_is_char_boundary", issue = "none")] #[inline] - pub fn is_char_boundary(&self, index: usize) -> bool { + pub const fn is_char_boundary(&self, index: usize) -> bool { // 0 is always ok. // Test for 0 explicitly so that it can optimize out the check // easily and skip reading string data for that case. @@ -418,8 +435,9 @@ impl str { /// assert!(v.get(..42).is_none()); /// ``` #[stable(feature = "str_checked_slicing", since = "1.20.0")] + #[rustc_const_unstable(feature = "const_slice_index", issue = "none")] #[inline] - pub fn get>(&self, i: I) -> Option<&I::Output> { + pub const fn get>(&self, i: I) -> Option<&I::Output> { i.get(self) } @@ -450,8 +468,9 @@ impl str { /// assert_eq!("HEllo", v); /// ``` #[stable(feature = "str_checked_slicing", since = "1.20.0")] + #[rustc_const_unstable(feature = "const_slice_index", issue = "none")] #[inline] - pub fn get_mut>(&mut self, i: I) -> Option<&mut I::Output> { + pub const fn get_mut>(&mut self, i: I) -> Option<&mut I::Output> { i.get_mut(self) } @@ -482,8 +501,9 @@ impl str { /// } /// ``` #[stable(feature = "str_checked_slicing", since = "1.20.0")] + #[rustc_const_unstable(feature = "const_slice_index", issue = "none")] #[inline] - pub unsafe fn get_unchecked>(&self, i: I) -> &I::Output { + pub const unsafe fn get_unchecked>(&self, i: I) -> &I::Output { // SAFETY: the caller must uphold the safety contract for `get_unchecked`; // the slice is dereferenceable because `self` is a safe reference. // The returned pointer is safe because impls of `SliceIndex` have to guarantee that it is. @@ -517,8 +537,12 @@ impl str { /// } /// ``` #[stable(feature = "str_checked_slicing", since = "1.20.0")] + #[rustc_const_unstable(feature = "const_slice_index", issue = "none")] #[inline] - pub unsafe fn get_unchecked_mut>(&mut self, i: I) -> &mut I::Output { + pub const unsafe fn get_unchecked_mut>( + &mut self, + i: I, + ) -> &mut I::Output { // SAFETY: the caller must uphold the safety contract for `get_unchecked_mut`; // the slice is dereferenceable because `self` is a safe reference. // The returned pointer is safe because impls of `SliceIndex` have to guarantee that it is. diff --git a/library/core/src/str/traits.rs b/library/core/src/str/traits.rs index 952676247489f..8b6b4fa02f833 100644 --- a/library/core/src/str/traits.rs +++ b/library/core/src/str/traits.rs @@ -53,9 +53,10 @@ impl PartialOrd for str { } #[stable(feature = "rust1", since = "1.0.0")] -impl ops::Index for str +#[rustc_const_unstable(feature = "const_slice_index", issue = "none")] +impl const ops::Index for str where - I: SliceIndex, + I: ~const SliceIndex, { type Output = I::Output; @@ -66,9 +67,10 @@ where } #[stable(feature = "rust1", since = "1.0.0")] -impl ops::IndexMut for str +#[rustc_const_unstable(feature = "const_slice_index", issue = "none")] +impl const ops::IndexMut for str where - I: SliceIndex, + I: ~const SliceIndex, { #[inline] fn index_mut(&mut self, index: I) -> &mut I::Output { @@ -79,7 +81,7 @@ where #[inline(never)] #[cold] #[track_caller] -fn str_index_overflow_fail() -> ! { +const fn str_index_overflow_fail() -> ! { panic!("attempted to index str up to maximum usize"); } @@ -96,7 +98,8 @@ fn str_index_overflow_fail() -> ! { /// /// Equivalent to `&self[0 .. len]` or `&mut self[0 .. len]`. #[stable(feature = "str_checked_slicing", since = "1.20.0")] -unsafe impl SliceIndex for ops::RangeFull { +#[rustc_const_unstable(feature = "const_slice_index", issue = "none")] +unsafe impl const SliceIndex for ops::RangeFull { type Output = str; #[inline] fn get(self, slice: &str) -> Option<&Self::Output> { @@ -160,7 +163,8 @@ unsafe impl SliceIndex for ops::RangeFull { /// // &s[3 .. 100]; /// ``` #[stable(feature = "str_checked_slicing", since = "1.20.0")] -unsafe impl SliceIndex for ops::Range { +#[rustc_const_unstable(feature = "const_slice_index", issue = "none")] +unsafe impl const SliceIndex for ops::Range { type Output = str; #[inline] fn get(self, slice: &str) -> Option<&Self::Output> { @@ -247,7 +251,8 @@ unsafe impl SliceIndex for ops::Range { /// Panics if `end` does not point to the starting byte offset of a /// character (as defined by `is_char_boundary`), or if `end > len`. #[stable(feature = "str_checked_slicing", since = "1.20.0")] -unsafe impl SliceIndex for ops::RangeTo { +#[rustc_const_unstable(feature = "const_slice_index", issue = "none")] +unsafe impl const SliceIndex for ops::RangeTo { type Output = str; #[inline] fn get(self, slice: &str) -> Option<&Self::Output> { @@ -317,7 +322,8 @@ unsafe impl SliceIndex for ops::RangeTo { /// Panics if `begin` does not point to the starting byte offset of /// a character (as defined by `is_char_boundary`), or if `begin > len`. #[stable(feature = "str_checked_slicing", since = "1.20.0")] -unsafe impl SliceIndex for ops::RangeFrom { +#[rustc_const_unstable(feature = "const_slice_index", issue = "none")] +unsafe impl const SliceIndex for ops::RangeFrom { type Output = str; #[inline] fn get(self, slice: &str) -> Option<&Self::Output> { @@ -393,7 +399,8 @@ unsafe impl SliceIndex for ops::RangeFrom { /// to the ending byte offset of a character (`end + 1` is either a starting /// byte offset or equal to `len`), if `begin > end`, or if `end >= len`. #[stable(feature = "inclusive_range", since = "1.26.0")] -unsafe impl SliceIndex for ops::RangeInclusive { +#[rustc_const_unstable(feature = "const_slice_index", issue = "none")] +unsafe impl const SliceIndex for ops::RangeInclusive { type Output = str; #[inline] fn get(self, slice: &str) -> Option<&Self::Output> { @@ -444,7 +451,8 @@ unsafe impl SliceIndex for ops::RangeInclusive { /// (`end + 1` is either a starting byte offset as defined by /// `is_char_boundary`, or equal to `len`), or if `end >= len`. #[stable(feature = "inclusive_range", since = "1.26.0")] -unsafe impl SliceIndex for ops::RangeToInclusive { +#[rustc_const_unstable(feature = "const_slice_index", issue = "none")] +unsafe impl const SliceIndex for ops::RangeToInclusive { type Output = str; #[inline] fn get(self, slice: &str) -> Option<&Self::Output> { diff --git a/library/core/tests/iter/traits/iterator.rs b/library/core/tests/iter/traits/iterator.rs index cf69f0a7a4d7e..32bd68e3d2554 100644 --- a/library/core/tests/iter/traits/iterator.rs +++ b/library/core/tests/iter/traits/iterator.rs @@ -543,6 +543,14 @@ fn test_try_collect() { assert_eq!(v, Continue(vec![4, 5])); } +#[test] +fn test_collect_into() { + let a = vec![1, 2, 3, 4, 5]; + let mut b = Vec::new(); + a.iter().cloned().collect_into(&mut b); + assert!(a == b); +} + // just tests by whether or not this compiles fn _empty_impl_all_auto_traits() { use std::panic::{RefUnwindSafe, UnwindSafe}; diff --git a/library/core/tests/lib.rs b/library/core/tests/lib.rs index 2437f4afdeb5e..dc3740228274b 100644 --- a/library/core/tests/lib.rs +++ b/library/core/tests/lib.rs @@ -62,6 +62,7 @@ #![feature(slice_partition_dedup)] #![feature(int_log)] #![feature(iter_advance_by)] +#![feature(iter_collect_into)] #![feature(iter_partition_in_place)] #![feature(iter_intersperse)] #![feature(iter_is_partitioned)] diff --git a/library/std/src/lib.rs b/library/std/src/lib.rs index c35389d44f9fc..232b66230782f 100644 --- a/library/std/src/lib.rs +++ b/library/std/src/lib.rs @@ -363,6 +363,11 @@ extern crate std as realstd; #[macro_use] mod macros; +// The runtime entry point and a few unstable public functions used by the +// compiler +#[macro_use] +pub mod rt; + // The Rust prelude pub mod prelude; @@ -547,11 +552,6 @@ pub mod arch { #[stable(feature = "simd_x86", since = "1.27.0")] pub use std_detect::is_x86_feature_detected; -// The runtime entry point and a few unstable public functions used by the -// compiler -#[macro_use] -pub mod rt; - // Platform-abstraction modules mod sys; mod sys_common; diff --git a/library/std/src/net/tcp/tests.rs b/library/std/src/net/tcp/tests.rs index 959fe6943f6ee..4d5cf658def0d 100644 --- a/library/std/src/net/tcp/tests.rs +++ b/library/std/src/net/tcp/tests.rs @@ -508,6 +508,7 @@ fn close_readwrite_smoke() { } #[test] +#[cfg_attr(target_env = "sgx", ignore)] fn close_read_wakes_up() { each_ip(&mut |addr| { let a = t!(TcpListener::bind(&addr)); diff --git a/library/std/src/thread/mod.rs b/library/std/src/thread/mod.rs index 5ffc86b4560fc..74b29454b94a4 100644 --- a/library/std/src/thread/mod.rs +++ b/library/std/src/thread/mod.rs @@ -1287,12 +1287,31 @@ unsafe impl<'scope, T: Sync> Sync for Packet<'scope, T> {} impl<'scope, T> Drop for Packet<'scope, T> { fn drop(&mut self) { + // If this packet was for a thread that ran in a scope, the thread + // panicked, and nobody consumed the panic payload, we make sure + // the scope function will panic. + let unhandled_panic = matches!(self.result.get_mut(), Some(Err(_))); + // Drop the result without causing unwinding. + // This is only relevant for threads that aren't join()ed, as + // join() will take the `result` and set it to None, such that + // there is nothing left to drop here. + // If this panics, we should handle that, because we're outside the + // outermost `catch_unwind` of our thread. + // We just abort in that case, since there's nothing else we can do. + // (And even if we tried to handle it somehow, we'd also need to handle + // the case where the panic payload we get out of it also panics on + // drop, and so on. See issue #86027.) + if let Err(_) = panic::catch_unwind(panic::AssertUnwindSafe(|| { + *self.result.get_mut() = None; + })) { + rtabort!("thread result panicked on drop"); + } // Book-keeping so the scope knows when it's done. if let Some(scope) = self.scope { - // If this packet was for a thread that ran in a scope, the thread - // panicked, and nobody consumed the panic payload, we make sure - // the scope function will panic. - let unhandled_panic = matches!(self.result.get_mut(), Some(Err(_))); + // Now that there will be no more user code running on this thread + // that can use 'scope, mark the thread as 'finished'. + // It's important we only do this after the `result` has been dropped, + // since dropping it might still use things it borrowed from 'scope. scope.decrement_num_running_threads(unhandled_panic); } } diff --git a/library/std/src/thread/scoped.rs b/library/std/src/thread/scoped.rs index 4af58f1a38075..c4847b529a361 100644 --- a/library/std/src/thread/scoped.rs +++ b/library/std/src/thread/scoped.rs @@ -107,6 +107,24 @@ impl ScopeData { /// a.push(4); /// assert_eq!(x, a.len()); /// ``` +/// +/// # Lifetimes +/// +/// Scoped threads involve two lifetimes: `'scope` and `'env`. +/// +/// The `'scope` lifetime represents the lifetime of the scope itself. +/// That is: the time during which new scoped threads may be spawned, +/// and also the time during which they might still be running. +/// Once this lifetime ends, all scoped threads are joined. +/// This lifetime starts within the `scope` function, before `f` (the argument to `scope`) starts. +/// It ends after `f` returns and all scoped threads have been joined, but before `scope` returns. +/// +/// The `'env` lifetime represents the lifetime of whatever is borrowed by the scoped threads. +/// This lifetime must outlast the call to `scope`, and thus cannot be smaller than `'scope`. +/// It can be as small as the call to `scope`, meaning that anything that outlives this call, +/// such as local variables defined right before the scope, can be borrowed by the scoped threads. +/// +/// The `'env: 'scope` bound is part of the definition of the `Scope` type. #[track_caller] pub fn scope<'env, F, T>(f: F) -> T where diff --git a/library/std/src/thread/tests.rs b/library/std/src/thread/tests.rs index 3323ba36bf310..7386fe1c442ab 100644 --- a/library/std/src/thread/tests.rs +++ b/library/std/src/thread/tests.rs @@ -4,10 +4,11 @@ use crate::mem; use crate::panic::panic_any; use crate::result; use crate::sync::{ + atomic::{AtomicBool, Ordering}, mpsc::{channel, Sender}, Arc, Barrier, }; -use crate::thread::{self, ThreadId}; +use crate::thread::{self, Scope, ThreadId}; use crate::time::Duration; use crate::time::Instant; @@ -293,5 +294,25 @@ fn test_thread_id_not_equal() { assert!(thread::current().id() != spawned_id); } -// NOTE: the corresponding test for stderr is in ui/thread-stderr, due -// to the test harness apparently interfering with stderr configuration. +#[test] +fn test_scoped_threads_drop_result_before_join() { + let actually_finished = &AtomicBool::new(false); + struct X<'scope, 'env>(&'scope Scope<'scope, 'env>, &'env AtomicBool); + impl Drop for X<'_, '_> { + fn drop(&mut self) { + thread::sleep(Duration::from_millis(20)); + let actually_finished = self.1; + self.0.spawn(move || { + thread::sleep(Duration::from_millis(20)); + actually_finished.store(true, Ordering::Relaxed); + }); + } + } + thread::scope(|s| { + s.spawn(move || { + thread::sleep(Duration::from_millis(20)); + X(s, actually_finished) + }); + }); + assert!(actually_finished.load(Ordering::Relaxed)); +} diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs index 2ae63858ff610..2ee67d623eec9 100644 --- a/src/bootstrap/lib.rs +++ b/src/bootstrap/lib.rs @@ -193,7 +193,6 @@ const EXTRA_CHECK_CFGS: &[(Option, &'static str, Option<&[&'static str]>)] (None, "bootstrap", None), (Some(Mode::Rustc), "parallel_compiler", None), (Some(Mode::ToolRustc), "parallel_compiler", None), - (Some(Mode::Std), "miri", None), (Some(Mode::Std), "stdarch_intel_sde", None), (Some(Mode::Std), "no_fp_fmt_parse", None), (Some(Mode::Std), "no_global_oom_handling", None), diff --git a/src/bootstrap/native.rs b/src/bootstrap/native.rs index f00c5ce5aa6f0..0fe39defae85d 100644 --- a/src/bootstrap/native.rs +++ b/src/bootstrap/native.rs @@ -259,18 +259,6 @@ impl Step for Llvm { cfg.define("LLVM_LINK_LLVM_DYLIB", "ON"); } - // For distribution we want the LLVM tools to be *statically* linked to libstdc++. - // We also do this if the user explicitly requested static libstdc++. - if builder.config.llvm_tools_enabled || builder.config.llvm_static_stdcpp { - if !target.contains("msvc") && !target.contains("netbsd") { - if target.contains("apple") { - ldflags.push_all("-static-libstdc++"); - } else { - ldflags.push_all("-Wl,-Bsymbolic -static-libstdc++"); - } - } - } - if target.starts_with("riscv") && !target.contains("freebsd") { // RISC-V GCC erroneously requires linking against // `libatomic` when using 1-byte and 2-byte C++ @@ -576,6 +564,18 @@ fn configure_cmake( ldflags.push_all(&flags); } + // For distribution we want the LLVM tools to be *statically* linked to libstdc++. + // We also do this if the user explicitly requested static libstdc++. + if builder.config.llvm_tools_enabled || builder.config.llvm_static_stdcpp { + if !target.contains("msvc") && !target.contains("netbsd") { + if target.contains("apple") { + ldflags.push_all("-static-libstdc++"); + } else { + ldflags.push_all("-Wl,-Bsymbolic -static-libstdc++"); + } + } + } + cfg.define("CMAKE_SHARED_LINKER_FLAGS", &ldflags.shared); cfg.define("CMAKE_MODULE_LINKER_FLAGS", &ldflags.module); cfg.define("CMAKE_EXE_LINKER_FLAGS", &ldflags.exe); diff --git a/src/doc/rustdoc/book.toml b/src/doc/rustdoc/book.toml index 97e08416d7686..45405a11765cc 100644 --- a/src/doc/rustdoc/book.toml +++ b/src/doc/rustdoc/book.toml @@ -4,3 +4,7 @@ title = "The rustdoc book" [output.html] git-repository-url = "https://github.com/rust-lang/rust/tree/master/src/doc/rustdoc" + +[output.html.redirect] +"/the-doc-attribute.html" = "write-documentation/the-doc-attribute.html" +"/documentation-tests.html" = "write-documentation/documentation-tests.html" diff --git a/src/doc/rustdoc/src/SUMMARY.md b/src/doc/rustdoc/src/SUMMARY.md index eb18185945387..d627f5b0389f3 100644 --- a/src/doc/rustdoc/src/SUMMARY.md +++ b/src/doc/rustdoc/src/SUMMARY.md @@ -1,16 +1,15 @@ # The Rustdoc Book - [What is rustdoc?](what-is-rustdoc.md) +- [Command-line arguments](command-line-arguments.md) - [How to read rustdoc output](how-to-read-rustdoc.md) - [How to write documentation](how-to-write-documentation.md) -- [What to include (and exclude)](what-to-include.md) -- [Command-line arguments](command-line-arguments.md) -- [The `#[doc]` attribute](the-doc-attribute.md) -- [Documentation tests](documentation-tests.md) -- [Linking to items by name](linking-to-items-by-name.md) -- [Lints](lints.md) + - [What to include (and exclude)](write-documentation/what-to-include.md) + - [The `#[doc]` attribute](write-documentation/the-doc-attribute.md) + - [Linking to items by name](write-documentation/linking-to-items-by-name.md) + - [Documentation tests](write-documentation/documentation-tests.md) +- [Rustdoc-specific lints](lints.md) - [Advanced features](advanced-features.md) - [Unstable features](unstable-features.md) -- [Website features](website-features.md) -- [Passes](passes.md) +- [Deprecated features](deprecated-features.md) - [References](references.md) diff --git a/src/doc/rustdoc/src/advanced-features.md b/src/doc/rustdoc/src/advanced-features.md index 6147bd0a97a96..dbf0baec04c03 100644 --- a/src/doc/rustdoc/src/advanced-features.md +++ b/src/doc/rustdoc/src/advanced-features.md @@ -88,3 +88,25 @@ You can add multiple aliases at the same time by using a list: #[doc(alias("x", "big"))] pub struct BigX; ``` + +## Custom search engines + +If you find yourself often referencing online Rust docs you might enjoy using a custom search +engine. This allows you to use the navigation bar directly to search a `rustdoc` website. +Most browsers support this feature by letting you define a URL template containing `%s` +which will be substituted for the search term. As an example, for the standard library you could use +this template: + +```text +https://doc.rust-lang.org/stable/std/?search=%s +``` + +Note that this will take you to a results page listing all matches. If you want to navigate to the first +result right away (which is often the best match) use the following instead: + +```text +https://doc.rust-lang.org/stable/std/?search=%s&go_to_first=true +``` + +This URL adds the `go_to_first=true` query parameter which can be appended to any `rustdoc` search URL +to automatically go to the first result. diff --git a/src/doc/rustdoc/src/command-line-arguments.md b/src/doc/rustdoc/src/command-line-arguments.md index 3ce57f88938f7..2a2e51b2f6331 100644 --- a/src/doc/rustdoc/src/command-line-arguments.md +++ b/src/doc/rustdoc/src/command-line-arguments.md @@ -177,7 +177,7 @@ $ rustdoc src/lib.rs --test ``` This flag will run your code examples as tests. For more, see [the chapter -on documentation tests](documentation-tests.md). +on documentation tests](write-documentation/documentation-tests.md). See also `--test-args`. @@ -190,7 +190,7 @@ $ rustdoc src/lib.rs --test --test-args ignored ``` This flag will pass options to the test runner when running documentation tests. -For more, see [the chapter on documentation tests](documentation-tests.md). +For more, see [the chapter on documentation tests](write-documentation/documentation-tests.md). See also `--test`. @@ -336,7 +336,7 @@ $ rustdoc src/lib.rs --sysroot /path/to/sysroot Similar to `rustc --sysroot`, this lets you change the sysroot `rustdoc` uses when compiling your code. -### `--edition`: control the edition of docs and doctests +## `--edition`: control the edition of docs and doctests Using this flag looks like this: @@ -403,12 +403,12 @@ encoded as UTF-8. ## `--passes`: add more rustdoc passes This flag is **deprecated**. -For more details on passes, see [the chapter on them](passes.md). +For more details on passes, see [the chapter on them](deprecated-features.md#passes). ## `--no-defaults`: don't run default passes This flag is **deprecated**. -For more details on passes, see [the chapter on them](passes.md). +For more details on passes, see [the chapter on them](deprecated-features.md#passes). ## `-r`/`--input-format`: input format @@ -417,23 +417,3 @@ This flag is **deprecated** and **has no effect**. Rustdoc only supports Rust source code and Markdown input formats. If the file ends in `.md` or `.markdown`, `rustdoc` treats it as a Markdown file. Otherwise, it assumes that the input file is Rust. - -# Unstable command line arguments - -## `--nocapture` - -When this flag is used with `--test`, the output (stdout and stderr) of your tests won't be -captured by rustdoc. Instead, the output will be directed to your terminal, -as if you had run the test executable manually. This is especially useful -for debugging your tests! - -## `--check` - -When this flag is supplied, rustdoc will type check and lint your code, but will not generate any -documentation or run your doctests. - -Using this flag looks like: - -```bash -rustdoc -Z unstable-options --check src/lib.rs -``` diff --git a/src/doc/rustdoc/src/passes.md b/src/doc/rustdoc/src/deprecated-features.md similarity index 94% rename from src/doc/rustdoc/src/passes.md rename to src/doc/rustdoc/src/deprecated-features.md index c3c3fd3068ec4..2bc6e8fc8ae4d 100644 --- a/src/doc/rustdoc/src/passes.md +++ b/src/doc/rustdoc/src/deprecated-features.md @@ -1,4 +1,6 @@ -# Passes +# Deprecated features + +## Passes Rustdoc has a concept called "passes". These are transformations that `rustdoc` runs on your documentation before producing its final output. diff --git a/src/doc/rustdoc/src/how-to-read-rustdoc.md b/src/doc/rustdoc/src/how-to-read-rustdoc.md index 99724d859ee75..098bc1879b56f 100644 --- a/src/doc/rustdoc/src/how-to-read-rustdoc.md +++ b/src/doc/rustdoc/src/how-to-read-rustdoc.md @@ -26,7 +26,7 @@ At the top is some at-a-glance info and controls: - a button to collapse or expand the top-level documentation for that item (`[+]` or `[-]`), - a link to the source code (`[src]`), - if [configured](the-doc-attribute.html#html_no_source), + if [configured](write-documentation/the-doc-attribute.html#html_no_source), and present (the source may not be available if the documentation was created with `cargo doc --no-deps`), - and the version in which the item became stable, @@ -52,7 +52,7 @@ For example, when looking at documentation for the crate root, it shows all the crates documented in the documentation bundle, and quick links to the modules, structs, traits, functions, and macros available from the current crate. -At the top, it displays a [configurable logo](the-doc-attribute.html#html_logo_url) +At the top, it displays a [configurable logo](write-documentation/the-doc-attribute.html#html_logo_url) alongside the current crate's name and version, or the current item whose documentation is being displayed. diff --git a/src/doc/rustdoc/src/lints.md b/src/doc/rustdoc/src/lints.md index 1773c15464a94..bff01d7cb7c14 100644 --- a/src/doc/rustdoc/src/lints.md +++ b/src/doc/rustdoc/src/lints.md @@ -13,11 +13,11 @@ Note that, except for `missing_docs`, these lints are only available when runnin Here is the list of the lints provided by `rustdoc`: -## broken_intra_doc_links +## `broken_intra_doc_links` This lint **warns by default**. This lint detects when an [intra-doc link] fails to be resolved. For example: -[intra-doc link]: linking-to-items-by-name.md +[intra-doc link]: write-documentation/linking-to-items-by-name.md ```rust /// I want to link to [`Nonexistent`] but it doesn't exist! @@ -64,7 +64,7 @@ help: to link to the function, add parentheses ``` -## private_intra_doc_links +## `private_intra_doc_links` This lint **warns by default**. This lint detects when [intra-doc links] from public to private items. For example: @@ -104,9 +104,9 @@ warning: public documentation for `public` links to private item `private` = note: this link resolves only because you passed `--document-private-items`, but will break without ``` -[intra-doc links]: linking-to-items-by-name.html +[intra-doc links]: write-documentation/linking-to-items-by-name.md -## missing_docs +## `missing_docs` This lint is **allowed by default**. It detects items missing documentation. For example: @@ -130,7 +130,7 @@ warning: missing documentation for a function Note that unlike other rustdoc lints, this lint is also available from `rustc` directly. -## missing_crate_level_docs +## `missing_crate_level_docs` This lint is **allowed by default**. It detects if there is no documentation at the crate root. For example: @@ -154,7 +154,7 @@ warning in the future. This is intended as a means to introduce new users on get started, without providing overwhelming warnings like `missing_docs` might. -## missing_doc_code_examples +## `missing_doc_code_examples` This lint is **allowed by default** and is **nightly-only**. It detects when a documentation block is missing a code example. For example: @@ -190,7 +190,7 @@ To fix the lint, you need to add a code example into the documentation block: pub fn no_code_example() {} ``` -## private_doc_tests +## `private_doc_tests` This lint is **allowed by default**. It detects documentation tests when they are on a private item. For example: @@ -223,7 +223,7 @@ warning: Documentation test in private item | |___________^ ``` -## invalid_codeblock_attributes +## `invalid_codeblock_attributes` This lint **warns by default**. It detects code block attributes in documentation examples that have potentially mis-typed values. For example: @@ -259,7 +259,7 @@ warning: unknown attribute `should-panic`. Did you mean `should_panic`? In the example above, the correct form is `should_panic`. This helps detect typo mistakes for some common attributes. -## invalid_html_tags +## `invalid_html_tags` This lint is **allowed by default** and is **nightly-only**. It detects unclosed or invalid HTML tags. For example: @@ -298,7 +298,7 @@ warning: unclosed HTML tag `h1` warning: 2 warnings emitted ``` -## invalid_rust_codeblocks +## `invalid_rust_codeblocks` This lint **warns by default**. It detects Rust code blocks in documentation examples that are invalid (e.g. empty, not parsable as Rust). For example: @@ -342,7 +342,7 @@ warning: could not parse code block as Rust code = note: error from rustc: unterminated character literal ``` -## bare_urls +## `bare_urls` This lint is **warn-by-default**. It detects URLs which are not links. For example: diff --git a/src/doc/rustdoc/src/unstable-features.md b/src/doc/rustdoc/src/unstable-features.md index 141d5d2d2b2d0..537ab48bbfc12 100644 --- a/src/doc/rustdoc/src/unstable-features.md +++ b/src/doc/rustdoc/src/unstable-features.md @@ -22,7 +22,7 @@ As detailed in [the chapter on documentation tests][doctest-attributes], you can nightly, you can optionally add an error number to state that a doctest should emit a specific error number: -[doctest-attributes]: documentation-tests.html#attributes +[doctest-attributes]: write-documentation/documentation-tests.html#attributes ``````markdown ```compile_fail,E0044 @@ -45,6 +45,8 @@ and enabled with a `#![feature(...)]` attribute in your crate. ### `#[doc(cfg)]`: Recording what platforms or features are required for code to be present + * Tracking issue: [#43781](https://github.com/rust-lang/rust/issues/43781) + You can use `#[doc(cfg(...))]` to tell Rustdoc exactly which platform items appear on. This has two effects: @@ -86,6 +88,8 @@ Book][unstable-doc-cfg] and [its tracking issue][issue-doc-cfg]. ### `doc_auto_cfg`: Automatically generate `#[doc(cfg)]` + * Tracking issue: [#43781](https://github.com/rust-lang/rust/issues/43781) + `doc_auto_cfg` is an extension to the `#[doc(cfg)]` feature. With it, you don't need to add `#[doc(cfg(...)]` anymore unless you want to override the default behaviour. So if we take the previous source code: @@ -123,6 +127,8 @@ And `doc` won't show up anymore! ### Adding your trait to the "Notable traits" dialog + * Tracking issue: [#45040](https://github.com/rust-lang/rust/issues/45040) + Rustdoc keeps a list of a few traits that are believed to be "fundamental" to types that implement them. These traits are intended to be the primary interface for their implementers, and are often most of the API available to be documented @@ -146,6 +152,8 @@ and [its tracking issue][issue-notable_trait]. ### Exclude certain dependencies from documentation + * Tracking issue: [#44027](https://github.com/rust-lang/rust/issues/44027) + The standard library uses several dependencies which, in turn, use several types and traits from the standard library. In addition, there are several compiler-internal crates that are not considered to be part of the official standard library, and thus would be a distraction to include in @@ -164,8 +172,7 @@ Book][unstable-masked] and [its tracking issue][issue-masked]. [unstable-masked]: ../unstable-book/language-features/doc-masked.html [issue-masked]: https://github.com/rust-lang/rust/issues/44027 - -## Document primitives +### Document primitives This is for Rust compiler internal use only. @@ -174,7 +181,7 @@ attributes. The `#[doc(primitive)]` attribute is used by the standard library to to generate documentation for primitive types, and requires `#![feature(rustdoc_internals)]` to enable. -## Document keywords +### Document keywords This is for Rust compiler internal use only. @@ -199,6 +206,8 @@ the flag in question to Rustdoc on the command-line. To do this from Cargo, you ### `--markdown-before-content`: include rendered Markdown before the content + * Tracking issue: [#44027](https://github.com/rust-lang/rust/issues/44027) + Using this flag looks like this: ```bash @@ -241,7 +250,7 @@ attribute][doc-playground]. Please be aware that the official Rust Playground at https://play.rust-lang.org does not have every crate available, so if your examples require your crate, make sure the playground you provide has your crate available. -[doc-playground]: the-doc-attribute.html#html_playground_url +[doc-playground]: write-documentation/the-doc-attribute.html#html_playground_url If both `--playground-url` and `--markdown-playground-url` are present when rendering a standalone Markdown file, the URL given to `--markdown-playground-url` will take precedence. If both @@ -279,6 +288,8 @@ between compilations. ### `--resource-suffix`: modifying the name of CSS/JavaScript in crate docs + * Tracking issue: [#54765](https://github.com/rust-lang/rust/issues/54765) + Using this flag looks like this: ```bash @@ -331,6 +342,24 @@ Using `index-page` option enables `enable-index-page` option as well. This feature allows the generation of a default index-page which lists the generated crates. +### `--nocapture`: disable output capture for test + +When this flag is used with `--test`, the output (stdout and stderr) of your tests won't be +captured by rustdoc. Instead, the output will be directed to your terminal, +as if you had run the test executable manually. This is especially useful +for debugging your tests! + +### `--check`: only checks the documentation + +When this flag is supplied, rustdoc will type check and lint your code, but will not generate any +documentation or run your doctests. + +Using this flag looks like: + +```bash +rustdoc -Z unstable-options --check src/lib.rs +``` + ### `--static-root-path`: control how static files are loaded in HTML output Using this flag looks like this: @@ -348,6 +377,8 @@ renamed with `--resource-suffix` will load from the given path. ### `--persist-doctests`: persist doctest executables after running + * Tracking issue: [#56925](https://github.com/rust-lang/rust/issues/56925) + Using this flag looks like this: ```bash @@ -360,6 +391,8 @@ with this option, you can keep those binaries around for farther testing. ### `--show-coverage`: calculate the percentage of items with documentation + * Tracking issue: [#58154](https://github.com/rust-lang/rust/issues/58154) + Using this flag looks like this: ```bash @@ -438,6 +471,8 @@ information. ### `--enable-per-target-ignores`: allow `ignore-foo` style filters for doctests + * Tracking issue: [#64245](https://github.com/rust-lang/rust/issues/64245) + Using this flag looks like this: ```bash @@ -471,6 +506,8 @@ override `ignore`. ### `--runtool`, `--runtool-arg`: program to run tests with; args to pass to it + * Tracking issue: [#64245](https://github.com/rust-lang/rust/issues/64245) + Using these options looks like this: ```bash @@ -488,6 +525,8 @@ Another use case would be to run a test inside an emulator, or through a Virtual ### `--with-examples`: include examples of uses of items as documentation + * Tracking issue: [#88791](https://github.com/rust-lang/rust/issues/88791) + This option, combined with `--scrape-examples-target-crate` and `--scrape-examples-output-path`, is used to implement the functionality in [RFC #3123](https://github.com/rust-lang/rfcs/pull/3123). Uses of an item (currently @@ -515,6 +554,8 @@ add the `--scrape-tests` flag. ### `--check-cfg`: check configuration flags + * Tracking issue: [#82450](https://github.com/rust-lang/rust/issues/82450) + This flag accepts the same values as `rustc --check-cfg`, and uses it to check configuration flags. Using this flag looks like this: diff --git a/src/doc/rustdoc/src/website-features.md b/src/doc/rustdoc/src/website-features.md deleted file mode 100644 index 5fade4e84a992..0000000000000 --- a/src/doc/rustdoc/src/website-features.md +++ /dev/null @@ -1,25 +0,0 @@ -# Website features - -These features are about using the website generated by `rustdoc`. - -## Custom search engines - -If you find yourself often referencing online Rust docs you might enjoy using a custom search -engine. This allows you to use the navigation bar directly to search a `rustdoc` website. -Most browsers support this feature by letting you define a URL template containing `%s` -which will be substituted for the search term. As an example, for the standard library you could use -this template: - -```text -https://doc.rust-lang.org/stable/std/?search=%s -``` - -Note that this will take you to a results page listing all matches. If you want to navigate to the first -result right away (which is often the best match) use the following instead: - -```text -https://doc.rust-lang.org/stable/std/?search=%s&go_to_first=true -``` - -This URL adds the `go_to_first=true` query parameter which can be appended to any `rustdoc` search URL -to automatically go to the first result. diff --git a/src/doc/rustdoc/src/documentation-tests.md b/src/doc/rustdoc/src/write-documentation/documentation-tests.md similarity index 99% rename from src/doc/rustdoc/src/documentation-tests.md rename to src/doc/rustdoc/src/write-documentation/documentation-tests.md index 534fd19b52eb2..1cb5b049df404 100644 --- a/src/doc/rustdoc/src/documentation-tests.md +++ b/src/doc/rustdoc/src/write-documentation/documentation-tests.md @@ -269,7 +269,7 @@ By default, this will still hide `unused` warnings, since so many examples use p you can add `#![warn(unused)]` to the top of your example if you want to see unused variables or dead code warnings. You can also use [`#![doc(test(attr(warn(unused))))]`][test-attr] in the crate root to enable warnings globally. -[test-attr]: ./the-doc-attribute.md#testattr +[test-attr]: the-doc-attribute.md#testattr ## Documenting macros diff --git a/src/doc/rustdoc/src/linking-to-items-by-name.md b/src/doc/rustdoc/src/write-documentation/linking-to-items-by-name.md similarity index 98% rename from src/doc/rustdoc/src/linking-to-items-by-name.md rename to src/doc/rustdoc/src/write-documentation/linking-to-items-by-name.md index 6ca1d1153b494..36bc312b9c99a 100644 --- a/src/doc/rustdoc/src/linking-to-items-by-name.md +++ b/src/doc/rustdoc/src/write-documentation/linking-to-items-by-name.md @@ -35,7 +35,7 @@ link to `Option`. You can refer to anything in scope, and use paths, including `Self`, `self`, `super`, and `crate`. Associated items (functions, types, and constants) are supported, but [not for blanket trait implementations][#79682]. Rustdoc also supports linking to all primitives listed in -[the standard library documentation](../std/index.html#primitives). +[the standard library documentation](../../std/index.html#primitives). [#79682]: https://github.com/rust-lang/rust/pull/79682 diff --git a/src/doc/rustdoc/src/the-doc-attribute.md b/src/doc/rustdoc/src/write-documentation/the-doc-attribute.md similarity index 98% rename from src/doc/rustdoc/src/the-doc-attribute.md rename to src/doc/rustdoc/src/write-documentation/the-doc-attribute.md index c5cc84022e389..25ef8b5bb9141 100644 --- a/src/doc/rustdoc/src/the-doc-attribute.md +++ b/src/doc/rustdoc/src/write-documentation/the-doc-attribute.md @@ -38,7 +38,7 @@ but given that docs are rendered via Markdown, it will remove these newlines. Another use case is for including external files as documentation: ```rust,no_run -#[doc = include_str!("../README.md")] +#[doc = include_str!("../../README.md")] # fn f() {} ``` @@ -73,7 +73,7 @@ left hand side of the docs. #![doc(html_logo_url = "https://example.com/logo.jpg")] ``` -This will put `logo` into +This will put `logo` into your docs, where the string for the attribute goes into the `{}`. If you don't use this attribute, there will be no logo. diff --git a/src/doc/rustdoc/src/what-to-include.md b/src/doc/rustdoc/src/write-documentation/what-to-include.md similarity index 99% rename from src/doc/rustdoc/src/what-to-include.md rename to src/doc/rustdoc/src/write-documentation/what-to-include.md index 2a6b62ebfd552..35e6ccbc38807 100644 --- a/src/doc/rustdoc/src/what-to-include.md +++ b/src/doc/rustdoc/src/write-documentation/what-to-include.md @@ -122,4 +122,4 @@ Here is an example of a new theme, [Ayu]. [API Guidelines]: https://rust-lang.github.io/api-guidelines/documentation.html#rustdoc-does-not-show-unhelpful-implementation-details-c-hidden [Documentation tests]: documentation-tests.md [on this blog]: https://blog.guillaume-gomez.fr/articles/2016-09-16+Generating+doc+with+rustdoc+and+a+custom+theme -[rustdoc-lints]: lints.md +[rustdoc-lints]: ../lints.md diff --git a/src/librustdoc/html/markdown.rs b/src/librustdoc/html/markdown.rs index 7061a9674e4fb..f2856690d257a 100644 --- a/src/librustdoc/html/markdown.rs +++ b/src/librustdoc/html/markdown.rs @@ -1460,6 +1460,7 @@ fn init_id_map() -> FxHashMap { map.insert("provided-methods".to_owned(), 1); map.insert("implementors".to_owned(), 1); map.insert("synthetic-implementors".to_owned(), 1); + map.insert("implementations-list".to_owned(), 1); map.insert("trait-implementations-list".to_owned(), 1); map.insert("synthetic-implementations-list".to_owned(), 1); map.insert("blanket-implementations-list".to_owned(), 1); diff --git a/src/librustdoc/html/render/mod.rs b/src/librustdoc/html/render/mod.rs index 558dbb3b3965a..26f29a3524b00 100644 --- a/src/librustdoc/html/render/mod.rs +++ b/src/librustdoc/html/render/mod.rs @@ -1065,14 +1065,15 @@ fn render_assoc_items_inner( let (non_trait, traits): (Vec<_>, _) = v.iter().partition(|i| i.inner_impl().trait_.is_none()); if !non_trait.is_empty() { let mut tmp_buf = Buffer::empty_from(w); - let render_mode = match what { + let (render_mode, id) = match what { AssocItemRender::All => { tmp_buf.write_str( "

\ - Implementations\ -

", + Implementations\ + \ + ", ); - RenderMode::Normal + (RenderMode::Normal, "implementations-list".to_owned()) } AssocItemRender::DerefFor { trait_, type_, deref_mut_ } => { let id = @@ -1090,7 +1091,7 @@ fn render_assoc_items_inner( trait_ = trait_.print(cx), type_ = type_.print(cx), ); - RenderMode::ForDeref { mut_: deref_mut_ } + (RenderMode::ForDeref { mut_: deref_mut_ }, cx.derive_id(id)) } }; let mut impls_buf = Buffer::empty_from(w); @@ -1115,7 +1116,9 @@ fn render_assoc_items_inner( } if !impls_buf.is_empty() { w.push_buffer(tmp_buf); + write!(w, "
", id); w.push_buffer(impls_buf); + w.write_str("
"); } } @@ -1146,7 +1149,8 @@ fn render_assoc_items_inner( write!( w, "

\ - Trait Implementations\ + Trait Implementations\ + \

\
{}
", impls diff --git a/src/librustdoc/html/static/js/main.js b/src/librustdoc/html/static/js/main.js index 8e1919f75d671..90592335d5ddf 100644 --- a/src/librustdoc/html/static/js/main.js +++ b/src/librustdoc/html/static/js/main.js @@ -741,7 +741,7 @@ function hideThemeButtonState() { } else { addClass(innerToggle, "will-expand"); onEachLazy(document.getElementsByClassName("rustdoc-toggle"), function(e) { - if (e.parentNode.id !== MAIN_ID || + if (e.parentNode.id !== "implementations-list" || (!hasClass(e, "implementors-toggle") && !hasClass(e, "type-contents-toggle"))) { diff --git a/src/test/mir-opt/inline/inline_generator.main.Inline.diff b/src/test/mir-opt/inline/inline_generator.main.Inline.diff index 1d72b34f83b81..831d73045dd63 100644 --- a/src/test/mir-opt/inline/inline_generator.main.Inline.diff +++ b/src/test/mir-opt/inline/inline_generator.main.Inline.diff @@ -46,11 +46,11 @@ - bb1: { + discriminant(_4) = 0; // scope 2 at $DIR/inline-generator.rs:15:5: 15:41 _3 = &mut _4; // scope 0 at $DIR/inline-generator.rs:9:23: 9:31 -- _2 = Pin::<&mut impl Generator>::new(move _3) -> [return: bb2, unwind: bb4]; // scope 0 at $DIR/inline-generator.rs:9:14: 9:32 +- _2 = Pin::<&mut [generator@$DIR/inline-generator.rs:15:5: 15:41]>::new(move _3) -> [return: bb2, unwind: bb4]; // scope 0 at $DIR/inline-generator.rs:9:14: 9:32 - // mir::Constant - // + span: $DIR/inline-generator.rs:9:14: 9:22 - // + user_ty: UserType(0) -- // + literal: Const { ty: fn(&mut impl Generator) -> Pin<&mut impl Generator> {Pin::<&mut impl Generator>::new}, val: Value(Scalar()) } +- // + literal: Const { ty: fn(&mut [generator@$DIR/inline-generator.rs:15:5: 15:41]) -> Pin<&mut [generator@$DIR/inline-generator.rs:15:5: 15:41]> {Pin::<&mut [generator@$DIR/inline-generator.rs:15:5: 15:41]>::new}, val: Value(Scalar()) } - } - - bb2: { @@ -62,10 +62,10 @@ + StorageDead(_6); // scope 5 at $SRC_DIR/core/src/pin.rs:LL:COL + StorageDead(_5); // scope 4 at $SRC_DIR/core/src/pin.rs:LL:COL StorageDead(_3); // scope 0 at $DIR/inline-generator.rs:9:31: 9:32 -- _1 = as Generator>::resume(move _2, const false) -> [return: bb3, unwind: bb4]; // scope 0 at $DIR/inline-generator.rs:9:14: 9:46 +- _1 = <[generator@$DIR/inline-generator.rs:15:5: 15:41] as Generator>::resume(move _2, const false) -> [return: bb3, unwind: bb4]; // scope 0 at $DIR/inline-generator.rs:9:14: 9:46 - // mir::Constant - // + span: $DIR/inline-generator.rs:9:33: 9:39 -- // + literal: Const { ty: for<'r> fn(Pin<&'r mut impl Generator>, bool) -> GeneratorState< as Generator>::Yield, as Generator>::Return> { as Generator>::resume}, val: Value(Scalar()) } +- // + literal: Const { ty: for<'r> fn(Pin<&'r mut [generator@$DIR/inline-generator.rs:15:5: 15:41]>, bool) -> GeneratorState<<[generator@$DIR/inline-generator.rs:15:5: 15:41] as Generator>::Yield, <[generator@$DIR/inline-generator.rs:15:5: 15:41] as Generator>::Return> {<[generator@$DIR/inline-generator.rs:15:5: 15:41] as Generator>::resume}, val: Value(Scalar()) } + StorageLive(_7); // scope 0 at $DIR/inline-generator.rs:9:14: 9:46 + _7 = const false; // scope 0 at $DIR/inline-generator.rs:9:14: 9:46 + StorageLive(_10); // scope 0 at $DIR/inline-generator.rs:9:14: 9:46 diff --git a/src/test/mir-opt/inline/issue_78442.bar.Inline.diff b/src/test/mir-opt/inline/issue_78442.bar.Inline.diff index 9e92f025b7661..ea6b91cba9e58 100644 --- a/src/test/mir-opt/inline/issue_78442.bar.Inline.diff +++ b/src/test/mir-opt/inline/issue_78442.bar.Inline.diff @@ -25,10 +25,10 @@ bb1: { _3 = &_4; // scope 0 at $DIR/issue-78442.rs:11:5: 11:15 StorageLive(_5); // scope 0 at $DIR/issue-78442.rs:11:5: 11:17 -- _2 = >::call(move _3, move _5) -> [return: bb2, unwind: bb4]; // scope 0 at $DIR/issue-78442.rs:11:5: 11:17 +- _2 = >::call(move _3, move _5) -> [return: bb2, unwind: bb4]; // scope 0 at $DIR/issue-78442.rs:11:5: 11:17 - // mir::Constant - // + span: $DIR/issue-78442.rs:11:5: 11:15 -- // + literal: Const { ty: for<'r> extern "rust-call" fn(&'r impl Fn(), ()) -> >::Output {>::call}, val: Value(Scalar()) } +- // + literal: Const { ty: for<'r> extern "rust-call" fn(&'r fn() {foo}, ()) -> >::Output {>::call}, val: Value(Scalar()) } + _2 = move (*_3)() -> [return: bb5, unwind: bb3]; // scope 1 at $SRC_DIR/core/src/ops/function.rs:LL:COL } diff --git a/src/test/mir-opt/inline/issue_78442.bar.RevealAll.diff b/src/test/mir-opt/inline/issue_78442.bar.RevealAll.diff index a8ae4008cbc96..ba9da7678e723 100644 --- a/src/test/mir-opt/inline/issue_78442.bar.RevealAll.diff +++ b/src/test/mir-opt/inline/issue_78442.bar.RevealAll.diff @@ -25,10 +25,12 @@ _3 = &_4; // scope 0 at $DIR/issue-78442.rs:11:5: 11:15 StorageLive(_5); // scope 0 at $DIR/issue-78442.rs:11:5: 11:17 nop; // scope 0 at $DIR/issue-78442.rs:11:5: 11:17 - _2 = >::call(move _3, move _5) -> [return: bb2, unwind: bb4]; // scope 0 at $DIR/issue-78442.rs:11:5: 11:17 +- _2 = >::call(move _3, move _5) -> [return: bb2, unwind: bb4]; // scope 0 at $DIR/issue-78442.rs:11:5: 11:17 ++ _2 = >::call(move _3, move _5) -> [return: bb2, unwind: bb4]; // scope 0 at $DIR/issue-78442.rs:11:5: 11:17 // mir::Constant // + span: $DIR/issue-78442.rs:11:5: 11:15 - // + literal: Const { ty: for<'r> extern "rust-call" fn(&'r impl Fn(), ()) -> >::Output {>::call}, val: Value(Scalar()) } +- // + literal: Const { ty: for<'r> extern "rust-call" fn(&'r impl Fn(), ()) -> >::Output {>::call}, val: Value(Scalar()) } ++ // + literal: Const { ty: for<'r> extern "rust-call" fn(&'r fn() {foo}, ()) -> >::Output {>::call}, val: Value(Scalar()) } } bb2: { diff --git a/src/test/mir-opt/nll/named_lifetimes_basic.use_x.nll.0.mir b/src/test/mir-opt/nll/named_lifetimes_basic.use_x.nll.0.mir index 975e2ffbf01e7..9f235248ca59f 100644 --- a/src/test/mir-opt/nll/named_lifetimes_basic.use_x.nll.0.mir +++ b/src/test/mir-opt/nll/named_lifetimes_basic.use_x.nll.0.mir @@ -42,7 +42,7 @@ fn use_x(_1: &'_#6r mut i32, _2: &'_#7r u32, _3: &'_#8r u32, _4: &'_#9r u32) -> let mut _0: bool; // return place in scope 0 at $DIR/named-lifetimes-basic.rs:12:81: 12:85 bb0: { - _0 = const Const(Value(Scalar(0x01)): bool); // bb0[0]: scope 0 at $DIR/named-lifetimes-basic.rs:12:88: 12:92 + _0 = const ConstValue(Scalar(0x01): bool); // bb0[0]: scope 0 at $DIR/named-lifetimes-basic.rs:12:88: 12:92 return; // bb0[1]: scope 0 at $DIR/named-lifetimes-basic.rs:12:94: 12:94 } } diff --git a/src/test/mir-opt/nll/region_subtyping_basic.main.nll.0.32bit.mir b/src/test/mir-opt/nll/region_subtyping_basic.main.nll.0.32bit.mir index 3ef7844ce434f..ed94a1ecf0062 100644 --- a/src/test/mir-opt/nll/region_subtyping_basic.main.nll.0.32bit.mir +++ b/src/test/mir-opt/nll/region_subtyping_basic.main.nll.0.32bit.mir @@ -45,11 +45,11 @@ fn main() -> () { bb0: { StorageLive(_1); // bb0[0]: scope 0 at $DIR/region-subtyping-basic.rs:17:9: 17:14 - _1 = [const Const(Value(Scalar(0x00000001)): usize), const Const(Value(Scalar(0x00000002)): usize), const Const(Value(Scalar(0x00000003)): usize)]; // bb0[1]: scope 0 at $DIR/region-subtyping-basic.rs:17:17: 17:26 + _1 = [const ConstValue(Scalar(0x00000001): usize), const ConstValue(Scalar(0x00000002): usize), const ConstValue(Scalar(0x00000003): usize)]; // bb0[1]: scope 0 at $DIR/region-subtyping-basic.rs:17:17: 17:26 FakeRead(ForLet(None), _1); // bb0[2]: scope 0 at $DIR/region-subtyping-basic.rs:17:9: 17:14 StorageLive(_2); // bb0[3]: scope 1 at $DIR/region-subtyping-basic.rs:18:9: 18:10 StorageLive(_3); // bb0[4]: scope 1 at $DIR/region-subtyping-basic.rs:18:16: 18:17 - _3 = const Const(Value(Scalar(0x00000000)): usize); // bb0[5]: scope 1 at $DIR/region-subtyping-basic.rs:18:16: 18:17 + _3 = const ConstValue(Scalar(0x00000000): usize); // bb0[5]: scope 1 at $DIR/region-subtyping-basic.rs:18:16: 18:17 _4 = Len(_1); // bb0[6]: scope 1 at $DIR/region-subtyping-basic.rs:18:14: 18:18 _5 = Lt(_3, _4); // bb0[7]: scope 1 at $DIR/region-subtyping-basic.rs:18:14: 18:18 assert(move _5, "index out of bounds: the length is {} but the index is {}", move _4, _3) -> [success: bb1, unwind: bb7]; // bb0[8]: scope 1 at $DIR/region-subtyping-basic.rs:18:14: 18:18 @@ -62,7 +62,7 @@ fn main() -> () { _6 = _2; // bb1[3]: scope 2 at $DIR/region-subtyping-basic.rs:19:13: 19:14 FakeRead(ForLet(None), _6); // bb1[4]: scope 2 at $DIR/region-subtyping-basic.rs:19:9: 19:10 StorageLive(_7); // bb1[5]: scope 3 at $DIR/region-subtyping-basic.rs:20:8: 20:12 - _7 = const Const(Value(Scalar(0x01)): bool); // bb1[6]: scope 3 at $DIR/region-subtyping-basic.rs:20:8: 20:12 + _7 = const ConstValue(Scalar(0x01): bool); // bb1[6]: scope 3 at $DIR/region-subtyping-basic.rs:20:8: 20:12 switchInt(move _7) -> [Const(Value(Scalar(0x00)): bool): bb4, otherwise: bb2]; // bb1[7]: scope 3 at $DIR/region-subtyping-basic.rs:20:8: 20:12 } @@ -70,7 +70,7 @@ fn main() -> () { StorageLive(_8); // bb2[0]: scope 3 at $DIR/region-subtyping-basic.rs:21:9: 21:18 StorageLive(_9); // bb2[1]: scope 3 at $DIR/region-subtyping-basic.rs:21:15: 21:17 _9 = (*_6); // bb2[2]: scope 3 at $DIR/region-subtyping-basic.rs:21:15: 21:17 - _8 = Const(Value(Scalar()): fn(usize) -> bool {use_x})(move _9) -> [return: bb3, unwind: bb7]; // bb2[3]: scope 3 at $DIR/region-subtyping-basic.rs:21:9: 21:18 + _8 = ConstValue(Scalar(): fn(usize) -> bool {use_x})(move _9) -> [return: bb3, unwind: bb7]; // bb2[3]: scope 3 at $DIR/region-subtyping-basic.rs:21:9: 21:18 // mir::Constant // + span: $DIR/region-subtyping-basic.rs:21:9: 21:14 // + literal: Const { ty: fn(usize) -> bool {use_x}, val: Value(Scalar()) } @@ -79,13 +79,13 @@ fn main() -> () { bb3: { StorageDead(_9); // bb3[0]: scope 3 at $DIR/region-subtyping-basic.rs:21:17: 21:18 StorageDead(_8); // bb3[1]: scope 3 at $DIR/region-subtyping-basic.rs:21:18: 21:19 - _0 = const Const(Value(Scalar()): ()); // bb3[2]: scope 3 at $DIR/region-subtyping-basic.rs:20:13: 22:6 + _0 = const ConstValue(Scalar(): ()); // bb3[2]: scope 3 at $DIR/region-subtyping-basic.rs:20:13: 22:6 goto -> bb6; // bb3[3]: scope 3 at $DIR/region-subtyping-basic.rs:20:5: 24:6 } bb4: { StorageLive(_10); // bb4[0]: scope 3 at $DIR/region-subtyping-basic.rs:23:9: 23:18 - _10 = Const(Value(Scalar()): fn(usize) -> bool {use_x})(const Const(Value(Scalar(0x00000016)): usize)) -> [return: bb5, unwind: bb7]; // bb4[1]: scope 3 at $DIR/region-subtyping-basic.rs:23:9: 23:18 + _10 = ConstValue(Scalar(): fn(usize) -> bool {use_x})(const ConstValue(Scalar(0x00000016): usize)) -> [return: bb5, unwind: bb7]; // bb4[1]: scope 3 at $DIR/region-subtyping-basic.rs:23:9: 23:18 // mir::Constant // + span: $DIR/region-subtyping-basic.rs:23:9: 23:14 // + literal: Const { ty: fn(usize) -> bool {use_x}, val: Value(Scalar()) } @@ -93,7 +93,7 @@ fn main() -> () { bb5: { StorageDead(_10); // bb5[0]: scope 3 at $DIR/region-subtyping-basic.rs:23:18: 23:19 - _0 = const Const(Value(Scalar()): ()); // bb5[1]: scope 3 at $DIR/region-subtyping-basic.rs:22:12: 24:6 + _0 = const ConstValue(Scalar(): ()); // bb5[1]: scope 3 at $DIR/region-subtyping-basic.rs:22:12: 24:6 goto -> bb6; // bb5[2]: scope 3 at $DIR/region-subtyping-basic.rs:20:5: 24:6 } diff --git a/src/test/mir-opt/nll/region_subtyping_basic.main.nll.0.64bit.mir b/src/test/mir-opt/nll/region_subtyping_basic.main.nll.0.64bit.mir index d6b4a0beebd65..95997b2070172 100644 --- a/src/test/mir-opt/nll/region_subtyping_basic.main.nll.0.64bit.mir +++ b/src/test/mir-opt/nll/region_subtyping_basic.main.nll.0.64bit.mir @@ -45,11 +45,11 @@ fn main() -> () { bb0: { StorageLive(_1); // bb0[0]: scope 0 at $DIR/region-subtyping-basic.rs:17:9: 17:14 - _1 = [const Const(Value(Scalar(0x0000000000000001)): usize), const Const(Value(Scalar(0x0000000000000002)): usize), const Const(Value(Scalar(0x0000000000000003)): usize)]; // bb0[1]: scope 0 at $DIR/region-subtyping-basic.rs:17:17: 17:26 + _1 = [const ConstValue(Scalar(0x0000000000000001): usize), const ConstValue(Scalar(0x0000000000000002): usize), const ConstValue(Scalar(0x0000000000000003): usize)]; // bb0[1]: scope 0 at $DIR/region-subtyping-basic.rs:17:17: 17:26 FakeRead(ForLet(None), _1); // bb0[2]: scope 0 at $DIR/region-subtyping-basic.rs:17:9: 17:14 StorageLive(_2); // bb0[3]: scope 1 at $DIR/region-subtyping-basic.rs:18:9: 18:10 StorageLive(_3); // bb0[4]: scope 1 at $DIR/region-subtyping-basic.rs:18:16: 18:17 - _3 = const Const(Value(Scalar(0x0000000000000000)): usize); // bb0[5]: scope 1 at $DIR/region-subtyping-basic.rs:18:16: 18:17 + _3 = const ConstValue(Scalar(0x0000000000000000): usize); // bb0[5]: scope 1 at $DIR/region-subtyping-basic.rs:18:16: 18:17 _4 = Len(_1); // bb0[6]: scope 1 at $DIR/region-subtyping-basic.rs:18:14: 18:18 _5 = Lt(_3, _4); // bb0[7]: scope 1 at $DIR/region-subtyping-basic.rs:18:14: 18:18 assert(move _5, "index out of bounds: the length is {} but the index is {}", move _4, _3) -> [success: bb1, unwind: bb7]; // bb0[8]: scope 1 at $DIR/region-subtyping-basic.rs:18:14: 18:18 @@ -62,7 +62,7 @@ fn main() -> () { _6 = _2; // bb1[3]: scope 2 at $DIR/region-subtyping-basic.rs:19:13: 19:14 FakeRead(ForLet(None), _6); // bb1[4]: scope 2 at $DIR/region-subtyping-basic.rs:19:9: 19:10 StorageLive(_7); // bb1[5]: scope 3 at $DIR/region-subtyping-basic.rs:20:8: 20:12 - _7 = const Const(Value(Scalar(0x01)): bool); // bb1[6]: scope 3 at $DIR/region-subtyping-basic.rs:20:8: 20:12 + _7 = const ConstValue(Scalar(0x01): bool); // bb1[6]: scope 3 at $DIR/region-subtyping-basic.rs:20:8: 20:12 switchInt(move _7) -> [Const(Value(Scalar(0x00)): bool): bb4, otherwise: bb2]; // bb1[7]: scope 3 at $DIR/region-subtyping-basic.rs:20:8: 20:12 } @@ -70,7 +70,7 @@ fn main() -> () { StorageLive(_8); // bb2[0]: scope 3 at $DIR/region-subtyping-basic.rs:21:9: 21:18 StorageLive(_9); // bb2[1]: scope 3 at $DIR/region-subtyping-basic.rs:21:15: 21:17 _9 = (*_6); // bb2[2]: scope 3 at $DIR/region-subtyping-basic.rs:21:15: 21:17 - _8 = Const(Value(Scalar()): fn(usize) -> bool {use_x})(move _9) -> [return: bb3, unwind: bb7]; // bb2[3]: scope 3 at $DIR/region-subtyping-basic.rs:21:9: 21:18 + _8 = ConstValue(Scalar(): fn(usize) -> bool {use_x})(move _9) -> [return: bb3, unwind: bb7]; // bb2[3]: scope 3 at $DIR/region-subtyping-basic.rs:21:9: 21:18 // mir::Constant // + span: $DIR/region-subtyping-basic.rs:21:9: 21:14 // + literal: Const { ty: fn(usize) -> bool {use_x}, val: Value(Scalar()) } @@ -79,13 +79,13 @@ fn main() -> () { bb3: { StorageDead(_9); // bb3[0]: scope 3 at $DIR/region-subtyping-basic.rs:21:17: 21:18 StorageDead(_8); // bb3[1]: scope 3 at $DIR/region-subtyping-basic.rs:21:18: 21:19 - _0 = const Const(Value(Scalar()): ()); // bb3[2]: scope 3 at $DIR/region-subtyping-basic.rs:20:13: 22:6 + _0 = const ConstValue(Scalar(): ()); // bb3[2]: scope 3 at $DIR/region-subtyping-basic.rs:20:13: 22:6 goto -> bb6; // bb3[3]: scope 3 at $DIR/region-subtyping-basic.rs:20:5: 24:6 } bb4: { StorageLive(_10); // bb4[0]: scope 3 at $DIR/region-subtyping-basic.rs:23:9: 23:18 - _10 = Const(Value(Scalar()): fn(usize) -> bool {use_x})(const Const(Value(Scalar(0x0000000000000016)): usize)) -> [return: bb5, unwind: bb7]; // bb4[1]: scope 3 at $DIR/region-subtyping-basic.rs:23:9: 23:18 + _10 = ConstValue(Scalar(): fn(usize) -> bool {use_x})(const ConstValue(Scalar(0x0000000000000016): usize)) -> [return: bb5, unwind: bb7]; // bb4[1]: scope 3 at $DIR/region-subtyping-basic.rs:23:9: 23:18 // mir::Constant // + span: $DIR/region-subtyping-basic.rs:23:9: 23:14 // + literal: Const { ty: fn(usize) -> bool {use_x}, val: Value(Scalar()) } @@ -93,7 +93,7 @@ fn main() -> () { bb5: { StorageDead(_10); // bb5[0]: scope 3 at $DIR/region-subtyping-basic.rs:23:18: 23:19 - _0 = const Const(Value(Scalar()): ()); // bb5[1]: scope 3 at $DIR/region-subtyping-basic.rs:22:12: 24:6 + _0 = const ConstValue(Scalar(): ()); // bb5[1]: scope 3 at $DIR/region-subtyping-basic.rs:22:12: 24:6 goto -> bb6; // bb5[2]: scope 3 at $DIR/region-subtyping-basic.rs:20:5: 24:6 } diff --git a/src/test/rustdoc-gui/docblock-table-overflow.goml b/src/test/rustdoc-gui/docblock-table-overflow.goml index a9af88189a67d..af76d2ea42760 100644 --- a/src/test/rustdoc-gui/docblock-table-overflow.goml +++ b/src/test/rustdoc-gui/docblock-table-overflow.goml @@ -12,10 +12,10 @@ assert-property: (".top-doc .docblock table", {"scrollWidth": "1573"}) // Logically, the ".docblock" and the "

" should have the same scroll width. compare-elements-property: ( - "#implementations + details .docblock", - "#implementations + details .docblock > p", + "#implementations-list > details .docblock", + "#implementations-list > details .docblock > p", ["scrollWidth"], ) -assert-property: ("#implementations + details .docblock", {"scrollWidth": "801"}) +assert-property: ("#implementations-list > details .docblock", {"scrollWidth": "801"}) // However, since there is overflow in the , its scroll width is bigger. -assert-property: ("#implementations + details .docblock table", {"scrollWidth": "1573"}) +assert-property: ("#implementations-list > details .docblock table", {"scrollWidth": "1573"}) diff --git a/src/test/rustdoc-gui/hash-item-expansion.goml b/src/test/rustdoc-gui/hash-item-expansion.goml index a680635ef8ae4..861f6928362c2 100644 --- a/src/test/rustdoc-gui/hash-item-expansion.goml +++ b/src/test/rustdoc-gui/hash-item-expansion.goml @@ -3,9 +3,9 @@ goto: file://|DOC_PATH|/test_docs/struct.Foo.html#method.borrow // In the blanket implementations list, "Borrow" is the second one, hence the ":nth(2)". assert-attribute: ("#blanket-implementations-list > details:nth-child(2)", {"open": ""}) // We first check that the impl block is open by default. -assert-attribute: ("#implementations + details", {"open": ""}) +assert-attribute: ("#implementations-list details", {"open": ""}) // To ensure that we will click on the currently hidden method. assert-text: (".sidebar-elems section .block li > a", "must_use") click: ".sidebar-elems section .block li > a" // We check that the impl block was opened as expected so that we can see the method. -assert-attribute: ("#implementations + details", {"open": ""}) +assert-attribute: ("#implementations-list > details", {"open": ""}) diff --git a/src/test/rustdoc-gui/impl-default-expansion.goml b/src/test/rustdoc-gui/impl-default-expansion.goml index 7c4496dc0cabc..6df2661e6c2bb 100644 --- a/src/test/rustdoc-gui/impl-default-expansion.goml +++ b/src/test/rustdoc-gui/impl-default-expansion.goml @@ -1,3 +1,3 @@ // This test ensures that the impl blocks are open by default. goto: file://|DOC_PATH|/test_docs/struct.Foo.html -assert-attribute: ("#main-content > details.implementors-toggle", {"open": ""}) +assert-attribute: ("#implementations-list details.implementors-toggle", {"open": ""}) diff --git a/src/test/rustdoc-gui/toggle-docs-mobile.goml b/src/test/rustdoc-gui/toggle-docs-mobile.goml index b502692300113..ee6bc3cf7675c 100644 --- a/src/test/rustdoc-gui/toggle-docs-mobile.goml +++ b/src/test/rustdoc-gui/toggle-docs-mobile.goml @@ -14,7 +14,7 @@ assert-attribute: (".top-doc", {"open": ""}) // Assert the position of the toggle on the top doc block. assert-position: (".top-doc summary::before", {"x": 4}) // Assert the position of the toggle on the impl block. -assert-position: ("#implementations + details > summary::before", {"x": 4}) +assert-position: ("#implementations-list > details > summary::before", {"x": 4}) // Assert the position of the toggle on a method. assert-position: ( "#trait-implementations-list .impl-items .method-toggle > summary::before", diff --git a/src/test/rustdoc-gui/toggle-docs.goml b/src/test/rustdoc-gui/toggle-docs.goml index 477105193d3ee..f98111484f315 100644 --- a/src/test/rustdoc-gui/toggle-docs.goml +++ b/src/test/rustdoc-gui/toggle-docs.goml @@ -24,7 +24,7 @@ wait-for: 50 assert-text: ("#toggle-all-docs", "[+]") // We check that all
are collapsed (except for the impl block ones). assert-attribute-false: ("details.rustdoc-toggle:not(.implementors-toggle)", {"open": ""}, ALL) -assert-attribute: ("details.rustdoc-toggle.implementors-toggle", {"open": ""}) +assert-attribute: ("#implementations-list > details.implementors-toggle", {"open": ""}) // We now check that the other impl blocks are collapsed. assert-attribute-false: ( "#blanket-implementations-list > details.rustdoc-toggle.implementors-toggle", diff --git a/src/test/rustdoc/duplicate_impls/issue-33054.rs b/src/test/rustdoc/duplicate_impls/issue-33054.rs index b018dd6cda58a..84c9e4ac0cd84 100644 --- a/src/test/rustdoc/duplicate_impls/issue-33054.rs +++ b/src/test/rustdoc/duplicate_impls/issue-33054.rs @@ -1,8 +1,10 @@ +// ignore-tidy-linelength + // @has issue_33054/impls/struct.Foo.html // @has - '//h3[@class="code-header in-band"]' 'impl Foo' // @has - '//h3[@class="code-header in-band"]' 'impl Bar for Foo' // @count - '//*[@id="trait-implementations-list"]//*[@class="impl has-srclink"]' 1 -// @count - '//*[@id="main-content"]/details/summary/*[@class="impl has-srclink"]' 1 +// @count - '//*[@id="main-content"]/div[@id="implementations-list"]/details/summary/*[@class="impl has-srclink"]' 1 // @has issue_33054/impls/bar/trait.Bar.html // @has - '//h3[@class="code-header in-band"]' 'impl Bar for Foo' // @count - '//*[@class="struct"]' 1 diff --git a/src/test/ui/associated-consts/defaults-cyclic-fail.rs b/src/test/ui/associated-consts/defaults-cyclic-fail.rs index 9fb1bbebc9610..2f1858d39b3c6 100644 --- a/src/test/ui/associated-consts/defaults-cyclic-fail.rs +++ b/src/test/ui/associated-consts/defaults-cyclic-fail.rs @@ -1,5 +1,5 @@ // build-fail -//~^ ERROR cycle detected when normalizing `<() as Tr>::A` +//~^ ERROR cycle detected when normalizing `<() as Tr>::A` [E0391] // Cyclic assoc. const defaults don't error unless *used* trait Tr { diff --git a/src/test/ui/check-cfg/well-known-names.rs b/src/test/ui/check-cfg/well-known-names.rs index a66568a2ffdc9..e57fb69a1e05f 100644 --- a/src/test/ui/check-cfg/well-known-names.rs +++ b/src/test/ui/check-cfg/well-known-names.rs @@ -24,4 +24,10 @@ fn unix_misspell() {} #[cfg(unix)] fn unix() {} +#[cfg(miri)] +fn miri() {} + +#[cfg(doc)] +fn doc() {} + fn main() {} diff --git a/src/test/ui/check-cfg/well-known-values.rs b/src/test/ui/check-cfg/well-known-values.rs index 46004be43d812..96375dc8d3130 100644 --- a/src/test/ui/check-cfg/well-known-values.rs +++ b/src/test/ui/check-cfg/well-known-values.rs @@ -25,4 +25,18 @@ fn unix_with_value() {} #[cfg(unix)] fn unix() {} +#[cfg(miri = "miri")] +//~^ WARNING unexpected `cfg` condition value +fn miri_with_value() {} + +#[cfg(miri)] +fn miri() {} + +#[cfg(doc = "linux")] +//~^ WARNING unexpected `cfg` condition value +fn doc_with_value() {} + +#[cfg(doc)] +fn doc() {} + fn main() {} diff --git a/src/test/ui/check-cfg/well-known-values.stderr b/src/test/ui/check-cfg/well-known-values.stderr index 8eefd6aaf35ec..8b04c770fb505 100644 --- a/src/test/ui/check-cfg/well-known-values.stderr +++ b/src/test/ui/check-cfg/well-known-values.stderr @@ -29,5 +29,25 @@ LL | #[cfg(unix = "aa")] | = note: no expected value for `unix` -warning: 3 warnings emitted +warning: unexpected `cfg` condition value + --> $DIR/well-known-values.rs:28:7 + | +LL | #[cfg(miri = "miri")] + | ^^^^--------- + | | + | help: remove the value + | + = note: no expected value for `miri` + +warning: unexpected `cfg` condition value + --> $DIR/well-known-values.rs:35:7 + | +LL | #[cfg(doc = "linux")] + | ^^^---------- + | | + | help: remove the value + | + = note: no expected value for `doc` + +warning: 5 warnings emitted diff --git a/src/test/ui/consts/const-eval/const-eval-query-stack.rs b/src/test/ui/consts/const-eval/const-eval-query-stack.rs index 8c3959cc11a43..e73e54ff5f1b3 100644 --- a/src/test/ui/consts/const-eval/const-eval-query-stack.rs +++ b/src/test/ui/consts/const-eval/const-eval-query-stack.rs @@ -1,5 +1,5 @@ -//~ERROR constructed but no error reported // compile-flags: -Ztreat-err-as-bug=2 +//~^ ERROR 1:1: 1:1: ty::ConstKind::Error constructed but no error reported // build-fail // failure-status: 101 // rustc-env:RUST_BACKTRACE=1 diff --git a/src/test/ui/consts/const-match-check.eval1.stderr b/src/test/ui/consts/const-match-check.eval1.stderr index 4141cc4ab1a48..6e61dbbd8eee3 100644 --- a/src/test/ui/consts/const-match-check.eval1.stderr +++ b/src/test/ui/consts/const-match-check.eval1.stderr @@ -7,10 +7,10 @@ LL | A = { let 0 = 0; 0 }, = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html = note: the matched value is of type `i32` -help: you might want to use `if let` to ignore the variant that isn't matched +help: you might want to use `if let` to ignore the variants that aren't matched | -LL | A = { if let 0 = 0 { /* */ } 0 }, - | ~~~~~~~~~~~~~~~~~~~~~~ +LL | A = { if let 0 = 0 { todo!() } 0 }, + | ++ ~~~~~~~~~~~ error: aborting due to previous error diff --git a/src/test/ui/consts/const-match-check.eval2.stderr b/src/test/ui/consts/const-match-check.eval2.stderr index af86ba0cc82f8..1b3b6e06c3df6 100644 --- a/src/test/ui/consts/const-match-check.eval2.stderr +++ b/src/test/ui/consts/const-match-check.eval2.stderr @@ -7,10 +7,10 @@ LL | let x: [i32; { let 0 = 0; 0 }] = []; = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html = note: the matched value is of type `i32` -help: you might want to use `if let` to ignore the variant that isn't matched +help: you might want to use `if let` to ignore the variants that aren't matched | -LL | let x: [i32; { if let 0 = 0 { /* */ } 0 }] = []; - | ~~~~~~~~~~~~~~~~~~~~~~ +LL | let x: [i32; { if let 0 = 0 { todo!() } 0 }] = []; + | ++ ~~~~~~~~~~~ error: aborting due to previous error diff --git a/src/test/ui/consts/const-match-check.matchck.stderr b/src/test/ui/consts/const-match-check.matchck.stderr index f71490eba6135..bc8edfa7af9f4 100644 --- a/src/test/ui/consts/const-match-check.matchck.stderr +++ b/src/test/ui/consts/const-match-check.matchck.stderr @@ -7,10 +7,10 @@ LL | const X: i32 = { let 0 = 0; 0 }; = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html = note: the matched value is of type `i32` -help: you might want to use `if let` to ignore the variant that isn't matched +help: you might want to use `if let` to ignore the variants that aren't matched | -LL | const X: i32 = { if let 0 = 0 { /* */ } 0 }; - | ~~~~~~~~~~~~~~~~~~~~~~ +LL | const X: i32 = { if let 0 = 0 { todo!() } 0 }; + | ++ ~~~~~~~~~~~ error[E0005]: refutable pattern in local binding: `i32::MIN..=-1_i32` and `1_i32..=i32::MAX` not covered --> $DIR/const-match-check.rs:8:23 @@ -21,10 +21,10 @@ LL | static Y: i32 = { let 0 = 0; 0 }; = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html = note: the matched value is of type `i32` -help: you might want to use `if let` to ignore the variant that isn't matched +help: you might want to use `if let` to ignore the variants that aren't matched | -LL | static Y: i32 = { if let 0 = 0 { /* */ } 0 }; - | ~~~~~~~~~~~~~~~~~~~~~~ +LL | static Y: i32 = { if let 0 = 0 { todo!() } 0 }; + | ++ ~~~~~~~~~~~ error[E0005]: refutable pattern in local binding: `i32::MIN..=-1_i32` and `1_i32..=i32::MAX` not covered --> $DIR/const-match-check.rs:13:26 @@ -35,10 +35,10 @@ LL | const X: i32 = { let 0 = 0; 0 }; = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html = note: the matched value is of type `i32` -help: you might want to use `if let` to ignore the variant that isn't matched +help: you might want to use `if let` to ignore the variants that aren't matched | -LL | const X: i32 = { if let 0 = 0 { /* */ } 0 }; - | ~~~~~~~~~~~~~~~~~~~~~~ +LL | const X: i32 = { if let 0 = 0 { todo!() } 0 }; + | ++ ~~~~~~~~~~~ error[E0005]: refutable pattern in local binding: `i32::MIN..=-1_i32` and `1_i32..=i32::MAX` not covered --> $DIR/const-match-check.rs:19:26 @@ -49,10 +49,10 @@ LL | const X: i32 = { let 0 = 0; 0 }; = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html = note: the matched value is of type `i32` -help: you might want to use `if let` to ignore the variant that isn't matched +help: you might want to use `if let` to ignore the variants that aren't matched | -LL | const X: i32 = { if let 0 = 0 { /* */ } 0 }; - | ~~~~~~~~~~~~~~~~~~~~~~ +LL | const X: i32 = { if let 0 = 0 { todo!() } 0 }; + | ++ ~~~~~~~~~~~ error: aborting due to 4 previous errors diff --git a/src/test/ui/empty/empty-never-array.stderr b/src/test/ui/empty/empty-never-array.stderr index 8dd0f377533ce..909aa73a74a38 100644 --- a/src/test/ui/empty/empty-never-array.stderr +++ b/src/test/ui/empty/empty-never-array.stderr @@ -16,8 +16,12 @@ LL | T(T, [!; 0]), = note: the matched value is of type `Helper` help: you might want to use `if let` to ignore the variant that isn't matched | -LL | if let Helper::U(u) = Helper::T(t, []) { /* */ } +LL | let u = if let Helper::U(u) = Helper::T(t, []) { u } else { todo!() }; + | ++++++++++ ++++++++++++++++++++++ +help: alternatively, on nightly, you might want to use `#![feature(let_else)]` to handle the variant that isn't matched | +LL | let Helper::U(u) = Helper::T(t, []) else { todo!() }; + | ++++++++++++++++ error: aborting due to previous error diff --git a/src/test/ui/error-codes/E0005.stderr b/src/test/ui/error-codes/E0005.stderr index 208c625a53e95..55b1112b5f8ec 100644 --- a/src/test/ui/error-codes/E0005.stderr +++ b/src/test/ui/error-codes/E0005.stderr @@ -22,8 +22,12 @@ LL | | } = note: the matched value is of type `Option` help: you might want to use `if let` to ignore the variant that isn't matched | -LL | if let Some(y) = x { /* */ } - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL | let y = if let Some(y) = x { y } else { todo!() }; + | ++++++++++ ++++++++++++++++++++++ +help: alternatively, on nightly, you might want to use `#![feature(let_else)]` to handle the variant that isn't matched + | +LL | let Some(y) = x else { todo!() }; + | ++++++++++++++++ error: aborting due to previous error diff --git a/src/test/ui/feature-gates/feature-gate-exhaustive-patterns.stderr b/src/test/ui/feature-gates/feature-gate-exhaustive-patterns.stderr index c2ffda6bb72d2..21180f31bbd26 100644 --- a/src/test/ui/feature-gates/feature-gate-exhaustive-patterns.stderr +++ b/src/test/ui/feature-gates/feature-gate-exhaustive-patterns.stderr @@ -21,8 +21,12 @@ LL | | } = note: the matched value is of type `Result` help: you might want to use `if let` to ignore the variant that isn't matched | -LL | if let Ok(_x) = foo() { /* */ } - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL | let _x = if let Ok(_x) = foo() { _x } else { todo!() }; + | +++++++++++ +++++++++++++++++++++++ +help: alternatively, on nightly, you might want to use `#![feature(let_else)]` to handle the variant that isn't matched + | +LL | let Ok(_x) = foo() else { todo!() }; + | ++++++++++++++++ error: aborting due to previous error diff --git a/src/test/ui/macros/rfc-3086-metavar-expr/dollar-dollar-has-correct-behavior.rs b/src/test/ui/macros/rfc-3086-metavar-expr/dollar-dollar-has-correct-behavior.rs new file mode 100644 index 0000000000000..ed94c27cf05ca --- /dev/null +++ b/src/test/ui/macros/rfc-3086-metavar-expr/dollar-dollar-has-correct-behavior.rs @@ -0,0 +1,28 @@ +// run-pass + +#![feature(macro_metavar_expr)] + +macro_rules! nested { + ( $a:ident ) => { + macro_rules! $a { + ( $$( $b:ident ),* ) => { + $$( + macro_rules! $b { + ( $$$$( $c:ident ),* ) => { + $$$$( + fn $c() -> &'static str { stringify!($c) } + ),* + }; + } + )* + }; + } + }; +} + +fn main() { + nested!(a); + a!(b); + b!(c); + assert_eq!(c(), "c"); +} diff --git a/src/test/ui/macros/rfc-3086-metavar-expr/feature-gate-macro_metavar_expr.rs b/src/test/ui/macros/rfc-3086-metavar-expr/feature-gate-macro_metavar_expr.rs new file mode 100644 index 0000000000000..6434ecc7e092d --- /dev/null +++ b/src/test/ui/macros/rfc-3086-metavar-expr/feature-gate-macro_metavar_expr.rs @@ -0,0 +1,14 @@ +// run-pass + +#![feature(macro_metavar_expr)] + +macro_rules! ignore { + ( $( $i:ident ),* ) => {{ + let array: [i32; 0] = [$( ${ignore(i)} )*]; + array + }}; +} + +fn main() { + assert_eq!(ignore!(a, b, c), []); +} diff --git a/src/test/ui/macros/rfc-3086-metavar-expr/required-feature.rs b/src/test/ui/macros/rfc-3086-metavar-expr/required-feature.rs new file mode 100644 index 0000000000000..cff6f29a15386 --- /dev/null +++ b/src/test/ui/macros/rfc-3086-metavar-expr/required-feature.rs @@ -0,0 +1,9 @@ +macro_rules! count { + ( $( $e:stmt ),* ) => { + ${ count(e) } + //~^ ERROR meta-variable expressions are unstable + }; +} + +fn main() { +} diff --git a/src/test/ui/macros/rfc-3086-metavar-expr/required-feature.stderr b/src/test/ui/macros/rfc-3086-metavar-expr/required-feature.stderr new file mode 100644 index 0000000000000..f573194479314 --- /dev/null +++ b/src/test/ui/macros/rfc-3086-metavar-expr/required-feature.stderr @@ -0,0 +1,12 @@ +error[E0658]: meta-variable expressions are unstable + --> $DIR/required-feature.rs:3:10 + | +LL | ${ count(e) } + | ^^^^^^^^^^^^ + | + = note: see issue #83527 for more information + = help: add `#![feature(macro_metavar_expr)]` to the crate attributes to enable + +error: aborting due to previous error + +For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/macros/rfc-3086-metavar-expr/syntax-errors.rs b/src/test/ui/macros/rfc-3086-metavar-expr/syntax-errors.rs new file mode 100644 index 0000000000000..ea73fd0813c5b --- /dev/null +++ b/src/test/ui/macros/rfc-3086-metavar-expr/syntax-errors.rs @@ -0,0 +1,148 @@ +#![feature(macro_metavar_expr)] + +// `curly` = Right hand side curly brackets +// `no_rhs_dollar` = No dollar sign at the right hand side meta variable "function" +// `round` = Left hand side round brackets + +macro_rules! curly__no_rhs_dollar__round { + ( $( $i:ident ),* ) => { ${ count(i) } }; +} + +macro_rules! curly__no_rhs_dollar__no_round { + ( $i:ident ) => { ${ count(i) } }; +} + +macro_rules! curly__rhs_dollar__round { + ( $( $i:ident ),* ) => { ${ count($i) } }; + //~^ ERROR expected identifier, found `$` + //~| ERROR expected expression, found `$` +} + +macro_rules! curly__rhs_dollar__no_round { + ( $i:ident ) => { ${ count($i) } }; + //~^ ERROR expected identifier, found `$` + //~| ERROR expected expression, found `$` +} + +macro_rules! no_curly__no_rhs_dollar__round { + ( $( $i:ident ),* ) => { count(i) }; + //~^ ERROR cannot find function `count` in this scope + //~| ERROR cannot find value `i` in this scope +} + +macro_rules! no_curly__no_rhs_dollar__no_round { + ( $i:ident ) => { count(i) }; + //~^ ERROR cannot find function `count` in this scope + //~| ERROR cannot find value `i` in this scope +} + +macro_rules! no_curly__rhs_dollar__round { + ( $( $i:ident ),* ) => { count($i) }; + //~^ ERROR variable 'i' is still repeating at this depth +} + +macro_rules! no_curly__rhs_dollar__no_round { + ( $i:ident ) => { count($i) }; + //~^ ERROR cannot find function `count` in this scope +} + +// Other scenarios + +macro_rules! dollar_dollar_in_the_lhs { + ( $$ $a:ident ) => { + //~^ ERROR unexpected token: $ + }; +} + +macro_rules! extra_garbage_after_metavar { + ( $( $i:ident ),* ) => { + ${count() a b c} + //~^ ERROR unexpected token: a + //~| ERROR expected expression, found `$` + ${count(i a b c)} + //~^ ERROR unexpected token: a + ${count(i, 1 a b c)} + //~^ ERROR unexpected token: a + ${count(i) a b c} + //~^ ERROR unexpected token: a + + ${ignore(i) a b c} + //~^ ERROR unexpected token: a + ${ignore(i a b c)} + //~^ ERROR unexpected token: a + + ${index() a b c} + //~^ ERROR unexpected token: a + ${index(1 a b c)} + //~^ ERROR unexpected token: a + + ${index() a b c} + //~^ ERROR unexpected token: a + ${index(1 a b c)} + //~^ ERROR unexpected token: a + }; +} + +const IDX: usize = 1; +macro_rules! metavar_depth_is_not_literal { + ( $( $i:ident ),* ) => { ${ index(IDX) } }; + //~^ ERROR meta-variable expression depth must be a literal + //~| ERROR expected expression, found `$` +} + +macro_rules! metavar_in_the_lhs { + ( ${ length() } ) => { + //~^ ERROR unexpected token: { + //~| ERROR expected one of: `*`, `+`, or `?` + }; +} + +macro_rules! metavar_token_without_ident { + ( $( $i:ident ),* ) => { ${ ignore() } }; + //~^ ERROR expected identifier + //~| ERROR expected expression, found `$` +} + +macro_rules! metavar_with_literal_suffix { + ( $( $i:ident ),* ) => { ${ index(1u32) } }; + //~^ ERROR only unsuffixes integer literals are supported in meta-variable expressions + //~| ERROR expected expression, found `$` +} + +macro_rules! metavar_without_parens { + ( $( $i:ident ),* ) => { ${ count{i} } }; + //~^ ERROR meta-variable expression parameter must be wrapped in parentheses + //~| ERROR expected expression, found `$` +} + +macro_rules! open_brackets_without_tokens { + ( $( $i:ident ),* ) => { ${ {} } }; + //~^ ERROR expected expression, found `$` + //~| ERROR expected identifier +} + +macro_rules! unknown_metavar { + ( $( $i:ident ),* ) => { ${ aaaaaaaaaaaaaa(i) } }; + //~^ ERROR unrecognized meta-variable expression + //~| ERROR expected expression +} + +fn main() { + curly__no_rhs_dollar__round!(a, b, c); + curly__no_rhs_dollar__no_round!(a); + curly__rhs_dollar__round!(a, b, c); + curly__rhs_dollar__no_round!(a); + no_curly__no_rhs_dollar__round!(a, b, c); + no_curly__no_rhs_dollar__no_round!(a); + no_curly__rhs_dollar__round!(a, b, c); + no_curly__rhs_dollar__no_round!(a); + //~^ ERROR cannot find value `a` in this scope + + extra_garbage_after_metavar!(a); + unknown_metavar!(a); + metavar_without_parens!(a); + metavar_token_without_ident!(a); + metavar_depth_is_not_literal!(a); + metavar_with_literal_suffix!(a); + open_brackets_without_tokens!(a) +} diff --git a/src/test/ui/macros/rfc-3086-metavar-expr/syntax-errors.stderr b/src/test/ui/macros/rfc-3086-metavar-expr/syntax-errors.stderr new file mode 100644 index 0000000000000..dc8b7a668c4ee --- /dev/null +++ b/src/test/ui/macros/rfc-3086-metavar-expr/syntax-errors.stderr @@ -0,0 +1,367 @@ +error: expected identifier, found `$` + --> $DIR/syntax-errors.rs:16:33 + | +LL | ( $( $i:ident ),* ) => { ${ count($i) } }; + | ^^^^^ - help: try removing `$` + +error: expected identifier, found `$` + --> $DIR/syntax-errors.rs:22:26 + | +LL | ( $i:ident ) => { ${ count($i) } }; + | ^^^^^ - help: try removing `$` + +error: unexpected token: $ + --> $DIR/syntax-errors.rs:52:8 + | +LL | ( $$ $a:ident ) => { + | ^ + +note: `$$` and meta-variable expressions are not allowed inside macro parameter definitions + --> $DIR/syntax-errors.rs:52:8 + | +LL | ( $$ $a:ident ) => { + | ^ + +error: unexpected token: a + --> $DIR/syntax-errors.rs:59:19 + | +LL | ${count() a b c} + | ^ + | +note: meta-variable expression must not have trailing tokens + --> $DIR/syntax-errors.rs:59:19 + | +LL | ${count() a b c} + | ^ + +error: unexpected token: a + --> $DIR/syntax-errors.rs:62:19 + | +LL | ${count(i a b c)} + | ^ + | +note: meta-variable expression must not have trailing tokens + --> $DIR/syntax-errors.rs:62:19 + | +LL | ${count(i a b c)} + | ^ + +error: unexpected token: a + --> $DIR/syntax-errors.rs:64:22 + | +LL | ${count(i, 1 a b c)} + | ^ + | +note: meta-variable expression must not have trailing tokens + --> $DIR/syntax-errors.rs:64:22 + | +LL | ${count(i, 1 a b c)} + | ^ + +error: unexpected token: a + --> $DIR/syntax-errors.rs:66:20 + | +LL | ${count(i) a b c} + | ^ + | +note: meta-variable expression must not have trailing tokens + --> $DIR/syntax-errors.rs:66:20 + | +LL | ${count(i) a b c} + | ^ + +error: unexpected token: a + --> $DIR/syntax-errors.rs:69:21 + | +LL | ${ignore(i) a b c} + | ^ + | +note: meta-variable expression must not have trailing tokens + --> $DIR/syntax-errors.rs:69:21 + | +LL | ${ignore(i) a b c} + | ^ + +error: unexpected token: a + --> $DIR/syntax-errors.rs:71:20 + | +LL | ${ignore(i a b c)} + | ^ + | +note: meta-variable expression must not have trailing tokens + --> $DIR/syntax-errors.rs:71:20 + | +LL | ${ignore(i a b c)} + | ^ + +error: unexpected token: a + --> $DIR/syntax-errors.rs:74:19 + | +LL | ${index() a b c} + | ^ + | +note: meta-variable expression must not have trailing tokens + --> $DIR/syntax-errors.rs:74:19 + | +LL | ${index() a b c} + | ^ + +error: unexpected token: a + --> $DIR/syntax-errors.rs:76:19 + | +LL | ${index(1 a b c)} + | ^ + | +note: meta-variable expression must not have trailing tokens + --> $DIR/syntax-errors.rs:76:19 + | +LL | ${index(1 a b c)} + | ^ + +error: unexpected token: a + --> $DIR/syntax-errors.rs:79:19 + | +LL | ${index() a b c} + | ^ + | +note: meta-variable expression must not have trailing tokens + --> $DIR/syntax-errors.rs:79:19 + | +LL | ${index() a b c} + | ^ + +error: unexpected token: a + --> $DIR/syntax-errors.rs:81:19 + | +LL | ${index(1 a b c)} + | ^ + | +note: meta-variable expression must not have trailing tokens + --> $DIR/syntax-errors.rs:81:19 + | +LL | ${index(1 a b c)} + | ^ + +error: meta-variable expression depth must be a literal + --> $DIR/syntax-errors.rs:88:33 + | +LL | ( $( $i:ident ),* ) => { ${ index(IDX) } }; + | ^^^^^ + +error: unexpected token: { + --> $DIR/syntax-errors.rs:94:8 + | +LL | ( ${ length() } ) => { + | ^^^^^^^^^^^^ + +note: `$$` and meta-variable expressions are not allowed inside macro parameter definitions + --> $DIR/syntax-errors.rs:94:8 + | +LL | ( ${ length() } ) => { + | ^^^^^^^^^^^^ + +error: expected one of: `*`, `+`, or `?` + --> $DIR/syntax-errors.rs:94:8 + | +LL | ( ${ length() } ) => { + | ^^^^^^^^^^^^ + +error: expected identifier + --> $DIR/syntax-errors.rs:101:33 + | +LL | ( $( $i:ident ),* ) => { ${ ignore() } }; + | ^^^^^^ + +error: only unsuffixes integer literals are supported in meta-variable expressions + --> $DIR/syntax-errors.rs:107:33 + | +LL | ( $( $i:ident ),* ) => { ${ index(1u32) } }; + | ^^^^^ + +error: meta-variable expression parameter must be wrapped in parentheses + --> $DIR/syntax-errors.rs:113:33 + | +LL | ( $( $i:ident ),* ) => { ${ count{i} } }; + | ^^^^^ + +error: expected identifier + --> $DIR/syntax-errors.rs:119:31 + | +LL | ( $( $i:ident ),* ) => { ${ {} } }; + | ^^^^^^ + +error: unrecognized meta-variable expression + --> $DIR/syntax-errors.rs:125:33 + | +LL | ( $( $i:ident ),* ) => { ${ aaaaaaaaaaaaaa(i) } }; + | ^^^^^^^^^^^^^^ help: supported expressions are count, ignore, index and length + +error: expected expression, found `$` + --> $DIR/syntax-errors.rs:16:30 + | +LL | ( $( $i:ident ),* ) => { ${ count($i) } }; + | ^ expected expression +... +LL | curly__rhs_dollar__round!(a, b, c); + | ---------------------------------- in this macro invocation + | + = note: this error originates in the macro `curly__rhs_dollar__round` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: expected expression, found `$` + --> $DIR/syntax-errors.rs:22:23 + | +LL | ( $i:ident ) => { ${ count($i) } }; + | ^ expected expression +... +LL | curly__rhs_dollar__no_round!(a); + | ------------------------------- in this macro invocation + | + = note: this error originates in the macro `curly__rhs_dollar__no_round` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: variable 'i' is still repeating at this depth + --> $DIR/syntax-errors.rs:40:36 + | +LL | ( $( $i:ident ),* ) => { count($i) }; + | ^^ + +error: expected expression, found `$` + --> $DIR/syntax-errors.rs:59:9 + | +LL | ${count() a b c} + | ^ expected expression +... +LL | extra_garbage_after_metavar!(a); + | ------------------------------- in this macro invocation + | + = note: this error originates in the macro `extra_garbage_after_metavar` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: expected expression, found `$` + --> $DIR/syntax-errors.rs:125:30 + | +LL | ( $( $i:ident ),* ) => { ${ aaaaaaaaaaaaaa(i) } }; + | ^ expected expression +... +LL | unknown_metavar!(a); + | ------------------- in this macro invocation + | + = note: this error originates in the macro `unknown_metavar` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: expected expression, found `$` + --> $DIR/syntax-errors.rs:113:30 + | +LL | ( $( $i:ident ),* ) => { ${ count{i} } }; + | ^ expected expression +... +LL | metavar_without_parens!(a); + | -------------------------- in this macro invocation + | + = note: this error originates in the macro `metavar_without_parens` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: expected expression, found `$` + --> $DIR/syntax-errors.rs:101:30 + | +LL | ( $( $i:ident ),* ) => { ${ ignore() } }; + | ^ expected expression +... +LL | metavar_token_without_ident!(a); + | ------------------------------- in this macro invocation + | + = note: this error originates in the macro `metavar_token_without_ident` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: expected expression, found `$` + --> $DIR/syntax-errors.rs:88:30 + | +LL | ( $( $i:ident ),* ) => { ${ index(IDX) } }; + | ^ expected expression +... +LL | metavar_depth_is_not_literal!(a); + | -------------------------------- in this macro invocation + | + = note: this error originates in the macro `metavar_depth_is_not_literal` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: expected expression, found `$` + --> $DIR/syntax-errors.rs:107:30 + | +LL | ( $( $i:ident ),* ) => { ${ index(1u32) } }; + | ^ expected expression +... +LL | metavar_with_literal_suffix!(a); + | ------------------------------- in this macro invocation + | + = note: this error originates in the macro `metavar_with_literal_suffix` (in Nightly builds, run with -Z macro-backtrace for more info) + +error: expected expression, found `$` + --> $DIR/syntax-errors.rs:119:30 + | +LL | ( $( $i:ident ),* ) => { ${ {} } }; + | ^ expected expression +... +LL | open_brackets_without_tokens!(a) + | -------------------------------- in this macro invocation + | + = note: this error originates in the macro `open_brackets_without_tokens` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0425]: cannot find function `count` in this scope + --> $DIR/syntax-errors.rs:28:30 + | +LL | ( $( $i:ident ),* ) => { count(i) }; + | ^^^^^ not found in this scope +... +LL | no_curly__no_rhs_dollar__round!(a, b, c); + | ---------------------------------------- in this macro invocation + | + = note: this error originates in the macro `no_curly__no_rhs_dollar__round` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0425]: cannot find value `i` in this scope + --> $DIR/syntax-errors.rs:28:36 + | +LL | ( $( $i:ident ),* ) => { count(i) }; + | ^ not found in this scope +... +LL | no_curly__no_rhs_dollar__round!(a, b, c); + | ---------------------------------------- in this macro invocation + | + = note: this error originates in the macro `no_curly__no_rhs_dollar__round` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0425]: cannot find function `count` in this scope + --> $DIR/syntax-errors.rs:34:23 + | +LL | ( $i:ident ) => { count(i) }; + | ^^^^^ not found in this scope +... +LL | no_curly__no_rhs_dollar__no_round!(a); + | ------------------------------------- in this macro invocation + | + = note: this error originates in the macro `no_curly__no_rhs_dollar__no_round` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0425]: cannot find value `i` in this scope + --> $DIR/syntax-errors.rs:34:29 + | +LL | ( $i:ident ) => { count(i) }; + | ^ not found in this scope +... +LL | no_curly__no_rhs_dollar__no_round!(a); + | ------------------------------------- in this macro invocation + | + = note: this error originates in the macro `no_curly__no_rhs_dollar__no_round` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0425]: cannot find function `count` in this scope + --> $DIR/syntax-errors.rs:45:23 + | +LL | ( $i:ident ) => { count($i) }; + | ^^^^^ not found in this scope +... +LL | no_curly__rhs_dollar__no_round!(a); + | ---------------------------------- in this macro invocation + | + = note: this error originates in the macro `no_curly__rhs_dollar__no_round` (in Nightly builds, run with -Z macro-backtrace for more info) + +error[E0425]: cannot find value `a` in this scope + --> $DIR/syntax-errors.rs:138:37 + | +LL | no_curly__rhs_dollar__no_round!(a); + | ^ not found in this scope + +error: aborting due to 37 previous errors + +For more information about this error, try `rustc --explain E0425`. diff --git a/src/test/ui/or-patterns/issue-69875-should-have-been-expanded-earlier-non-exhaustive.stderr b/src/test/ui/or-patterns/issue-69875-should-have-been-expanded-earlier-non-exhaustive.stderr index 37a35700b36d5..95b22ac059482 100644 --- a/src/test/ui/or-patterns/issue-69875-should-have-been-expanded-earlier-non-exhaustive.stderr +++ b/src/test/ui/or-patterns/issue-69875-should-have-been-expanded-earlier-non-exhaustive.stderr @@ -7,10 +7,10 @@ LL | let (0 | (1 | 2)) = 0; = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html = note: the matched value is of type `i32` -help: you might want to use `if let` to ignore the variant that isn't matched +help: you might want to use `if let` to ignore the variants that aren't matched | -LL | if let (0 | (1 | 2)) = 0 { /* */ } - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL | if let (0 | (1 | 2)) = 0 { todo!() } + | ++ ~~~~~~~~~~~ error[E0004]: non-exhaustive patterns: `i32::MIN..=-1_i32` and `3_i32..=i32::MAX` not covered --> $DIR/issue-69875-should-have-been-expanded-earlier-non-exhaustive.rs:3:11 diff --git a/src/test/ui/pattern/usefulness/issue-31561.stderr b/src/test/ui/pattern/usefulness/issue-31561.stderr index dffcfc016072f..9da6b5eeead23 100644 --- a/src/test/ui/pattern/usefulness/issue-31561.stderr +++ b/src/test/ui/pattern/usefulness/issue-31561.stderr @@ -17,10 +17,14 @@ LL | Bar, LL | Baz | ^^^ not covered = note: the matched value is of type `Thing` -help: you might want to use `if let` to ignore the variant that isn't matched +help: you might want to use `if let` to ignore the variants that aren't matched | -LL | if let Thing::Foo(y) = Thing::Foo(1) { /* */ } +LL | let y = if let Thing::Foo(y) = Thing::Foo(1) { y } else { todo!() }; + | ++++++++++ ++++++++++++++++++++++ +help: alternatively, on nightly, you might want to use `#![feature(let_else)]` to handle the variants that aren't matched | +LL | let Thing::Foo(y) = Thing::Foo(1) else { todo!() }; + | ++++++++++++++++ error: aborting due to previous error diff --git a/src/test/ui/pattern/usefulness/non-exhaustive-defined-here.stderr b/src/test/ui/pattern/usefulness/non-exhaustive-defined-here.stderr index 8f5adccea806d..0f06c31c468b1 100644 --- a/src/test/ui/pattern/usefulness/non-exhaustive-defined-here.stderr +++ b/src/test/ui/pattern/usefulness/non-exhaustive-defined-here.stderr @@ -42,10 +42,10 @@ LL | B, LL | C | ^ not covered = note: the matched value is of type `E` -help: you might want to use `if let` to ignore the variant that isn't matched +help: you might want to use `if let` to ignore the variants that aren't matched | -LL | if let E::A = e { /* */ } - | ~~~~~~~~~~~~~~~~~~~~~~~~~ +LL | if let E::A = e { todo!() } + | ++ ~~~~~~~~~~~ error[E0004]: non-exhaustive patterns: `&B` and `&C` not covered --> $DIR/non-exhaustive-defined-here.rs:52:11 @@ -91,10 +91,10 @@ LL | B, LL | C | ^ not covered = note: the matched value is of type `&E` -help: you might want to use `if let` to ignore the variant that isn't matched +help: you might want to use `if let` to ignore the variants that aren't matched | -LL | if let E::A = e { /* */ } - | ~~~~~~~~~~~~~~~~~~~~~~~~~ +LL | if let E::A = e { todo!() } + | ++ ~~~~~~~~~~~ error[E0004]: non-exhaustive patterns: `&&mut &B` and `&&mut &C` not covered --> $DIR/non-exhaustive-defined-here.rs:66:11 @@ -140,10 +140,10 @@ LL | B, LL | C | ^ not covered = note: the matched value is of type `&&mut &E` -help: you might want to use `if let` to ignore the variant that isn't matched - | -LL | if let E::A = e { /* */ } +help: you might want to use `if let` to ignore the variants that aren't matched | +LL | if let E::A = e { todo!() } + | ++ ~~~~~~~~~~~ error[E0004]: non-exhaustive patterns: `None` not covered --> $DIR/non-exhaustive-defined-here.rs:92:11 @@ -185,8 +185,12 @@ LL | None, = note: the matched value is of type `Opt` help: you might want to use `if let` to ignore the variant that isn't matched | -LL | if let Opt::Some(ref _x) = e { /* */ } - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL | let _x = if let Opt::Some(ref _x) = e { _x } else { todo!() }; + | +++++++++++ +++++++++++++++++++++++ +help: alternatively, on nightly, you might want to use `#![feature(let_else)]` to handle the variant that isn't matched + | +LL | let Opt::Some(ref _x) = e else { todo!() }; + | ++++++++++++++++ error: aborting due to 8 previous errors diff --git a/src/test/ui/pattern/usefulness/refutable-pattern-errors.stderr b/src/test/ui/pattern/usefulness/refutable-pattern-errors.stderr index 74ec646e31cca..d1dacc822e942 100644 --- a/src/test/ui/pattern/usefulness/refutable-pattern-errors.stderr +++ b/src/test/ui/pattern/usefulness/refutable-pattern-errors.stderr @@ -15,10 +15,10 @@ LL | let (1, (Some(1), 2..=3)) = (1, (None, 2)); = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html = note: the matched value is of type `(i32, (Option, i32))` -help: you might want to use `if let` to ignore the variant that isn't matched - | -LL | if let (1, (Some(1), 2..=3)) = (1, (None, 2)) { /* */ } +help: you might want to use `if let` to ignore the variants that aren't matched | +LL | if let (1, (Some(1), 2..=3)) = (1, (None, 2)) { todo!() } + | ++ ~~~~~~~~~~~ error: aborting due to 2 previous errors diff --git a/src/test/ui/recursion/recursive-types-are-not-uninhabited.stderr b/src/test/ui/recursion/recursive-types-are-not-uninhabited.stderr index ded3cf3ad1d44..a9159562d9d51 100644 --- a/src/test/ui/recursion/recursive-types-are-not-uninhabited.stderr +++ b/src/test/ui/recursion/recursive-types-are-not-uninhabited.stderr @@ -21,8 +21,12 @@ LL | | } = note: the matched value is of type `Result` help: you might want to use `if let` to ignore the variant that isn't matched | -LL | if let Ok(x) = res { /* */ } +LL | let x = if let Ok(x) = res { x } else { todo!() }; + | ++++++++++ ++++++++++++++++++++++ +help: alternatively, on nightly, you might want to use `#![feature(let_else)]` to handle the variant that isn't matched | +LL | let Ok(x) = res else { todo!() }; + | ++++++++++++++++ error: aborting due to previous error diff --git a/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.stderr b/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.stderr index 897de54a7bf8c..dd090a3a5483d 100644 --- a/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.stderr +++ b/src/test/ui/rfc-2497-if-let-chains/disallowed-positions.stderr @@ -15,7 +15,7 @@ error: `let` expressions are not supported here LL | if &let 0 = 0 {} | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions + = note: only supported directly in conditions of `if` and `while` expressions = note: as well as when nested within `&&` and parentheses in those conditions error: `let` expressions are not supported here @@ -24,7 +24,7 @@ error: `let` expressions are not supported here LL | if !let 0 = 0 {} | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions + = note: only supported directly in conditions of `if` and `while` expressions = note: as well as when nested within `&&` and parentheses in those conditions error: `let` expressions are not supported here @@ -33,7 +33,7 @@ error: `let` expressions are not supported here LL | if *let 0 = 0 {} | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions + = note: only supported directly in conditions of `if` and `while` expressions = note: as well as when nested within `&&` and parentheses in those conditions error: `let` expressions are not supported here @@ -42,7 +42,7 @@ error: `let` expressions are not supported here LL | if -let 0 = 0 {} | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions + = note: only supported directly in conditions of `if` and `while` expressions = note: as well as when nested within `&&` and parentheses in those conditions error: `let` expressions are not supported here @@ -51,7 +51,7 @@ error: `let` expressions are not supported here LL | if (let 0 = 0)? {} | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions + = note: only supported directly in conditions of `if` and `while` expressions = note: as well as when nested within `&&` and parentheses in those conditions error: `let` expressions are not supported here @@ -60,8 +60,13 @@ error: `let` expressions are not supported here LL | if true || let 0 = 0 {} | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions + = note: only supported directly in conditions of `if` and `while` expressions = note: as well as when nested within `&&` and parentheses in those conditions +note: `||` operators are not allowed in let chain expressions + --> $DIR/disallowed-positions.rs:47:13 + | +LL | if true || let 0 = 0 {} + | ^^ error: `let` expressions are not supported here --> $DIR/disallowed-positions.rs:48:17 @@ -69,8 +74,13 @@ error: `let` expressions are not supported here LL | if (true || let 0 = 0) {} | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions + = note: only supported directly in conditions of `if` and `while` expressions = note: as well as when nested within `&&` and parentheses in those conditions +note: `||` operators are not allowed in let chain expressions + --> $DIR/disallowed-positions.rs:48:14 + | +LL | if (true || let 0 = 0) {} + | ^^ error: `let` expressions are not supported here --> $DIR/disallowed-positions.rs:49:25 @@ -78,8 +88,13 @@ error: `let` expressions are not supported here LL | if true && (true || let 0 = 0) {} | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions + = note: only supported directly in conditions of `if` and `while` expressions = note: as well as when nested within `&&` and parentheses in those conditions +note: `||` operators are not allowed in let chain expressions + --> $DIR/disallowed-positions.rs:49:22 + | +LL | if true && (true || let 0 = 0) {} + | ^^ error: `let` expressions are not supported here --> $DIR/disallowed-positions.rs:50:25 @@ -87,8 +102,13 @@ error: `let` expressions are not supported here LL | if true || (true && let 0 = 0) {} | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions + = note: only supported directly in conditions of `if` and `while` expressions = note: as well as when nested within `&&` and parentheses in those conditions +note: `||` operators are not allowed in let chain expressions + --> $DIR/disallowed-positions.rs:50:13 + | +LL | if true || (true && let 0 = 0) {} + | ^^ error: `let` expressions are not supported here --> $DIR/disallowed-positions.rs:53:12 @@ -96,7 +116,7 @@ error: `let` expressions are not supported here LL | if x = let 0 = 0 {} | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions + = note: only supported directly in conditions of `if` and `while` expressions = note: as well as when nested within `&&` and parentheses in those conditions error: `let` expressions are not supported here @@ -105,7 +125,7 @@ error: `let` expressions are not supported here LL | if true..(let 0 = 0) {} | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions + = note: only supported directly in conditions of `if` and `while` expressions = note: as well as when nested within `&&` and parentheses in those conditions error: `let` expressions are not supported here @@ -114,7 +134,7 @@ error: `let` expressions are not supported here LL | if ..(let 0 = 0) {} | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions + = note: only supported directly in conditions of `if` and `while` expressions = note: as well as when nested within `&&` and parentheses in those conditions error: `let` expressions are not supported here @@ -123,7 +143,7 @@ error: `let` expressions are not supported here LL | if (let 0 = 0).. {} | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions + = note: only supported directly in conditions of `if` and `while` expressions = note: as well as when nested within `&&` and parentheses in those conditions error: `let` expressions are not supported here @@ -132,7 +152,7 @@ error: `let` expressions are not supported here LL | if let Range { start: _, end: _ } = true..true && false {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions + = note: only supported directly in conditions of `if` and `while` expressions = note: as well as when nested within `&&` and parentheses in those conditions error: `let` expressions are not supported here @@ -141,7 +161,7 @@ error: `let` expressions are not supported here LL | if let Range { start: _, end: _ } = true..true || false {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions + = note: only supported directly in conditions of `if` and `while` expressions = note: as well as when nested within `&&` and parentheses in those conditions error: `let` expressions are not supported here @@ -150,7 +170,7 @@ error: `let` expressions are not supported here LL | if let Range { start: F, end } = F..|| true {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions + = note: only supported directly in conditions of `if` and `while` expressions = note: as well as when nested within `&&` and parentheses in those conditions error: `let` expressions are not supported here @@ -159,7 +179,7 @@ error: `let` expressions are not supported here LL | if let Range { start: true, end } = t..&&false {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions + = note: only supported directly in conditions of `if` and `while` expressions = note: as well as when nested within `&&` and parentheses in those conditions error: `let` expressions are not supported here @@ -168,7 +188,7 @@ error: `let` expressions are not supported here LL | if let true = let true = true {} | ^^^^^^^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions + = note: only supported directly in conditions of `if` and `while` expressions = note: as well as when nested within `&&` and parentheses in those conditions error: `let` expressions are not supported here @@ -177,7 +197,7 @@ error: `let` expressions are not supported here LL | while &let 0 = 0 {} | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions + = note: only supported directly in conditions of `if` and `while` expressions = note: as well as when nested within `&&` and parentheses in those conditions error: `let` expressions are not supported here @@ -186,7 +206,7 @@ error: `let` expressions are not supported here LL | while !let 0 = 0 {} | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions + = note: only supported directly in conditions of `if` and `while` expressions = note: as well as when nested within `&&` and parentheses in those conditions error: `let` expressions are not supported here @@ -195,7 +215,7 @@ error: `let` expressions are not supported here LL | while *let 0 = 0 {} | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions + = note: only supported directly in conditions of `if` and `while` expressions = note: as well as when nested within `&&` and parentheses in those conditions error: `let` expressions are not supported here @@ -204,7 +224,7 @@ error: `let` expressions are not supported here LL | while -let 0 = 0 {} | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions + = note: only supported directly in conditions of `if` and `while` expressions = note: as well as when nested within `&&` and parentheses in those conditions error: `let` expressions are not supported here @@ -213,7 +233,7 @@ error: `let` expressions are not supported here LL | while (let 0 = 0)? {} | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions + = note: only supported directly in conditions of `if` and `while` expressions = note: as well as when nested within `&&` and parentheses in those conditions error: `let` expressions are not supported here @@ -222,8 +242,13 @@ error: `let` expressions are not supported here LL | while true || let 0 = 0 {} | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions + = note: only supported directly in conditions of `if` and `while` expressions = note: as well as when nested within `&&` and parentheses in those conditions +note: `||` operators are not allowed in let chain expressions + --> $DIR/disallowed-positions.rs:111:16 + | +LL | while true || let 0 = 0 {} + | ^^ error: `let` expressions are not supported here --> $DIR/disallowed-positions.rs:112:20 @@ -231,8 +256,13 @@ error: `let` expressions are not supported here LL | while (true || let 0 = 0) {} | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions + = note: only supported directly in conditions of `if` and `while` expressions = note: as well as when nested within `&&` and parentheses in those conditions +note: `||` operators are not allowed in let chain expressions + --> $DIR/disallowed-positions.rs:112:17 + | +LL | while (true || let 0 = 0) {} + | ^^ error: `let` expressions are not supported here --> $DIR/disallowed-positions.rs:113:28 @@ -240,8 +270,13 @@ error: `let` expressions are not supported here LL | while true && (true || let 0 = 0) {} | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions + = note: only supported directly in conditions of `if` and `while` expressions = note: as well as when nested within `&&` and parentheses in those conditions +note: `||` operators are not allowed in let chain expressions + --> $DIR/disallowed-positions.rs:113:25 + | +LL | while true && (true || let 0 = 0) {} + | ^^ error: `let` expressions are not supported here --> $DIR/disallowed-positions.rs:114:28 @@ -249,8 +284,13 @@ error: `let` expressions are not supported here LL | while true || (true && let 0 = 0) {} | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions + = note: only supported directly in conditions of `if` and `while` expressions = note: as well as when nested within `&&` and parentheses in those conditions +note: `||` operators are not allowed in let chain expressions + --> $DIR/disallowed-positions.rs:114:16 + | +LL | while true || (true && let 0 = 0) {} + | ^^ error: `let` expressions are not supported here --> $DIR/disallowed-positions.rs:117:15 @@ -258,7 +298,7 @@ error: `let` expressions are not supported here LL | while x = let 0 = 0 {} | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions + = note: only supported directly in conditions of `if` and `while` expressions = note: as well as when nested within `&&` and parentheses in those conditions error: `let` expressions are not supported here @@ -267,7 +307,7 @@ error: `let` expressions are not supported here LL | while true..(let 0 = 0) {} | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions + = note: only supported directly in conditions of `if` and `while` expressions = note: as well as when nested within `&&` and parentheses in those conditions error: `let` expressions are not supported here @@ -276,7 +316,7 @@ error: `let` expressions are not supported here LL | while ..(let 0 = 0) {} | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions + = note: only supported directly in conditions of `if` and `while` expressions = note: as well as when nested within `&&` and parentheses in those conditions error: `let` expressions are not supported here @@ -285,7 +325,7 @@ error: `let` expressions are not supported here LL | while (let 0 = 0).. {} | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions + = note: only supported directly in conditions of `if` and `while` expressions = note: as well as when nested within `&&` and parentheses in those conditions error: `let` expressions are not supported here @@ -294,7 +334,7 @@ error: `let` expressions are not supported here LL | while let Range { start: _, end: _ } = true..true && false {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions + = note: only supported directly in conditions of `if` and `while` expressions = note: as well as when nested within `&&` and parentheses in those conditions error: `let` expressions are not supported here @@ -303,7 +343,7 @@ error: `let` expressions are not supported here LL | while let Range { start: _, end: _ } = true..true || false {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions + = note: only supported directly in conditions of `if` and `while` expressions = note: as well as when nested within `&&` and parentheses in those conditions error: `let` expressions are not supported here @@ -312,7 +352,7 @@ error: `let` expressions are not supported here LL | while let Range { start: F, end } = F..|| true {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions + = note: only supported directly in conditions of `if` and `while` expressions = note: as well as when nested within `&&` and parentheses in those conditions error: `let` expressions are not supported here @@ -321,7 +361,7 @@ error: `let` expressions are not supported here LL | while let Range { start: true, end } = t..&&false {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions + = note: only supported directly in conditions of `if` and `while` expressions = note: as well as when nested within `&&` and parentheses in those conditions error: `let` expressions are not supported here @@ -330,7 +370,7 @@ error: `let` expressions are not supported here LL | while let true = let true = true {} | ^^^^^^^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions + = note: only supported directly in conditions of `if` and `while` expressions = note: as well as when nested within `&&` and parentheses in those conditions error: `let` expressions are not supported here @@ -339,7 +379,7 @@ error: `let` expressions are not supported here LL | &let 0 = 0; | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions + = note: only supported directly in conditions of `if` and `while` expressions = note: as well as when nested within `&&` and parentheses in those conditions error: `let` expressions are not supported here @@ -348,7 +388,7 @@ error: `let` expressions are not supported here LL | !let 0 = 0; | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions + = note: only supported directly in conditions of `if` and `while` expressions = note: as well as when nested within `&&` and parentheses in those conditions error: `let` expressions are not supported here @@ -357,7 +397,7 @@ error: `let` expressions are not supported here LL | *let 0 = 0; | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions + = note: only supported directly in conditions of `if` and `while` expressions = note: as well as when nested within `&&` and parentheses in those conditions error: `let` expressions are not supported here @@ -366,7 +406,7 @@ error: `let` expressions are not supported here LL | -let 0 = 0; | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions + = note: only supported directly in conditions of `if` and `while` expressions = note: as well as when nested within `&&` and parentheses in those conditions error: `let` expressions are not supported here @@ -375,7 +415,7 @@ error: `let` expressions are not supported here LL | (let 0 = 0)?; | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions + = note: only supported directly in conditions of `if` and `while` expressions = note: as well as when nested within `&&` and parentheses in those conditions error: `let` expressions are not supported here @@ -384,8 +424,13 @@ error: `let` expressions are not supported here LL | true || let 0 = 0; | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions + = note: only supported directly in conditions of `if` and `while` expressions = note: as well as when nested within `&&` and parentheses in those conditions +note: `||` operators are not allowed in let chain expressions + --> $DIR/disallowed-positions.rs:184:10 + | +LL | true || let 0 = 0; + | ^^ error: `let` expressions are not supported here --> $DIR/disallowed-positions.rs:185:14 @@ -393,8 +438,13 @@ error: `let` expressions are not supported here LL | (true || let 0 = 0); | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions + = note: only supported directly in conditions of `if` and `while` expressions = note: as well as when nested within `&&` and parentheses in those conditions +note: `||` operators are not allowed in let chain expressions + --> $DIR/disallowed-positions.rs:185:11 + | +LL | (true || let 0 = 0); + | ^^ error: `let` expressions are not supported here --> $DIR/disallowed-positions.rs:186:22 @@ -402,8 +452,13 @@ error: `let` expressions are not supported here LL | true && (true || let 0 = 0); | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions + = note: only supported directly in conditions of `if` and `while` expressions = note: as well as when nested within `&&` and parentheses in those conditions +note: `||` operators are not allowed in let chain expressions + --> $DIR/disallowed-positions.rs:186:19 + | +LL | true && (true || let 0 = 0); + | ^^ error: `let` expressions are not supported here --> $DIR/disallowed-positions.rs:189:9 @@ -411,7 +466,7 @@ error: `let` expressions are not supported here LL | x = let 0 = 0; | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions + = note: only supported directly in conditions of `if` and `while` expressions = note: as well as when nested within `&&` and parentheses in those conditions error: `let` expressions are not supported here @@ -420,7 +475,7 @@ error: `let` expressions are not supported here LL | true..(let 0 = 0); | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions + = note: only supported directly in conditions of `if` and `while` expressions = note: as well as when nested within `&&` and parentheses in those conditions error: `let` expressions are not supported here @@ -429,7 +484,7 @@ error: `let` expressions are not supported here LL | ..(let 0 = 0); | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions + = note: only supported directly in conditions of `if` and `while` expressions = note: as well as when nested within `&&` and parentheses in those conditions error: `let` expressions are not supported here @@ -438,7 +493,7 @@ error: `let` expressions are not supported here LL | (let 0 = 0)..; | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions + = note: only supported directly in conditions of `if` and `while` expressions = note: as well as when nested within `&&` and parentheses in those conditions error: `let` expressions are not supported here @@ -447,7 +502,7 @@ error: `let` expressions are not supported here LL | (let Range { start: _, end: _ } = true..true || false); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions + = note: only supported directly in conditions of `if` and `while` expressions = note: as well as when nested within `&&` and parentheses in those conditions error: `let` expressions are not supported here @@ -456,7 +511,7 @@ error: `let` expressions are not supported here LL | (let true = let true = true); | ^^^^^^^^^^^^^^^^^^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions + = note: only supported directly in conditions of `if` and `while` expressions = note: as well as when nested within `&&` and parentheses in those conditions error: `let` expressions are not supported here @@ -465,7 +520,7 @@ error: `let` expressions are not supported here LL | &let 0 = 0 | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions + = note: only supported directly in conditions of `if` and `while` expressions = note: as well as when nested within `&&` and parentheses in those conditions error: `let` expressions are not supported here @@ -474,7 +529,7 @@ error: `let` expressions are not supported here LL | true && let 1 = 1 | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions + = note: only supported directly in conditions of `if` and `while` expressions = note: as well as when nested within `&&` and parentheses in those conditions error: `let` expressions are not supported here @@ -483,7 +538,7 @@ error: `let` expressions are not supported here LL | true && let 1 = 1 | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions + = note: only supported directly in conditions of `if` and `while` expressions = note: as well as when nested within `&&` and parentheses in those conditions error: `let` expressions are not supported here @@ -492,7 +547,7 @@ error: `let` expressions are not supported here LL | true && let 1 = 1 | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions + = note: only supported directly in conditions of `if` and `while` expressions = note: as well as when nested within `&&` and parentheses in those conditions error: `let` expressions are not supported here @@ -501,7 +556,7 @@ error: `let` expressions are not supported here LL | true && let 1 = 1 | ^^^^^^^^^ | - = note: only supported directly in conditions of `if`- and `while`-expressions + = note: only supported directly in conditions of `if` and `while` expressions = note: as well as when nested within `&&` and parentheses in those conditions error[E0308]: mismatched types diff --git a/src/test/ui/str/str-idx.stderr b/src/test/ui/str/str-idx.stderr index 9c3c3646139dc..9ab409bbdcdf8 100644 --- a/src/test/ui/str/str-idx.stderr +++ b/src/test/ui/str/str-idx.stderr @@ -23,8 +23,8 @@ LL | let _ = s.get(4); note: required by a bound in `core::str::::get` --> $SRC_DIR/core/src/str/mod.rs:LL:COL | -LL | pub fn get>(&self, i: I) -> Option<&I::Output> { - | ^^^^^^^^^^^^^^^ required by this bound in `core::str::::get` +LL | pub const fn get>(&self, i: I) -> Option<&I::Output> { + | ^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `core::str::::get` error[E0277]: the type `str` cannot be indexed by `{integer}` --> $DIR/str-idx.rs:5:29 @@ -40,8 +40,8 @@ LL | let _ = s.get_unchecked(4); note: required by a bound in `core::str::::get_unchecked` --> $SRC_DIR/core/src/str/mod.rs:LL:COL | -LL | pub unsafe fn get_unchecked>(&self, i: I) -> &I::Output { - | ^^^^^^^^^^^^^^^ required by this bound in `core::str::::get_unchecked` +LL | pub const unsafe fn get_unchecked>(&self, i: I) -> &I::Output { + | ^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `core::str::::get_unchecked` error[E0277]: the type `str` cannot be indexed by `char` --> $DIR/str-idx.rs:6:17 diff --git a/src/test/ui/str/str-mut-idx.stderr b/src/test/ui/str/str-mut-idx.stderr index 2559ee9eb49b2..5956e363b0c09 100644 --- a/src/test/ui/str/str-mut-idx.stderr +++ b/src/test/ui/str/str-mut-idx.stderr @@ -47,8 +47,8 @@ LL | s.get_mut(1); note: required by a bound in `core::str::::get_mut` --> $SRC_DIR/core/src/str/mod.rs:LL:COL | -LL | pub fn get_mut>(&mut self, i: I) -> Option<&mut I::Output> { - | ^^^^^^^^^^^^^^^ required by this bound in `core::str::::get_mut` +LL | pub const fn get_mut>(&mut self, i: I) -> Option<&mut I::Output> { + | ^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `core::str::::get_mut` error[E0277]: the type `str` cannot be indexed by `{integer}` --> $DIR/str-mut-idx.rs:11:25 @@ -64,8 +64,8 @@ LL | s.get_unchecked_mut(1); note: required by a bound in `core::str::::get_unchecked_mut` --> $SRC_DIR/core/src/str/mod.rs:LL:COL | -LL | pub unsafe fn get_unchecked_mut>(&mut self, i: I) -> &mut I::Output { - | ^^^^^^^^^^^^^^^ required by this bound in `core::str::::get_unchecked_mut` +LL | pub const unsafe fn get_unchecked_mut>( + | ^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `core::str::::get_unchecked_mut` error[E0277]: the type `str` cannot be indexed by `char` --> $DIR/str-mut-idx.rs:13:5 diff --git a/src/test/ui/uninhabited/uninhabited-irrefutable.rs b/src/test/ui/uninhabited/uninhabited-irrefutable.rs index 48cd92719b49a..661b5486adc12 100644 --- a/src/test/ui/uninhabited/uninhabited-irrefutable.rs +++ b/src/test/ui/uninhabited/uninhabited-irrefutable.rs @@ -19,10 +19,10 @@ enum Foo { A(foo::SecretlyEmpty), B(foo::NotSoSecretlyEmpty), C(NotSoSecretlyEmpty), - D(u32), + D(u32, u32), } fn main() { - let x: Foo = Foo::D(123); - let Foo::D(_y) = x; //~ ERROR refutable pattern in local binding: `A(_)` not covered + let x: Foo = Foo::D(123, 456); + let Foo::D(_y, _z) = x; //~ ERROR refutable pattern in local binding: `A(_)` not covered } diff --git a/src/test/ui/uninhabited/uninhabited-irrefutable.stderr b/src/test/ui/uninhabited/uninhabited-irrefutable.stderr index ad19c34a40a11..c571e17a7b372 100644 --- a/src/test/ui/uninhabited/uninhabited-irrefutable.stderr +++ b/src/test/ui/uninhabited/uninhabited-irrefutable.stderr @@ -1,8 +1,8 @@ error[E0005]: refutable pattern in local binding: `A(_)` not covered --> $DIR/uninhabited-irrefutable.rs:27:9 | -LL | let Foo::D(_y) = x; - | ^^^^^^^^^^ pattern `A(_)` not covered +LL | let Foo::D(_y, _z) = x; + | ^^^^^^^^^^^^^^ pattern `A(_)` not covered | = note: `let` bindings require an "irrefutable pattern", like a `struct` or an `enum` with only one variant = note: for more information, visit https://doc.rust-lang.org/book/ch18-02-refutability.html @@ -16,8 +16,12 @@ LL | A(foo::SecretlyEmpty), = note: the matched value is of type `Foo` help: you might want to use `if let` to ignore the variant that isn't matched | -LL | if let Foo::D(_y) = x { /* */ } - | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +LL | let (_y, _z) = if let Foo::D(_y, _z) = x { (_y, _z) } else { todo!() }; + | +++++++++++++++++ +++++++++++++++++++++++++++++ +help: alternatively, on nightly, you might want to use `#![feature(let_else)]` to handle the variant that isn't matched + | +LL | let Foo::D(_y, _z) = x else { todo!() }; + | ++++++++++++++++ error: aborting due to previous error diff --git a/src/test/ui/uninhabited/uninhabited-matches-feature-gated.stderr b/src/test/ui/uninhabited/uninhabited-matches-feature-gated.stderr index d90075d82f47b..74216d265d034 100644 --- a/src/test/ui/uninhabited/uninhabited-matches-feature-gated.stderr +++ b/src/test/ui/uninhabited/uninhabited-matches-feature-gated.stderr @@ -132,8 +132,12 @@ LL | | } = note: the matched value is of type `Result` help: you might want to use `if let` to ignore the variant that isn't matched | -LL | if let Ok(x) = x { /* */ } +LL | let x = if let Ok(x) = x { x } else { todo!() }; + | ++++++++++ ++++++++++++++++++++++ +help: alternatively, on nightly, you might want to use `#![feature(let_else)]` to handle the variant that isn't matched | +LL | let Ok(x) = x else { todo!() }; + | ++++++++++++++++ error: aborting due to 7 previous errors diff --git a/src/tools/linkchecker/main.rs b/src/tools/linkchecker/main.rs index 46daaf42883f0..c9b1649200d9c 100644 --- a/src/tools/linkchecker/main.rs +++ b/src/tools/linkchecker/main.rs @@ -489,16 +489,22 @@ fn is_exception(file: &Path, link: &str) -> bool { /// If the given HTML file contents is an HTML redirect, this returns the /// destination path given in the redirect. fn maybe_redirect(source: &str) -> Option { - const REDIRECT: &str = "

Redirecting to Redirecting to Redirecting to... (source: &str, attr: &str, mut f: F) {