[[package]]
name = "ammonia"
-version = "3.1.3"
+version = "3.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b74b175af97d1aecc1add0878b1cbfcbf3bd4c22d7713eeb6d597da23e29bc0d"
+checksum = "d5ed2509ee88cc023cccee37a6fab35826830fe8b748b3869790e7720c2c4a74"
dependencies = [
"html5ever",
- "lazy_static",
"maplit",
- "markup5ever_rcdom",
- "matches",
+ "once_cell",
"tendril",
"url 2.2.2",
]
"pretty_assertions",
"serde",
"serde_json",
+ "tar",
"toml",
"winapi",
+ "xz2",
]
[[package]]
"tar",
"tempfile",
"termcolor",
- "toml_edit",
+ "toml_edit 0.14.3",
"unicode-width",
"unicode-xid",
"url 2.2.2",
"serde_json",
"tar",
"termcolor",
- "toml_edit",
+ "toml_edit 0.13.4",
"url 2.2.2",
]
[[package]]
name = "crossbeam-channel"
-version = "0.5.2"
+version = "0.5.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "e54ea8bc3fb1ee042f5aace6e3c6e025d3874866da222930f70ce62aceba0bfa"
+checksum = "5aaa7bd5fb665c6864b5f963dd9097905c54125909c7aa94c9e18507cdbe6c53"
dependencies = [
"cfg-if 1.0.0",
"crossbeam-utils",
[[package]]
name = "crossbeam-utils"
-version = "0.8.6"
+version = "0.8.8"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cfcae03edb34f947e64acdb1c33ec169824e20657e9ecb61cef6c8c74dcb8120"
+checksum = "0bf124c720b7686e3c2663cf54062ab0f68a88af2fb6a030e87e30bf721fcb38"
dependencies = [
"cfg-if 1.0.0",
"lazy_static",
[[package]]
name = "html5ever"
-version = "0.25.1"
+version = "0.26.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "aafcf38a1a36118242d29b92e1b08ef84e67e4a5ed06e0a80be20e6a32bfed6b"
+checksum = "bea68cab48b8459f17cf1c944c67ddc572d272d9f2b274140f223ecb1da4a3b7"
dependencies = [
"log",
"mac",
"serde",
]
+[[package]]
+name = "kstring"
+version = "2.0.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ec3066350882a1cd6d950d055997f379ac37fd39f81cd4d8ed186032eb3c5747"
+dependencies = [
+ "static_assertions",
+]
+
[[package]]
name = "lazy_static"
version = "1.4.0"
[[package]]
name = "markup5ever"
-version = "0.10.1"
+version = "0.11.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "a24f40fb03852d1cdd84330cddcaf98e9ec08a7b7768e952fad3b4cf048ec8fd"
+checksum = "7a2629bb1404f3d34c2e921f21fd34ba00b206124c81f65c50b43b6aaefeb016"
dependencies = [
"log",
"phf",
"tendril",
]
-[[package]]
-name = "markup5ever_rcdom"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f015da43bcd8d4f144559a3423f4591d69b8ce0652c905374da7205df336ae2b"
-dependencies = [
- "html5ever",
- "markup5ever",
- "tendril",
- "xml5ever",
-]
-
[[package]]
name = "matchers"
version = "0.1.0"
"libc",
"log",
"measureme 9.1.2",
- "rand 0.8.4",
+ "rand 0.8.5",
"rustc-workspace-hack",
"rustc_version",
"shell-escape",
[[package]]
name = "once_cell"
-version = "1.7.2"
+version = "1.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "af8b08b04175473088b46763e51ee54da5f9a164bc162f615b91bc179dbf15a3"
+checksum = "87f3e037eac156d1775da914196f0f37741a274155e34a0b7e427c35d2a2ecb9"
[[package]]
name = "opaque-debug"
[[package]]
name = "openssl-src"
-version = "111.17.0+1.1.1m"
+version = "111.18.0+1.1.1n"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "05d6a336abd10814198f66e2a91ccd7336611f30334119ca8ce300536666fcf4"
+checksum = "7897a926e1e8d00219127dc020130eca4292e5ca666dd592480d72c3eca2ff6c"
dependencies = [
"cc",
]
[[package]]
name = "phf"
-version = "0.8.0"
+version = "0.10.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3dfb61232e34fcb633f43d12c58f83c1df82962dcdfa565a4e866ffc17dafe12"
+checksum = "fabbf1ead8a5bcbc20f5f8b939ee3f5b0f6f281b6ad3468b84656b658b455259"
dependencies = [
"phf_shared",
]
[[package]]
name = "phf_codegen"
-version = "0.8.0"
+version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "cbffee61585b0411840d3ece935cce9cb6321f01c45477d30066498cd5e1a815"
+checksum = "4fb1c3a8bc4dd4e5cfce29b44ffc14bedd2ee294559a294e2a4d4c9e9a6a13cd"
dependencies = [
"phf_generator",
"phf_shared",
[[package]]
name = "phf_generator"
-version = "0.8.0"
+version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "17367f0cc86f2d25802b2c26ee58a7b23faeccf78a396094c13dced0d0182526"
+checksum = "5d5285893bb5eb82e6aaf5d59ee909a06a16737a8970984dd7746ba9283498d6"
dependencies = [
"phf_shared",
- "rand 0.7.3",
+ "rand 0.8.5",
]
[[package]]
name = "phf_shared"
-version = "0.8.0"
+version = "0.10.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "c00cf8b9eafe68dde5e9eaa2cef8ee84a9336a47d566ec55ca16589633b65af7"
+checksum = "b6796ad771acdc0123d2a88dc428b5e38ef24456743ddb1744ed628f9815c096"
dependencies = [
"siphasher",
]
[[package]]
name = "proc-macro2"
-version = "1.0.30"
+version = "1.0.37"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "edc3358ebc67bc8b7fa0c007f945b0b18226f78437d61bec735a9eb96b61ee70"
+checksum = "ec757218438d5fda206afc041538b2f6d889286160d649a86a24d37e1235afd1"
dependencies = [
"unicode-xid",
]
[[package]]
name = "quote"
-version = "1.0.7"
+version = "1.0.18"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37"
+checksum = "a1feb54ed693b93a84e14094943b84b7c4eae204c512b7ccb95ab0c66d278ad1"
dependencies = [
"proc-macro2",
]
"libc",
"rand_chacha 0.2.2",
"rand_core 0.5.1",
- "rand_hc 0.2.0",
- "rand_pcg",
+ "rand_hc",
]
[[package]]
name = "rand"
-version = "0.8.4"
+version = "0.8.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2e7573632e6454cf6b99d7aac4ccca54be06da05aca2ef7423d22d27d4d4bcd8"
+checksum = "34af8d1a0e25924bc5b7c43c079c942339d8f0a8b57c39049bef581b46327404"
dependencies = [
"libc",
"rand_chacha 0.3.0",
"rand_core 0.6.2",
- "rand_hc 0.3.0",
]
[[package]]
"rand_core 0.5.1",
]
-[[package]]
-name = "rand_hc"
-version = "0.3.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3190ef7066a446f2e7f42e239d161e905420ccab01eb967c9eb27d21b2322a73"
-dependencies = [
- "rand_core 0.6.2",
-]
-
-[[package]]
-name = "rand_pcg"
-version = "0.2.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "16abd0c1b639e9eb4d7c50c0b8100b0d0f849be2349829c740fe8e6eb4816429"
-dependencies = [
- "rand_core 0.5.1",
-]
-
[[package]]
name = "rand_xorshift"
version = "0.2.0"
"num_cpus",
"ordslice",
"racer",
- "rand 0.8.4",
+ "rand 0.8.5",
"rayon",
"regex",
"rls-analysis",
"tokio-stream",
"tokio-util",
"toml",
- "toml_edit",
+ "toml_edit 0.13.4",
"url 2.2.2",
"walkdir",
]
"env_logger 0.9.0",
"futures 0.3.19",
"log",
- "rand 0.8.4",
+ "rand 0.8.5",
"rls-data",
"rls-ipc",
"serde",
name = "rustc_incremental"
version = "0.0.0"
dependencies = [
- "rand 0.8.4",
+ "rand 0.8.5",
"rustc_ast",
"rustc_data_structures",
"rustc_errors",
"either",
"gsgdt",
"polonius-engine",
- "rand 0.8.4",
+ "rand 0.8.5",
"rand_xoshiro 0.6.0",
"rustc-rayon",
"rustc-rayon-core",
"rustc_errors",
"rustc_feature",
"rustc_lexer",
+ "rustc_macros",
"rustc_session",
"rustc_span",
"tracing",
"expect-test",
"itertools",
"minifier",
+ "once_cell",
"pulldown-cmark",
"rayon",
"regex",
"winapi",
]
+[[package]]
+name = "static_assertions"
+version = "1.1.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "a2eb9349b6444b326872e140eb1cf5e7c522154d69e7a0ffb0fb81c06b37543f"
+
[[package]]
name = "std"
version = "0.0.0"
[[package]]
name = "string_cache"
-version = "0.8.0"
+version = "0.8.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2940c75beb4e3bf3a494cef919a747a2cb81e52571e212bfbd185074add7208a"
+checksum = "33994d0838dc2d152d17a62adf608a869b5e846b65b389af7f3dbc1de45c5b26"
dependencies = [
"lazy_static",
"new_debug_unreachable",
+ "parking_lot",
"phf_shared",
"precomputed-hash",
"serde",
[[package]]
name = "string_cache_codegen"
-version = "0.5.1"
+version = "0.5.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "f24c8e5e19d22a726626f1a5e16fe15b132dcf21d10177fa5a45ce7962996b97"
+checksum = "6bb30289b722be4ff74a408c3cc27edeaad656e06cb1fe8fa9231fa59c728988"
dependencies = [
"phf_generator",
"phf_shared",
[[package]]
name = "syn"
-version = "1.0.80"
+version = "1.0.91"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d010a1623fbd906d51d650a9916aaefc05ffa0e4053ff7fe601167f3e715d194"
+checksum = "b683b2b825c8eef438b77c36a06dc262294da3d5a5813fac20da149241dcd44d"
dependencies = [
"proc-macro2",
"quote",
dependencies = [
"cfg-if 1.0.0",
"libc",
- "rand 0.8.4",
+ "rand 0.8.5",
"redox_syscall",
"remove_dir_all",
"winapi",
"combine",
"indexmap",
"itertools",
- "kstring",
+ "kstring 1.0.6",
+ "serde",
+]
+
+[[package]]
+name = "toml_edit"
+version = "0.14.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "ba98375fd631b83696f87c64e4ed8e29e6a1f3404d6aed95fa95163bad38e705"
+dependencies = [
+ "combine",
+ "indexmap",
+ "itertools",
+ "kstring 2.0.0",
"serde",
]
"libc",
]
-[[package]]
-name = "xml5ever"
-version = "0.16.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "0b1b52e6e8614d4a58b8e70cf51ec0cc21b256ad8206708bcff8139b5bbd6a59"
-dependencies = [
- "log",
- "mac",
- "markup5ever",
- "time",
-]
-
[[package]]
name = "xz2"
version = "0.1.6"
pub use UnsafeSource::*;
use crate::ptr::P;
-use crate::token::{self, CommentKind, DelimToken, Token};
+use crate::token::{self, CommentKind, Delimiter, Token};
use crate::tokenstream::{DelimSpan, LazyTokenStream, TokenStream, TokenTree};
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
pub bounds: GenericBounds,
pub is_placeholder: bool,
pub kind: GenericParamKind,
+ pub colon_span: Option<Span>,
}
impl GenericParam {
ExprKind::Paren(..) => ExprPrecedence::Paren,
ExprKind::Try(..) => ExprPrecedence::Try,
ExprKind::Yield(..) => ExprPrecedence::Yield,
+ ExprKind::Yeet(..) => ExprPrecedence::Yeet,
ExprKind::Err => ExprPrecedence::Err,
}
}
/// A `yield`, with an optional value to be yielded.
Yield(Option<P<Expr>>),
+ /// A `do yeet` (aka `throw`/`fail`/`bail`/`raise`/whatever),
+ /// with an optional value to be returned.
+ Yeet(Option<P<Expr>>),
+
/// Placeholder for an expression that wasn't syntactically well formed in some way.
Err,
}
}
impl MacArgs {
- pub fn delim(&self) -> DelimToken {
+ pub fn delim(&self) -> Option<Delimiter> {
match self {
- MacArgs::Delimited(_, delim, _) => delim.to_token(),
- MacArgs::Empty | MacArgs::Eq(..) => token::NoDelim,
+ MacArgs::Delimited(_, delim, _) => Some(delim.to_token()),
+ MacArgs::Empty | MacArgs::Eq(..) => None,
}
}
}
impl MacDelimiter {
- pub fn to_token(self) -> DelimToken {
+ pub fn to_token(self) -> Delimiter {
match self {
- MacDelimiter::Parenthesis => DelimToken::Paren,
- MacDelimiter::Bracket => DelimToken::Bracket,
- MacDelimiter::Brace => DelimToken::Brace,
+ MacDelimiter::Parenthesis => Delimiter::Parenthesis,
+ MacDelimiter::Bracket => Delimiter::Bracket,
+ MacDelimiter::Brace => Delimiter::Brace,
}
}
- pub fn from_token(delim: DelimToken) -> Option<MacDelimiter> {
+ pub fn from_token(delim: Delimiter) -> Option<MacDelimiter> {
match delim {
- token::Paren => Some(MacDelimiter::Parenthesis),
- token::Bracket => Some(MacDelimiter::Bracket),
- token::Brace => Some(MacDelimiter::Brace),
- token::NoDelim => None,
+ Delimiter::Parenthesis => Some(MacDelimiter::Parenthesis),
+ Delimiter::Bracket => Some(MacDelimiter::Bracket),
+ Delimiter::Brace => Some(MacDelimiter::Brace),
+ Delimiter::Invisible => None,
}
}
}
use crate::ast::{Lit, LitKind};
use crate::ast::{MacArgs, MacDelimiter, MetaItem, MetaItemKind, NestedMetaItem};
use crate::ast::{Path, PathSegment};
-use crate::token::{self, CommentKind, Token};
+use crate::token::{self, CommentKind, Delimiter, Token};
use crate::tokenstream::{AttrAnnotatedTokenStream, AttrAnnotatedTokenTree};
use crate::tokenstream::{DelimSpan, Spacing, TokenTree, TreeAndSpacing};
use crate::tokenstream::{LazyTokenStream, TokenStream};
vec![
TokenTree::Delimited(
DelimSpan::from_single(span),
- token::Paren,
+ Delimiter::Parenthesis,
TokenStream::new(tokens),
)
.into(),
tokens: &mut impl Iterator<Item = TokenTree>,
) -> Option<MetaItemKind> {
match tokens.next() {
- Some(TokenTree::Delimited(_, token::NoDelim, inner_tokens)) => {
+ Some(TokenTree::Delimited(_, Delimiter::Invisible, inner_tokens)) => {
MetaItemKind::name_value_from_tokens(&mut inner_tokens.trees())
}
Some(TokenTree::Token(token)) => {
tokens: &mut iter::Peekable<impl Iterator<Item = TokenTree>>,
) -> Option<MetaItemKind> {
match tokens.peek() {
- Some(TokenTree::Delimited(_, token::Paren, inner_tokens)) => {
+ Some(TokenTree::Delimited(_, Delimiter::Parenthesis, inner_tokens)) => {
let inner_tokens = inner_tokens.clone();
tokens.next();
MetaItemKind::list_from_tokens(inner_tokens)
tokens.next();
return Some(NestedMetaItem::Literal(lit));
}
- Some(TokenTree::Delimited(_, token::NoDelim, inner_tokens)) => {
+ Some(TokenTree::Delimited(_, Delimiter::Invisible, inner_tokens)) => {
let inner_tokens = inner_tokens.clone();
tokens.next();
return NestedMetaItem::from_tokens(&mut inner_tokens.into_trees().peekable());
mut param: GenericParam,
vis: &mut T,
) -> SmallVec<[GenericParam; 1]> {
- let GenericParam { id, ident, attrs, bounds, kind, is_placeholder: _ } = &mut param;
+ let GenericParam { id, ident, attrs, bounds, kind, colon_span, is_placeholder: _ } = &mut param;
vis.visit_id(id);
vis.visit_ident(ident);
+ if let Some(ref mut colon_span) = colon_span {
+ vis.visit_span(colon_span);
+ }
visit_thin_attrs(attrs, vis);
visit_vec(bounds, |bound| noop_visit_param_bound(bound, vis));
match kind {
ExprKind::Ret(expr) => {
visit_opt(expr, |expr| vis.visit_expr(expr));
}
+ ExprKind::Yeet(expr) => {
+ visit_opt(expr, |expr| vis.visit_expr(expr));
+ }
ExprKind::InlineAsm(asm) => vis.visit_inline_asm(asm),
ExprKind::MacCall(mac) => vis.visit_mac_call(mac),
ExprKind::Struct(se) => {
pub use BinOpToken::*;
-pub use DelimToken::*;
pub use LitKind::*;
pub use Nonterminal::*;
pub use TokenKind::*;
Shr,
}
-/// A delimiter token.
-#[derive(Clone, PartialEq, Eq, Encodable, Decodable, Hash, Debug, Copy)]
-#[derive(HashStable_Generic)]
-pub enum DelimToken {
- /// A round parenthesis (i.e., `(` or `)`).
- Paren,
- /// A square bracket (i.e., `[` or `]`).
- Bracket,
- /// A curly brace (i.e., `{` or `}`).
+/// Describes how a sequence of token trees is delimited.
+/// Cannot use `proc_macro::Delimiter` directly because this
+/// structure should implement some additional traits.
+/// The `None` variant is also renamed to `Invisible` to be
+/// less confusing and better convey the semantics.
+#[derive(Copy, Clone, Debug, PartialEq, Eq)]
+#[derive(Encodable, Decodable, Hash, HashStable_Generic)]
+pub enum Delimiter {
+ /// `( ... )`
+ Parenthesis,
+ /// `{ ... }`
Brace,
- /// An empty delimiter.
- NoDelim,
+ /// `[ ... ]`
+ Bracket,
+ /// `Ø ... Ø`
+ /// An invisible delimiter, that may, for example, appear around tokens coming from a
+ /// "macro variable" `$var`. It is important to preserve operator priorities in cases like
+ /// `$var * 3` where `$var` is `1 + 2`.
+ /// Invisible delimiters might not survive roundtrip of a token stream through a string.
+ Invisible,
}
#[derive(Clone, Copy, PartialEq, Encodable, Decodable, Debug, HashStable_Generic)]
/// Used by proc macros for representing lifetimes, not generated by lexer right now.
SingleQuote,
/// An opening delimiter (e.g., `{`).
- OpenDelim(DelimToken),
+ OpenDelim(Delimiter),
/// A closing delimiter (e.g., `}`).
- CloseDelim(DelimToken),
+ CloseDelim(Delimiter),
/* Literals */
Literal(Lit),
match self.uninterpolate().kind {
Ident(name, is_raw) =>
ident_can_begin_type(name, self.span, is_raw), // type name or keyword
- OpenDelim(Paren) | // tuple
- OpenDelim(Bracket) | // array
+ OpenDelim(Delimiter::Parenthesis) | // tuple
+ OpenDelim(Delimiter::Bracket) | // array
Not | // never
BinOp(Star) | // raw pointer
BinOp(And) | // reference
/// Returns `true` if the token can appear at the start of a const param.
pub fn can_begin_const_arg(&self) -> bool {
match self.kind {
- OpenDelim(Brace) => true,
+ OpenDelim(Delimiter::Brace) => true,
Interpolated(ref nt) => matches!(**nt, NtExpr(..) | NtBlock(..) | NtLiteral(..)),
_ => self.can_begin_literal_maybe_minus(),
}
|| self.is_lifetime()
|| self.is_keyword(kw::For)
|| self == &Question
- || self == &OpenDelim(Paren)
+ || self == &OpenDelim(Delimiter::Parenthesis)
}
/// Returns `true` if the token is any literal.
//! and a borrowed `TokenStream` is sufficient to build an owned `TokenStream` without taking
//! ownership of the original.
-use crate::token::{self, DelimToken, Token, TokenKind};
+use crate::token::{self, Delimiter, Token, TokenKind};
use crate::AttrVec;
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
/// A single token.
Token(Token),
/// A delimited sequence of token trees.
- Delimited(DelimSpan, DelimToken, TokenStream),
+ Delimited(DelimSpan, Delimiter, TokenStream),
}
#[derive(Copy, Clone)]
where
Token: Send + Sync,
DelimSpan: Send + Sync,
- DelimToken: Send + Sync,
+ Delimiter: Send + Sync,
TokenStream: Send + Sync,
{
}
TokenTree::Token(Token::new(kind, span))
}
- /// Returns the opening delimiter as a token tree.
- pub fn open_tt(span: DelimSpan, delim: DelimToken) -> TokenTree {
- TokenTree::token(token::OpenDelim(delim), span.open)
- }
-
- /// Returns the closing delimiter as a token tree.
- pub fn close_tt(span: DelimSpan, delim: DelimToken) -> TokenTree {
- TokenTree::token(token::CloseDelim(delim), span.close)
- }
-
pub fn uninterpolate(self) -> TokenTree {
match self {
TokenTree::Token(token) => TokenTree::Token(token.uninterpolate().into_owned()),
#[derive(Clone, Debug, Encodable, Decodable)]
pub enum AttrAnnotatedTokenTree {
Token(Token),
- Delimited(DelimSpan, DelimToken, AttrAnnotatedTokenStream),
+ Delimited(DelimSpan, Delimiter, AttrAnnotatedTokenStream),
/// Stores the attributes for an attribute target,
/// along with the tokens for that attribute target.
/// See `AttributesData` for more information
Cursor { stream, index: 0 }
}
+ #[inline]
pub fn next_with_spacing(&mut self) -> Option<TreeAndSpacing> {
- if self.index < self.stream.len() {
+ self.stream.0.get(self.index).map(|tree| {
self.index += 1;
- Some(self.stream.0[self.index - 1].clone())
- } else {
- None
- }
+ tree.clone()
+ })
+ }
+
+ #[inline]
+ pub fn next_with_spacing_ref(&mut self) -> Option<&TreeAndSpacing> {
+ self.stream.0.get(self.index).map(|tree| {
+ self.index += 1;
+ tree
+ })
}
pub fn index(&self) -> usize {
Continue,
Ret,
Yield,
+ Yeet,
Range,
ExprPrecedence::Break |
ExprPrecedence::Continue |
ExprPrecedence::Ret |
- ExprPrecedence::Yield => PREC_JUMP,
+ ExprPrecedence::Yield |
+ ExprPrecedence::Yeet => PREC_JUMP,
// `Range` claims to have higher precedence than `Assign`, but `x .. x = x` fails to
// parse, instead of parsing as `(x .. x) = x`. Giving `Range` a lower precedence
Assoc(AssocCtxt),
}
+#[derive(Copy, Clone, Debug)]
+pub enum BoundKind {
+ /// Trait bounds in generics bounds and type/trait alias.
+ /// E.g., `<T: Bound>`, `type A: Bound`, or `where T: Bound`.
+ Bound,
+
+ /// Trait bounds in `impl` type.
+ /// E.g., `type Foo = impl Bound1 + Bound2 + Bound3`.
+ Impl,
+
+ /// Trait bounds in trait object type.
+ /// E.g., `dyn Bound1 + Bound2 + Bound3`.
+ TraitObject,
+
+ /// Super traits of a trait.
+ /// E.g., `trait A: B`
+ SuperTraits,
+}
+
#[derive(Copy, Clone, Debug)]
pub enum FnKind<'a> {
/// E.g., `fn foo()`, `fn foo(&self)`, or `extern "Abi" fn foo()`.
fn visit_trait_ref(&mut self, t: &'ast TraitRef) {
walk_trait_ref(self, t)
}
- fn visit_param_bound(&mut self, bounds: &'ast GenericBound) {
+ fn visit_param_bound(&mut self, bounds: &'ast GenericBound, _ctxt: BoundKind) {
walk_param_bound(self, bounds)
}
fn visit_poly_trait_ref(&mut self, t: &'ast PolyTraitRef, m: &'ast TraitBoundModifier) {
ItemKind::GlobalAsm(ref asm) => walk_inline_asm(visitor, asm),
ItemKind::TyAlias(box TyAlias { ref generics, ref bounds, ref ty, .. }) => {
visitor.visit_generics(generics);
- walk_list!(visitor, visit_param_bound, bounds);
+ walk_list!(visitor, visit_param_bound, bounds, BoundKind::Bound);
walk_list!(visitor, visit_ty, ty);
}
ItemKind::Enum(ref enum_definition, ref generics) => {
ref items,
}) => {
visitor.visit_generics(generics);
- walk_list!(visitor, visit_param_bound, bounds);
+ walk_list!(visitor, visit_param_bound, bounds, BoundKind::SuperTraits);
walk_list!(visitor, visit_assoc_item, items, AssocCtxt::Trait);
}
ItemKind::TraitAlias(ref generics, ref bounds) => {
visitor.visit_generics(generics);
- walk_list!(visitor, visit_param_bound, bounds);
+ walk_list!(visitor, visit_param_bound, bounds, BoundKind::Bound);
}
ItemKind::MacCall(ref mac) => visitor.visit_mac_call(mac),
ItemKind::MacroDef(ref ts) => visitor.visit_mac_def(ts, item.id),
visitor.visit_ty(ty);
visitor.visit_anon_const(length)
}
- TyKind::TraitObject(ref bounds, ..) | TyKind::ImplTrait(_, ref bounds) => {
- walk_list!(visitor, visit_param_bound, bounds);
+ TyKind::TraitObject(ref bounds, ..) => {
+ walk_list!(visitor, visit_param_bound, bounds, BoundKind::TraitObject);
+ }
+ TyKind::ImplTrait(_, ref bounds) => {
+ walk_list!(visitor, visit_param_bound, bounds, BoundKind::Impl);
}
TyKind::Typeof(ref expression) => visitor.visit_anon_const(expression),
TyKind::Infer | TyKind::ImplicitSelf | TyKind::Err => {}
Term::Const(c) => visitor.visit_anon_const(c),
},
AssocConstraintKind::Bound { ref bounds } => {
- walk_list!(visitor, visit_param_bound, bounds);
+ walk_list!(visitor, visit_param_bound, bounds, BoundKind::Bound);
}
}
}
}
ForeignItemKind::TyAlias(box TyAlias { generics, bounds, ty, .. }) => {
visitor.visit_generics(generics);
- walk_list!(visitor, visit_param_bound, bounds);
+ walk_list!(visitor, visit_param_bound, bounds, BoundKind::Bound);
walk_list!(visitor, visit_ty, ty);
}
ForeignItemKind::MacCall(mac) => {
pub fn walk_generic_param<'a, V: Visitor<'a>>(visitor: &mut V, param: &'a GenericParam) {
visitor.visit_ident(param.ident);
walk_list!(visitor, visit_attribute, param.attrs.iter());
- walk_list!(visitor, visit_param_bound, ¶m.bounds);
+ walk_list!(visitor, visit_param_bound, ¶m.bounds, BoundKind::Bound);
match param.kind {
GenericParamKind::Lifetime => (),
GenericParamKind::Type { ref default } => walk_list!(visitor, visit_ty, default),
..
}) => {
visitor.visit_ty(bounded_ty);
- walk_list!(visitor, visit_param_bound, bounds);
+ walk_list!(visitor, visit_param_bound, bounds, BoundKind::Bound);
walk_list!(visitor, visit_generic_param, bound_generic_params);
}
WherePredicate::RegionPredicate(WhereRegionPredicate {
ref lifetime, ref bounds, ..
}) => {
visitor.visit_lifetime(lifetime);
- walk_list!(visitor, visit_param_bound, bounds);
+ walk_list!(visitor, visit_param_bound, bounds, BoundKind::Bound);
}
WherePredicate::EqPredicate(WhereEqPredicate { ref lhs_ty, ref rhs_ty, .. }) => {
visitor.visit_ty(lhs_ty);
}
AssocItemKind::TyAlias(box TyAlias { generics, bounds, ty, .. }) => {
visitor.visit_generics(generics);
- walk_list!(visitor, visit_param_bound, bounds);
+ walk_list!(visitor, visit_param_bound, bounds, BoundKind::Bound);
walk_list!(visitor, visit_ty, ty);
}
AssocItemKind::MacCall(mac) => {
ExprKind::Ret(ref optional_expression) => {
walk_list!(visitor, visit_expr, optional_expression);
}
+ ExprKind::Yeet(ref optional_expression) => {
+ walk_list!(visitor, visit_expr, optional_expression);
+ }
ExprKind::MacCall(ref mac) => visitor.visit_mac_call(mac),
ExprKind::Paren(ref subexpression) => visitor.visit_expr(subexpression),
ExprKind::InlineAsm(ref asm) => walk_inline_asm(visitor, asm),
e.span,
seg,
ParamMode::Optional,
- 0,
ParenthesizedGenericArgs::Err,
ImplTraitContext::Disallowed(ImplTraitPosition::Path),
));
let e = e.as_ref().map(|x| self.lower_expr(x));
hir::ExprKind::Ret(e)
}
+ ExprKind::Yeet(ref sub_expr) => self.lower_expr_yeet(e.span, sub_expr.as_deref()),
ExprKind::InlineAsm(ref asm) => {
hir::ExprKind::InlineAsm(self.lower_inline_asm(e.span, asm))
}
)
}
+ /// Desugar `ExprKind::Yeet` from: `do yeet <expr>` into:
+ /// ```rust
+ /// // If there is an enclosing `try {...}`:
+ /// break 'catch_target FromResidual::from_residual(Yeet(residual)),
+ /// // Otherwise:
+ /// return FromResidual::from_residual(Yeet(residual)),
+ /// ```
+ /// But to simplify this, there's a `from_yeet` lang item function which
+ /// handles the combined `FromResidual::from_residual(Yeet(residual))`.
+ fn lower_expr_yeet(&mut self, span: Span, sub_expr: Option<&Expr>) -> hir::ExprKind<'hir> {
+ // The expression (if present) or `()` otherwise.
+ let (yeeted_span, yeeted_expr) = if let Some(sub_expr) = sub_expr {
+ (sub_expr.span, self.lower_expr(sub_expr))
+ } else {
+ (self.mark_span_with_reason(DesugaringKind::YeetExpr, span, None), self.expr_unit(span))
+ };
+
+ let unstable_span = self.mark_span_with_reason(
+ DesugaringKind::YeetExpr,
+ span,
+ self.allow_try_trait.clone(),
+ );
+
+ let from_yeet_expr = self.wrap_in_try_constructor(
+ hir::LangItem::TryTraitFromYeet,
+ unstable_span,
+ yeeted_expr,
+ yeeted_span,
+ );
+
+ if let Some(catch_node) = self.catch_scope {
+ let target_id = Ok(self.lower_node_id(catch_node));
+ hir::ExprKind::Break(hir::Destination { label: None, target_id }, Some(from_yeet_expr))
+ } else {
+ hir::ExprKind::Ret(Some(from_yeet_expr))
+ }
+ }
+
// =========================================================================
// Helper methods for building HIR.
// =========================================================================
definitions: &'a definitions::Definitions,
}
+#[tracing::instrument(level = "debug", skip(sess, definitions, bodies))]
pub(super) fn index_hir<'hir>(
sess: &Session,
definitions: &definitions::Definitions,
}
impl<'a, 'hir> NodeCollector<'a, 'hir> {
+ #[tracing::instrument(level = "debug", skip(self))]
fn insert(&mut self, span: Span, hir_id: HirId, node: Node<'hir>) {
debug_assert_eq!(self.owner, hir_id.owner);
debug_assert_ne!(hir_id.local_id.as_u32(), 0);
});
}
+ #[tracing::instrument(level = "debug", skip(self))]
fn visit_item(&mut self, i: &'hir Item<'hir>) {
- debug!("visit_item: {:?}", i);
debug_assert_eq!(i.def_id, self.owner);
self.with_parent(i.hir_id(), |this| {
if let ItemKind::Struct(ref struct_def, _) = i.kind {
});
}
+ #[tracing::instrument(level = "debug", skip(self))]
fn visit_foreign_item(&mut self, fi: &'hir ForeignItem<'hir>) {
debug_assert_eq!(fi.def_id, self.owner);
self.with_parent(fi.hir_id(), |this| {
})
}
+ #[tracing::instrument(level = "debug", skip(self))]
fn visit_trait_item(&mut self, ti: &'hir TraitItem<'hir>) {
debug_assert_eq!(ti.def_id, self.owner);
self.with_parent(ti.hir_id(), |this| {
});
}
+ #[tracing::instrument(level = "debug", skip(self))]
fn visit_impl_item(&mut self, ii: &'hir ImplItem<'hir>) {
debug_assert_eq!(ii.def_id, self.owner);
self.with_parent(ii.hir_id(), |this| {
self.insert(lifetime.span, lifetime.hir_id, Node::Lifetime(lifetime));
}
- fn visit_vis(&mut self, visibility: &'hir Visibility<'hir>) {
- match visibility.node {
- VisibilityKind::Public | VisibilityKind::Crate(_) | VisibilityKind::Inherited => {}
- VisibilityKind::Restricted { hir_id, .. } => {
- self.insert(visibility.span, hir_id, Node::Visibility(visibility));
- self.with_parent(hir_id, |this| {
- intravisit::walk_vis(this, visibility);
- });
- }
- }
- }
-
fn visit_variant(&mut self, v: &'hir Variant<'hir>, g: &'hir Generics<'hir>, item_id: HirId) {
self.insert(v.span, v.id, Node::Variant(v));
self.with_parent(v.id, |this| {
-use super::{AnonymousLifetimeMode, LoweringContext, ParamMode};
use super::{AstOwner, ImplTraitContext, ImplTraitPosition, ResolverAstLowering};
+use super::{LoweringContext, ParamMode};
use crate::{Arena, FnDeclKind};
use rustc_ast::ptr::P;
use rustc_ast::visit::AssocCtxt;
use rustc_ast::*;
-use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::sorted_map::SortedMap;
use rustc_errors::struct_span_err;
use rustc_hir as hir;
use rustc_index::vec::{Idx, IndexVec};
use rustc_session::utils::NtToTokenstream;
use rustc_session::Session;
-use rustc_span::source_map::{respan, DesugaringKind};
+use rustc_span::source_map::DesugaringKind;
use rustc_span::symbol::{kw, sym, Ident};
use rustc_span::Span;
use rustc_target::spec::abi;
is_in_loop_condition: false,
is_in_trait_impl: false,
is_in_dyn_type: false,
- anonymous_lifetime_mode: AnonymousLifetimeMode::PassThrough,
generator_kind: None,
task_context: None,
current_item: None,
- lifetimes_to_define: Vec::new(),
- is_collecting_anonymous_lifetimes: None,
- in_scope_lifetimes: Vec::new(),
- allow_try_trait: Some([sym::try_trait_v2][..].into()),
+ captured_lifetimes: None,
+ allow_try_trait: Some([sym::try_trait_v2, sym::yeet_desugar_details][..].into()),
allow_gen_future: Some([sym::gen_future][..].into()),
allow_into_future: Some([sym::into_future][..].into()),
};
LocalDefId { local_def_index }
};
- let parent_hir = self.lower_node(parent_id).unwrap().node().expect_item();
+ let parent_hir = self.lower_node(parent_id).unwrap();
self.with_lctx(item.id, |lctx| {
// Evaluate with the lifetimes in `params` in-scope.
// This is used to track which lifetimes have already been defined,
// and which need to be replicated when lowering an async fn.
- match parent_hir.kind {
- hir::ItemKind::Impl(hir::Impl { ref of_trait, ref generics, .. }) => {
+ match parent_hir.node().expect_item().kind {
+ hir::ItemKind::Impl(hir::Impl { ref of_trait, .. }) => {
lctx.is_in_trait_impl = of_trait.is_some();
- lctx.in_scope_lifetimes = generics
- .params
- .iter()
- .filter(|param| {
- matches!(param.kind, hir::GenericParamKind::Lifetime { .. })
- })
- .map(|param| param.name)
- .collect();
- }
- hir::ItemKind::Trait(_, _, ref generics, ..) => {
- lctx.in_scope_lifetimes = generics
- .params
- .iter()
- .filter(|param| {
- matches!(param.kind, hir::GenericParamKind::Lifetime { .. })
- })
- .map(|param| param.name)
- .collect();
}
_ => {}
};
fn lower_item(&mut self, i: &Item) -> &'hir hir::Item<'hir> {
let mut ident = i.ident;
- let mut vis = self.lower_visibility(&i.vis);
+ let vis_span = self.lower_span(i.vis.span);
let hir_id = self.lower_node_id(i.id);
let attrs = self.lower_attrs(hir_id, &i.attrs);
- let kind = self.lower_item_kind(i.span, i.id, hir_id, &mut ident, attrs, &mut vis, &i.kind);
+ let kind = self.lower_item_kind(i.span, i.id, hir_id, &mut ident, attrs, vis_span, &i.kind);
let item = hir::Item {
def_id: hir_id.expect_owner(),
ident: self.lower_ident(ident),
kind,
- vis,
+ vis_span,
span: self.lower_span(i.span),
};
self.arena.alloc(item)
hir_id: hir::HirId,
ident: &mut Ident,
attrs: Option<&'hir [Attribute]>,
- vis: &mut hir::Visibility<'hir>,
+ vis_span: Span,
i: &ItemKind,
) -> hir::ItemKind<'hir> {
match *i {
// Start with an empty prefix.
let prefix = Path { segments: vec![], span: use_tree.span, tokens: None };
- self.lower_use_tree(use_tree, &prefix, id, vis, ident, attrs)
+ self.lower_use_tree(use_tree, &prefix, id, vis_span, ident, attrs)
}
ItemKind::Static(ref t, m, ref e) => {
let (ty, body_id) = self.lower_const_item(t, span, e.as_deref());
ref body,
..
}) => {
- let fn_def_id = self.resolver.local_def_id(id);
self.with_new_scopes(|this| {
this.current_item = Some(ident.span);
let body_id =
this.lower_maybe_async_body(span, &decl, asyncness, body.as_deref());
- let (generics, decl) = this.add_in_band_defs(
- generics,
- fn_def_id,
- AnonymousLifetimeMode::PassThrough,
- |this, idty| {
+ let (generics, decl) =
+ this.add_implicit_generics(generics, id, |this, idty, idpb| {
let ret_id = asyncness.opt_return_id();
this.lower_fn_decl(
&decl,
- Some((fn_def_id, idty)),
+ Some((id, idty, idpb)),
FnDeclKind::Fn,
ret_id,
)
- },
- );
+ });
let sig = hir::FnSig {
decl,
header: this.lower_fn_header(header),
//
// type Foo = Foo1
// opaque type Foo1: Trait
- let ty = self.lower_ty(
- ty,
- ImplTraitContext::TypeAliasesOpaqueTy {
- capturable_lifetimes: &mut FxHashSet::default(),
- },
- );
+ let ty = self.lower_ty(ty, ImplTraitContext::TypeAliasesOpaqueTy);
let mut generics = generics.clone();
add_ty_alias_where_clause(&mut generics, where_clauses, true);
let generics = self.lower_generics(
// method, it will not be considered an in-band
// lifetime to be added, but rather a reference to a
// parent lifetime.
- let lowered_trait_def_id = hir_id.expect_owner();
- let (generics, (trait_ref, lowered_ty)) = self.add_in_band_defs(
- ast_generics,
- lowered_trait_def_id,
- AnonymousLifetimeMode::CreateParameter,
- |this, _| {
+ let (generics, (trait_ref, lowered_ty)) =
+ self.add_implicit_generics(ast_generics, id, |this, _, _| {
let trait_ref = trait_ref.as_ref().map(|trait_ref| {
this.lower_trait_ref(
trait_ref,
.lower_ty(ty, ImplTraitContext::Disallowed(ImplTraitPosition::Type));
(trait_ref, lowered_ty)
- },
- );
-
- let new_impl_items =
- self.with_in_scope_lifetime_defs(&ast_generics.params, |this| {
- this.arena.alloc_from_iter(
- impl_items.iter().map(|item| this.lower_impl_item_ref(item)),
- )
});
+ let new_impl_items = self
+ .arena
+ .alloc_from_iter(impl_items.iter().map(|item| self.lower_impl_item_ref(item)));
+
// `defaultness.has_value()` is never called for an `impl`, always `true` in order
// to not cause an assertion failure inside the `lower_defaultness` function.
let has_val = true;
ImplPolarity::Positive => ImplPolarity::Positive,
ImplPolarity::Negative(s) => ImplPolarity::Negative(self.lower_span(s)),
};
- hir::ItemKind::Impl(hir::Impl {
+ hir::ItemKind::Impl(self.arena.alloc(hir::Impl {
unsafety: self.lower_unsafety(unsafety),
polarity,
defaultness,
of_trait: trait_ref,
self_ty: lowered_ty,
items: new_impl_items,
- })
+ }))
}
ItemKind::Trait(box Trait {
is_auto,
tree: &UseTree,
prefix: &Path,
id: NodeId,
- vis: &mut hir::Visibility<'hir>,
+ vis_span: Span,
ident: &mut Ident,
attrs: Option<&'hir [Attribute]>,
) -> hir::ItemKind<'hir> {
debug!("lower_use_tree(tree={:?})", tree);
- debug!("lower_use_tree: vis = {:?}", vis);
let path = &tree.prefix;
let segments = prefix.segments.iter().chain(path.segments.iter()).cloned().collect();
let res = this.lower_res(res);
let path = this.lower_path_extra(res, &path, ParamMode::Explicit);
let kind = hir::ItemKind::Use(path, hir::UseKind::Single);
- let vis = this.rebuild_vis(&vis);
if let Some(attrs) = attrs {
this.attrs.insert(hir::ItemLocalId::new(0), attrs);
}
def_id: new_id,
ident: this.lower_ident(ident),
kind,
- vis,
+ vis_span,
span: this.lower_span(span),
};
hir::OwnerNode::Item(this.arena.alloc(item))
// own its own names, we have to adjust the owner before
// lowering the rest of the import.
self.with_hir_id_owner(id, |this| {
- let mut vis = this.rebuild_vis(&vis);
let mut ident = *ident;
let kind =
- this.lower_use_tree(use_tree, &prefix, id, &mut vis, &mut ident, attrs);
+ this.lower_use_tree(use_tree, &prefix, id, vis_span, &mut ident, attrs);
if let Some(attrs) = attrs {
this.attrs.insert(hir::ItemLocalId::new(0), attrs);
}
def_id: new_hir_id,
ident: this.lower_ident(ident),
kind,
- vis,
+ vis_span,
span: this.lower_span(use_tree.span),
};
hir::OwnerNode::Item(this.arena.alloc(item))
});
}
- // Subtle and a bit hacky: we lower the privacy level
- // of the list stem to "private" most of the time, but
- // not for "restricted" paths. The key thing is that
- // we don't want it to stay as `pub` (with no caveats)
- // because that affects rustdoc and also the lints
- // about `pub` items. But we can't *always* make it
- // private -- particularly not for restricted paths --
- // because it contains node-ids that would then be
- // unused, failing the check that HirIds are "densely
- // assigned".
- match vis.node {
- hir::VisibilityKind::Public
- | hir::VisibilityKind::Crate(_)
- | hir::VisibilityKind::Inherited => {
- *vis = respan(
- self.lower_span(prefix.span.shrink_to_lo()),
- hir::VisibilityKind::Inherited,
- );
- }
- hir::VisibilityKind::Restricted { .. } => {
- // Do nothing here, as described in the comment on the match.
- }
- }
-
let res = self.expect_full_res_from_use(id).next().unwrap_or(Res::Err);
let res = self.lower_res(res);
let path = self.lower_path_extra(res, &prefix, ParamMode::Explicit);
}
}
- /// Paths like the visibility path in `pub(super) use foo::{bar, baz}` are repeated
- /// many times in the HIR tree; for each occurrence, we need to assign distinct
- /// `NodeId`s. (See, e.g., #56128.)
- fn rebuild_use_path(&mut self, path: &hir::Path<'hir>) -> &'hir hir::Path<'hir> {
- debug!("rebuild_use_path(path = {:?})", path);
- let segments =
- self.arena.alloc_from_iter(path.segments.iter().map(|seg| hir::PathSegment {
- ident: seg.ident,
- hir_id: seg.hir_id.map(|_| self.next_id()),
- res: seg.res,
- args: None,
- infer_args: seg.infer_args,
- }));
- self.arena.alloc(hir::Path { span: path.span, res: path.res, segments })
- }
-
- fn rebuild_vis(&mut self, vis: &hir::Visibility<'hir>) -> hir::Visibility<'hir> {
- let vis_kind = match vis.node {
- hir::VisibilityKind::Public => hir::VisibilityKind::Public,
- hir::VisibilityKind::Crate(sugar) => hir::VisibilityKind::Crate(sugar),
- hir::VisibilityKind::Inherited => hir::VisibilityKind::Inherited,
- hir::VisibilityKind::Restricted { ref path, hir_id: _ } => {
- hir::VisibilityKind::Restricted {
- path: self.rebuild_use_path(path),
- hir_id: self.next_id(),
- }
- }
- };
- respan(self.lower_span(vis.span), vis_kind)
- }
-
fn lower_foreign_item(&mut self, i: &ForeignItem) -> &'hir hir::ForeignItem<'hir> {
let hir_id = self.lower_node_id(i.id);
let def_id = hir_id.expect_owner();
kind: match i.kind {
ForeignItemKind::Fn(box Fn { ref sig, ref generics, .. }) => {
let fdec = &sig.decl;
- let (generics, (fn_dec, fn_args)) = self.add_in_band_defs(
- generics,
- def_id,
- AnonymousLifetimeMode::PassThrough,
- |this, _| {
+ let (generics, (fn_dec, fn_args)) =
+ self.add_implicit_generics(generics, i.id, |this, _, _| {
(
// Disallow `impl Trait` in foreign items.
this.lower_fn_decl(fdec, None, FnDeclKind::ExternFn, None),
this.lower_fn_params_to_names(fdec),
)
- },
- );
+ });
hir::ForeignItemKind::Fn(fn_dec, fn_args, generics)
}
ForeignItemKind::TyAlias(..) => hir::ForeignItemKind::Type,
ForeignItemKind::MacCall(_) => panic!("macro shouldn't exist here"),
},
- vis: self.lower_visibility(&i.vis),
+ vis_span: self.lower_span(i.vis.span),
span: self.lower_span(i.span),
};
self.arena.alloc(item)
// FIXME(jseyfried): positional field hygiene.
None => Ident::new(sym::integer(index), self.lower_span(f.span)),
},
- vis: self.lower_visibility(&f.vis),
+ vis_span: self.lower_span(f.vis.span),
ty,
}
}
}
AssocItemKind::Fn(box Fn { ref sig, ref generics, body: None, .. }) => {
let names = self.lower_fn_params_to_names(&sig.decl);
- let (generics, sig) = self.lower_method_sig(
- generics,
- sig,
- trait_item_def_id,
- FnDeclKind::Trait,
- None,
- );
+ let (generics, sig) =
+ self.lower_method_sig(generics, sig, i.id, FnDeclKind::Trait, None);
(generics, hir::TraitItemKind::Fn(sig, hir::TraitFn::Required(names)))
}
AssocItemKind::Fn(box Fn { ref sig, ref generics, body: Some(ref body), .. }) => {
let (generics, sig) = self.lower_method_sig(
generics,
sig,
- trait_item_def_id,
+ i.id,
FnDeclKind::Trait,
asyncness.opt_return_id(),
);
}
fn lower_impl_item(&mut self, i: &AssocItem) -> &'hir hir::ImplItem<'hir> {
- let impl_item_def_id = self.resolver.local_def_id(i.id);
-
let (generics, kind) = match &i.kind {
AssocItemKind::Const(_, ty, expr) => {
let ty = self.lower_ty(ty, ImplTraitContext::Disallowed(ImplTraitPosition::Type));
let (generics, sig) = self.lower_method_sig(
generics,
sig,
- impl_item_def_id,
+ i.id,
if self.is_in_trait_impl { FnDeclKind::Impl } else { FnDeclKind::Inherent },
asyncness.opt_return_id(),
);
hir::ImplItemKind::TyAlias(ty)
}
Some(ty) => {
- let ty = self.lower_ty(
- ty,
- ImplTraitContext::TypeAliasesOpaqueTy {
- capturable_lifetimes: &mut FxHashSet::default(),
- },
- );
+ let ty = self.lower_ty(ty, ImplTraitContext::TypeAliasesOpaqueTy);
hir::ImplItemKind::TyAlias(ty)
}
};
def_id: hir_id.expect_owner(),
ident: self.lower_ident(i.ident),
generics,
- vis: self.lower_visibility(&i.vis),
kind,
+ vis_span: self.lower_span(i.vis.span),
span: self.lower_span(i.span),
};
self.arena.alloc(item)
}
}
- /// If an `explicit_owner` is given, this method allocates the `HirId` in
- /// the address space of that item instead of the item currently being
- /// lowered. This can happen during `lower_impl_item_ref()` where we need to
- /// lower a `Visibility` value although we haven't lowered the owning
- /// `ImplItem` in question yet.
- fn lower_visibility(&mut self, v: &Visibility) -> hir::Visibility<'hir> {
- let node = match v.kind {
- VisibilityKind::Public => hir::VisibilityKind::Public,
- VisibilityKind::Crate(sugar) => hir::VisibilityKind::Crate(sugar),
- VisibilityKind::Restricted { ref path, id } => {
- debug!("lower_visibility: restricted path id = {:?}", id);
- let lowered_id = self.lower_node_id(id);
- hir::VisibilityKind::Restricted {
- path: self.lower_path(id, path, ParamMode::Explicit),
- hir_id: lowered_id,
- }
- }
- VisibilityKind::Inherited => hir::VisibilityKind::Inherited,
- };
- respan(self.lower_span(v.span), node)
- }
-
fn lower_defaultness(
&self,
d: Defaultness,
&mut self,
generics: &Generics,
sig: &FnSig,
- fn_def_id: LocalDefId,
+ id: NodeId,
kind: FnDeclKind,
is_async: Option<NodeId>,
- ) -> (hir::Generics<'hir>, hir::FnSig<'hir>) {
+ ) -> (&'hir hir::Generics<'hir>, hir::FnSig<'hir>) {
let header = self.lower_fn_header(sig.header);
- let (generics, decl) = self.add_in_band_defs(
- generics,
- fn_def_id,
- AnonymousLifetimeMode::PassThrough,
- |this, idty| this.lower_fn_decl(&sig.decl, Some((fn_def_id, idty)), kind, is_async),
- );
+ let (generics, decl) = self.add_implicit_generics(generics, id, |this, idty, idpb| {
+ this.lower_fn_decl(&sig.decl, Some((id, idty, idpb)), kind, is_async)
+ });
(generics, hir::FnSig { header, decl, span: self.lower_span(sig.span) })
}
pub(super) fn lower_generics_mut(
&mut self,
generics: &Generics,
- itctx: ImplTraitContext<'_, 'hir>,
+ mut itctx: ImplTraitContext<'_, 'hir>,
) -> GenericsCtor<'hir> {
// Error if `?Trait` bounds in where clauses don't refer directly to type parameters.
// Note: we used to clone these bounds directly onto the type parameter (and avoid lowering
}
}
+ let mut predicates = SmallVec::new();
+ predicates.extend(generics.params.iter().filter_map(|param| {
+ let bounds = self.lower_param_bounds(¶m.bounds, itctx.reborrow());
+ self.lower_generic_bound_predicate(param.ident, param.id, ¶m.kind, bounds)
+ }));
+ predicates.extend(
+ generics
+ .where_clause
+ .predicates
+ .iter()
+ .map(|predicate| self.lower_where_predicate(predicate)),
+ );
+
GenericsCtor {
- params: self.lower_generic_params_mut(&generics.params, itctx).collect(),
- where_clause: self.lower_where_clause(&generics.where_clause),
+ params: self.lower_generic_params_mut(&generics.params).collect(),
+ predicates,
+ has_where_clause: !generics.where_clause.predicates.is_empty(),
+ where_clause_span: self.lower_span(generics.where_clause.span),
span: self.lower_span(generics.span),
}
}
&mut self,
generics: &Generics,
itctx: ImplTraitContext<'_, 'hir>,
- ) -> hir::Generics<'hir> {
+ ) -> &'hir hir::Generics<'hir> {
let generics_ctor = self.lower_generics_mut(generics, itctx);
generics_ctor.into_generics(self.arena)
}
- fn lower_where_clause(&mut self, wc: &WhereClause) -> hir::WhereClause<'hir> {
- self.with_anonymous_lifetime_mode(AnonymousLifetimeMode::ReportError, |this| {
- hir::WhereClause {
- predicates: this.arena.alloc_from_iter(
- wc.predicates.iter().map(|predicate| this.lower_where_predicate(predicate)),
- ),
- span: this.lower_span(wc.span),
+ pub(super) fn lower_generic_bound_predicate(
+ &mut self,
+ ident: Ident,
+ id: NodeId,
+ kind: &GenericParamKind,
+ bounds: &'hir [hir::GenericBound<'hir>],
+ ) -> Option<hir::WherePredicate<'hir>> {
+ // Do not create a clause if we do not have anything inside it.
+ if bounds.is_empty() {
+ return None;
+ }
+ let ident = self.lower_ident(ident);
+ let param_span = ident.span;
+ let span = bounds
+ .iter()
+ .fold(Some(param_span.shrink_to_hi()), |span: Option<Span>, bound| {
+ let bound_span = bound.span();
+ // We include bounds that come from a `#[derive(_)]` but point at the user's code,
+ // as we use this method to get a span appropriate for suggestions.
+ if !bound_span.can_be_used_for_suggestions() {
+ None
+ } else if let Some(span) = span {
+ Some(span.to(bound_span))
+ } else {
+ Some(bound_span)
+ }
+ })
+ .unwrap_or(param_span.shrink_to_hi());
+ match kind {
+ GenericParamKind::Const { .. } => None,
+ GenericParamKind::Type { .. } => {
+ let def_id = self.resolver.local_def_id(id).to_def_id();
+ let ty_path = self.arena.alloc(hir::Path {
+ span: param_span,
+ res: Res::Def(DefKind::TyParam, def_id),
+ segments: self.arena.alloc_from_iter([hir::PathSegment::from_ident(ident)]),
+ });
+ let ty_id = self.next_id();
+ let bounded_ty =
+ self.ty_path(ty_id, param_span, hir::QPath::Resolved(None, ty_path));
+ Some(hir::WherePredicate::BoundPredicate(hir::WhereBoundPredicate {
+ bounded_ty: self.arena.alloc(bounded_ty),
+ bounds,
+ span,
+ bound_generic_params: &[],
+ in_where_clause: false,
+ }))
}
- })
+ GenericParamKind::Lifetime => {
+ let ident_span = self.lower_span(ident.span);
+ let ident = self.lower_ident(ident);
+ let res = self.resolver.get_lifetime_res(id).unwrap_or_else(|| {
+ panic!("Missing resolution for lifetime {:?} at {:?}", id, ident.span)
+ });
+ let lt_id = self.resolver.next_node_id();
+ let lifetime = self.new_named_lifetime_with_res(lt_id, ident_span, ident, res);
+ Some(hir::WherePredicate::RegionPredicate(hir::WhereRegionPredicate {
+ lifetime,
+ span,
+ bounds,
+ in_where_clause: false,
+ }))
+ }
+ }
}
fn lower_where_predicate(&mut self, pred: &WherePredicate) -> hir::WherePredicate<'hir> {
ref bounded_ty,
ref bounds,
span,
- }) => self.with_in_scope_lifetime_defs(&bound_generic_params, |this| {
- hir::WherePredicate::BoundPredicate(hir::WhereBoundPredicate {
- bound_generic_params: this.lower_generic_params(
- bound_generic_params,
- ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
- ),
- bounded_ty: this.lower_ty(
- bounded_ty,
- ImplTraitContext::Disallowed(ImplTraitPosition::Type),
- ),
- bounds: this.arena.alloc_from_iter(bounds.iter().map(|bound| {
- this.lower_param_bound(
- bound,
- ImplTraitContext::Disallowed(ImplTraitPosition::Bound),
- )
- })),
- span: this.lower_span(span),
- })
+ }) => hir::WherePredicate::BoundPredicate(hir::WhereBoundPredicate {
+ bound_generic_params: self.lower_generic_params(bound_generic_params),
+ bounded_ty: self
+ .lower_ty(bounded_ty, ImplTraitContext::Disallowed(ImplTraitPosition::Type)),
+ bounds: self.arena.alloc_from_iter(bounds.iter().map(|bound| {
+ self.lower_param_bound(
+ bound,
+ ImplTraitContext::Disallowed(ImplTraitPosition::Bound),
+ )
+ })),
+ span: self.lower_span(span),
+ in_where_clause: true,
}),
WherePredicate::RegionPredicate(WhereRegionPredicate {
ref lifetime,
bounds,
ImplTraitContext::Disallowed(ImplTraitPosition::Bound),
),
+ in_where_clause: true,
}),
WherePredicate::EqPredicate(WhereEqPredicate { id, ref lhs_ty, ref rhs_ty, span }) => {
hir::WherePredicate::EqPredicate(hir::WhereEqPredicate {
/// Helper struct for delayed construction of Generics.
pub(super) struct GenericsCtor<'hir> {
pub(super) params: SmallVec<[hir::GenericParam<'hir>; 4]>,
- where_clause: hir::WhereClause<'hir>,
+ pub(super) predicates: SmallVec<[hir::WherePredicate<'hir>; 4]>,
+ has_where_clause: bool,
+ where_clause_span: Span,
span: Span,
}
impl<'hir> GenericsCtor<'hir> {
- pub(super) fn into_generics(self, arena: &'hir Arena<'hir>) -> hir::Generics<'hir> {
- hir::Generics {
+ pub(super) fn into_generics(self, arena: &'hir Arena<'hir>) -> &'hir hir::Generics<'hir> {
+ arena.alloc(hir::Generics {
params: arena.alloc_from_iter(self.params),
- where_clause: self.where_clause,
+ predicates: arena.alloc_from_iter(self.predicates),
+ has_where_clause: self.has_where_clause,
+ where_clause_span: self.where_clause_span,
span: self.span,
- }
+ })
}
}
#![feature(crate_visibility_modifier)]
#![feature(box_patterns)]
+#![feature(let_chains)]
#![feature(let_else)]
#![feature(never_type)]
#![recursion_limit = "256"]
#![allow(rustc::potential_query_instability)]
-use rustc_ast::token::{self, Token};
+use rustc_ast::token::{Delimiter, Token};
use rustc_ast::tokenstream::{CanSynthesizeMissingTokens, TokenStream, TokenTree};
use rustc_ast::visit;
use rustc_ast::{self as ast, *};
use rustc_hir::def::{DefKind, Namespace, PartialRes, PerNS, Res};
use rustc_hir::def_id::{DefId, DefPathHash, LocalDefId, CRATE_DEF_ID};
use rustc_hir::definitions::{DefKey, DefPathData, Definitions};
-use rustc_hir::intravisit;
use rustc_hir::{ConstArg, GenericArg, ItemLocalId, ParamName, TraitCandidate};
use rustc_index::vec::{Idx, IndexVec};
use rustc_query_system::ich::StableHashingContext;
use rustc_session::utils::{FlattenNonterminals, NtToTokenstream};
use rustc_session::Session;
use rustc_span::hygiene::{ExpnId, MacroKind};
-use rustc_span::source_map::{respan, DesugaringKind};
+use rustc_span::source_map::DesugaringKind;
use rustc_span::symbol::{kw, sym, Ident, Symbol};
use rustc_span::{Span, DUMMY_SP};
is_in_trait_impl: bool,
is_in_dyn_type: bool,
- /// What to do when we encounter an "anonymous lifetime
- /// reference". The term "anonymous" is meant to encompass both
- /// `'_` lifetimes as well as fully elided cases where nothing is
- /// written at all (e.g., `&T` or `std::cell::Ref<T>`).
- anonymous_lifetime_mode: AnonymousLifetimeMode,
-
- /// Used to create lifetime definitions for anonymous lifetimes.
- /// When an anonymous lifetime is encountered in a function or impl header and
- /// requires to create a fresh lifetime parameter, it is added
- /// to this list. The results of this list are then added to the list of
- /// lifetime definitions in the corresponding impl or function generics.
- lifetimes_to_define: Vec<(Span, NodeId)>,
-
- /// If anonymous lifetimes are being collected, this field holds the parent
- /// `LocalDefId` to create the fresh lifetime parameters' `LocalDefId`.
- is_collecting_anonymous_lifetimes: Option<LocalDefId>,
-
- /// Currently in-scope lifetimes defined in impl headers, fn headers, or HRTB.
- /// We always store a `normalize_to_macros_2_0()` version of the param-name in this
- /// vector.
- in_scope_lifetimes: Vec<ParamName>,
+ /// Used to handle lifetimes appearing in impl-traits.
+ captured_lifetimes: Option<LifetimeCaptureContext>,
current_hir_id_owner: LocalDefId,
item_local_id_counter: hir::ItemLocalId,
allow_into_future: Option<Lrc<[Symbol]>>,
}
+/// Resolution for a lifetime appearing in a type.
+#[derive(Copy, Clone, Debug)]
+pub enum LifetimeRes {
+ /// Successfully linked the lifetime to a generic parameter.
+ Param {
+ /// Id of the generic parameter that introduced it.
+ param: LocalDefId,
+ /// Id of the introducing place. That can be:
+ /// - an item's id, for the item's generic parameters;
+ /// - a TraitRef's ref_id, identifying the `for<...>` binder;
+ /// - a BareFn type's id;
+ /// - a Path's id when this path has parenthesized generic args.
+ ///
+ /// This information is used for impl-trait lifetime captures, to know when to or not to
+ /// capture any given lifetime.
+ binder: NodeId,
+ },
+ /// Created a generic parameter for an anonymous lifetime.
+ Fresh {
+ /// Id of the generic parameter that introduced it.
+ param: LocalDefId,
+ /// Id of the introducing place. See `Param`.
+ binder: NodeId,
+ },
+ /// This variant is used for anonymous lifetimes that we did not resolve during
+ /// late resolution. Shifting the work to the HIR lifetime resolver.
+ Anonymous {
+ /// Id of the introducing place. See `Param`.
+ binder: NodeId,
+ /// Whether this lifetime was spelled or elided.
+ elided: bool,
+ },
+ /// Explicit `'static` lifetime.
+ Static,
+ /// Resolution failure.
+ Error,
+ /// HACK: This is used to recover the NodeId of an elided lifetime.
+ ElidedAnchor { start: NodeId, end: NodeId },
+}
+
+/// When we lower a lifetime, it is inserted in `captures`, and the resolution is modified so
+/// to point to the lifetime parameter impl-trait will generate.
+/// When traversing `for<...>` binders, they are inserted in `binders_to_ignore` so we know *not*
+/// to rebind the introduced lifetimes.
+#[derive(Debug)]
+struct LifetimeCaptureContext {
+ /// parent def_id for new definitions
+ parent_def_id: LocalDefId,
+ /// Set of lifetimes to rebind.
+ captures: FxHashMap<
+ LocalDefId, // original parameter id
+ (
+ Span, // Span
+ NodeId, // synthetized parameter id
+ ParamName, // parameter name
+ LifetimeRes, // original resolution
+ ),
+ >,
+ /// Traversed binders. The ids in this set should *not* be rebound.
+ binders_to_ignore: FxHashSet<NodeId>,
+}
+
pub trait ResolverAstLowering {
fn def_key(&self, id: DefId) -> DefKey;
/// Obtains resolution for a label with the given `NodeId`.
fn get_label_res(&self, id: NodeId) -> Option<NodeId>;
+ /// Obtains resolution for a lifetime with the given `NodeId`.
+ fn get_lifetime_res(&self, id: NodeId) -> Option<LifetimeRes>;
+
+ /// Obtain the list of lifetimes parameters to add to an item.
+ fn take_extra_lifetime_params(&mut self, id: NodeId) -> Vec<(Ident, NodeId, LifetimeRes)>;
+
fn create_stable_hashing_context(&self) -> StableHashingContext<'_>;
fn definitions(&self) -> &Definitions;
/// equivalent to a fresh universal parameter like `fn foo<T: Debug>(x: T)`.
///
/// Newly generated parameters should be inserted into the given `Vec`.
- Universal(&'b mut Vec<hir::GenericParam<'a>>, LocalDefId),
+ Universal(&'b mut Vec<hir::GenericParam<'a>>, &'b mut Vec<hir::WherePredicate<'a>>, LocalDefId),
/// Treat `impl Trait` as shorthand for a new opaque type.
/// Example: `fn foo() -> impl Debug`, where `impl Debug` is conceptually
/// equivalent to a new opaque type like `type T = impl Debug; fn foo() -> T`.
///
ReturnPositionOpaqueTy {
- /// `DefId` for the parent function, used to look up necessary
- /// information later.
- fn_def_id: LocalDefId,
/// Origin: Either OpaqueTyOrigin::FnReturn or OpaqueTyOrigin::AsyncFn,
origin: hir::OpaqueTyOrigin,
},
/// Impl trait in type aliases.
- TypeAliasesOpaqueTy {
- /// Set of lifetimes that this opaque type can capture, if it uses
- /// them. This includes lifetimes bound since we entered this context.
- /// For example:
- ///
- /// ```
- /// type A<'b> = impl for<'a> Trait<'a, Out = impl Sized + 'a>;
- /// ```
- ///
- /// Here the inner opaque type captures `'a` because it uses it. It doesn't
- /// need to capture `'b` because it already inherits the lifetime
- /// parameter from `A`.
- // FIXME(impl_trait): but `required_region_bounds` will ICE later
- // anyway.
- capturable_lifetimes: &'b mut FxHashSet<hir::LifetimeName>,
- },
+ TypeAliasesOpaqueTy,
/// `impl Trait` is not accepted in this position.
Disallowed(ImplTraitPosition),
}
fn reborrow<'this>(&'this mut self) -> ImplTraitContext<'this, 'a> {
use self::ImplTraitContext::*;
match self {
- Universal(params, parent) => Universal(params, *parent),
- ReturnPositionOpaqueTy { fn_def_id, origin } => {
- ReturnPositionOpaqueTy { fn_def_id: *fn_def_id, origin: *origin }
- }
- TypeAliasesOpaqueTy { capturable_lifetimes } => {
- TypeAliasesOpaqueTy { capturable_lifetimes }
- }
+ Universal(params, bounds, parent) => Universal(params, bounds, *parent),
+ ReturnPositionOpaqueTy { origin } => ReturnPositionOpaqueTy { origin: *origin },
+ TypeAliasesOpaqueTy => TypeAliasesOpaqueTy,
Disallowed(pos) => Disallowed(*pos),
}
}
Err,
}
-/// What to do when we encounter an **anonymous** lifetime
-/// reference. Anonymous lifetime references come in two flavors. You
-/// have implicit, or fully elided, references to lifetimes, like the
-/// one in `&T` or `Ref<T>`, and you have `'_` lifetimes, like `&'_ T`
-/// or `Ref<'_, T>`. These often behave the same, but not always:
-///
-/// - certain usages of implicit references are deprecated, like
-/// `Ref<T>`, and we sometimes just give hard errors in those cases
-/// as well.
-/// - for object bounds there is a difference: `Box<dyn Foo>` is not
-/// the same as `Box<dyn Foo + '_>`.
-///
-/// We describe the effects of the various modes in terms of three cases:
-///
-/// - **Modern** -- includes all uses of `'_`, but also the lifetime arg
-/// of a `&` (e.g., the missing lifetime in something like `&T`)
-/// - **Dyn Bound** -- if you have something like `Box<dyn Foo>`,
-/// there is an elided lifetime bound (`Box<dyn Foo + 'X>`). These
-/// elided bounds follow special rules. Note that this only covers
-/// cases where *nothing* is written; the `'_` in `Box<dyn Foo +
-/// '_>` is a case of "modern" elision.
-/// - **Deprecated** -- this covers cases like `Ref<T>`, where the lifetime
-/// parameter to ref is completely elided. `Ref<'_, T>` would be the modern,
-/// non-deprecated equivalent.
-///
-/// Currently, the handling of lifetime elision is somewhat spread out
-/// between HIR lowering and -- as described below -- the
-/// `resolve_lifetime` module. Often we "fallthrough" to that code by generating
-/// an "elided" or "underscore" lifetime name. In the future, we probably want to move
-/// everything into HIR lowering.
-#[derive(Copy, Clone, Debug)]
-pub enum AnonymousLifetimeMode {
- /// For **Modern** cases, create a new anonymous region parameter
- /// and reference that.
- ///
- /// For **Dyn Bound** cases, pass responsibility to
- /// `resolve_lifetime` code.
- ///
- /// For **Deprecated** cases, report an error.
- CreateParameter,
-
- /// Give a hard error when either `&` or `'_` is written. Used to
- /// rule out things like `where T: Foo<'_>`. Does not imply an
- /// error on default object bounds (e.g., `Box<dyn Foo>`).
- ReportError,
-
- /// Pass responsibility to `resolve_lifetime` code for all cases.
- PassThrough,
-}
-
impl<'a, 'hir> LoweringContext<'a, 'hir> {
fn with_hir_id_owner(
&mut self,
)
}
- fn with_anonymous_lifetime_mode<R>(
- &mut self,
- anonymous_lifetime_mode: AnonymousLifetimeMode,
- op: impl FnOnce(&mut Self) -> R,
- ) -> R {
- debug!(
- "with_anonymous_lifetime_mode(anonymous_lifetime_mode={:?})",
- anonymous_lifetime_mode,
- );
- let old_anonymous_lifetime_mode = self.anonymous_lifetime_mode;
- self.anonymous_lifetime_mode = anonymous_lifetime_mode;
- let result = op(self);
- self.anonymous_lifetime_mode = old_anonymous_lifetime_mode;
- debug!(
- "with_anonymous_lifetime_mode: restoring anonymous_lifetime_mode={:?}",
- old_anonymous_lifetime_mode
- );
- result
- }
-
/// Intercept all spans entering HIR.
/// Mark a span as relative to the current owning item.
fn lower_span(&self, span: Span) -> Span {
Ident::new(ident.name, self.lower_span(ident.span))
}
- /// Creates a new `hir::GenericParam` for every new lifetime and
- /// type parameter encountered while evaluating `f`. Definitions
- /// are created with the parent provided. If no `parent_id` is
- /// provided, no definitions will be returned.
- ///
- /// Presuming that in-band lifetimes are enabled, then
- /// `self.anonymous_lifetime_mode` will be updated to match the
- /// parameter while `f` is running (and restored afterwards).
- fn collect_in_band_defs<T>(
- &mut self,
- parent_def_id: LocalDefId,
- f: impl FnOnce(&mut Self) -> T,
- ) -> (Vec<(Span, NodeId)>, T) {
- let was_collecting =
- std::mem::replace(&mut self.is_collecting_anonymous_lifetimes, Some(parent_def_id));
- let len = self.lifetimes_to_define.len();
-
- let res = f(self);
-
- let lifetimes_to_define = self.lifetimes_to_define.split_off(len);
- self.is_collecting_anonymous_lifetimes = was_collecting;
- (lifetimes_to_define, res)
- }
-
/// Converts a lifetime into a new generic parameter.
- fn fresh_lifetime_to_generic_param(
+ fn lifetime_res_to_generic_param(
&mut self,
- span: Span,
+ ident: Ident,
node_id: NodeId,
- ) -> hir::GenericParam<'hir> {
+ res: LifetimeRes,
+ ) -> Option<hir::GenericParam<'hir>> {
+ let (name, kind) = match res {
+ LifetimeRes::Param { .. } => {
+ (hir::ParamName::Plain(ident), hir::LifetimeParamKind::Explicit)
+ }
+ LifetimeRes::Fresh { param, .. } => {
+ (hir::ParamName::Fresh(param), hir::LifetimeParamKind::Elided)
+ }
+ LifetimeRes::Static | LifetimeRes::Error => return None,
+ res => panic!(
+ "Unexpected lifetime resolution {:?} for {:?} at {:?}",
+ res, ident, ident.span
+ ),
+ };
let hir_id = self.lower_node_id(node_id);
- let def_id = self.resolver.local_def_id(node_id);
- hir::GenericParam {
+ Some(hir::GenericParam {
hir_id,
- name: hir::ParamName::Fresh(def_id),
- bounds: &[],
- span: self.lower_span(span),
+ name,
+ span: self.lower_span(ident.span),
pure_wrt_drop: false,
- kind: hir::GenericParamKind::Lifetime { kind: hir::LifetimeParamKind::Elided },
- }
+ kind: hir::GenericParamKind::Lifetime { kind },
+ colon_span: None,
+ })
}
- /// When we have either an elided or `'_` lifetime in an impl
- /// header, we convert it to an in-band lifetime.
- fn collect_fresh_anonymous_lifetime(&mut self, span: Span) -> ParamName {
- let Some(parent_def_id) = self.is_collecting_anonymous_lifetimes else { panic!() };
-
- let node_id = self.resolver.next_node_id();
+ /// Creates a new `hir::GenericParam` for every new `Fresh` lifetime and
+ /// universal `impl Trait` type parameter encountered while evaluating `f`.
+ /// Definitions are created with the provided `parent_def_id`.
+ fn add_implicit_generics<T>(
+ &mut self,
+ generics: &Generics,
+ parent_node_id: NodeId,
+ f: impl FnOnce(
+ &mut Self,
+ &mut Vec<hir::GenericParam<'hir>>,
+ &mut Vec<hir::WherePredicate<'hir>>,
+ ) -> T,
+ ) -> (&'hir hir::Generics<'hir>, T) {
+ let mut impl_trait_defs = Vec::new();
+ let mut impl_trait_bounds = Vec::new();
+ let mut lowered_generics = self.lower_generics_mut(
+ generics,
+ ImplTraitContext::Universal(
+ &mut impl_trait_defs,
+ &mut impl_trait_bounds,
+ self.current_hir_id_owner,
+ ),
+ );
+ let res = f(self, &mut impl_trait_defs, &mut impl_trait_bounds);
- // Add a definition for the in-band lifetime def.
- let param_def_id = self.resolver.create_def(
- parent_def_id,
- node_id,
- DefPathData::LifetimeNs(kw::UnderscoreLifetime),
- ExpnId::root(),
- span.with_parent(None),
+ let extra_lifetimes = self.resolver.take_extra_lifetime_params(parent_node_id);
+ lowered_generics.params.extend(
+ extra_lifetimes
+ .into_iter()
+ .filter_map(|(ident, node_id, res)| {
+ self.lifetime_res_to_generic_param(ident, node_id, res)
+ })
+ .chain(impl_trait_defs),
);
+ lowered_generics.predicates.extend(impl_trait_bounds);
- let hir_name = ParamName::Fresh(param_def_id);
- self.lifetimes_to_define.push((span, node_id));
- hir_name
+ let lowered_generics = lowered_generics.into_generics(self.arena);
+ (lowered_generics, res)
}
- // Evaluates `f` with the lifetimes in `params` in-scope.
- // This is used to track which lifetimes have already been defined, and
- // which are new in-band lifetimes that need to have a definition created
- // for them.
- fn with_in_scope_lifetime_defs<T>(
+ /// Setup lifetime capture for and impl-trait.
+ /// The captures will be added to `captures`.
+ fn while_capturing_lifetimes<T>(
&mut self,
- params: &[GenericParam],
+ parent_def_id: LocalDefId,
+ captures: &mut FxHashMap<LocalDefId, (Span, NodeId, ParamName, LifetimeRes)>,
f: impl FnOnce(&mut Self) -> T,
) -> T {
- let old_len = self.in_scope_lifetimes.len();
- let lt_def_names = params.iter().filter_map(|param| match param.kind {
- GenericParamKind::Lifetime { .. } => {
- Some(ParamName::Plain(param.ident.normalize_to_macros_2_0()))
- }
- _ => None,
- });
- self.in_scope_lifetimes.extend(lt_def_names);
-
- let res = f(self);
+ let lifetime_stash = std::mem::replace(
+ &mut self.captured_lifetimes,
+ Some(LifetimeCaptureContext {
+ parent_def_id,
+ captures: std::mem::take(captures),
+ binders_to_ignore: Default::default(),
+ }),
+ );
- self.in_scope_lifetimes.truncate(old_len);
- res
- }
+ let ret = f(self);
- /// Appends in-band lifetime defs and argument-position `impl
- /// Trait` defs to the existing set of generics.
- ///
- /// Presuming that in-band lifetimes are enabled, then
- /// `self.anonymous_lifetime_mode` will be updated to match the
- /// parameter while `f` is running (and restored afterwards).
- fn add_in_band_defs<T>(
- &mut self,
- generics: &Generics,
- parent_def_id: LocalDefId,
- anonymous_lifetime_mode: AnonymousLifetimeMode,
- f: impl FnOnce(&mut Self, &mut Vec<hir::GenericParam<'hir>>) -> T,
- ) -> (hir::Generics<'hir>, T) {
- let (lifetimes_to_define, (mut lowered_generics, impl_trait_defs, res)) = self
- .collect_in_band_defs(parent_def_id, |this| {
- this.with_anonymous_lifetime_mode(anonymous_lifetime_mode, |this| {
- this.with_in_scope_lifetime_defs(&generics.params, |this| {
- let mut impl_trait_defs = Vec::new();
- // Note: it is necessary to lower generics *before* calling `f`.
- // When lowering `async fn`, there's a final step when lowering
- // the return type that assumes that all in-scope lifetimes have
- // already been added to either `in_scope_lifetimes` or
- // `lifetimes_to_define`. If we swapped the order of these two,
- // in-band-lifetimes introduced by generics or where-clauses
- // wouldn't have been added yet.
- let generics = this.lower_generics_mut(
- generics,
- ImplTraitContext::Universal(
- &mut impl_trait_defs,
- this.current_hir_id_owner,
- ),
- );
- let res = f(this, &mut impl_trait_defs);
- (generics, impl_trait_defs, res)
- })
- })
- });
+ let ctxt = std::mem::replace(&mut self.captured_lifetimes, lifetime_stash).unwrap();
+ *captures = ctxt.captures;
- lowered_generics.params.extend(
- lifetimes_to_define
- .into_iter()
- .map(|(span, node_id)| self.fresh_lifetime_to_generic_param(span, node_id))
- .chain(impl_trait_defs),
- );
+ ret
+ }
- let lowered_generics = lowered_generics.into_generics(self.arena);
- (lowered_generics, res)
+ /// Register a binder to be ignored for lifetime capture.
+ #[tracing::instrument(level = "debug", skip(self, f))]
+ #[inline]
+ fn with_lifetime_binder<T>(&mut self, binder: NodeId, f: impl FnOnce(&mut Self) -> T) -> T {
+ if let Some(ctxt) = &mut self.captured_lifetimes {
+ ctxt.binders_to_ignore.insert(binder);
+ }
+ let ret = f(self);
+ if let Some(ctxt) = &mut self.captured_lifetimes {
+ ctxt.binders_to_ignore.remove(&binder);
+ }
+ ret
}
fn with_dyn_type_scope<T>(&mut self, in_scope: bool, f: impl FnOnce(&mut Self) -> T) -> T {
match tokens.into_trees().next() {
Some(TokenTree::Token(token)) => token,
Some(TokenTree::Delimited(_, delim, tokens)) => {
- if delim != token::NoDelim {
+ if delim != Delimiter::Invisible {
sess.diagnostic().delay_span_bug(
span,
"unexpected delimiter in key-value attribute's value",
hir::TypeBindingKind::Equality { term }
}
AssocConstraintKind::Bound { ref bounds } => {
- let mut capturable_lifetimes;
let mut parent_def_id = self.current_hir_id_owner;
// Piggy-back on the `impl Trait` context to figure out the correct behavior.
let (desugar_to_impl_trait, itctx) = match itctx {
// so desugar to
//
// fn foo(x: dyn Iterator<Item = impl Debug>)
- ImplTraitContext::Universal(_, parent) if self.is_in_dyn_type => {
+ ImplTraitContext::Universal(_, _, parent) if self.is_in_dyn_type => {
parent_def_id = parent;
(true, itctx)
}
//
// FIXME: this is only needed until `impl Trait` is allowed in type aliases.
ImplTraitContext::Disallowed(_) if self.is_in_dyn_type => {
- capturable_lifetimes = FxHashSet::default();
- (
- true,
- ImplTraitContext::TypeAliasesOpaqueTy {
- capturable_lifetimes: &mut capturable_lifetimes,
- },
- )
+ (true, ImplTraitContext::TypeAliasesOpaqueTy)
}
// We are in the parameter position, but not within a dyn type:
TyKind::Slice(ref ty) => hir::TyKind::Slice(self.lower_ty(ty, itctx)),
TyKind::Ptr(ref mt) => hir::TyKind::Ptr(self.lower_mt(mt, itctx)),
TyKind::Rptr(ref region, ref mt) => {
- let span = self.sess.source_map().next_point(t.span.shrink_to_lo());
- let lifetime = match *region {
- Some(ref lt) => self.lower_lifetime(lt),
- None => self.elided_ref_lifetime(span),
- };
+ let region = region.unwrap_or_else(|| {
+ let Some(LifetimeRes::ElidedAnchor { start, end }) = self.resolver.get_lifetime_res(t.id) else {
+ panic!()
+ };
+ debug_assert_eq!(start.plus(1), end);
+ let span = self.sess.source_map().next_point(t.span.shrink_to_lo());
+ Lifetime {
+ ident: Ident::new(kw::UnderscoreLifetime, span),
+ id: start,
+ }
+ });
+ let lifetime = self.lower_lifetime(®ion);
hir::TyKind::Rptr(lifetime, self.lower_mt(mt, itctx))
}
- TyKind::BareFn(ref f) => self.with_in_scope_lifetime_defs(&f.generic_params, |this| {
- this.with_anonymous_lifetime_mode(AnonymousLifetimeMode::PassThrough, |this| {
- hir::TyKind::BareFn(this.arena.alloc(hir::BareFnTy {
- generic_params: this.lower_generic_params(
- &f.generic_params,
- ImplTraitContext::Disallowed(ImplTraitPosition::Generic),
- ),
- unsafety: this.lower_unsafety(f.unsafety),
- abi: this.lower_extern(f.ext),
- decl: this.lower_fn_decl(&f.decl, None, FnDeclKind::Pointer, None),
- param_names: this.lower_fn_params_to_names(&f.decl),
- }))
- })
+ TyKind::BareFn(ref f) => self.with_lifetime_binder(t.id, |this| {
+ hir::TyKind::BareFn(this.arena.alloc(hir::BareFnTy {
+ generic_params: this.lower_generic_params(&f.generic_params),
+ unsafety: this.lower_unsafety(f.unsafety),
+ abi: this.lower_extern(f.ext),
+ decl: this.lower_fn_decl(&f.decl, None, FnDeclKind::Pointer, None),
+ param_names: this.lower_fn_params_to_names(&f.decl),
+ }))
}),
TyKind::Never => hir::TyKind::Never,
TyKind::Tup(ref tys) => {
TyKind::ImplTrait(def_node_id, ref bounds) => {
let span = t.span;
match itctx {
- ImplTraitContext::ReturnPositionOpaqueTy { fn_def_id, origin } => self
- .lower_opaque_impl_trait(
- span,
- Some(fn_def_id),
- origin,
- def_node_id,
- None,
- |this| this.lower_param_bounds(bounds, itctx),
- ),
- ImplTraitContext::TypeAliasesOpaqueTy { ref capturable_lifetimes } => {
- // Reset capturable lifetimes, any nested impl trait
- // types will inherit lifetimes from this opaque type,
- // so don't need to capture them again.
- let nested_itctx = ImplTraitContext::TypeAliasesOpaqueTy {
- capturable_lifetimes: &mut FxHashSet::default(),
- };
+ ImplTraitContext::ReturnPositionOpaqueTy { origin } => self
+ .lower_opaque_impl_trait(span, origin, def_node_id, |this| {
+ this.lower_param_bounds(bounds, itctx)
+ }),
+ ImplTraitContext::TypeAliasesOpaqueTy => {
+ let nested_itctx = ImplTraitContext::TypeAliasesOpaqueTy;
self.lower_opaque_impl_trait(
span,
- None,
hir::OpaqueTyOrigin::TyAlias,
def_node_id,
- Some(capturable_lifetimes),
|this| this.lower_param_bounds(bounds, nested_itctx),
)
}
- ImplTraitContext::Universal(in_band_ty_params, parent_def_id) => {
+ ImplTraitContext::Universal(
+ in_band_ty_params,
+ in_band_ty_bounds,
+ parent_def_id,
+ ) => {
// Add a definition for the in-band `Param`.
let def_id = self.resolver.local_def_id(def_node_id);
let hir_bounds = self.lower_param_bounds(
bounds,
- ImplTraitContext::Universal(in_band_ty_params, parent_def_id),
+ ImplTraitContext::Universal(
+ in_band_ty_params,
+ in_band_ty_bounds,
+ parent_def_id,
+ ),
);
// Set the name to `impl Bound1 + Bound2`.
let ident = Ident::from_str_and_span(&pprust::ty_to_string(t), span);
hir_id: self.lower_node_id(def_node_id),
name: ParamName::Plain(self.lower_ident(ident)),
pure_wrt_drop: false,
- bounds: hir_bounds,
span: self.lower_span(span),
kind: hir::GenericParamKind::Type { default: None, synthetic: true },
+ colon_span: None,
});
+ if let Some(preds) = self.lower_generic_bound_predicate(
+ ident,
+ def_node_id,
+ &GenericParamKind::Type { default: None },
+ hir_bounds,
+ ) {
+ in_band_ty_bounds.push(preds)
+ }
hir::TyKind::Path(hir::QPath::Resolved(
None,
hir::Ty { kind, span: self.lower_span(t.span), hir_id: self.lower_node_id(t.id) }
}
+ #[tracing::instrument(level = "debug", skip(self, lower_bounds))]
fn lower_opaque_impl_trait(
&mut self,
span: Span,
- fn_def_id: Option<LocalDefId>,
origin: hir::OpaqueTyOrigin,
opaque_ty_node_id: NodeId,
- capturable_lifetimes: Option<&FxHashSet<hir::LifetimeName>>,
lower_bounds: impl FnOnce(&mut Self) -> hir::GenericBounds<'hir>,
) -> hir::TyKind<'hir> {
- debug!(
- "lower_opaque_impl_trait(fn_def_id={:?}, opaque_ty_node_id={:?}, span={:?})",
- fn_def_id, opaque_ty_node_id, span,
- );
-
// Make sure we know that some funky desugaring has been going on here.
// This is a first: there is code in other places like for loop
// desugaring that explicitly states that we don't want to track that.
let opaque_ty_def_id = self.resolver.local_def_id(opaque_ty_node_id);
- let mut collected_lifetimes = Vec::new();
+ let mut collected_lifetimes = FxHashMap::default();
self.with_hir_id_owner(opaque_ty_node_id, |lctx| {
- let hir_bounds = lower_bounds(lctx);
+ let hir_bounds = if origin == hir::OpaqueTyOrigin::TyAlias {
+ lower_bounds(lctx)
+ } else {
+ lctx.while_capturing_lifetimes(
+ opaque_ty_def_id,
+ &mut collected_lifetimes,
+ lower_bounds,
+ )
+ };
+ debug!(?collected_lifetimes);
- collected_lifetimes = lifetimes_from_impl_trait_bounds(
- opaque_ty_node_id,
- &hir_bounds,
- capturable_lifetimes,
- );
+ let lifetime_defs = lctx.arena.alloc_from_iter(collected_lifetimes.iter().map(
+ |(_, &(span, p_id, p_name, _))| {
+ let hir_id = lctx.lower_node_id(p_id);
+ debug_assert_ne!(lctx.resolver.opt_local_def_id(p_id), None);
- let lifetime_defs =
- lctx.arena.alloc_from_iter(collected_lifetimes.iter().map(|&(name, span)| {
- let def_node_id = lctx.resolver.next_node_id();
- lctx.resolver.create_def(
- opaque_ty_def_id,
- def_node_id,
- DefPathData::LifetimeNs(name.ident().name),
- ExpnId::root(),
- span.with_parent(None),
- );
- let hir_id = lctx.lower_node_id(def_node_id);
-
- let (name, kind) = match name {
- hir::LifetimeName::Underscore => (
- hir::ParamName::Plain(Ident::with_dummy_span(kw::UnderscoreLifetime)),
- hir::LifetimeParamKind::Elided,
- ),
- hir::LifetimeName::Param(param_name) => {
- (param_name, hir::LifetimeParamKind::Explicit)
- }
- _ => panic!("expected `LifetimeName::Param` or `ParamName::Plain`"),
+ let kind = if p_name.ident().name == kw::UnderscoreLifetime {
+ hir::LifetimeParamKind::Elided
+ } else {
+ hir::LifetimeParamKind::Explicit
};
hir::GenericParam {
hir_id,
- name,
+ name: p_name,
span,
pure_wrt_drop: false,
- bounds: &[],
kind: hir::GenericParamKind::Lifetime { kind },
+ colon_span: None,
}
- }));
+ },
+ ));
debug!("lower_opaque_impl_trait: lifetime_defs={:#?}", lifetime_defs);
let opaque_ty_item = hir::OpaqueTy {
- generics: hir::Generics {
+ generics: self.arena.alloc(hir::Generics {
params: lifetime_defs,
- where_clause: hir::WhereClause { predicates: &[], span: lctx.lower_span(span) },
+ predicates: &[],
+ has_where_clause: false,
+ where_clause_span: lctx.lower_span(span),
span: lctx.lower_span(span),
- },
+ }),
bounds: hir_bounds,
origin,
};
lctx.generate_opaque_type(opaque_ty_def_id, opaque_ty_item, span, opaque_ty_span)
});
- let lifetimes =
- self.arena.alloc_from_iter(collected_lifetimes.into_iter().map(|(name, span)| {
- hir::GenericArg::Lifetime(hir::Lifetime { hir_id: self.next_id(), span, name })
- }));
+ let lifetimes = self.arena.alloc_from_iter(collected_lifetimes.into_iter().map(
+ |(_, (span, _, p_name, res))| {
+ let id = self.resolver.next_node_id();
+ let ident = Ident::new(p_name.ident().name, span);
+ let l = self.new_named_lifetime_with_res(id, span, ident, res);
+ hir::GenericArg::Lifetime(l)
+ },
+ ));
debug!("lower_opaque_impl_trait: lifetimes={:#?}", lifetimes);
def_id: opaque_ty_id,
ident: Ident::empty(),
kind: opaque_ty_item_kind,
- vis: respan(self.lower_span(span.shrink_to_lo()), hir::VisibilityKind::Inherited),
+ vis_span: self.lower_span(span.shrink_to_lo()),
span: self.lower_span(opaque_ty_span),
};
hir::OwnerNode::Item(self.arena.alloc(opaque_ty_item))
fn lower_fn_decl(
&mut self,
decl: &FnDecl,
- mut in_band_ty_params: Option<(LocalDefId, &mut Vec<hir::GenericParam<'hir>>)>,
+ mut in_band_ty_params: Option<(
+ NodeId,
+ &mut Vec<hir::GenericParam<'hir>>,
+ &mut Vec<hir::WherePredicate<'hir>>,
+ )>,
kind: FnDeclKind,
make_ret_async: Option<NodeId>,
) -> &'hir hir::FnDecl<'hir> {
make_ret_async: {:?})",
decl, in_band_ty_params, kind, make_ret_async,
);
- let lt_mode = if make_ret_async.is_some() {
- // In `async fn`, argument-position elided lifetimes
- // must be transformed into fresh generic parameters so that
- // they can be applied to the opaque `impl Trait` return type.
- AnonymousLifetimeMode::CreateParameter
- } else {
- self.anonymous_lifetime_mode
- };
let c_variadic = decl.c_variadic();
- // Remember how many lifetimes were already around so that we can
- // only look at the lifetime parameters introduced by the arguments.
- let inputs = self.with_anonymous_lifetime_mode(lt_mode, |this| {
- // Skip the `...` (`CVarArgs`) trailing arguments from the AST,
- // as they are not explicit in HIR/Ty function signatures.
- // (instead, the `c_variadic` flag is set to `true`)
- let mut inputs = &decl.inputs[..];
- if c_variadic {
- inputs = &inputs[..inputs.len() - 1];
+ // Skip the `...` (`CVarArgs`) trailing arguments from the AST,
+ // as they are not explicit in HIR/Ty function signatures.
+ // (instead, the `c_variadic` flag is set to `true`)
+ let mut inputs = &decl.inputs[..];
+ if c_variadic {
+ inputs = &inputs[..inputs.len() - 1];
+ }
+ let inputs = self.arena.alloc_from_iter(inputs.iter().map(|param| {
+ if let Some((_, ibty, ibpb)) = &mut in_band_ty_params {
+ self.lower_ty_direct(
+ ¶m.ty,
+ ImplTraitContext::Universal(ibty, ibpb, self.current_hir_id_owner),
+ )
+ } else {
+ self.lower_ty_direct(
+ ¶m.ty,
+ ImplTraitContext::Disallowed(match kind {
+ FnDeclKind::Fn | FnDeclKind::Inherent => {
+ unreachable!("fn should allow in-band lifetimes")
+ }
+ FnDeclKind::ExternFn => ImplTraitPosition::ExternFnParam,
+ FnDeclKind::Closure => ImplTraitPosition::ClosureParam,
+ FnDeclKind::Pointer => ImplTraitPosition::PointerParam,
+ FnDeclKind::Trait => ImplTraitPosition::TraitParam,
+ FnDeclKind::Impl => ImplTraitPosition::ImplParam,
+ }),
+ )
}
- this.arena.alloc_from_iter(inputs.iter().map(|param| {
- if let Some((_, ibty)) = &mut in_band_ty_params {
- this.lower_ty_direct(
- ¶m.ty,
- ImplTraitContext::Universal(ibty, this.current_hir_id_owner),
- )
- } else {
- this.lower_ty_direct(
- ¶m.ty,
- ImplTraitContext::Disallowed(match kind {
- FnDeclKind::Fn | FnDeclKind::Inherent => {
- unreachable!("fn should allow in-band lifetimes")
- }
- FnDeclKind::ExternFn => ImplTraitPosition::ExternFnParam,
- FnDeclKind::Closure => ImplTraitPosition::ClosureParam,
- FnDeclKind::Pointer => ImplTraitPosition::PointerParam,
- FnDeclKind::Trait => ImplTraitPosition::TraitParam,
- FnDeclKind::Impl => ImplTraitPosition::ImplParam,
- }),
- )
- }
- }))
- });
+ }));
let output = if let Some(ret_id) = make_ret_async {
self.lower_async_fn_ret_ty(
match decl.output {
FnRetTy::Ty(ref ty) => {
let context = match in_band_ty_params {
- Some((def_id, _)) if kind.impl_trait_return_allowed() => {
+ Some((node_id, _, _)) if kind.impl_trait_return_allowed() => {
+ let fn_def_id = self.resolver.local_def_id(node_id);
ImplTraitContext::ReturnPositionOpaqueTy {
- fn_def_id: def_id,
- origin: hir::OpaqueTyOrigin::FnReturn(def_id),
+ origin: hir::OpaqueTyOrigin::FnReturn(fn_def_id),
}
}
_ => ImplTraitContext::Disallowed(match kind {
// `fn_def_id`: `DefId` of the parent function (used to create child impl trait definition)
// `opaque_ty_node_id`: `NodeId` of the opaque `impl Trait` type that should be created
// `elided_lt_replacement`: replacement for elided lifetimes in the return type
+ #[tracing::instrument(level = "debug", skip(self))]
fn lower_async_fn_ret_ty(
&mut self,
output: &FnRetTy,
- fn_def_id: LocalDefId,
+ fn_node_id: NodeId,
opaque_ty_node_id: NodeId,
) -> hir::FnRetTy<'hir> {
- debug!(
- "lower_async_fn_ret_ty(\
- output={:?}, \
- fn_def_id={:?}, \
- opaque_ty_node_id={:?})",
- output, fn_def_id, opaque_ty_node_id,
- );
-
let span = output.span();
let opaque_ty_span = self.mark_span_with_reason(DesugaringKind::Async, span, None);
let opaque_ty_def_id = self.resolver.local_def_id(opaque_ty_node_id);
+ let fn_def_id = self.resolver.local_def_id(fn_node_id);
// When we create the opaque type for this async fn, it is going to have
// to capture all the lifetimes involved in the signature (including in the
// should be figured out using the ordinary elision rules, and
// this desugaring achieves that.
- debug!("lower_async_fn_ret_ty: in_scope_lifetimes={:#?}", self.in_scope_lifetimes);
- debug!("lower_async_fn_ret_ty: lifetimes_to_define={:#?}", self.lifetimes_to_define);
-
// Calculate all the lifetimes that should be captured
// by the opaque type. This should include all in-scope
// lifetime parameters, including those defined in-band.
- //
- // `lifetime_params` is a vector of tuple (span, parameter name, lifetime name).
-
- // Input lifetime like `'a` or `'1`:
- let mut lifetime_params: Vec<_> = self
- .in_scope_lifetimes
- .iter()
- .cloned()
- .map(|name| (name.ident().span, hir::LifetimeName::Param(name)))
- .chain(self.lifetimes_to_define.iter().map(|&(span, node_id)| {
- let def_id = self.resolver.local_def_id(node_id);
- let name = hir::ParamName::Fresh(def_id);
- (span, hir::LifetimeName::Param(name))
- }))
- .collect();
+
+ let mut captures = FxHashMap::default();
+
+ let extra_lifetime_params = self.resolver.take_extra_lifetime_params(opaque_ty_node_id);
+ debug!(?extra_lifetime_params);
+ for (ident, outer_node_id, outer_res) in extra_lifetime_params {
+ let Ident { name, span } = ident;
+ let outer_def_id = self.resolver.local_def_id(outer_node_id);
+ let inner_node_id = self.resolver.next_node_id();
+
+ // Add a definition for the in scope lifetime def.
+ self.resolver.create_def(
+ opaque_ty_def_id,
+ inner_node_id,
+ DefPathData::LifetimeNs(name),
+ ExpnId::root(),
+ span.with_parent(None),
+ );
+
+ let (p_name, inner_res) = match outer_res {
+ // Input lifetime like `'a`:
+ LifetimeRes::Param { param, .. } => {
+ (hir::ParamName::Plain(ident), LifetimeRes::Param { param, binder: fn_node_id })
+ }
+ // Input lifetime like `'1`:
+ LifetimeRes::Fresh { param, .. } => (
+ hir::ParamName::Fresh(outer_def_id),
+ LifetimeRes::Fresh { param, binder: fn_node_id },
+ ),
+ LifetimeRes::Static | LifetimeRes::Error => continue,
+ res => {
+ panic!("Unexpected lifetime resolution {:?} for {:?} at {:?}", res, ident, span)
+ }
+ };
+
+ captures.insert(outer_def_id, (span, inner_node_id, p_name, inner_res));
+ }
+
+ debug!(?captures);
self.with_hir_id_owner(opaque_ty_node_id, |this| {
- let mut generic_params: Vec<_> = lifetime_params
- .iter()
- .map(|&(span, name)| {
- // We can only get lifetime names from the outside.
- let hir::LifetimeName::Param(hir_name) = name else { panic!() };
-
- let node_id = this.resolver.next_node_id();
-
- // Add a definition for the in-band lifetime def.
- let def_id = this.resolver.create_def(
- opaque_ty_def_id,
- node_id,
- DefPathData::LifetimeNs(hir_name.ident().name),
- ExpnId::root(),
- span.with_parent(None),
- );
+ let future_bound =
+ this.while_capturing_lifetimes(opaque_ty_def_id, &mut captures, |this| {
+ // We have to be careful to get elision right here. The
+ // idea is that we create a lifetime parameter for each
+ // lifetime in the return type. So, given a return type
+ // like `async fn foo(..) -> &[&u32]`, we lower to `impl
+ // Future<Output = &'1 [ &'2 u32 ]>`.
+ //
+ // Then, we will create `fn foo(..) -> Foo<'_, '_>`, and
+ // hence the elision takes place at the fn site.
+ this.lower_async_fn_output_type_to_future_bound(output, fn_def_id, span)
+ });
+ debug!("lower_async_fn_ret_ty: future_bound={:#?}", future_bound);
+ debug!("lower_async_fn_ret_ty: captures={:#?}", captures);
- let (kind, name) = match hir_name {
- ParamName::Plain(ident) => {
- (hir::LifetimeParamKind::Explicit, hir::ParamName::Plain(ident))
- }
- ParamName::Fresh(_) => {
- (hir::LifetimeParamKind::Elided, hir::ParamName::Fresh(def_id))
- }
- ParamName::Error => (hir::LifetimeParamKind::Error, hir::ParamName::Error),
+ let generic_params =
+ this.arena.alloc_from_iter(captures.iter().map(|(_, &(span, p_id, p_name, _))| {
+ let hir_id = this.lower_node_id(p_id);
+ debug_assert_ne!(this.resolver.opt_local_def_id(p_id), None);
+
+ let kind = if p_name.ident().name == kw::UnderscoreLifetime {
+ hir::LifetimeParamKind::Elided
+ } else {
+ hir::LifetimeParamKind::Explicit
};
hir::GenericParam {
- hir_id: this.lower_node_id(node_id),
- name,
- bounds: &[],
- span: this.lower_span(span),
+ hir_id,
+ name: p_name,
+ span,
pure_wrt_drop: false,
kind: hir::GenericParamKind::Lifetime { kind },
+ colon_span: None,
}
- })
- .collect();
-
- // We have to be careful to get elision right here. The
- // idea is that we create a lifetime parameter for each
- // lifetime in the return type. So, given a return type
- // like `async fn foo(..) -> &[&u32]`, we lower to `impl
- // Future<Output = &'1 [ &'2 u32 ]>`.
- //
- // Then, we will create `fn foo(..) -> Foo<'_, '_>`, and
- // hence the elision takes place at the fn site.
- let (lifetimes_to_define, future_bound) =
- this.with_anonymous_lifetime_mode(AnonymousLifetimeMode::CreateParameter, |this| {
- this.collect_in_band_defs(opaque_ty_def_id, |this| {
- this.lower_async_fn_output_type_to_future_bound(output, fn_def_id, span)
- })
- });
- debug!("lower_async_fn_ret_ty: future_bound={:#?}", future_bound);
- debug!("lower_async_fn_ret_ty: lifetimes_to_define={:#?}", lifetimes_to_define);
-
- // Output lifetime like `'_`:
- for (span, node_id) in lifetimes_to_define {
- let param = this.fresh_lifetime_to_generic_param(span, node_id);
- lifetime_params.push((span, hir::LifetimeName::Implicit));
- generic_params.push(param);
- }
- let generic_params = this.arena.alloc_from_iter(generic_params);
- debug!("lower_async_fn_ret_ty: lifetime_params={:#?}", lifetime_params);
+ }));
debug!("lower_async_fn_ret_ty: generic_params={:#?}", generic_params);
let opaque_ty_item = hir::OpaqueTy {
- generics: hir::Generics {
+ generics: this.arena.alloc(hir::Generics {
params: generic_params,
- where_clause: hir::WhereClause { predicates: &[], span: this.lower_span(span) },
+ predicates: &[],
+ has_where_clause: false,
+ where_clause_span: this.lower_span(span),
span: this.lower_span(span),
- },
+ }),
bounds: arena_vec![this; future_bound],
origin: hir::OpaqueTyOrigin::AsyncFn(fn_def_id),
};
this.generate_opaque_type(opaque_ty_def_id, opaque_ty_item, span, opaque_ty_span)
});
- // As documented above on the variable
- // `input_lifetimes_count`, we need to create the lifetime
+ // As documented above, we need to create the lifetime
// arguments to our opaque type. Continuing with our example,
// we're creating the type arguments for the return type:
//
// For the "output" lifetime parameters, we just want to
// generate `'_`.
let generic_args =
- self.arena.alloc_from_iter(lifetime_params.into_iter().map(|(span, name)| {
- GenericArg::Lifetime(hir::Lifetime {
- hir_id: self.next_id(),
- span: self.lower_span(span),
- name,
- })
+ self.arena.alloc_from_iter(captures.into_iter().map(|(_, (span, _, p_name, res))| {
+ let id = self.resolver.next_node_id();
+ let ident = Ident::new(p_name.ident().name, span);
+ let l = self.new_named_lifetime_with_res(id, span, ident, res);
+ hir::GenericArg::Lifetime(l)
}));
// Create the `Foo<...>` reference itself. Note that the `type
// `impl Future` opaque type that `async fn` implicitly
// generates.
let context = ImplTraitContext::ReturnPositionOpaqueTy {
- fn_def_id,
origin: hir::OpaqueTyOrigin::FnReturn(fn_def_id),
};
self.lower_ty(ty, context)
fn lower_lifetime(&mut self, l: &Lifetime) -> hir::Lifetime {
let span = self.lower_span(l.ident.span);
- match l.ident {
- ident if ident.name == kw::StaticLifetime => {
- self.new_named_lifetime(l.id, span, hir::LifetimeName::Static)
- }
- ident if ident.name == kw::UnderscoreLifetime => match self.anonymous_lifetime_mode {
- AnonymousLifetimeMode::CreateParameter => {
- let fresh_name = self.collect_fresh_anonymous_lifetime(span);
- self.new_named_lifetime(l.id, span, hir::LifetimeName::Param(fresh_name))
- }
-
- AnonymousLifetimeMode::PassThrough => {
- self.new_named_lifetime(l.id, span, hir::LifetimeName::Underscore)
- }
-
- AnonymousLifetimeMode::ReportError => self.new_error_lifetime(Some(l.id), span),
- },
- ident => {
- let param_name = ParamName::Plain(self.lower_ident(ident));
- self.new_named_lifetime(l.id, span, hir::LifetimeName::Param(param_name))
- }
- }
+ let ident = self.lower_ident(l.ident);
+ let res = self
+ .resolver
+ .get_lifetime_res(l.id)
+ .unwrap_or_else(|| panic!("Missing resolution for lifetime {:?} at {:?}", l, span));
+ self.new_named_lifetime_with_res(l.id, span, ident, res)
}
- fn new_named_lifetime(
+ #[tracing::instrument(level = "debug", skip(self))]
+ fn new_named_lifetime_with_res(
&mut self,
id: NodeId,
span: Span,
- name: hir::LifetimeName,
+ ident: Ident,
+ res: LifetimeRes,
) -> hir::Lifetime {
+ debug!(?self.captured_lifetimes);
+ let name = match res {
+ LifetimeRes::Param { param, binder } => {
+ debug_assert_ne!(ident.name, kw::UnderscoreLifetime);
+ let p_name = ParamName::Plain(ident);
+ if let Some(LifetimeCaptureContext { parent_def_id, captures, binders_to_ignore }) =
+ &mut self.captured_lifetimes
+ && !binders_to_ignore.contains(&binder)
+ {
+ match captures.entry(param) {
+ Entry::Occupied(_) => {}
+ Entry::Vacant(v) => {
+ let p_id = self.resolver.next_node_id();
+ self.resolver.create_def(
+ *parent_def_id,
+ p_id,
+ DefPathData::LifetimeNs(p_name.ident().name),
+ ExpnId::root(),
+ span.with_parent(None),
+ );
+
+ v.insert((span, p_id, p_name, res));
+ }
+ }
+ }
+ hir::LifetimeName::Param(p_name)
+ }
+ LifetimeRes::Fresh { mut param, binder } => {
+ debug_assert_eq!(ident.name, kw::UnderscoreLifetime);
+ if let Some(LifetimeCaptureContext { parent_def_id, captures, binders_to_ignore }) =
+ &mut self.captured_lifetimes
+ && !binders_to_ignore.contains(&binder)
+ {
+ match captures.entry(param) {
+ Entry::Occupied(o) => param = self.resolver.local_def_id(o.get().1),
+ Entry::Vacant(v) => {
+ let p_id = self.resolver.next_node_id();
+ let p_def_id = self.resolver.create_def(
+ *parent_def_id,
+ p_id,
+ DefPathData::LifetimeNs(kw::UnderscoreLifetime),
+ ExpnId::root(),
+ span.with_parent(None),
+ );
+
+ let p_name = ParamName::Fresh(param);
+ v.insert((span, p_id, p_name, res));
+ param = p_def_id;
+ }
+ }
+ }
+ let p_name = ParamName::Fresh(param);
+ hir::LifetimeName::Param(p_name)
+ }
+ LifetimeRes::Anonymous { binder, elided } => {
+ let l_name = if elided {
+ hir::LifetimeName::Implicit
+ } else {
+ hir::LifetimeName::Underscore
+ };
+ if let Some(LifetimeCaptureContext { parent_def_id, captures, binders_to_ignore }) =
+ &mut self.captured_lifetimes
+ && !binders_to_ignore.contains(&binder)
+ {
+ let p_id = self.resolver.next_node_id();
+ let p_def_id = self.resolver.create_def(
+ *parent_def_id,
+ p_id,
+ DefPathData::LifetimeNs(kw::UnderscoreLifetime),
+ ExpnId::root(),
+ span.with_parent(None),
+ );
+ let p_name = ParamName::Fresh(p_def_id);
+ captures.insert(p_def_id, (span, p_id, p_name, res));
+ hir::LifetimeName::Param(p_name)
+ } else {
+ l_name
+ }
+ }
+ LifetimeRes::Static => hir::LifetimeName::Static,
+ LifetimeRes::Error => hir::LifetimeName::Error,
+ res => panic!("Unexpected lifetime resolution {:?} for {:?} at {:?}", res, ident, span),
+ };
+ debug!(?self.captured_lifetimes);
hir::Lifetime { hir_id: self.lower_node_id(id), span: self.lower_span(span), name }
}
fn lower_generic_params_mut<'s>(
&'s mut self,
params: &'s [GenericParam],
- mut itctx: ImplTraitContext<'s, 'hir>,
) -> impl Iterator<Item = hir::GenericParam<'hir>> + Captures<'a> + Captures<'s> {
- params.iter().map(move |param| self.lower_generic_param(param, itctx.reborrow()))
+ params.iter().map(move |param| self.lower_generic_param(param))
}
- fn lower_generic_params(
- &mut self,
- params: &[GenericParam],
- itctx: ImplTraitContext<'_, 'hir>,
- ) -> &'hir [hir::GenericParam<'hir>] {
- self.arena.alloc_from_iter(self.lower_generic_params_mut(params, itctx))
+ fn lower_generic_params(&mut self, params: &[GenericParam]) -> &'hir [hir::GenericParam<'hir>] {
+ self.arena.alloc_from_iter(self.lower_generic_params_mut(params))
}
- fn lower_generic_param(
- &mut self,
- param: &GenericParam,
- mut itctx: ImplTraitContext<'_, 'hir>,
- ) -> hir::GenericParam<'hir> {
- let bounds: Vec<_> = self
- .with_anonymous_lifetime_mode(AnonymousLifetimeMode::ReportError, |this| {
- this.lower_param_bounds_mut(¶m.bounds, itctx.reborrow()).collect()
- });
-
+ fn lower_generic_param(&mut self, param: &GenericParam) -> hir::GenericParam<'hir> {
let (name, kind) = match param.kind {
GenericParamKind::Lifetime => {
- let was_collecting_in_band = self.is_collecting_anonymous_lifetimes;
- self.is_collecting_anonymous_lifetimes = None;
-
- let lt = self
- .with_anonymous_lifetime_mode(AnonymousLifetimeMode::ReportError, |this| {
- this.lower_lifetime(&Lifetime { id: param.id, ident: param.ident })
- });
- let param_name = match lt.name {
- hir::LifetimeName::Param(param_name) => param_name,
- hir::LifetimeName::Implicit | hir::LifetimeName::Underscore => {
- hir::ParamName::Plain(lt.name.ident())
- }
- hir::LifetimeName::ImplicitObjectLifetimeDefault => {
- self.sess.diagnostic().span_bug(
- param.ident.span,
- "object-lifetime-default should not occur here",
- );
- }
- hir::LifetimeName::Static | hir::LifetimeName::Error => ParamName::Error,
+ let param_name = if param.ident.name == kw::StaticLifetime
+ || param.ident.name == kw::UnderscoreLifetime
+ {
+ ParamName::Error
+ } else {
+ let ident = self.lower_ident(param.ident);
+ ParamName::Plain(ident)
};
-
let kind =
hir::GenericParamKind::Lifetime { kind: hir::LifetimeParamKind::Explicit };
- self.is_collecting_anonymous_lifetimes = was_collecting_in_band;
-
(param_name, kind)
}
GenericParamKind::Type { ref default, .. } => {
(hir::ParamName::Plain(self.lower_ident(param.ident)), kind)
}
GenericParamKind::Const { ref ty, kw_span: _, ref default } => {
- let ty =
- self.with_anonymous_lifetime_mode(AnonymousLifetimeMode::ReportError, |this| {
- this.lower_ty(&ty, ImplTraitContext::Disallowed(ImplTraitPosition::Type))
- });
+ let ty = self.lower_ty(&ty, ImplTraitContext::Disallowed(ImplTraitPosition::Type));
let default = default.as_ref().map(|def| self.lower_anon_const(def));
(
hir::ParamName::Plain(self.lower_ident(param.ident)),
name,
span: self.lower_span(param.span()),
pure_wrt_drop: self.sess.contains_name(¶m.attrs, sym::may_dangle),
- bounds: self.arena.alloc_from_iter(bounds),
kind,
+ colon_span: param.colon_span.map(|s| self.lower_span(s)),
}
}
hir::TraitRef { path, hir_ref_id: self.lower_node_id(p.ref_id) }
}
+ #[tracing::instrument(level = "debug", skip(self))]
fn lower_poly_trait_ref(
&mut self,
p: &PolyTraitRef,
mut itctx: ImplTraitContext<'_, 'hir>,
) -> hir::PolyTraitRef<'hir> {
- let bound_generic_params =
- self.lower_generic_params(&p.bound_generic_params, itctx.reborrow());
-
- let trait_ref = self.with_in_scope_lifetime_defs(&p.bound_generic_params, |this| {
- // Any impl Trait types defined within this scope can capture
- // lifetimes bound on this predicate.
- let lt_def_names = p.bound_generic_params.iter().filter_map(|param| match param.kind {
- GenericParamKind::Lifetime { .. } => Some(hir::LifetimeName::Param(
- ParamName::Plain(param.ident.normalize_to_macros_2_0()),
- )),
- _ => None,
- });
- if let ImplTraitContext::TypeAliasesOpaqueTy { ref mut capturable_lifetimes } = itctx {
- capturable_lifetimes.extend(lt_def_names.clone());
- }
-
- let res = this.lower_trait_ref(&p.trait_ref, itctx.reborrow());
+ let bound_generic_params = self.lower_generic_params(&p.bound_generic_params);
- if let ImplTraitContext::TypeAliasesOpaqueTy { ref mut capturable_lifetimes } = itctx {
- for param in lt_def_names {
- capturable_lifetimes.remove(¶m);
- }
- }
- res
+ let trait_ref = self.with_lifetime_binder(p.trait_ref.ref_id, |this| {
+ this.lower_trait_ref(&p.trait_ref, itctx.reborrow())
});
hir::PolyTraitRef { bound_generic_params, trait_ref, span: self.lower_span(p.span) }
hir::Ty { hir_id, kind, span: self.lower_span(span) }
}
- /// Invoked to create the lifetime argument for a type `&T`
- /// with no explicit lifetime.
- fn elided_ref_lifetime(&mut self, span: Span) -> hir::Lifetime {
- match self.anonymous_lifetime_mode {
- // Intercept when we are in an impl header or async fn and introduce an in-band
- // lifetime.
- // Hence `impl Foo for &u32` becomes `impl<'f> Foo for &'f u32` for some fresh
- // `'f`.
- AnonymousLifetimeMode::CreateParameter => {
- let fresh_name = self.collect_fresh_anonymous_lifetime(span);
- hir::Lifetime {
- hir_id: self.next_id(),
- span: self.lower_span(span),
- name: hir::LifetimeName::Param(fresh_name),
- }
- }
-
- AnonymousLifetimeMode::ReportError => self.new_error_lifetime(None, span),
-
- AnonymousLifetimeMode::PassThrough => self.new_implicit_lifetime(span),
- }
- }
-
- /// Report an error on illegal use of `'_` or a `&T` with no explicit lifetime;
- /// return an "error lifetime".
- fn new_error_lifetime(&mut self, id: Option<NodeId>, span: Span) -> hir::Lifetime {
- let id = id.unwrap_or_else(|| self.resolver.next_node_id());
- self.new_named_lifetime(id, span, hir::LifetimeName::Error)
- }
-
- /// Invoked to create the lifetime argument(s) for a path like
- /// `std::cell::Ref<T>`; note that implicit lifetimes in these
- /// sorts of cases are deprecated. This may therefore report a warning or an
- /// error, depending on the mode.
- fn elided_path_lifetimes<'s>(
- &'s mut self,
- span: Span,
- count: usize,
- ) -> impl Iterator<Item = hir::Lifetime> + Captures<'a> + Captures<'s> + Captures<'hir> {
- (0..count).map(move |_| self.elided_path_lifetime(span))
- }
-
- fn elided_path_lifetime(&mut self, span: Span) -> hir::Lifetime {
- match self.anonymous_lifetime_mode {
- AnonymousLifetimeMode::CreateParameter => {
- // We should have emitted E0726 when processing this path above
- self.sess
- .delay_span_bug(span, "expected 'implicit elided lifetime not allowed' error");
- let id = self.resolver.next_node_id();
- self.new_named_lifetime(id, span, hir::LifetimeName::Error)
- }
- // `PassThrough` is the normal case.
- // `new_error_lifetime`, which would usually be used in the case of `ReportError`,
- // is unsuitable here, as these can occur from missing lifetime parameters in a
- // `PathSegment`, for which there is no associated `'_` or `&T` with no explicit
- // lifetime. Instead, we simply create an implicit lifetime, which will be checked
- // later, at which point a suitable error will be emitted.
- AnonymousLifetimeMode::PassThrough | AnonymousLifetimeMode::ReportError => {
- self.new_implicit_lifetime(span)
- }
- }
- }
-
/// Invoked to create the lifetime argument(s) for an elided trait object
/// bound, like the bound in `Box<dyn Debug>`. This method is not invoked
/// when the bound is written, even if it is written with `'_` like in
/// `Box<dyn Debug + '_>`. In those cases, `lower_lifetime` is invoked.
fn elided_dyn_bound(&mut self, span: Span) -> hir::Lifetime {
- match self.anonymous_lifetime_mode {
- // NB. We intentionally ignore the create-parameter mode here.
- // and instead "pass through" to resolve-lifetimes, which will apply
- // the object-lifetime-defaulting rules. Elided object lifetime defaults
- // do not act like other elided lifetimes. In other words, given this:
- //
- // impl Foo for Box<dyn Debug>
- //
- // we do not introduce a fresh `'_` to serve as the bound, but instead
- // ultimately translate to the equivalent of:
- //
- // impl Foo for Box<dyn Debug + 'static>
- //
- // `resolve_lifetime` has the code to make that happen.
- AnonymousLifetimeMode::CreateParameter => {}
-
- AnonymousLifetimeMode::ReportError => {
- // ReportError applies to explicit use of `'_`.
- }
-
- // This is the normal case.
- AnonymousLifetimeMode::PassThrough => {}
- }
-
let r = hir::Lifetime {
hir_id: self.next_id(),
span: self.lower_span(span),
debug!("elided_dyn_bound: r={:?}", r);
r
}
-
- fn new_implicit_lifetime(&mut self, span: Span) -> hir::Lifetime {
- hir::Lifetime {
- hir_id: self.next_id(),
- span: self.lower_span(span),
- name: hir::LifetimeName::Implicit,
- }
- }
}
/// Helper struct for delayed construction of GenericArgs.
this.arena.alloc(ga)
}
}
-
-#[tracing::instrument(level = "debug")]
-fn lifetimes_from_impl_trait_bounds(
- opaque_ty_id: NodeId,
- bounds: hir::GenericBounds<'_>,
- lifetimes_to_include: Option<&FxHashSet<hir::LifetimeName>>,
-) -> Vec<(hir::LifetimeName, Span)> {
- // This visitor walks over `impl Trait` bounds and creates defs for all lifetimes that
- // appear in the bounds, excluding lifetimes that are created within the bounds.
- // E.g., `'a`, `'b`, but not `'c` in `impl for<'c> SomeTrait<'a, 'b, 'c>`.
- struct ImplTraitLifetimeCollector<'r> {
- collect_elided_lifetimes: bool,
- currently_bound_lifetimes: Vec<hir::LifetimeName>,
- already_defined_lifetimes: FxHashSet<hir::LifetimeName>,
- lifetimes: Vec<(hir::LifetimeName, Span)>,
- lifetimes_to_include: Option<&'r FxHashSet<hir::LifetimeName>>,
- }
-
- impl<'r, 'v> intravisit::Visitor<'v> for ImplTraitLifetimeCollector<'r> {
- fn visit_generic_args(&mut self, span: Span, parameters: &'v hir::GenericArgs<'v>) {
- // Don't collect elided lifetimes used inside of `Fn()` syntax.
- if parameters.parenthesized {
- let old_collect_elided_lifetimes = self.collect_elided_lifetimes;
- self.collect_elided_lifetimes = false;
- intravisit::walk_generic_args(self, span, parameters);
- self.collect_elided_lifetimes = old_collect_elided_lifetimes;
- } else {
- intravisit::walk_generic_args(self, span, parameters);
- }
- }
-
- fn visit_ty(&mut self, t: &'v hir::Ty<'v>) {
- // Don't collect elided lifetimes used inside of `fn()` syntax.
- if let hir::TyKind::BareFn(_) = t.kind {
- let old_collect_elided_lifetimes = self.collect_elided_lifetimes;
- self.collect_elided_lifetimes = false;
-
- // Record the "stack height" of `for<'a>` lifetime bindings
- // to be able to later fully undo their introduction.
- let old_len = self.currently_bound_lifetimes.len();
- intravisit::walk_ty(self, t);
- self.currently_bound_lifetimes.truncate(old_len);
-
- self.collect_elided_lifetimes = old_collect_elided_lifetimes;
- } else {
- intravisit::walk_ty(self, t)
- }
- }
-
- fn visit_poly_trait_ref(
- &mut self,
- trait_ref: &'v hir::PolyTraitRef<'v>,
- modifier: hir::TraitBoundModifier,
- ) {
- // Record the "stack height" of `for<'a>` lifetime bindings
- // to be able to later fully undo their introduction.
- let old_len = self.currently_bound_lifetimes.len();
- intravisit::walk_poly_trait_ref(self, trait_ref, modifier);
- self.currently_bound_lifetimes.truncate(old_len);
- }
-
- fn visit_generic_param(&mut self, param: &'v hir::GenericParam<'v>) {
- // Record the introduction of 'a in `for<'a> ...`.
- if let hir::GenericParamKind::Lifetime { .. } = param.kind {
- // Introduce lifetimes one at a time so that we can handle
- // cases like `fn foo<'d>() -> impl for<'a, 'b: 'a, 'c: 'b + 'd>`.
- let lt_name = hir::LifetimeName::Param(param.name);
- self.currently_bound_lifetimes.push(lt_name);
- }
-
- intravisit::walk_generic_param(self, param);
- }
-
- fn visit_lifetime(&mut self, lifetime: &'v hir::Lifetime) {
- let name = match lifetime.name {
- hir::LifetimeName::Implicit | hir::LifetimeName::Underscore => {
- if self.collect_elided_lifetimes {
- // Use `'_` for both implicit and underscore lifetimes in
- // `type Foo<'_> = impl SomeTrait<'_>;`.
- hir::LifetimeName::Underscore
- } else {
- return;
- }
- }
- hir::LifetimeName::Param(_) => lifetime.name,
-
- // Refers to some other lifetime that is "in
- // scope" within the type.
- hir::LifetimeName::ImplicitObjectLifetimeDefault => return,
-
- hir::LifetimeName::Error | hir::LifetimeName::Static => return,
- };
-
- if !self.currently_bound_lifetimes.contains(&name)
- && !self.already_defined_lifetimes.contains(&name)
- && self.lifetimes_to_include.map_or(true, |lifetimes| lifetimes.contains(&name))
- {
- self.already_defined_lifetimes.insert(name);
-
- self.lifetimes.push((name, lifetime.span));
- }
- }
- }
-
- let mut lifetime_collector = ImplTraitLifetimeCollector {
- collect_elided_lifetimes: true,
- currently_bound_lifetimes: Vec::new(),
- already_defined_lifetimes: FxHashSet::default(),
- lifetimes: Vec::new(),
- lifetimes_to_include,
- };
-
- for bound in bounds {
- intravisit::walk_param_bound(&mut lifetime_collector, &bound);
- }
-
- lifetime_collector.lifetimes
-}
use crate::ImplTraitPosition;
-use super::{AnonymousLifetimeMode, ImplTraitContext, LoweringContext, ParamMode};
-use super::{GenericArgsCtor, ParenthesizedGenericArgs};
+use super::{GenericArgsCtor, LifetimeRes, ParenthesizedGenericArgs};
+use super::{ImplTraitContext, LoweringContext, ParamMode};
use rustc_ast::{self as ast, *};
use rustc_errors::{struct_span_err, Applicability};
use rustc_hir as hir;
use rustc_hir::def::{DefKind, PartialRes, Res};
-use rustc_hir::def_id::DefId;
use rustc_hir::GenericArg;
-use rustc_span::symbol::Ident;
+use rustc_span::symbol::{kw, Ident};
use rustc_span::{BytePos, Span, DUMMY_SP};
use smallvec::smallvec;
_ => param_mode,
};
- // Figure out if this is a type/trait segment,
- // which may need lifetime elision performed.
- let parent_def_id = |this: &mut Self, def_id: DefId| DefId {
- krate: def_id.krate,
- index: this.resolver.def_key(def_id).parent.expect("missing parent"),
- };
- let type_def_id = match partial_res.base_res() {
- Res::Def(DefKind::AssocTy, def_id) if i + 2 == proj_start => {
- Some(parent_def_id(self, def_id))
- }
- Res::Def(DefKind::Variant, def_id) if i + 1 == proj_start => {
- Some(parent_def_id(self, def_id))
- }
- Res::Def(DefKind::Struct, def_id)
- | Res::Def(DefKind::Union, def_id)
- | Res::Def(DefKind::Enum, def_id)
- | Res::Def(DefKind::TyAlias, def_id)
- | Res::Def(DefKind::Trait, def_id)
- if i + 1 == proj_start =>
- {
- Some(def_id)
- }
- _ => None,
- };
let parenthesized_generic_args = match partial_res.base_res() {
// `a::b::Trait(Args)`
Res::Def(DefKind::Trait, _) if i + 1 == proj_start => {
_ => ParenthesizedGenericArgs::Err,
};
- let num_lifetimes = type_def_id
- .map_or(0, |def_id| self.resolver.item_generics_num_lifetimes(def_id));
self.lower_path_segment(
p.span,
segment,
param_mode,
- num_lifetimes,
parenthesized_generic_args,
itctx.reborrow(),
)
p.span,
segment,
param_mode,
- 0,
ParenthesizedGenericArgs::Err,
itctx.reborrow(),
));
p.span,
segment,
param_mode,
- 0,
ParenthesizedGenericArgs::Err,
ImplTraitContext::Disallowed(ImplTraitPosition::Path),
)
path_span: Span,
segment: &PathSegment,
param_mode: ParamMode,
- expected_lifetimes: usize,
parenthesized_generic_args: ParenthesizedGenericArgs,
itctx: ImplTraitContext<'_, 'hir>,
) -> hir::PathSegment<'hir> {
- debug!(
- "path_span: {:?}, lower_path_segment(segment: {:?}, expected_lifetimes: {:?})",
- path_span, segment, expected_lifetimes
- );
+ debug!("path_span: {:?}, lower_path_segment(segment: {:?})", path_span, segment,);
let (mut generic_args, infer_args) = if let Some(ref generic_args) = segment.args {
let msg = "parenthesized type parameters may only be used with a `Fn` trait";
match **generic_args {
self.lower_angle_bracketed_parameter_data(data, param_mode, itctx)
}
GenericArgs::Parenthesized(ref data) => match parenthesized_generic_args {
- ParenthesizedGenericArgs::Ok => self.lower_parenthesized_parameter_data(data),
+ ParenthesizedGenericArgs::Ok => {
+ self.lower_parenthesized_parameter_data(segment.id, data)
+ }
ParenthesizedGenericArgs::Err => {
let mut err = struct_span_err!(self.sess, data.span, E0214, "{}", msg);
err.span_label(data.span, "only `Fn` traits may use parentheses");
let has_lifetimes =
generic_args.args.iter().any(|arg| matches!(arg, GenericArg::Lifetime(_)));
- if !generic_args.parenthesized && !has_lifetimes && expected_lifetimes > 0 {
- // Note: these spans are used for diagnostics when they can't be inferred.
- // See rustc_resolve::late::lifetimes::LifetimeContext::add_missing_lifetime_specifiers_label
- let elided_lifetime_span = if generic_args.span.is_empty() {
- // If there are no brackets, use the identifier span.
- // HACK: we use find_ancestor_inside to properly suggest elided spans in paths
- // originating from macros, since the segment's span might be from a macro arg.
- segment.ident.span.find_ancestor_inside(path_span).unwrap_or(path_span)
- } else if generic_args.is_empty() {
- // If there are brackets, but not generic arguments, then use the opening bracket
- generic_args.span.with_hi(generic_args.span.lo() + BytePos(1))
- } else {
- // Else use an empty span right after the opening bracket.
- generic_args.span.with_lo(generic_args.span.lo() + BytePos(1)).shrink_to_lo()
- };
- generic_args.args = self
- .elided_path_lifetimes(elided_lifetime_span, expected_lifetimes)
- .map(GenericArg::Lifetime)
- .chain(generic_args.args.into_iter())
- .collect();
- if let (ParamMode::Explicit, AnonymousLifetimeMode::CreateParameter) =
- (param_mode, self.anonymous_lifetime_mode)
- {
- // Late resolver should have issued the error.
- self.sess
- .delay_span_bug(elided_lifetime_span, "implicit lifetime not allowed here");
- }
+ if !generic_args.parenthesized && !has_lifetimes {
+ self.maybe_insert_elided_lifetimes_in_path(
+ path_span,
+ segment.id,
+ segment.ident.span,
+ &mut generic_args,
+ );
}
let res = self.expect_full_res(segment.id);
}
}
+ fn maybe_insert_elided_lifetimes_in_path(
+ &mut self,
+ path_span: Span,
+ segment_id: NodeId,
+ segment_ident_span: Span,
+ generic_args: &mut GenericArgsCtor<'hir>,
+ ) {
+ let (start, end) = match self.resolver.get_lifetime_res(segment_id) {
+ Some(LifetimeRes::ElidedAnchor { start, end }) => (start, end),
+ None => return,
+ Some(_) => panic!(),
+ };
+ let expected_lifetimes = end.as_usize() - start.as_usize();
+ debug!(expected_lifetimes);
+
+ // Note: these spans are used for diagnostics when they can't be inferred.
+ // See rustc_resolve::late::lifetimes::LifetimeContext::add_missing_lifetime_specifiers_label
+ let elided_lifetime_span = if generic_args.span.is_empty() {
+ // If there are no brackets, use the identifier span.
+ // HACK: we use find_ancestor_inside to properly suggest elided spans in paths
+ // originating from macros, since the segment's span might be from a macro arg.
+ segment_ident_span.find_ancestor_inside(path_span).unwrap_or(path_span)
+ } else if generic_args.is_empty() {
+ // If there are brackets, but not generic arguments, then use the opening bracket
+ generic_args.span.with_hi(generic_args.span.lo() + BytePos(1))
+ } else {
+ // Else use an empty span right after the opening bracket.
+ generic_args.span.with_lo(generic_args.span.lo() + BytePos(1)).shrink_to_lo()
+ };
+
+ generic_args.args.insert_many(
+ 0,
+ (start.as_u32()..end.as_u32()).map(|i| {
+ let id = NodeId::from_u32(i);
+ let l = self.lower_lifetime(&Lifetime {
+ id,
+ ident: Ident::new(kw::UnderscoreLifetime, elided_lifetime_span),
+ });
+ GenericArg::Lifetime(l)
+ }),
+ );
+ }
+
pub(crate) fn lower_angle_bracketed_parameter_data(
&mut self,
data: &AngleBracketedArgs,
fn lower_parenthesized_parameter_data(
&mut self,
+ id: NodeId,
data: &ParenthesizedArgs,
) -> (GenericArgsCtor<'hir>, bool) {
// Switch to `PassThrough` mode for anonymous lifetimes; this
// a hidden lifetime parameter. This is needed for backwards
// compatibility, even in contexts like an impl header where
// we generally don't permit such things (see #51008).
- self.with_anonymous_lifetime_mode(AnonymousLifetimeMode::PassThrough, |this| {
+ self.with_lifetime_binder(id, |this| {
let ParenthesizedArgs { span, inputs, inputs_span, output } = data;
let inputs = this.arena.alloc_from_iter(inputs.iter().map(|ty| {
this.lower_ty_direct(
use itertools::{Either, Itertools};
use rustc_ast::ptr::P;
-use rustc_ast::visit::{self, AssocCtxt, FnCtxt, FnKind, Visitor};
+use rustc_ast::visit::{self, AssocCtxt, BoundKind, FnCtxt, FnKind, Visitor};
use rustc_ast::walk_list;
use rustc_ast::*;
use rustc_ast_pretty::pprust::{self, State};
}
}
- // FIXME(ecstaticmorse): Instead, use `bound_context` to check this in `visit_param_bound`.
- fn no_questions_in_bounds(&self, bounds: &GenericBounds, where_: &str, is_trait: bool) {
- for bound in bounds {
- if let GenericBound::Trait(ref poly, TraitBoundModifier::Maybe) = *bound {
- let mut err = self.err_handler().struct_span_err(
- poly.span,
- &format!("`?Trait` is not permitted in {}", where_),
- );
- if is_trait {
- let path_str = pprust::path_to_string(&poly.trait_ref.path);
- err.note(&format!("traits are `?{}` by default", path_str));
- }
- err.emit();
- }
- }
- }
-
fn check_late_bound_lifetime_defs(&self, params: &[GenericParam]) {
// Check only lifetime parameters are present and that the lifetime
// parameters that are present have no bounds.
any_lifetime_bounds = true;
}
}
- self.no_questions_in_bounds(bounds, "trait object types", false);
}
TyKind::ImplTrait(_, ref bounds) => {
if self.is_impl_trait_banned {
self.deny_where_clause(&generics.where_clause, item.ident.span);
self.deny_items(items, item.ident.span);
}
- self.no_questions_in_bounds(bounds, "supertraits", true);
// Equivalent of `visit::walk_item` for `ItemKind::Trait` that inserts a bound
// context for the supertraits.
self.visit_vis(&item.vis);
self.visit_ident(item.ident);
self.visit_generics(generics);
- self.with_banned_tilde_const(|this| walk_list!(this, visit_param_bound, bounds));
+ self.with_banned_tilde_const(|this| {
+ walk_list!(this, visit_param_bound, bounds, BoundKind::SuperTraits)
+ });
walk_list!(self, visit_assoc_item, items, AssocCtxt::Trait);
walk_list!(self, visit_attribute, &item.attrs);
return;
visit::walk_generic_param(self, param);
}
- fn visit_param_bound(&mut self, bound: &'a GenericBound) {
- match bound {
- GenericBound::Trait(_, TraitBoundModifier::MaybeConst) => {
- if !self.is_tilde_const_allowed {
+ fn visit_param_bound(&mut self, bound: &'a GenericBound, ctxt: BoundKind) {
+ if let GenericBound::Trait(ref poly, modify) = *bound {
+ match (ctxt, modify) {
+ (BoundKind::SuperTraits, TraitBoundModifier::Maybe) => {
+ let mut err = self.err_handler().struct_span_err(
+ poly.span,
+ &format!("`?Trait` is not permitted in supertraits"),
+ );
+ let path_str = pprust::path_to_string(&poly.trait_ref.path);
+ err.note(&format!("traits are `?{}` by default", path_str));
+ err.emit();
+ }
+ (BoundKind::TraitObject, TraitBoundModifier::Maybe) => {
+ let mut err = self.err_handler().struct_span_err(
+ poly.span,
+ &format!("`?Trait` is not permitted in trait object types"),
+ );
+ err.emit();
+ }
+ (_, TraitBoundModifier::MaybeConst) => {
+ if !self.is_tilde_const_allowed {
+ self.err_handler()
+ .struct_span_err(bound.span(), "`~const` is not allowed here")
+ .note("only allowed on bounds on traits' associated types and functions, const fns, const impls and its associated functions")
+ .emit();
+ }
+ }
+ (_, TraitBoundModifier::MaybeConstMaybe) => {
self.err_handler()
- .struct_span_err(bound.span(), "`~const` is not allowed here")
- .note("only allowed on bounds on traits' associated types and functions, const fns, const impls and its associated functions")
- .emit();
+ .span_err(bound.span(), "`~const` and `?` are mutually exclusive");
}
+ _ => {}
}
-
- GenericBound::Trait(_, TraitBoundModifier::MaybeConstMaybe) => {
- self.err_handler()
- .span_err(bound.span(), "`~const` and `?` are mutually exclusive");
- }
-
- _ => {}
}
visit::walk_param_bound(self, bound)
walk_list!(self, visit_attribute, &item.attrs);
self.with_tilde_const_allowed(|this| {
this.visit_generics(generics);
- walk_list!(this, visit_param_bound, bounds);
+ walk_list!(this, visit_param_bound, bounds, BoundKind::Bound);
});
walk_list!(self, visit_ty, ty);
}
gate_all!(inline_const, "inline-const is experimental");
gate_all!(inline_const_pat, "inline-const in pattern position is experimental");
gate_all!(associated_const_equality, "associated const equality is incomplete");
+ gate_all!(yeet_expr, "`do yeet` expression is experimental");
// All uses of `gate_all!` below this point were added in #65742,
// and subsequently disabled (with the non-early gating readded).
self.count += 1;
walk_trait_ref(self, t)
}
- fn visit_param_bound(&mut self, bounds: &GenericBound) {
+ fn visit_param_bound(&mut self, bounds: &GenericBound, _ctxt: BoundKind) {
self.count += 1;
walk_param_bound(self, bounds)
}
use crate::pp::{self, Breaks};
use rustc_ast::ptr::P;
-use rustc_ast::token::{self, BinOpToken, CommentKind, DelimToken, Nonterminal, Token, TokenKind};
+use rustc_ast::token::{self, BinOpToken, CommentKind, Delimiter, Nonterminal, Token, TokenKind};
use rustc_ast::tokenstream::{TokenStream, TokenTree};
use rustc_ast::util::classify;
use rustc_ast::util::comments::{gather_comments, Comment, CommentStyle};
}
match tt {
TokenTree::Token(token) => !matches!(token.kind, token::Comma | token::Not | token::Dot),
- TokenTree::Delimited(_, DelimToken::Paren, _) => {
+ TokenTree::Delimited(_, Delimiter::Parenthesis, _) => {
!matches!(prev, TokenTree::Token(Token { kind: token::Ident(..), .. }))
}
- TokenTree::Delimited(_, DelimToken::Bracket, _) => {
+ TokenTree::Delimited(_, Delimiter::Bracket, _) => {
!matches!(prev, TokenTree::Token(Token { kind: token::Pound, .. }))
}
TokenTree::Delimited(..) => true,
Some(MacHeader::Path(&item.path)),
false,
None,
- delim.to_token(),
+ Some(delim.to_token()),
tokens,
true,
span,
None,
false,
None,
- *delim,
+ Some(*delim),
tts,
convert_dollar_crate,
dspan.entire(),
header: Option<MacHeader<'_>>,
has_bang: bool,
ident: Option<Ident>,
- delim: DelimToken,
+ delim: Option<Delimiter>,
tts: &TokenStream,
convert_dollar_crate: bool,
span: Span,
) {
- if delim == DelimToken::Brace {
+ if delim == Some(Delimiter::Brace) {
self.cbox(INDENT_UNIT);
}
match header {
self.print_ident(ident);
}
match delim {
- DelimToken::Brace => {
+ Some(Delimiter::Brace) => {
if header.is_some() || has_bang || ident.is_some() {
self.nbsp();
}
if !tts.is_empty() {
self.space();
}
- }
- _ => {
- let token_str = self.token_kind_to_string(&token::OpenDelim(delim));
- self.word(token_str)
- }
- }
- self.ibox(0);
- self.print_tts(tts, convert_dollar_crate);
- self.end();
- match delim {
- DelimToken::Brace => {
+ self.ibox(0);
+ self.print_tts(tts, convert_dollar_crate);
+ self.end();
let empty = tts.is_empty();
self.bclose(span, empty);
}
- _ => {
+ Some(delim) => {
+ let token_str = self.token_kind_to_string(&token::OpenDelim(delim));
+ self.word(token_str);
+ self.ibox(0);
+ self.print_tts(tts, convert_dollar_crate);
+ self.end();
let token_str = self.token_kind_to_string(&token::CloseDelim(delim));
- self.word(token_str)
+ self.word(token_str);
+ }
+ None => {
+ self.ibox(0);
+ self.print_tts(tts, convert_dollar_crate);
+ self.end();
}
}
}
token::RArrow => "->".into(),
token::LArrow => "<-".into(),
token::FatArrow => "=>".into(),
- token::OpenDelim(token::Paren) => "(".into(),
- token::CloseDelim(token::Paren) => ")".into(),
- token::OpenDelim(token::Bracket) => "[".into(),
- token::CloseDelim(token::Bracket) => "]".into(),
- token::OpenDelim(token::Brace) => "{".into(),
- token::CloseDelim(token::Brace) => "}".into(),
- token::OpenDelim(token::NoDelim) | token::CloseDelim(token::NoDelim) => "".into(),
+ token::OpenDelim(Delimiter::Parenthesis) => "(".into(),
+ token::CloseDelim(Delimiter::Parenthesis) => ")".into(),
+ token::OpenDelim(Delimiter::Bracket) => "[".into(),
+ token::CloseDelim(Delimiter::Bracket) => "]".into(),
+ token::OpenDelim(Delimiter::Brace) => "{".into(),
+ token::CloseDelim(Delimiter::Brace) => "}".into(),
+ token::OpenDelim(Delimiter::Invisible) | token::CloseDelim(Delimiter::Invisible) => {
+ "".into()
+ }
token::Pound => "#".into(),
token::Dollar => "$".into(),
token::Question => "?".into(),
// parses as the erroneous construct `if (return {})`, not `if (return) {}`.
pub(super) fn cond_needs_par(expr: &ast::Expr) -> bool {
match expr.kind {
- ast::ExprKind::Break(..) | ast::ExprKind::Closure(..) | ast::ExprKind::Ret(..) => true,
+ ast::ExprKind::Break(..)
+ | ast::ExprKind::Closure(..)
+ | ast::ExprKind::Ret(..)
+ | ast::ExprKind::Yeet(..) => true,
_ => parser::contains_exterior_struct_lit(expr),
}
}
self.print_expr_maybe_paren(expr, parser::PREC_JUMP);
}
}
+ ast::ExprKind::Yeet(ref result) => {
+ self.word("do");
+ self.word(" ");
+ self.word("yeet");
+ if let Some(ref expr) = *result {
+ self.word(" ");
+ self.print_expr_maybe_paren(expr, parser::PREC_JUMP);
+ }
+ }
ast::ExprKind::InlineAsm(ref a) => {
self.word("asm!");
self.print_inline_asm(a);
/// Given a region `R`, iterate over all regions `R1` such that
/// there exists a constraint `R: R1`.
- crate fn outgoing_regions(&self, region_sup: RegionVid) -> Successors<'_, 'tcx, D> {
+ crate fn outgoing_regions(&self, region_sup: RegionVid) -> Successors<'s, 'tcx, D> {
Successors {
edges: self.constraint_graph.outgoing_edges(region_sup, self.set, self.static_region),
}
}
}
-impl<'s, 'graph, 'tcx, D: ConstraintGraphDirecton> graph::GraphSuccessors<'graph>
- for RegionGraph<'s, 'tcx, D>
-{
+impl<'s, 'tcx, D: ConstraintGraphDirecton> graph::GraphSuccessors<'_> for RegionGraph<'s, 'tcx, D> {
type Item = RegionVid;
- // FIXME - why can't this be `'graph, 'tcx`
- type Iter = Successors<'graph, 'graph, D>;
+ type Iter = Successors<'s, 'tcx, D>;
}
use rustc_hir::{AsyncGeneratorKind, GeneratorKind};
use rustc_infer::infer::TyCtxtInferExt;
use rustc_infer::traits::ObligationCause;
+use rustc_middle::mir::tcx::PlaceTy;
use rustc_middle::mir::{
self, AggregateKind, BindingForm, BorrowKind, ClearCrossCrate, ConstraintCategory,
FakeReadCause, LocalDecl, LocalInfo, LocalKind, Location, Operand, Place, PlaceRef,
use crate::borrow_set::TwoPhaseActivation;
use crate::borrowck_errors;
+use crate::diagnostics::conflict_errors::StorageDeadOrDrop::LocalStorageDead;
use crate::diagnostics::find_all_local_uses;
use crate::{
borrow_set::BorrowData, diagnostics::Instance, prefixes::IsPrefixOf,
err.span_suggestion_hidden(
return_span,
"use `.collect()` to allocate the iterator",
- format!("{}{}", snippet, ".collect::<Vec<_>>()"),
+ format!("{snippet}.collect::<Vec<_>>()"),
Applicability::MaybeIncorrect,
);
}
fn classify_drop_access_kind(&self, place: PlaceRef<'tcx>) -> StorageDeadOrDrop<'tcx> {
let tcx = self.infcx.tcx;
- match place.last_projection() {
- None => StorageDeadOrDrop::LocalStorageDead,
- Some((place_base, elem)) => {
- // FIXME(spastorino) make this iterate
- let base_access = self.classify_drop_access_kind(place_base);
- match elem {
- ProjectionElem::Deref => match base_access {
- StorageDeadOrDrop::LocalStorageDead
- | StorageDeadOrDrop::BoxedStorageDead => {
- assert!(
- place_base.ty(self.body, tcx).ty.is_box(),
- "Drop of value behind a reference or raw pointer"
- );
- StorageDeadOrDrop::BoxedStorageDead
- }
- StorageDeadOrDrop::Destructor(_) => base_access,
- },
- ProjectionElem::Field(..) | ProjectionElem::Downcast(..) => {
- let base_ty = place_base.ty(self.body, tcx).ty;
- match base_ty.kind() {
- ty::Adt(def, _) if def.has_dtor(tcx) => {
- // Report the outermost adt with a destructor
- match base_access {
- StorageDeadOrDrop::Destructor(_) => base_access,
- StorageDeadOrDrop::LocalStorageDead
- | StorageDeadOrDrop::BoxedStorageDead => {
- StorageDeadOrDrop::Destructor(base_ty)
+ let (kind, _place_ty) = place.projection.iter().fold(
+ (LocalStorageDead, PlaceTy::from_ty(self.body.local_decls[place.local].ty)),
+ |(kind, place_ty), &elem| {
+ (
+ match elem {
+ ProjectionElem::Deref => match kind {
+ StorageDeadOrDrop::LocalStorageDead
+ | StorageDeadOrDrop::BoxedStorageDead => {
+ assert!(
+ place_ty.ty.is_box(),
+ "Drop of value behind a reference or raw pointer"
+ );
+ StorageDeadOrDrop::BoxedStorageDead
+ }
+ StorageDeadOrDrop::Destructor(_) => kind,
+ },
+ ProjectionElem::Field(..) | ProjectionElem::Downcast(..) => {
+ match place_ty.ty.kind() {
+ ty::Adt(def, _) if def.has_dtor(tcx) => {
+ // Report the outermost adt with a destructor
+ match kind {
+ StorageDeadOrDrop::Destructor(_) => kind,
+ StorageDeadOrDrop::LocalStorageDead
+ | StorageDeadOrDrop::BoxedStorageDead => {
+ StorageDeadOrDrop::Destructor(place_ty.ty)
+ }
}
}
+ _ => kind,
}
- _ => base_access,
}
- }
- ProjectionElem::ConstantIndex { .. }
- | ProjectionElem::Subslice { .. }
- | ProjectionElem::Index(_) => base_access,
- }
- }
- }
+ ProjectionElem::ConstantIndex { .. }
+ | ProjectionElem::Subslice { .. }
+ | ProjectionElem::Index(_) => kind,
+ },
+ place_ty.projection_ty(tcx, elem),
+ )
+ },
+ );
+ kind
}
/// Describe the reason for the fake borrow that was assigned to `place`.
crate use region_errors::{ErrorConstraintInfo, RegionErrorKind, RegionErrors};
crate use region_name::{RegionName, RegionNameSource};
crate use rustc_const_eval::util::CallKind;
+use rustc_middle::mir::tcx::PlaceTy;
pub(super) struct IncludingDowncast(pub(super) bool);
{
if let ty::FnDef(id, _) = *literal.ty().kind() {
debug!("add_moved_or_invoked_closure_note: id={:?}", id);
- if self.infcx.tcx.parent(id) == self.infcx.tcx.lang_items().fn_once_trait() {
+ if Some(self.infcx.tcx.parent(id)) == self.infcx.tcx.lang_items().fn_once_trait() {
let closure = match args.first() {
Some(Operand::Copy(ref place)) | Some(Operand::Move(ref place))
if target == place.local_or_deref_local() =>
/// End-user visible description of the `field`nth field of `base`
fn describe_field(&self, place: PlaceRef<'tcx>, field: Field) -> String {
- // FIXME Place2 Make this work iteratively
- match place {
- PlaceRef { local, projection: [] } => {
- let local = &self.body.local_decls[local];
- self.describe_field_from_ty(local.ty, field, None)
- }
+ let place_ty = match place {
+ PlaceRef { local, projection: [] } => PlaceTy::from_ty(self.body.local_decls[local].ty),
PlaceRef { local, projection: [proj_base @ .., elem] } => match elem {
- ProjectionElem::Deref => {
- self.describe_field(PlaceRef { local, projection: proj_base }, field)
- }
- ProjectionElem::Downcast(_, variant_index) => {
- let base_ty = place.ty(self.body, self.infcx.tcx).ty;
- self.describe_field_from_ty(base_ty, field, Some(*variant_index))
- }
- ProjectionElem::Field(_, field_type) => {
- self.describe_field_from_ty(*field_type, field, None)
- }
- ProjectionElem::Index(..)
+ ProjectionElem::Deref
+ | ProjectionElem::Index(..)
| ProjectionElem::ConstantIndex { .. }
| ProjectionElem::Subslice { .. } => {
- self.describe_field(PlaceRef { local, projection: proj_base }, field)
+ PlaceRef { local, projection: proj_base }.ty(self.body, self.infcx.tcx)
}
+ ProjectionElem::Downcast(..) => place.ty(self.body, self.infcx.tcx),
+ ProjectionElem::Field(_, field_type) => PlaceTy::from_ty(*field_type),
},
- }
+ };
+ self.describe_field_from_ty(place_ty.ty, field, place_ty.variant_index)
}
/// End-user visible description of the `field_index`nth field of `ty`
err.span_suggestion_verbose(
span,
"consider changing this to be mutable",
- " mut ".into(),
+ " mut ",
Applicability::MaybeIncorrect,
);
}
//! Error reporting machinery for lifetime errors.
-use rustc_errors::{Applicability, Diagnostic, DiagnosticBuilder, ErrorGuaranteed};
+use rustc_errors::{Diagnostic, DiagnosticBuilder, ErrorGuaranteed};
use rustc_infer::infer::{
- error_reporting::nice_region_error::NiceRegionError,
- error_reporting::unexpected_hidden_region_diagnostic, NllRegionVariableOrigin,
+ error_reporting::nice_region_error::{
+ self, find_anon_type, find_param_with_region, suggest_adding_lifetime_params,
+ NiceRegionError,
+ },
+ error_reporting::unexpected_hidden_region_diagnostic,
+ NllRegionVariableOrigin, RelateParamBound,
};
use rustc_middle::hir::place::PlaceBase;
use rustc_middle::mir::{ConstraintCategory, ReturnConstraint};
-use rustc_middle::ty::subst::{InternalSubsts, Subst};
+use rustc_middle::ty::subst::InternalSubsts;
use rustc_middle::ty::{self, RegionVid, Ty};
-use rustc_span::symbol::{kw, sym};
-use rustc_span::{BytePos, Span};
+use rustc_span::symbol::sym;
+use rustc_span::Span;
use crate::borrowck_errors;
let type_test_span = type_test.locations.span(&self.body);
if let Some(lower_bound_region) = lower_bound_region {
+ let generic_ty = type_test.generic_kind.to_ty(self.infcx.tcx);
+ let origin = RelateParamBound(type_test_span, generic_ty, None);
self.buffer_error(self.infcx.construct_generic_bound_failure(
type_test_span,
- None,
+ Some(origin),
type_test.generic_kind,
lower_bound_region,
+ self.body.source.def_id().as_local(),
));
} else {
// FIXME. We should handle this case better. It
}
self.add_static_impl_trait_suggestion(&mut diag, *fr, fr_name, *outlived_fr);
+ self.suggest_adding_lifetime_params(&mut diag, *fr, *outlived_fr);
diag
}
fr_name: RegionName,
outlived_fr: RegionVid,
) {
- if let (Some(f), Some(ty::ReStatic)) =
- (self.to_error_region(fr), self.to_error_region(outlived_fr).as_deref())
+ if let (Some(f), Some(outlived_f)) =
+ (self.to_error_region(fr), self.to_error_region(outlived_fr))
{
- if let Some(&ty::Opaque(did, substs)) = self
+ if *outlived_f != ty::ReStatic {
+ return;
+ }
+
+ let fn_returns = self
.infcx
.tcx
.is_suitable_region(f)
- .map(|r| r.def_id)
- .and_then(|id| self.infcx.tcx.return_type_impl_trait(id))
- .map(|(ty, _)| ty.kind())
- {
- // Check whether or not the impl trait return type is intended to capture
- // data with the static lifetime.
- //
- // eg. check for `impl Trait + 'static` instead of `impl Trait`.
- let has_static_predicate = {
- let bounds = self.infcx.tcx.explicit_item_bounds(did);
-
- let mut found = false;
- for (bound, _) in bounds {
- if let ty::PredicateKind::TypeOutlives(ty::OutlivesPredicate(_, r)) =
- bound.kind().skip_binder()
- {
- let r = r.subst(self.infcx.tcx, substs);
- if r.is_static() {
- found = true;
- break;
- } else {
- // If there's already a lifetime bound, don't
- // suggest anything.
- return;
- }
- }
- }
+ .map(|r| self.infcx.tcx.return_type_impl_or_dyn_traits(r.def_id))
+ .unwrap_or_default();
- found
- };
-
- debug!(
- "add_static_impl_trait_suggestion: has_static_predicate={:?}",
- has_static_predicate
- );
- let static_str = kw::StaticLifetime;
- // If there is a static predicate, then the only sensible suggestion is to replace
- // fr with `'static`.
- if has_static_predicate {
- diag.help(&format!("consider replacing `{fr_name}` with `{static_str}`"));
- } else {
- // Otherwise, we should suggest adding a constraint on the return type.
- let span = self.infcx.tcx.def_span(did);
- if let Ok(snippet) = self.infcx.tcx.sess.source_map().span_to_snippet(span) {
- let suggestable_fr_name = if fr_name.was_named() {
- fr_name.to_string()
- } else {
- "'_".to_string()
- };
- let span = if snippet.ends_with(';') {
- // `type X = impl Trait;`
- span.with_hi(span.hi() - BytePos(1))
- } else {
- span
- };
- let suggestion = format!(" + {suggestable_fr_name}");
- let span = span.shrink_to_hi();
- diag.span_suggestion(
- span,
- &format!(
- "to allow this `impl Trait` to capture borrowed data with lifetime \
- `{fr_name}`, add `{suggestable_fr_name}` as a bound",
- ),
- suggestion,
- Applicability::MachineApplicable,
- );
- }
- }
+ if fn_returns.is_empty() {
+ return;
}
+
+ let param = if let Some(param) = find_param_with_region(self.infcx.tcx, f, outlived_f) {
+ param
+ } else {
+ return;
+ };
+
+ let lifetime = if f.has_name() { fr_name.to_string() } else { "'_".to_string() };
+
+ let arg = match param.param.pat.simple_ident() {
+ Some(simple_ident) => format!("argument `{}`", simple_ident),
+ None => "the argument".to_string(),
+ };
+ let captures = format!("captures data from {}", arg);
+
+ return nice_region_error::suggest_new_region_bound(
+ self.infcx.tcx,
+ diag,
+ fn_returns,
+ lifetime,
+ Some(arg),
+ captures,
+ Some((param.param_ty_span, param.param_ty.to_string())),
+ );
}
}
+
+ fn suggest_adding_lifetime_params(
+ &self,
+ diag: &mut Diagnostic,
+ sub: RegionVid,
+ sup: RegionVid,
+ ) {
+ let (Some(sub), Some(sup)) = (self.to_error_region(sub), self.to_error_region(sup)) else {
+ return
+ };
+
+ let Some((ty_sub, _)) = self
+ .infcx
+ .tcx
+ .is_suitable_region(sub)
+ .and_then(|anon_reg| find_anon_type(self.infcx.tcx, sub, &anon_reg.boundregion)) else {
+ return
+ };
+
+ let Some((ty_sup, _)) = self
+ .infcx
+ .tcx
+ .is_suitable_region(sup)
+ .and_then(|anon_reg| find_anon_type(self.infcx.tcx, sup, &anon_reg.boundregion)) else {
+ return
+ };
+
+ suggest_adding_lifetime_params(self.infcx.tcx, sub, ty_sup, ty_sub, diag);
+ }
}
use rustc_span::def_id::CRATE_DEF_ID;
use rustc_span::{Span, DUMMY_SP};
use rustc_target::abi::VariantIdx;
-use rustc_trait_selection::infer::InferCtxtExt as _;
-use rustc_trait_selection::traits::error_reporting::InferCtxtExt as _;
use rustc_trait_selection::traits::query::type_op;
use rustc_trait_selection::traits::query::type_op::custom::scrape_region_constraints;
use rustc_trait_selection::traits::query::type_op::custom::CustomTypeOp;
use rustc_trait_selection::traits::query::type_op::{TypeOp, TypeOpOutput};
use rustc_trait_selection::traits::query::Fallible;
-use rustc_trait_selection::traits::{self, ObligationCause, PredicateObligation};
+use rustc_trait_selection::traits::PredicateObligation;
-use rustc_const_eval::transform::{
- check_consts::ConstCx, promote_consts::is_const_fn_in_array_repeat_expression,
-};
use rustc_mir_dataflow::impls::MaybeInitializedPlaces;
use rustc_mir_dataflow::move_paths::MoveData;
use rustc_mir_dataflow::ResultsCursor;
Operand::Move(place) => {
// Make sure that repeated elements implement `Copy`.
let span = body.source_info(location).span;
- let ty = operand.ty(body, tcx);
- if !self.infcx.type_is_copy_modulo_regions(self.param_env, ty, span) {
- let ccx = ConstCx::new_with_param_env(tcx, body, self.param_env);
- let is_const_fn =
- is_const_fn_in_array_repeat_expression(&ccx, &place, &body);
-
- debug!("check_rvalue: is_const_fn={:?}", is_const_fn);
-
- let def_id = body.source.def_id().expect_local();
- let obligation = traits::Obligation::new(
- ObligationCause::new(
- span,
- self.tcx().hir().local_def_id_to_hir_id(def_id),
- traits::ObligationCauseCode::RepeatElementCopy {
- is_const_fn,
- },
- ),
- self.param_env,
- ty::Binder::dummy(ty::TraitRef::new(
- self.tcx().require_lang_item(
- LangItem::Copy,
- Some(self.last_span),
- ),
- tcx.mk_substs_trait(ty, &[]),
- ))
- .without_const()
- .to_predicate(self.tcx()),
- );
- self.infcx.report_selection_error(
- obligation.clone(),
- &obligation,
- &traits::SelectionError::Unimplemented,
- false,
- );
- }
+ let ty = place.ty(body, tcx).ty;
+ let trait_ref = ty::TraitRef::new(
+ tcx.require_lang_item(LangItem::Copy, Some(span)),
+ tcx.mk_substs_trait(ty, &[]),
+ );
+
+ self.prove_trait_ref(
+ trait_ref,
+ Locations::Single(location),
+ ConstraintCategory::CopyBound,
+ );
}
}
}
use rustc_ast as ast;
use rustc_ast::ptr::P;
-use rustc_ast::token;
+use rustc_ast::token::{self, Delimiter};
use rustc_ast::tokenstream::TokenStream;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_errors::{Applicability, PResult};
) -> PResult<'a, ()> {
let span_start = p.prev_token.span;
- p.expect(&token::OpenDelim(token::DelimToken::Paren))?;
+ p.expect(&token::OpenDelim(Delimiter::Parenthesis))?;
- while !p.eat(&token::CloseDelim(token::DelimToken::Paren)) {
+ while !p.eat(&token::CloseDelim(Delimiter::Parenthesis)) {
if !is_global_asm && p.eat_keyword(sym::pure) {
try_set_option(p, args, sym::pure, ast::InlineAsmOptions::PURE);
} else if !is_global_asm && p.eat_keyword(sym::nomem) {
}
// Allow trailing commas
- if p.eat(&token::CloseDelim(token::DelimToken::Paren)) {
+ if p.eat(&token::CloseDelim(Delimiter::Parenthesis)) {
break;
}
p.expect(&token::Comma)?;
fn parse_clobber_abi<'a>(p: &mut Parser<'a>, args: &mut AsmArgs) -> PResult<'a, ()> {
let span_start = p.prev_token.span;
- p.expect(&token::OpenDelim(token::DelimToken::Paren))?;
+ p.expect(&token::OpenDelim(Delimiter::Parenthesis))?;
- if p.eat(&token::CloseDelim(token::DelimToken::Paren)) {
+ if p.eat(&token::CloseDelim(Delimiter::Parenthesis)) {
let err = p.sess.span_diagnostic.struct_span_err(
p.token.span,
"at least one abi must be provided as an argument to `clobber_abi`",
}
Err(opt_lit) => {
// If the non-string literal is a closing paren then it's the end of the list and is fine
- if p.eat(&token::CloseDelim(token::DelimToken::Paren)) {
+ if p.eat(&token::CloseDelim(Delimiter::Parenthesis)) {
break;
}
let span = opt_lit.map_or(p.token.span, |lit| lit.span);
};
// Allow trailing commas
- if p.eat(&token::CloseDelim(token::DelimToken::Paren)) {
+ if p.eat(&token::CloseDelim(Delimiter::Parenthesis)) {
break;
}
p.expect(&token::Comma)?;
p: &mut Parser<'a>,
explicit_reg: &mut bool,
) -> PResult<'a, ast::InlineAsmRegOrRegClass> {
- p.expect(&token::OpenDelim(token::DelimToken::Paren))?;
+ p.expect(&token::OpenDelim(Delimiter::Parenthesis))?;
let result = match p.token.uninterpolate().kind {
token::Ident(name, false) => ast::InlineAsmRegOrRegClass::RegClass(name),
token::Literal(token::Lit { kind: token::LitKind::Str, symbol, suffix: _ }) => {
}
};
p.bump();
- p.expect(&token::CloseDelim(token::DelimToken::Paren))?;
+ p.expect(&token::CloseDelim(Delimiter::Parenthesis))?;
Ok(result)
}
bounds: Default::default(),
is_placeholder: false,
kind: GenericParamKind::Lifetime,
+ colon_span: None,
})
}
.span_suggestion_hidden(
attr.span,
"try using `#[default]`",
- "#[default]".into(),
+ "#[default]",
Applicability::MaybeIncorrect,
)
.emit();
[[package]]
name = "cranelift-bforest"
-version = "0.82.1"
+version = "0.83.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "d16922317bd7dd104d509a373887822caa0242fc1def00de66abb538db221db4"
+checksum = "ed44413e7e2fe3260d0ed73e6956ab188b69c10ee92b892e401e0f4f6808c68b"
dependencies = [
"cranelift-entity",
]
[[package]]
name = "cranelift-codegen"
-version = "0.82.1"
+version = "0.83.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "8b80bf40380256307b68a3dcbe1b91cac92a533e212b5b635abc3e4525781a0a"
+checksum = "0b5d83f0f26bf213f971f45589d17e5b65e4861f9ed22392b0cbb6eaa5bd329c"
dependencies = [
"cranelift-bforest",
"cranelift-codegen-meta",
[[package]]
name = "cranelift-codegen-meta"
-version = "0.82.1"
+version = "0.83.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "703d0ed7d3bc6c7a814ca12858175bf4e93167a3584127858c686e4b5dd6e432"
+checksum = "6800dc386177df6ecc5a32680607ed8ba1fa0d31a2a59c8c61fbf44826b8191d"
dependencies = [
"cranelift-codegen-shared",
]
[[package]]
name = "cranelift-codegen-shared"
-version = "0.82.1"
+version = "0.83.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "80f52311e1c90de12dcf8c4b9999c6ebfd1ed360373e88c357160936844511f6"
+checksum = "c961f85070985ebc8fcdb81b838a5cf842294d1e6ed4852446161c7e246fd455"
[[package]]
name = "cranelift-entity"
-version = "0.82.1"
+version = "0.83.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "66bc82ef522c1f643baf7d4d40b7c52643ee4549d8960b0e6a047daacb83f897"
+checksum = "2347b2b8d1d5429213668f2a8e36c85ee3c73984a2f6a79007e365d3e575e7ed"
[[package]]
name = "cranelift-frontend"
-version = "0.82.1"
+version = "0.83.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "3cc35e4251864b17515845ba47447bca88fec9ca1a4186b19fe42526e36140e8"
+checksum = "4cbcdbf7bed29e363568b778649b69dabc3d727256d5d25236096ef693757654"
dependencies = [
"cranelift-codegen",
"log",
[[package]]
name = "cranelift-jit"
-version = "0.82.1"
+version = "0.83.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "93c66d594ad3bfe4e58b1fbd8d17877a7c6564a5f2d6f78cbbf4b0182af1927f"
+checksum = "7c769d4e0d76f59c8b2a3bf0477d89ee149bb0731b53fbb245ee081d49063095"
dependencies = [
"anyhow",
"cranelift-codegen",
[[package]]
name = "cranelift-module"
-version = "0.82.1"
+version = "0.83.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "bf356697c40232aa09e1e3fb8a350ee894e849ccecc4eac56ff0570a4575c325"
+checksum = "0ab57d399a2401074bb0cc40b3031e420f3d66d46ec0cf21eeae53ac04bd73e2"
dependencies = [
"anyhow",
"cranelift-codegen",
[[package]]
name = "cranelift-native"
-version = "0.82.1"
+version = "0.83.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b882b2251c9845d509d92aebfdb6c8bb3b3b48e207ac951f21fbd20cfe7f90b3"
+checksum = "8f4cdf93552e5ceb2e3c042829ebb4de4378492705f769eadc6a7c6c5251624c"
dependencies = [
"cranelift-codegen",
"libc",
[[package]]
name = "cranelift-object"
-version = "0.82.1"
+version = "0.83.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "2d3f1a88e654e567d2591169239ed157ab290811a729a6468f53999c01001263"
+checksum = "cf8e65f4839c26e6237fc0744911d79b0a2ac5e76b4e4eebd14db2b8d849fd31"
dependencies = [
"anyhow",
"cranelift-codegen",
[dependencies]
# These have to be in sync with each other
-cranelift-codegen = { version = "0.82.1", features = ["unwind", "all-arch"] }
-cranelift-frontend = "0.82.1"
-cranelift-module = "0.82.1"
-cranelift-native = "0.82.1"
-cranelift-jit = { version = "0.82.1", optional = true }
-cranelift-object = "0.82.1"
+cranelift-codegen = { version = "0.83.0", features = ["unwind", "all-arch"] }
+cranelift-frontend = "0.83.0"
+cranelift-module = "0.83.0"
+cranelift-native = "0.83.0"
+cranelift-jit = { version = "0.83.0", optional = true }
+cranelift-object = "0.83.0"
target-lexicon = "0.12.0"
gimli = { version = "0.26.0", default-features = false, features = ["write"]}
object = { version = "0.27.0", default-features = false, features = ["std", "read_core", "write", "archive", "coff", "elf", "macho", "pe"] }
[[package]]
name = "compiler_builtins"
-version = "0.1.71"
+version = "0.1.72"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "163437f05ca8f29d7e9128ea728dedf5eb620e445fbca273641d3a3050305f23"
+checksum = "afdbb35d279238cf77f0c9e8d90ad50d6c7bff476ab342baafa29440f0f10bff"
dependencies = [
"rustc-std-workspace-core",
]
[[package]]
name = "libc"
-version = "0.2.121"
+version = "0.2.124"
source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "efaa7b300f3b5fe8eb6bf21ce3895e1751d9665086af2d64b42f19701015ff4f"
+checksum = "21a41fed9d98f27ab1c6d161da622a4fa35e8a54a8adc24bbf3ddd0ef70b0e50"
dependencies = [
"rustc-std-workspace-core",
]
name = "proc_macro"
version = "0.0.0"
dependencies = [
+ "core",
"std",
]
--- /dev/null
+// Copied from https://github.com/rust-lang/rust/blob/3fe3b89cd57229343eeca753fdd8c63d9b03c65c/src/test/ui/simd/intrinsic/float-minmax-pass.rs
+// run-pass
+// ignore-emscripten
+
+// Test that the simd_f{min,max} intrinsics produce the correct results.
+
+#![feature(repr_simd, platform_intrinsics)]
+#![allow(non_camel_case_types)]
+
+#[repr(simd)]
+#[derive(Copy, Clone, PartialEq, Debug)]
+struct f32x4(pub f32, pub f32, pub f32, pub f32);
+
+extern "platform-intrinsic" {
+ fn simd_fmin<T>(x: T, y: T) -> T;
+ fn simd_fmax<T>(x: T, y: T) -> T;
+}
+
+fn main() {
+ let x = f32x4(1.0, 2.0, 3.0, 4.0);
+ let y = f32x4(2.0, 1.0, 4.0, 3.0);
+
+ #[cfg(not(any(target_arch = "mips", target_arch = "mips64")))]
+ let nan = f32::NAN;
+ // MIPS hardware treats f32::NAN as SNAN. Clear the signaling bit.
+ // See https://github.com/rust-lang/rust/issues/52746.
+ #[cfg(any(target_arch = "mips", target_arch = "mips64"))]
+ let nan = f32::from_bits(f32::NAN.to_bits() - 1);
+
+ let n = f32x4(nan, nan, nan, nan);
+
+ unsafe {
+ let min0 = simd_fmin(x, y);
+ let min1 = simd_fmin(y, x);
+ assert_eq!(min0, min1);
+ let e = f32x4(1.0, 1.0, 3.0, 3.0);
+ assert_eq!(min0, e);
+ let minn = simd_fmin(x, n);
+ assert_eq!(minn, x);
+ let minn = simd_fmin(y, n);
+ assert_eq!(minn, y);
+
+ let max0 = simd_fmax(x, y);
+ let max1 = simd_fmax(y, x);
+ assert_eq!(max0, max1);
+ let e = f32x4(2.0, 2.0, 4.0, 4.0);
+ assert_eq!(max0, e);
+ let maxn = simd_fmax(x, n);
+ assert_eq!(maxn, x);
+ let maxn = simd_fmax(y, n);
+ assert_eq!(maxn, y);
+ }
+}
#[lang = "sized"]
pub trait Sized {}
+#[lang = "destruct"]
+pub trait Destruct {}
+
#[lang = "unsize"]
pub trait Unsize<T: ?Sized> {}
fn deref(&self) -> &Self::Target;
}
+#[repr(transparent)]
+#[rustc_layout_scalar_valid_range_start(1)]
+#[rustc_nonnull_optimization_guaranteed]
+pub struct NonNull<T: ?Sized>(pub *mut T);
+
+impl<T: ?Sized, U: ?Sized> CoerceUnsized<NonNull<U>> for NonNull<T> where T: Unsize<U> {}
+impl<T: ?Sized, U: ?Sized> DispatchFromDyn<NonNull<U>> for NonNull<T> where T: Unsize<U> {}
+
pub struct Unique<T: ?Sized> {
- pub pointer: *const T,
+ pub pointer: NonNull<T>,
pub _marker: PhantomData<T>,
}
impl<T: ?Sized, U: ?Sized> CoerceUnsized<Unique<U>> for Unique<T> where T: Unsize<U> {}
-
impl<T: ?Sized, U: ?Sized> DispatchFromDyn<Unique<U>> for Unique<T> where T: Unsize<U> {}
#[lang = "owned_box"]
#[lang = "box_free"]
unsafe fn box_free<T: ?Sized>(ptr: Unique<T>, alloc: ()) {
- libc::free(ptr.pointer as *mut u8);
+ libc::free(ptr.pointer.0 as *mut u8);
}
#[lang = "drop"]
#[allow(unreachable_code)] // FIXME false positive
fn main() {
take_unique(Unique {
- pointer: 0 as *const (),
+ pointer: unsafe { NonNull(1 as *mut ()) },
_marker: PhantomData,
});
take_f32(0.1);
assert!(intrinsics::needs_drop::<NoisyDrop>());
Unique {
- pointer: 0 as *const &str,
+ pointer: NonNull(1 as *mut &str),
_marker: PhantomData,
} as Unique<dyn SomeTrait>;
-#![feature(core_intrinsics, generators, generator_trait, is_sorted)]
+#![feature(core_intrinsics, generators, generator_trait, is_sorted, bench_black_box)]
#[cfg(target_arch = "x86_64")]
use std::arch::x86_64::*;
+use std::hint::black_box;
use std::io::Write;
use std::ops::Generator;
assert_eq!(houndred_f64 as i128, 100);
assert_eq!(1u128.rotate_left(2), 4);
+ assert_eq!(black_box(f32::NAN) as i128, 0);
+ assert_eq!(black_box(f32::NAN) as u128, 0);
+
// Test signed 128bit comparing
let max = usize::MAX as i128;
if 100i128 < 0i128 || 100i128 > max {
}
}
}
-diff --git a/crates/core_simd/tests/ops_macros.rs b/crates/core_simd/tests/ops_macros.rs
-index 31b7ee2..bd04b3c 100644
---- a/crates/core_simd/tests/ops_macros.rs
-+++ b/crates/core_simd/tests/ops_macros.rs
-@@ -567,6 +567,7 @@ macro_rules! impl_float_tests {
- });
- }
-
-+ /*
- fn horizontal_max<const LANES: usize>() {
- test_helpers::test_1(&|x| {
- let vmax = Vector::<LANES>::from_array(x).horizontal_max();
-@@ -590,6 +591,7 @@ macro_rules! impl_float_tests {
- Ok(())
- });
- }
-+ */
- }
-
- #[cfg(feature = "std")]
-@@ -604,6 +606,7 @@ macro_rules! impl_float_tests {
- )
- }
-
-+ /*
- fn mul_add<const LANES: usize>() {
- test_helpers::test_ternary_elementwise(
- &Vector::<LANES>::mul_add,
-@@ -611,6 +614,7 @@ macro_rules! impl_float_tests {
- &|_, _, _| true,
- )
- }
-+ */
- }
- }
- }
--
2.26.2.7.g19db9cfb68
[toolchain]
-channel = "nightly-2022-03-19"
+channel = "nightly-2022-04-21"
components = ["rust-src", "rustc-dev", "llvm-tools-preview"]
-#!/bin/bash
+#!/usr/bin/env bash
#![forbid(unsafe_code)]/* This line is ignored by bash
# This block is ignored by rustc
pushd $(dirname "$0")/../
-#!/bin/bash
+#!/usr/bin/env bash
set -e
./y.rs build --no-unstable-features
-#!/bin/bash
+#!/usr/bin/env bash
set -e
cd $(dirname "$0")/../
command -v rg >/dev/null 2>&1 || cargo install ripgrep
rm -r src/test/ui/{extern/,unsized-locals/,lto/,linkage*} || true
-for test in $(rg --files-with-matches "asm!|lto|// needs-asm-support|// needs-unwind" src/test/{ui,incremental}); do
+for test in $(rg --files-with-matches "lto|// needs-asm-support|// needs-unwind" src/test/{ui,incremental}); do
rm $test
done
# ================
# requires stack unwinding
-rm src/test/ui/backtrace.rs
-rm src/test/ui/process/multi-panic.rs
-rm src/test/ui/numbers-arithmetic/issue-8460.rs
rm src/test/incremental/change_crate_dep_kind.rs
rm src/test/incremental/issue-80691-bad-eval-cache.rs # -Cpanic=abort causes abort instead of exit(101)
-rm src/test/ui/panic-while-printing.rs
-rm src/test/ui/test-attrs/test-panic-while-printing.rs
-rm src/test/ui/test-attrs/test-type.rs
# requires compiling with -Cpanic=unwind
rm src/test/ui/test-attrs/test-fn-signature-verification-for-explicit-return-type.rs # "Cannot run dynamic test fn out-of-process"
# giving different but possibly correct results
# =============================================
-rm src/test/ui/numbers-arithmetic/saturating-float-casts.rs # intrinsic gives different but valid result
-rm src/test/ui/simd/intrinsic/float-minmax-pass.rs # same
rm src/test/ui/mir/mir_misc_casts.rs # depends on deduplication of constants
rm src/test/ui/mir/mir_raw_fat_ptr.rs # same
rm src/test/ui/consts/issue-33537.rs # same
rm src/test/ui/simd/intrinsic/generic-reduction-pass.rs # simd_reduce_add_unordered doesn't accept an accumulator for integer vectors
+rm src/test/ui/rfc-2091-track-caller/intrinsic-wrapper.rs # wrong result from `Location::caller()`
+
# bugs in the test suite
# ======================
-rm src/test/ui/unsafe/union.rs # has UB caught by cg_clif. see rust-lang/rust#95075
+rm src/test/ui/backtrace.rs # TODO warning
+rm src/test/ui/empty_global_asm.rs # TODO add needs-asm-support
+rm src/test/ui/simple_global_asm.rs # TODO add needs-asm-support
+rm src/test/ui/test-attrs/test-type.rs # TODO panic message on stderr. correct stdout
echo "[TEST] rustc test suite"
RUST_TEST_NOCAPTURE=1 COMPILETEST_FORCE_STAGE0=1 ./x.py test --stage 0 src/test/{codegen-units,run-make,run-pass-valgrind,ui,incremental}
$MY_RUSTC example/track-caller-attribute.rs --crate-type bin -Cpanic=abort --target "$TARGET_TRIPLE"
$RUN_WRAPPER ./target/out/track-caller-attribute
+ echo "[AOT] float-minmax-pass"
+ $MY_RUSTC example/float-minmax-pass.rs --crate-type bin -Cpanic=abort --target "$TARGET_TRIPLE"
+ $RUN_WRAPPER ./target/out/float-minmax-pass
+
echo "[AOT] mod_bench"
$MY_RUSTC example/mod_bench.rs --crate-type bin --target "$TARGET_TRIPLE"
$RUN_WRAPPER ./target/out/mod_bench
if cplace.layout().ty.is_box() {
cplace = cplace
.place_field(fx, Field::new(0)) // Box<T> -> Unique<T>
- .place_field(fx, Field::new(0)) // Unique<T> -> *const T
+ .place_field(fx, Field::new(0)) // Unique<T> -> NonNull<T>
+ .place_field(fx, Field::new(0)) // NonNull<T> -> *mut T
.place_deref(fx);
} else {
cplace = cplace.place_deref(fx);
fx.bcx.ins().fcvt_from_uint(to_ty, from)
}
} else if from_ty.is_float() && to_ty.is_int() {
- if to_ty == types::I128 {
+ let val = if to_ty == types::I128 {
// _____sssf___
// __fix sfti: f32 -> i128
// __fix dfti: f64 -> i128
let to_rust_ty = if to_signed { fx.tcx.types.i128 } else { fx.tcx.types.u128 };
- return fx
- .easy_call(&name, &[CValue::by_val(from, fx.layout_of(from_rust_ty))], to_rust_ty)
- .load_scalar(fx);
- }
-
- // float -> int-like
- if to_ty == types::I8 || to_ty == types::I16 {
+ fx.easy_call(&name, &[CValue::by_val(from, fx.layout_of(from_rust_ty))], to_rust_ty)
+ .load_scalar(fx)
+ } else if to_ty == types::I8 || to_ty == types::I16 {
// FIXME implement fcvt_to_*int_sat.i8/i16
let val = if to_signed {
fx.bcx.ins().fcvt_to_sint_sat(types::I32, from)
fx.bcx.ins().fcvt_to_sint_sat(to_ty, from)
} else {
fx.bcx.ins().fcvt_to_uint_sat(to_ty, from)
+ };
+
+ if let Some(false) = fx.tcx.sess.opts.debugging_opts.saturating_float_casts {
+ return val;
+ }
+
+ let is_not_nan = fx.bcx.ins().fcmp(FloatCC::Equal, from, from);
+ if to_ty == types::I128 {
+ // FIXME(bytecodealliance/wasmtime#3963): select.i128 on fcmp eq miscompiles
+ let (lsb, msb) = fx.bcx.ins().isplit(val);
+ let zero = fx.bcx.ins().iconst(types::I64, 0);
+ let lsb = fx.bcx.ins().select(is_not_nan, lsb, zero);
+ let msb = fx.bcx.ins().select(is_not_nan, msb, zero);
+ fx.bcx.ins().iconcat(lsb, msb)
+ } else {
+ let zero = fx.bcx.ins().iconst(to_ty, 0);
+ fx.bcx.ins().select(is_not_nan, val, zero)
}
} else if from_ty.is_float() && to_ty.is_float() {
// float -> float
/// Can be set using `-Cllvm-args=display_cg_time=...`.
pub display_cg_time: bool,
- /// The register allocator to use.
- ///
- /// Defaults to the value of `CG_CLIF_REGALLOC` or `backtracking` otherwise. Can be set using
- /// `-Cllvm-args=regalloc=...`.
- pub regalloc: String,
-
/// Enable the Cranelift ir verifier for all compilation passes. If not set it will only run
/// once before passing the clif ir to Cranelift for compilation.
///
args.split(' ').map(|arg| arg.to_string()).collect()
},
display_cg_time: bool_env_var("CG_CLIF_DISPLAY_CG_TIME"),
- regalloc: std::env::var("CG_CLIF_REGALLOC")
- .unwrap_or_else(|_| "backtracking".to_string()),
enable_verifier: cfg!(debug_assertions) || bool_env_var("CG_CLIF_ENABLE_VERIFIER"),
disable_incr_cache: bool_env_var("CG_CLIF_DISABLE_INCR_CACHE"),
}
match name {
"mode" => config.codegen_mode = value.parse()?,
"display_cg_time" => config.display_cg_time = parse_bool(name, value)?,
- "regalloc" => config.regalloc = value.to_string(),
"enable_verifier" => config.enable_verifier = parse_bool(name, value)?,
"disable_incr_cache" => config.disable_incr_cache = parse_bool(name, value)?,
_ => return Err(format!("Unknown option `{}`", name)),
let relative_discr = if niche_start == 0 {
tag
} else {
- // FIXME handle niche_start > i64::MAX
- fx.bcx.ins().iadd_imm(tag, -i64::try_from(niche_start).unwrap())
+ let niche_start = match fx.bcx.func.dfg.value_type(tag) {
+ types::I128 => {
+ let lsb = fx.bcx.ins().iconst(types::I64, niche_start as u64 as i64);
+ let msb =
+ fx.bcx.ins().iconst(types::I64, (niche_start >> 64) as u64 as i64);
+ fx.bcx.ins().iconcat(lsb, msb)
+ }
+ ty => fx.bcx.ins().iconst(ty, niche_start as i64),
+ };
+ fx.bcx.ins().isub(tag, niche_start)
};
let relative_max = niche_variants.end().as_u32() - niche_variants.start().as_u32();
let is_niche = {
ret.write_cvalue(fx, old);
};
- // In Rust floating point min and max don't propagate NaN. In Cranelift they do however.
- // For this reason it is necessary to use `a.is_nan() ? b : (a >= b ? b : a)` for `minnumf*`
- // and `a.is_nan() ? b : (a <= b ? b : a)` for `maxnumf*`. NaN checks are done by comparing
- // a float against itself. Only in case of NaN is it not equal to itself.
minnumf32, (v a, v b) {
- let a_is_nan = fx.bcx.ins().fcmp(FloatCC::NotEqual, a, a);
- let a_ge_b = fx.bcx.ins().fcmp(FloatCC::GreaterThanOrEqual, a, b);
- let temp = fx.bcx.ins().select(a_ge_b, b, a);
- let val = fx.bcx.ins().select(a_is_nan, b, temp);
+ let val = crate::num::codegen_float_min(fx, a, b);
let val = CValue::by_val(val, fx.layout_of(fx.tcx.types.f32));
ret.write_cvalue(fx, val);
};
minnumf64, (v a, v b) {
- let a_is_nan = fx.bcx.ins().fcmp(FloatCC::NotEqual, a, a);
- let a_ge_b = fx.bcx.ins().fcmp(FloatCC::GreaterThanOrEqual, a, b);
- let temp = fx.bcx.ins().select(a_ge_b, b, a);
- let val = fx.bcx.ins().select(a_is_nan, b, temp);
+ let val = crate::num::codegen_float_min(fx, a, b);
let val = CValue::by_val(val, fx.layout_of(fx.tcx.types.f64));
ret.write_cvalue(fx, val);
};
maxnumf32, (v a, v b) {
- let a_is_nan = fx.bcx.ins().fcmp(FloatCC::NotEqual, a, a);
- let a_le_b = fx.bcx.ins().fcmp(FloatCC::LessThanOrEqual, a, b);
- let temp = fx.bcx.ins().select(a_le_b, b, a);
- let val = fx.bcx.ins().select(a_is_nan, b, temp);
+ let val = crate::num::codegen_float_max(fx, a, b);
let val = CValue::by_val(val, fx.layout_of(fx.tcx.types.f32));
ret.write_cvalue(fx, val);
};
maxnumf64, (v a, v b) {
- let a_is_nan = fx.bcx.ins().fcmp(FloatCC::NotEqual, a, a);
- let a_le_b = fx.bcx.ins().fcmp(FloatCC::LessThanOrEqual, a, b);
- let temp = fx.bcx.ins().select(a_le_b, b, a);
- let val = fx.bcx.ins().select(a_is_nan, b, temp);
+ let val = crate::num::codegen_float_max(fx, a, b);
let val = CValue::by_val(val, fx.layout_of(fx.tcx.types.f64));
ret.write_cvalue(fx, val);
};
}
assert_eq!(a.layout(), b.layout());
assert_eq!(a.layout(), c.layout());
- let layout = a.layout();
+ assert_eq!(a.layout(), ret.layout());
- let (lane_count, _lane_ty) = layout.ty.simd_size_and_type(fx.tcx);
- let (ret_lane_count, ret_lane_ty) = ret.layout().ty.simd_size_and_type(fx.tcx);
- assert_eq!(lane_count, ret_lane_count);
- let ret_lane_layout = fx.layout_of(ret_lane_ty);
+ let layout = a.layout();
+ let (lane_count, lane_ty) = layout.ty.simd_size_and_type(fx.tcx);
for lane in 0..lane_count {
- let a_lane = a.value_lane(fx, lane).load_scalar(fx);
- let b_lane = b.value_lane(fx, lane).load_scalar(fx);
- let c_lane = c.value_lane(fx, lane).load_scalar(fx);
+ let a_lane = a.value_lane(fx, lane);
+ let b_lane = b.value_lane(fx, lane);
+ let c_lane = c.value_lane(fx, lane);
- let mul_lane = fx.bcx.ins().fmul(a_lane, b_lane);
- let res_lane = CValue::by_val(fx.bcx.ins().fadd(mul_lane, c_lane), ret_lane_layout);
+ let res_lane = match lane_ty.kind() {
+ ty::Float(FloatTy::F32) => fx.easy_call("fmaf", &[a_lane, b_lane, c_lane], lane_ty),
+ ty::Float(FloatTy::F64) => fx.easy_call("fma", &[a_lane, b_lane, c_lane], lane_ty),
+ _ => unreachable!(),
+ };
ret.place_lane(fx, lane).write_cvalue(fx, res_lane);
}
_ => unreachable!("{:?}", lane_ty),
}
match intrinsic {
- sym::simd_fmin => fx.bcx.ins().fmin(x_lane, y_lane),
- sym::simd_fmax => fx.bcx.ins().fmax(x_lane, y_lane),
+ sym::simd_fmin => crate::num::codegen_float_min(fx, x_lane, y_lane),
+ sym::simd_fmax => crate::num::codegen_float_max(fx, x_lane, y_lane),
_ => unreachable!(),
}
});
let lt = match ty.kind() {
ty::Int(_) => fx.bcx.ins().icmp(IntCC::SignedLessThan, a, b),
ty::Uint(_) => fx.bcx.ins().icmp(IntCC::UnsignedLessThan, a, b),
- ty::Float(_) => fx.bcx.ins().fcmp(FloatCC::LessThan, a, b),
+ ty::Float(_) => return crate::num::codegen_float_min(fx, a, b),
_ => unreachable!(),
};
fx.bcx.ins().select(lt, a, b)
let gt = match ty.kind() {
ty::Int(_) => fx.bcx.ins().icmp(IntCC::SignedGreaterThan, a, b),
ty::Uint(_) => fx.bcx.ins().icmp(IntCC::UnsignedGreaterThan, a, b),
- ty::Float(_) => fx.bcx.ins().fcmp(FloatCC::GreaterThan, a, b),
+ ty::Float(_) => return crate::num::codegen_float_max(fx, a, b),
_ => unreachable!(),
};
fx.bcx.ins().select(gt, a, b)
flags_builder.set("enable_llvm_abi_extensions", "true").unwrap();
- flags_builder.set("regalloc", &backend_config.regalloc).unwrap();
-
use rustc_session::config::OptLevel;
match sess.opts.optimize {
OptLevel::No => {
CValue::by_val(fx.bcx.ins().bint(types::I8, res), fx.layout_of(fx.tcx.types.bool))
}
}
+
+// In Rust floating point min and max don't propagate NaN. In Cranelift they do however.
+// For this reason it is necessary to use `a.is_nan() ? b : (a >= b ? b : a)` for `minnumf*`
+// and `a.is_nan() ? b : (a <= b ? b : a)` for `maxnumf*`. NaN checks are done by comparing
+// a float against itself. Only in case of NaN is it not equal to itself.
+pub(crate) fn codegen_float_min(fx: &mut FunctionCx<'_, '_, '_>, a: Value, b: Value) -> Value {
+ let a_is_nan = fx.bcx.ins().fcmp(FloatCC::NotEqual, a, a);
+ let a_ge_b = fx.bcx.ins().fcmp(FloatCC::GreaterThanOrEqual, a, b);
+ let temp = fx.bcx.ins().select(a_ge_b, b, a);
+ fx.bcx.ins().select(a_is_nan, b, temp)
+}
+
+pub(crate) fn codegen_float_max(fx: &mut FunctionCx<'_, '_, '_>, a: Value, b: Value) -> Value {
+ let a_is_nan = fx.bcx.ins().fcmp(FloatCC::NotEqual, a, a);
+ let a_le_b = fx.bcx.ins().fcmp(FloatCC::LessThanOrEqual, a, b);
+ let temp = fx.bcx.ins().select(a_le_b, b, a);
+ fx.bcx.ins().select(a_is_nan, b, temp)
+}
}
impl ExtraBackendMethods for GccCodegenBackend {
- fn new_metadata<'tcx>(&self, _tcx: TyCtxt<'tcx>, _mod_name: &str) -> Self::Module {
- GccContext {
+ fn codegen_allocator<'tcx>(&self, tcx: TyCtxt<'tcx>, module_name: &str, kind: AllocatorKind, has_alloc_error_handler: bool) -> Self::Module {
+ let mut mods = GccContext {
context: Context::default(),
- }
- }
-
- fn codegen_allocator<'tcx>(&self, tcx: TyCtxt<'tcx>, mods: &mut Self::Module, module_name: &str, kind: AllocatorKind, has_alloc_error_handler: bool) {
- unsafe { allocator::codegen(tcx, mods, module_name, kind, has_alloc_error_handler) }
+ };
+ unsafe { allocator::codegen(tcx, &mut mods, module_name, kind, has_alloc_error_handler); }
+ mods
}
fn compile_codegen_unit<'tcx>(&self, tcx: TyCtxt<'tcx>, cgu_name: Symbol) -> (ModuleCodegen<Self::Module>, u64) {
unimplemented!();
}
};
- Ok(LtoModuleCodegen::Fat { module: Some(module), _serialized_bitcode: vec![] })
+ Ok(LtoModuleCodegen::Fat { module, _serialized_bitcode: vec![] })
}
fn run_thin_lto(_cgcx: &CodegenContext<Self>, _modules: Vec<(String, Self::ThinBuffer)>, _cached_modules: Vec<(SerializedModule<Self::ModuleBuffer>, WorkProduct)>) -> Result<(Vec<LtoModuleCodegen<Self>>, Vec<WorkProduct>), FatalError> {
Ok(())
}
- unsafe fn optimize_thin(_cgcx: &CodegenContext<Self>, _thin: &mut ThinModule<Self>) -> Result<ModuleCodegen<Self::Module>, FatalError> {
+ fn optimize_fat(_cgcx: &CodegenContext<Self>, _module: &mut ModuleCodegen<Self::Module>) -> Result<(), FatalError> {
+ // TODO(antoyo)
+ Ok(())
+ }
+
+ unsafe fn optimize_thin(_cgcx: &CodegenContext<Self>, _thin: ThinModule<Self>) -> Result<ModuleCodegen<Self::Module>, FatalError> {
unimplemented!();
}
unimplemented!();
}
- fn run_lto_pass_manager(_cgcx: &CodegenContext<Self>, _module: &ModuleCodegen<Self::Module>, _config: &ModuleConfig, _thin: bool) -> Result<(), FatalError> {
- // TODO(antoyo)
- Ok(())
- }
-
fn run_link(cgcx: &CodegenContext<Self>, diag_handler: &Handler, modules: Vec<ModuleCodegen<Self::Module>>) -> Result<ModuleCodegen<Self::Module>, FatalError> {
back::write::link(cgcx, diag_handler, modules)
}
use crate::{llvm_util, LlvmCodegenBackend, ModuleLlvm};
use rustc_codegen_ssa::back::lto::{LtoModuleCodegen, SerializedModule, ThinModule, ThinShared};
use rustc_codegen_ssa::back::symbol_export;
-use rustc_codegen_ssa::back::write::{
- CodegenContext, FatLTOInput, ModuleConfig, TargetMachineFactoryConfig,
-};
+use rustc_codegen_ssa::back::write::{CodegenContext, FatLTOInput, TargetMachineFactoryConfig};
use rustc_codegen_ssa::traits::*;
use rustc_codegen_ssa::{looks_like_rust_object_file, ModuleCodegen, ModuleKind};
use rustc_data_structures::fx::FxHashMap;
use rustc_hir::def_id::LOCAL_CRATE;
use rustc_middle::bug;
use rustc_middle::dep_graph::WorkProduct;
-use rustc_middle::middle::exported_symbols::SymbolExportLevel;
+use rustc_middle::middle::exported_symbols::{SymbolExportInfo, SymbolExportLevel};
use rustc_session::cgu_reuse_tracker::CguReuse;
use rustc_session::config::{self, CrateType, Lto};
use tracing::{debug, info};
Lto::No => panic!("didn't request LTO but we're doing LTO"),
};
- let symbol_filter = &|&(ref name, level): &(String, SymbolExportLevel)| {
- if level.is_below_threshold(export_threshold) {
+ let symbol_filter = &|&(ref name, info): &(String, SymbolExportInfo)| {
+ if info.level.is_below_threshold(export_threshold) || info.used {
Some(CString::new(name.as_str()).unwrap())
} else {
None
}
}
- Ok(LtoModuleCodegen::Fat { module: Some(module), _serialized_bitcode: serialized_bitcode })
+ Ok(LtoModuleCodegen::Fat { module, _serialized_bitcode: serialized_bitcode })
}
crate struct Linker<'a>(&'a mut llvm::Linker<'a>);
pub(crate) fn run_pass_manager(
cgcx: &CodegenContext<LlvmCodegenBackend>,
diag_handler: &Handler,
- module: &ModuleCodegen<ModuleLlvm>,
- config: &ModuleConfig,
+ module: &mut ModuleCodegen<ModuleLlvm>,
thin: bool,
) -> Result<(), FatalError> {
let _timer = cgcx.prof.extra_verbose_generic_activity("LLVM_lto_optimize", &*module.name);
+ let config = cgcx.config(module.kind);
// Now we have one massive module inside of llmod. Time to run the
// LTO-specific optimization passes that LLVM provides.
if thin {
llvm::LLVMRustPassManagerBuilderPopulateThinLTOPassManager(b, pm);
} else {
- llvm::LLVMPassManagerBuilderPopulateLTOPassManager(
+ llvm::LLVMRustPassManagerBuilderPopulateLTOPassManager(
b, pm, /* Internalize = */ False, /* RunInliner = */ True,
);
}
}
pub unsafe fn optimize_thin_module(
- thin_module: &mut ThinModule<LlvmCodegenBackend>,
+ thin_module: ThinModule<LlvmCodegenBackend>,
cgcx: &CodegenContext<LlvmCodegenBackend>,
) -> Result<ModuleCodegen<ModuleLlvm>, FatalError> {
let diag_handler = cgcx.create_diag_handler();
// that LLVM Context and Module.
let llcx = llvm::LLVMRustContextCreate(cgcx.fewer_names);
let llmod_raw = parse_module(llcx, module_name, thin_module.data(), &diag_handler)? as *const _;
- let module = ModuleCodegen {
+ let mut module = ModuleCodegen {
module_llvm: ModuleLlvm { llmod_raw, llcx, tm },
name: thin_module.name().to_string(),
kind: ModuleKind::Regular,
// little differently.
{
info!("running thin lto passes over {}", module.name);
- let config = cgcx.config(module.kind);
- run_pass_manager(cgcx, &diag_handler, &module, config, true)?;
+ run_pass_manager(cgcx, &diag_handler, &mut module, true)?;
save_temp_bitcode(cgcx, &module, "thin-lto-after-pm");
}
}
let module_name = module.name.clone();
let module_name = Some(&module_name[..]);
+ if let Some(false) = config.new_llvm_pass_manager && llvm_util::get_version() >= (15, 0, 0) {
+ diag_handler.warn(
+ "ignoring `-Z new-llvm-pass-manager=no`, which is no longer supported with LLVM 15",
+ );
+ }
+
if config.emit_no_opt_bc {
let out = cgcx.output_filenames.temp_path_ext("no-opt.bc", module_name);
let out = path_to_c_string(&out);
extra_passes.as_ptr(),
extra_passes.len() as size_t,
);
- llvm::LLVMPassManagerBuilderPopulateFunctionPassManager(b, fpm);
- llvm::LLVMPassManagerBuilderPopulateModulePassManager(b, mpm);
+ llvm::LLVMRustPassManagerBuilderPopulateFunctionPassManager(b, fpm);
+ llvm::LLVMRustPassManagerBuilderPopulateModulePassManager(b, mpm);
});
have_name_anon_globals_pass = have_name_anon_globals_pass || prepare_for_thin_lto;
// Create the PassManagerBuilder for LLVM. We configure it with
// reasonable defaults and prepare it to actually populate the pass
// manager.
- let builder = llvm::LLVMPassManagerBuilderCreate();
+ let builder = llvm::LLVMRustPassManagerBuilderCreate();
let opt_size = config.opt_size.map_or(llvm::CodeGenOptSizeNone, |x| to_llvm_opt_settings(x).1);
let inline_threshold = config.inline_threshold;
let pgo_gen_path = get_pgo_gen_path(config);
pgo_gen_path.as_ref().map_or(ptr::null(), |s| s.as_ptr()),
pgo_use_path.as_ref().map_or(ptr::null(), |s| s.as_ptr()),
pgo_sample_use_path.as_ref().map_or(ptr::null(), |s| s.as_ptr()),
+ opt_size as c_int,
);
- llvm::LLVMPassManagerBuilderSetSizeLevel(builder, opt_size as u32);
-
- if opt_size != llvm::CodeGenOptSizeNone {
- llvm::LLVMPassManagerBuilderSetDisableUnrollLoops(builder, 1);
- }
-
llvm::LLVMRustAddBuilderLibraryInfo(builder, llmod, config.no_builtins);
// Here we match what clang does (kinda). For O0 we only inline
// thresholds copied from clang.
match (opt_level, opt_size, inline_threshold) {
(.., Some(t)) => {
- llvm::LLVMPassManagerBuilderUseInlinerWithThreshold(builder, t);
+ llvm::LLVMRustPassManagerBuilderUseInlinerWithThreshold(builder, t);
}
(llvm::CodeGenOptLevel::Aggressive, ..) => {
- llvm::LLVMPassManagerBuilderUseInlinerWithThreshold(builder, 275);
+ llvm::LLVMRustPassManagerBuilderUseInlinerWithThreshold(builder, 275);
}
(_, llvm::CodeGenOptSizeDefault, _) => {
- llvm::LLVMPassManagerBuilderUseInlinerWithThreshold(builder, 75);
+ llvm::LLVMRustPassManagerBuilderUseInlinerWithThreshold(builder, 75);
}
(_, llvm::CodeGenOptSizeAggressive, _) => {
- llvm::LLVMPassManagerBuilderUseInlinerWithThreshold(builder, 25);
+ llvm::LLVMRustPassManagerBuilderUseInlinerWithThreshold(builder, 25);
}
(llvm::CodeGenOptLevel::None, ..) => {
llvm::LLVMRustAddAlwaysInlinePass(builder, config.emit_lifetime_markers);
llvm::LLVMRustAddAlwaysInlinePass(builder, config.emit_lifetime_markers);
}
(llvm::CodeGenOptLevel::Default, ..) => {
- llvm::LLVMPassManagerBuilderUseInlinerWithThreshold(builder, 225);
+ llvm::LLVMRustPassManagerBuilderUseInlinerWithThreshold(builder, 225);
}
}
f(builder);
- llvm::LLVMPassManagerBuilderDispose(builder);
+ llvm::LLVMRustPassManagerBuilderDispose(builder);
}
// Create a `__imp_<symbol> = &symbol` global for every public static `symbol`.
cx: &CodegenCx<'ll, 'tcx>,
instance: Instance<'tcx>,
mir: &Body<'tcx>,
- fn_dbg_scope: &'ll DIScope,
debug_context: &mut FunctionDebugContext<&'ll DIScope, &'ll DILocation>,
) {
// Find all scopes with variables defined in them.
// Nothing to emit, of course.
None
};
-
+ let mut instantiated = BitSet::new_empty(mir.source_scopes.len());
// Instantiate all scopes.
for idx in 0..mir.source_scopes.len() {
let scope = SourceScope::new(idx);
- make_mir_scope(cx, instance, mir, fn_dbg_scope, &variables, debug_context, scope);
+ make_mir_scope(cx, instance, mir, &variables, debug_context, &mut instantiated, scope);
}
+ assert!(instantiated.count() == mir.source_scopes.len());
}
fn make_mir_scope<'ll, 'tcx>(
cx: &CodegenCx<'ll, 'tcx>,
instance: Instance<'tcx>,
mir: &Body<'tcx>,
- fn_dbg_scope: &'ll DIScope,
variables: &Option<BitSet<SourceScope>>,
debug_context: &mut FunctionDebugContext<&'ll DIScope, &'ll DILocation>,
+ instantiated: &mut BitSet<SourceScope>,
scope: SourceScope,
) {
- if debug_context.scopes[scope].dbg_scope.is_some() {
+ if instantiated.contains(scope) {
return;
}
let scope_data = &mir.source_scopes[scope];
let parent_scope = if let Some(parent) = scope_data.parent_scope {
- make_mir_scope(cx, instance, mir, fn_dbg_scope, variables, debug_context, parent);
+ make_mir_scope(cx, instance, mir, variables, debug_context, instantiated, parent);
debug_context.scopes[parent]
} else {
// The root is the function itself.
let loc = cx.lookup_debug_loc(mir.span.lo());
debug_context.scopes[scope] = DebugScope {
- dbg_scope: Some(fn_dbg_scope),
- inlined_at: None,
file_start_pos: loc.file.start_pos,
file_end_pos: loc.file.end_pos,
+ ..debug_context.scopes[scope]
};
+ instantiated.insert(scope);
return;
};
// Do not create a DIScope if there are no variables defined in this
// MIR `SourceScope`, and it's not `inlined`, to avoid debuginfo bloat.
debug_context.scopes[scope] = parent_scope;
+ instantiated.insert(scope);
return;
}
None => unsafe {
llvm::LLVMRustDIBuilderCreateLexicalBlock(
DIB(cx),
- parent_scope.dbg_scope.unwrap(),
+ parent_scope.dbg_scope,
file_metadata,
loc.line,
loc.col,
});
debug_context.scopes[scope] = DebugScope {
- dbg_scope: Some(dbg_scope),
+ dbg_scope,
inlined_at: inlined_at.or(parent_scope.inlined_at),
file_start_pos: loc.file.start_pos,
file_end_pos: loc.file.end_pos,
};
+ instantiated.insert(scope);
}
let DINodeCreationResult { di_node, already_stored_in_typemap } = match *t.kind() {
ty::Never | ty::Bool | ty::Char | ty::Int(_) | ty::Uint(_) | ty::Float(_) => {
- DINodeCreationResult::new(build_basic_type_di_node(cx, t), false)
- }
- ty::Tuple(elements) if elements.is_empty() => {
- DINodeCreationResult::new(build_basic_type_di_node(cx, t), false)
+ build_basic_type_di_node(cx, t)
}
+ ty::Tuple(elements) if elements.is_empty() => build_basic_type_di_node(cx, t),
ty::Array(..) => build_fixed_size_array_di_node(cx, unique_type_id, t),
ty::Slice(_) | ty::Str => build_slice_type_di_node(cx, t, unique_type_id),
ty::Dynamic(..) => build_dyn_type_di_node(cx, t, unique_type_id),
}
}
-fn build_basic_type_di_node<'ll, 'tcx>(cx: &CodegenCx<'ll, 'tcx>, t: Ty<'tcx>) -> &'ll DIType {
+fn build_basic_type_di_node<'ll, 'tcx>(
+ cx: &CodegenCx<'ll, 'tcx>,
+ t: Ty<'tcx>,
+) -> DINodeCreationResult<'ll> {
debug!("build_basic_type_di_node: {:?}", t);
// When targeting MSVC, emit MSVC style type names for compatibility with
let (name, encoding) = match t.kind() {
ty::Never => ("!", DW_ATE_unsigned),
- ty::Tuple(elements) if elements.is_empty() => ("()", DW_ATE_unsigned),
+ ty::Tuple(elements) if elements.is_empty() => {
+ if cpp_like_debuginfo {
+ return build_tuple_type_di_node(cx, UniqueTypeId::for_ty(cx.tcx, t));
+ } else {
+ ("()", DW_ATE_unsigned)
+ }
+ }
ty::Bool => ("bool", DW_ATE_boolean),
ty::Char => ("char", DW_ATE_UTF),
ty::Int(int_ty) if cpp_like_debuginfo => (int_ty.msvc_basic_name(), DW_ATE_signed),
};
if !cpp_like_debuginfo {
- return ty_di_node;
+ return DINodeCreationResult::new(ty_di_node, false);
}
let typedef_name = match t.kind() {
ty::Int(int_ty) => int_ty.name_str(),
ty::Uint(uint_ty) => uint_ty.name_str(),
ty::Float(float_ty) => float_ty.name_str(),
- _ => return ty_di_node,
+ _ => return DINodeCreationResult::new(ty_di_node, false),
};
let typedef_di_node = unsafe {
)
};
- typedef_di_node
+ DINodeCreationResult::new(typedef_di_node, false)
}
fn build_foreign_type_di_node<'ll, 'tcx>(
}
// Initialize fn debug context (including scopes).
- // FIXME(eddyb) figure out a way to not need `Option` for `dbg_scope`.
let empty_scope = DebugScope {
- dbg_scope: None,
+ dbg_scope: self.dbg_scope_fn(instance, fn_abi, Some(llfn)),
inlined_at: None,
file_start_pos: BytePos(0),
file_end_pos: BytePos(0),
FunctionDebugContext { scopes: IndexVec::from_elem(empty_scope, &mir.source_scopes) };
// Fill in all the scopes, with the information from the MIR body.
- compute_mir_scopes(
- self,
- instance,
- mir,
- self.dbg_scope_fn(instance, fn_abi, Some(llfn)),
- &mut fn_debug_context,
- );
+ compute_mir_scopes(self, instance, mir, &mut fn_debug_context);
Some(fn_debug_context)
}
}
pub fn get_namespace_for_item<'ll>(cx: &CodegenCx<'ll, '_>, def_id: DefId) -> &'ll DIScope {
- item_namespace(cx, cx.tcx.parent(def_id).expect("get_namespace_for_item: missing parent?"))
+ item_namespace(cx, cx.tcx.parent(def_id))
}
#[derive(Debug, PartialEq, Eq)]
}
impl ExtraBackendMethods for LlvmCodegenBackend {
- fn new_metadata(&self, tcx: TyCtxt<'_>, mod_name: &str) -> ModuleLlvm {
- ModuleLlvm::new_metadata(tcx, mod_name)
- }
-
fn codegen_allocator<'tcx>(
&self,
tcx: TyCtxt<'tcx>,
- module_llvm: &mut ModuleLlvm,
module_name: &str,
kind: AllocatorKind,
has_alloc_error_handler: bool,
- ) {
- unsafe { allocator::codegen(tcx, module_llvm, module_name, kind, has_alloc_error_handler) }
+ ) -> ModuleLlvm {
+ let mut module_llvm = ModuleLlvm::new_metadata(tcx, module_name);
+ unsafe {
+ allocator::codegen(tcx, &mut module_llvm, module_name, kind, has_alloc_error_handler);
+ }
+ module_llvm
}
fn compile_codegen_unit(
&self,
) -> Result<(), FatalError> {
back::write::optimize(cgcx, diag_handler, module, config)
}
+ fn optimize_fat(
+ cgcx: &CodegenContext<Self>,
+ module: &mut ModuleCodegen<Self::Module>,
+ ) -> Result<(), FatalError> {
+ let diag_handler = cgcx.create_diag_handler();
+ back::lto::run_pass_manager(cgcx, &diag_handler, module, false)
+ }
unsafe fn optimize_thin(
cgcx: &CodegenContext<Self>,
- thin: &mut ThinModule<Self>,
+ thin: ThinModule<Self>,
) -> Result<ModuleCodegen<Self::Module>, FatalError> {
back::lto::optimize_thin_module(thin, cgcx)
}
fn serialize_module(module: ModuleCodegen<Self::Module>) -> (String, Self::ModuleBuffer) {
(module.name, back::lto::ModuleBuffer::new(module.module_llvm.llmod()))
}
- fn run_lto_pass_manager(
- cgcx: &CodegenContext<Self>,
- module: &ModuleCodegen<Self::Module>,
- config: &ModuleConfig,
- thin: bool,
- ) -> Result<(), FatalError> {
- let diag_handler = cgcx.create_diag_handler();
- back::lto::run_pass_manager(cgcx, &diag_handler, module, config, thin)
- }
}
unsafe impl Send for LlvmCodegenBackend {} // Llvm is on a per-thread basis
pub fn LLVMAddAnalysisPasses<'a>(T: &'a TargetMachine, PM: &PassManager<'a>);
- pub fn LLVMPassManagerBuilderCreate() -> &'static mut PassManagerBuilder;
- pub fn LLVMPassManagerBuilderDispose(PMB: &'static mut PassManagerBuilder);
- pub fn LLVMPassManagerBuilderSetSizeLevel(PMB: &PassManagerBuilder, Value: Bool);
- pub fn LLVMPassManagerBuilderSetDisableUnrollLoops(PMB: &PassManagerBuilder, Value: Bool);
- pub fn LLVMPassManagerBuilderUseInlinerWithThreshold(
+ pub fn LLVMRustPassManagerBuilderCreate() -> &'static mut PassManagerBuilder;
+ pub fn LLVMRustPassManagerBuilderDispose(PMB: &'static mut PassManagerBuilder);
+ pub fn LLVMRustPassManagerBuilderUseInlinerWithThreshold(
PMB: &PassManagerBuilder,
threshold: c_uint,
);
- pub fn LLVMPassManagerBuilderPopulateModulePassManager(
+ pub fn LLVMRustPassManagerBuilderPopulateModulePassManager(
PMB: &PassManagerBuilder,
PM: &PassManager<'_>,
);
- pub fn LLVMPassManagerBuilderPopulateFunctionPassManager(
+ pub fn LLVMRustPassManagerBuilderPopulateFunctionPassManager(
PMB: &PassManagerBuilder,
PM: &PassManager<'_>,
);
- pub fn LLVMPassManagerBuilderPopulateLTOPassManager(
+ pub fn LLVMRustPassManagerBuilderPopulateLTOPassManager(
PMB: &PassManagerBuilder,
PM: &PassManager<'_>,
Internalize: Bool,
PGOGenPath: *const c_char,
PGOUsePath: *const c_char,
PGOSampleUsePath: *const c_char,
+ SizeLevel: c_int,
);
pub fn LLVMRustAddLibraryInfo<'a>(
PM: &PassManager<'a>,
// The new pass manager is enabled by default for LLVM >= 13.
// This matches Clang, which also enables it since Clang 13.
+ // Since LLVM 15, the legacy pass manager is no longer supported.
+ if llvm_util::get_version() >= (15, 0, 0) {
+ return true;
+ }
+
// There are some perf issues with the new pass manager when targeting
// s390x with LLVM 13, so enable the new pass manager only with LLVM 14.
// See https://github.com/rust-lang/rust/issues/89609.
use rustc_fs_util::fix_windows_verbatim_for_gcc;
use rustc_hir::def_id::CrateNum;
use rustc_middle::middle::dependency_format::Linkage;
+use rustc_middle::middle::exported_symbols::SymbolExportKind;
use rustc_session::config::{self, CFGuard, CrateType, DebugInfo, LdImpl, Strip};
use rustc_session::config::{OutputFilenames, OutputType, PrintRequest, SplitDwarfKind};
use rustc_session::cstore::DllImport;
}
}
+/// Add a synthetic object file that contains reference to all symbols that we want to expose to
+/// the linker.
+///
+/// Background: we implement rlibs as static library (archives). Linkers treat archives
+/// differently from object files: all object files participate in linking, while archives will
+/// only participate in linking if they can satisfy at least one undefined reference (version
+/// scripts doesn't count). This causes `#[no_mangle]` or `#[used]` items to be ignored by the
+/// linker, and since they never participate in the linking, using `KEEP` in the linker scripts
+/// can't keep them either. This causes #47384.
+///
+/// To keep them around, we could use `--whole-archive` and equivalents to force rlib to
+/// participate in linking like object files, but this proves to be expensive (#93791). Therefore
+/// we instead just introduce an undefined reference to them. This could be done by `-u` command
+/// line option to the linker or `EXTERN(...)` in linker scripts, however they does not only
+/// introduce an undefined reference, but also make them the GC roots, preventing `--gc-sections`
+/// from removing them, and this is especially problematic for embedded programming where every
+/// byte counts.
+///
+/// This method creates a synthetic object file, which contains undefined references to all symbols
+/// that are necessary for the linking. They are only present in symbol table but not actually
+/// used in any sections, so the linker will therefore pick relevant rlibs for linking, but
+/// unused `#[no_mangle]` or `#[used]` can still be discard by GC sections.
+fn add_linked_symbol_object(
+ cmd: &mut dyn Linker,
+ sess: &Session,
+ tmpdir: &Path,
+ symbols: &[(String, SymbolExportKind)],
+) {
+ if symbols.is_empty() {
+ return;
+ }
+
+ let Some(mut file) = super::metadata::create_object_file(sess) else {
+ return;
+ };
+
+ // NOTE(nbdd0121): MSVC will hang if the input object file contains no sections,
+ // so add an empty section.
+ if file.format() == object::BinaryFormat::Coff {
+ file.add_section(Vec::new(), ".text".into(), object::SectionKind::Text);
+
+ // We handle the name decoration of COFF targets in `symbol_export.rs`, so disable the
+ // default mangler in `object` crate.
+ file.set_mangling(object::write::Mangling::None);
+
+ // Add feature flags to the object file. On MSVC this is optional but LLD will complain if
+ // not present.
+ let mut feature = 0;
+
+ if file.architecture() == object::Architecture::I386 {
+ // Indicate that all SEH handlers are registered in .sxdata section.
+ // We don't have generate any code, so we don't need .sxdata section but LLD still
+ // expects us to set this bit (see #96498).
+ // Reference: https://docs.microsoft.com/en-us/windows/win32/debug/pe-format
+ feature |= 1;
+ }
+
+ file.add_symbol(object::write::Symbol {
+ name: "@feat.00".into(),
+ value: feature,
+ size: 0,
+ kind: object::SymbolKind::Data,
+ scope: object::SymbolScope::Compilation,
+ weak: false,
+ section: object::write::SymbolSection::Absolute,
+ flags: object::SymbolFlags::None,
+ });
+ }
+
+ for (sym, kind) in symbols.iter() {
+ file.add_symbol(object::write::Symbol {
+ name: sym.clone().into(),
+ value: 0,
+ size: 0,
+ kind: match kind {
+ SymbolExportKind::Text => object::SymbolKind::Text,
+ SymbolExportKind::Data => object::SymbolKind::Data,
+ SymbolExportKind::Tls => object::SymbolKind::Tls,
+ },
+ scope: object::SymbolScope::Unknown,
+ weak: false,
+ section: object::write::SymbolSection::Undefined,
+ flags: object::SymbolFlags::None,
+ });
+ }
+
+ let path = tmpdir.join("symbols.o");
+ let result = std::fs::write(&path, file.write().unwrap());
+ if let Err(e) = result {
+ sess.fatal(&format!("failed to write {}: {}", path.display(), e));
+ }
+ cmd.add_object(&path);
+}
+
/// Add object files containing code from the current crate.
fn add_local_crate_regular_objects(cmd: &mut dyn Linker, codegen_results: &CodegenResults) {
for obj in codegen_results.modules.iter().filter_map(|m| m.object.as_ref()) {
// Pre-link CRT objects.
add_pre_link_objects(cmd, sess, link_output_kind, crt_objects_fallback);
+ add_linked_symbol_object(
+ cmd,
+ sess,
+ tmpdir,
+ &codegen_results.crate_info.linked_symbols[&crate_type],
+ );
+
// Sanitizer libraries.
add_sanitizer_libraries(sess, crate_type, cmd);
// This change is somewhat breaking in practice due to local static libraries being linked
// as whole-archive (#85144), so removing whole-archive may be a pre-requisite.
if sess.opts.debugging_opts.link_native_libraries {
- add_local_native_libraries(cmd, sess, codegen_results, crate_type);
+ add_local_native_libraries(cmd, sess, codegen_results);
}
// Upstream rust libraries and their nobundle static libraries
add_rpath_args(cmd, sess, codegen_results, out_filename);
}
-// A dylib may reexport symbols from the linked rlib or native static library.
-// Even if some symbol is reexported it's still not necessarily counted as used and may be
-// dropped, at least with `ld`-like ELF linkers. So we have to link some rlibs and static
-// libraries as whole-archive to avoid losing reexported symbols.
-// FIXME: Find a way to mark reexported symbols as used and avoid this use of whole-archive.
-fn default_to_whole_archive(sess: &Session, crate_type: CrateType, cmd: &dyn Linker) -> bool {
- crate_type == CrateType::Dylib
- && !(sess.target.limit_rdylib_exports && cmd.exported_symbol_means_used_symbol())
-}
-
/// # Native library linking
///
/// User-supplied library search paths (-L on the command line). These are the same paths used to
cmd: &mut dyn Linker,
sess: &Session,
codegen_results: &CodegenResults,
- crate_type: CrateType,
) {
let filesearch = sess.target_filesearch(PathKind::All);
for search_path in filesearch.search_paths() {
}
NativeLibKind::Static { whole_archive, bundle, .. } => {
if whole_archive == Some(true)
- || (whole_archive == None && default_to_whole_archive(sess, crate_type, cmd))
// Backward compatibility case: this can be a rlib (so `+whole-archive` cannot
// be added explicitly if necessary, see the error in `fn link_rlib`) compiled
// as an executable due to `--test`. Use whole-archive implicitly, like before
let src = &codegen_results.crate_info.used_crate_source[&cnum];
match data[cnum.as_usize() - 1] {
_ if codegen_results.crate_info.profiler_runtime == Some(cnum) => {
- add_static_crate::<B>(cmd, sess, codegen_results, tmpdir, crate_type, cnum);
+ add_static_crate::<B>(cmd, sess, codegen_results, tmpdir, cnum);
}
// compiler-builtins are always placed last to ensure that they're
// linked correctly.
}
Linkage::NotLinked | Linkage::IncludedFromDylib => {}
Linkage::Static => {
- add_static_crate::<B>(cmd, sess, codegen_results, tmpdir, crate_type, cnum);
+ add_static_crate::<B>(cmd, sess, codegen_results, tmpdir, cnum);
// Link static native libs with "-bundle" modifier only if the crate they originate from
// is being linked statically to the current crate. If it's linked dynamically
lib.kind
{
let verbatim = lib.verbatim.unwrap_or(false);
- if whole_archive == Some(true)
- || (whole_archive == None
- && default_to_whole_archive(sess, crate_type, cmd))
- {
+ if whole_archive == Some(true) {
cmd.link_whole_staticlib(
name,
verbatim,
// was already "included" in a dylib (e.g., `libstd` when `-C prefer-dynamic`
// is used)
if let Some(cnum) = compiler_builtins {
- add_static_crate::<B>(cmd, sess, codegen_results, tmpdir, crate_type, cnum);
+ add_static_crate::<B>(cmd, sess, codegen_results, tmpdir, cnum);
}
// Converts a library file-stem into a cc -l argument
sess: &'a Session,
codegen_results: &CodegenResults,
tmpdir: &Path,
- crate_type: CrateType,
cnum: CrateNum,
) {
let src = &codegen_results.crate_info.used_crate_source[&cnum];
let cratepath = &src.rlib.as_ref().unwrap().0;
let mut link_upstream = |path: &Path| {
- // We don't want to include the whole compiler-builtins crate (e.g., compiler-rt)
- // regardless of the default because it'll get repeatedly linked anyway.
- let path = fix_windows_verbatim_for_gcc(path);
- if default_to_whole_archive(sess, crate_type, cmd)
- && codegen_results.crate_info.compiler_builtins != Some(cnum)
- {
- cmd.link_whole_rlib(&path);
- } else {
- cmd.link_rlib(&path);
- }
+ cmd.link_rlib(&fix_windows_verbatim_for_gcc(path));
};
// See the comment above in `link_staticlib` and `link_rlib` for why if
use rustc_hir::def_id::{CrateNum, LOCAL_CRATE};
use rustc_middle::middle::dependency_format::Linkage;
+use rustc_middle::middle::exported_symbols::{ExportedSymbol, SymbolExportInfo, SymbolExportKind};
use rustc_middle::ty::TyCtxt;
use rustc_serialize::{json, Encoder};
use rustc_session::config::{self, CrateType, DebugInfo, LinkerPluginLto, Lto, OptLevel, Strip};
fn no_crt_objects(&mut self);
fn no_default_libraries(&mut self);
fn export_symbols(&mut self, tmpdir: &Path, crate_type: CrateType, symbols: &[String]);
- fn exported_symbol_means_used_symbol(&self) -> bool {
- true
- }
fn subsystem(&mut self, subsystem: &str);
fn group_start(&mut self);
fn group_end(&mut self);
}
}
- fn exported_symbol_means_used_symbol(&self) -> bool {
- self.sess.target.is_like_windows || self.sess.target.is_like_osx
- }
-
fn subsystem(&mut self, subsystem: &str) {
self.linker_arg("--subsystem");
self.linker_arg(&subsystem);
return;
}
- fn exported_symbol_means_used_symbol(&self) -> bool {
- false
- }
-
fn subsystem(&mut self, subsystem: &str) {
self.cmd.arg(&format!("--subsystem {}", subsystem));
}
}
}
+fn for_each_exported_symbols_include_dep<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ crate_type: CrateType,
+ mut callback: impl FnMut(ExportedSymbol<'tcx>, SymbolExportInfo, CrateNum),
+) {
+ for &(symbol, info) in tcx.exported_symbols(LOCAL_CRATE).iter() {
+ callback(symbol, info, LOCAL_CRATE);
+ }
+
+ let formats = tcx.dependency_formats(());
+ let deps = formats.iter().find_map(|(t, list)| (*t == crate_type).then_some(list)).unwrap();
+
+ for (index, dep_format) in deps.iter().enumerate() {
+ let cnum = CrateNum::new(index + 1);
+ // For each dependency that we are linking to statically ...
+ if *dep_format == Linkage::Static {
+ for &(symbol, info) in tcx.exported_symbols(cnum).iter() {
+ callback(symbol, info, cnum);
+ }
+ }
+ }
+}
+
pub(crate) fn exported_symbols(tcx: TyCtxt<'_>, crate_type: CrateType) -> Vec<String> {
if let Some(ref exports) = tcx.sess.target.override_export_symbols {
return exports.iter().map(ToString::to_string).collect();
let mut symbols = Vec::new();
let export_threshold = symbol_export::crates_export_threshold(&[crate_type]);
- for &(symbol, level) in tcx.exported_symbols(LOCAL_CRATE).iter() {
- if level.is_below_threshold(export_threshold) {
- symbols.push(symbol_export::symbol_name_for_instance_in_crate(
- tcx,
- symbol,
- LOCAL_CRATE,
- ));
+ for_each_exported_symbols_include_dep(tcx, crate_type, |symbol, info, cnum| {
+ if info.level.is_below_threshold(export_threshold) {
+ symbols.push(symbol_export::symbol_name_for_instance_in_crate(tcx, symbol, cnum));
}
- }
-
- let formats = tcx.dependency_formats(());
- let deps = formats.iter().find_map(|(t, list)| (*t == crate_type).then_some(list)).unwrap();
+ });
- for (index, dep_format) in deps.iter().enumerate() {
- let cnum = CrateNum::new(index + 1);
- // For each dependency that we are linking to statically ...
- if *dep_format == Linkage::Static {
- // ... we add its symbol list to our export list.
- for &(symbol, level) in tcx.exported_symbols(cnum).iter() {
- if !level.is_below_threshold(export_threshold) {
- continue;
- }
+ symbols
+}
- symbols.push(symbol_export::symbol_name_for_instance_in_crate(tcx, symbol, cnum));
- }
+pub(crate) fn linked_symbols(
+ tcx: TyCtxt<'_>,
+ crate_type: CrateType,
+) -> Vec<(String, SymbolExportKind)> {
+ match crate_type {
+ CrateType::Executable | CrateType::Cdylib | CrateType::Dylib => (),
+ CrateType::Staticlib | CrateType::ProcMacro | CrateType::Rlib => {
+ return Vec::new();
}
}
+ let mut symbols = Vec::new();
+
+ let export_threshold = symbol_export::crates_export_threshold(&[crate_type]);
+ for_each_exported_symbols_include_dep(tcx, crate_type, |symbol, info, cnum| {
+ if info.level.is_below_threshold(export_threshold) || info.used {
+ symbols.push((
+ symbol_export::linking_symbol_name_for_instance_in_crate(tcx, symbol, cnum),
+ info.kind,
+ ));
+ }
+ });
+
symbols
}
pub enum LtoModuleCodegen<B: WriteBackendMethods> {
Fat {
- module: Option<ModuleCodegen<B::Module>>,
+ module: ModuleCodegen<B::Module>,
_serialized_bitcode: Vec<SerializedModule<B::ModuleBuffer>>,
},
/// It's intended that the module returned is immediately code generated and
/// dropped, and then this LTO module is dropped.
pub unsafe fn optimize(
- &mut self,
+ self,
cgcx: &CodegenContext<B>,
) -> Result<ModuleCodegen<B::Module>, FatalError> {
- match *self {
- LtoModuleCodegen::Fat { ref mut module, .. } => {
- let module = module.take().unwrap();
- {
- let config = cgcx.config(module.kind);
- B::run_lto_pass_manager(cgcx, &module, config, false)?;
- }
+ match self {
+ LtoModuleCodegen::Fat { mut module, .. } => {
+ B::optimize_fat(cgcx, &mut module)?;
Ok(module)
}
- LtoModuleCodegen::Thin(ref mut thin) => B::optimize_thin(cgcx, thin),
+ LtoModuleCodegen::Thin(thin) => B::optimize_thin(cgcx, thin),
}
}
.map_err(|e| format!("failed to read {} section in '{}': {}", section, path.display(), e))
}
-fn create_object_file(sess: &Session) -> Option<write::Object<'static>> {
+pub(crate) fn create_object_file(sess: &Session) -> Option<write::Object<'static>> {
let endianness = match sess.target.options.endian {
Endian::Little => Endianness::Little,
Endian::Big => Endianness::Big,
use rustc_hir::Node;
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
use rustc_middle::middle::exported_symbols::{
- metadata_symbol_name, ExportedSymbol, SymbolExportLevel,
+ metadata_symbol_name, ExportedSymbol, SymbolExportInfo, SymbolExportKind, SymbolExportLevel,
};
use rustc_middle::ty::query::{ExternProviders, Providers};
use rustc_middle::ty::subst::{GenericArgKind, SubstsRef};
use rustc_middle::ty::Instance;
-use rustc_middle::ty::{SymbolName, TyCtxt};
+use rustc_middle::ty::{self, SymbolName, TyCtxt};
use rustc_session::config::CrateType;
use rustc_target::spec::SanitizerSet;
}
}
-fn reachable_non_generics_provider(tcx: TyCtxt<'_>, cnum: CrateNum) -> DefIdMap<SymbolExportLevel> {
+fn reachable_non_generics_provider(tcx: TyCtxt<'_>, cnum: CrateNum) -> DefIdMap<SymbolExportInfo> {
assert_eq!(cnum, LOCAL_CRATE);
if !tcx.sess.opts.output_types.should_codegen() {
}
})
.map(|def_id| {
- let export_level = if special_runtime_crate {
+ let (export_level, used) = if special_runtime_crate {
let name = tcx.symbol_name(Instance::mono(tcx, def_id.to_def_id())).name;
- // We can probably do better here by just ensuring that
- // it has hidden visibility rather than public
- // visibility, as this is primarily here to ensure it's
- // not stripped during LTO.
- //
- // In general though we won't link right if these
- // symbols are stripped, and LTO currently strips them.
- match name {
+ // We won't link right if these symbols are stripped during LTO.
+ let used = match name {
"rust_eh_personality"
| "rust_eh_register_frames"
- | "rust_eh_unregister_frames" =>
- SymbolExportLevel::C,
- _ => SymbolExportLevel::Rust,
- }
+ | "rust_eh_unregister_frames" => true,
+ _ => false,
+ };
+ (SymbolExportLevel::Rust, used)
} else {
- symbol_export_level(tcx, def_id.to_def_id())
+ (symbol_export_level(tcx, def_id.to_def_id()), false)
};
+ let codegen_attrs = tcx.codegen_fn_attrs(def_id.to_def_id());
debug!(
"EXPORTED SYMBOL (local): {} ({:?})",
tcx.symbol_name(Instance::mono(tcx, def_id.to_def_id())),
export_level
);
- (def_id.to_def_id(), export_level)
+ (def_id.to_def_id(), SymbolExportInfo {
+ level: export_level,
+ kind: if tcx.is_static(def_id.to_def_id()) {
+ if codegen_attrs.flags.contains(CodegenFnAttrFlags::THREAD_LOCAL) {
+ SymbolExportKind::Tls
+ } else {
+ SymbolExportKind::Data
+ }
+ } else {
+ SymbolExportKind::Text
+ },
+ used: codegen_attrs.flags.contains(CodegenFnAttrFlags::USED)
+ || codegen_attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER) || used,
+ })
})
.collect();
if let Some(id) = tcx.proc_macro_decls_static(()) {
- reachable_non_generics.insert(id.to_def_id(), SymbolExportLevel::C);
+ reachable_non_generics.insert(
+ id.to_def_id(),
+ SymbolExportInfo {
+ level: SymbolExportLevel::C,
+ kind: SymbolExportKind::Data,
+ used: false,
+ },
+ );
}
reachable_non_generics
fn is_reachable_non_generic_provider_local(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
let export_threshold = threshold(tcx);
- if let Some(&level) = tcx.reachable_non_generics(def_id.krate).get(&def_id) {
- level.is_below_threshold(export_threshold)
+ if let Some(&info) = tcx.reachable_non_generics(def_id.krate).get(&def_id) {
+ info.level.is_below_threshold(export_threshold)
} else {
false
}
fn exported_symbols_provider_local<'tcx>(
tcx: TyCtxt<'tcx>,
cnum: CrateNum,
-) -> &'tcx [(ExportedSymbol<'tcx>, SymbolExportLevel)] {
+) -> &'tcx [(ExportedSymbol<'tcx>, SymbolExportInfo)] {
assert_eq!(cnum, LOCAL_CRATE);
if !tcx.sess.opts.output_types.should_codegen() {
let mut symbols: Vec<_> = tcx
.reachable_non_generics(LOCAL_CRATE)
.iter()
- .map(|(&def_id, &level)| (ExportedSymbol::NonGeneric(def_id), level))
+ .map(|(&def_id, &info)| (ExportedSymbol::NonGeneric(def_id), info))
.collect();
if tcx.entry_fn(()).is_some() {
let exported_symbol = ExportedSymbol::NoDefId(SymbolName::new(tcx, "main"));
- symbols.push((exported_symbol, SymbolExportLevel::C));
+ symbols.push((
+ exported_symbol,
+ SymbolExportInfo {
+ level: SymbolExportLevel::C,
+ kind: SymbolExportKind::Text,
+ used: false,
+ },
+ ));
}
if tcx.allocator_kind(()).is_some() {
let symbol_name = format!("__rust_{}", method.name);
let exported_symbol = ExportedSymbol::NoDefId(SymbolName::new(tcx, &symbol_name));
- symbols.push((exported_symbol, SymbolExportLevel::Rust));
+ symbols.push((
+ exported_symbol,
+ SymbolExportInfo {
+ level: SymbolExportLevel::Rust,
+ kind: SymbolExportKind::Text,
+ used: false,
+ },
+ ));
}
}
symbols.extend(PROFILER_WEAK_SYMBOLS.iter().map(|sym| {
let exported_symbol = ExportedSymbol::NoDefId(SymbolName::new(tcx, sym));
- (exported_symbol, SymbolExportLevel::C)
+ (
+ exported_symbol,
+ SymbolExportInfo {
+ level: SymbolExportLevel::C,
+ kind: SymbolExportKind::Data,
+ used: false,
+ },
+ )
}));
}
if tcx.sess.opts.debugging_opts.sanitizer.contains(SanitizerSet::MEMORY) {
+ let mut msan_weak_symbols = Vec::new();
+
// Similar to profiling, preserve weak msan symbol during LTO.
- const MSAN_WEAK_SYMBOLS: [&str; 2] = ["__msan_track_origins", "__msan_keep_going"];
+ if tcx.sess.opts.debugging_opts.sanitizer_recover.contains(SanitizerSet::MEMORY) {
+ msan_weak_symbols.push("__msan_keep_going");
+ }
- symbols.extend(MSAN_WEAK_SYMBOLS.iter().map(|sym| {
+ if tcx.sess.opts.debugging_opts.sanitizer_memory_track_origins != 0 {
+ msan_weak_symbols.push("__msan_track_origins");
+ }
+
+ symbols.extend(msan_weak_symbols.into_iter().map(|sym| {
let exported_symbol = ExportedSymbol::NoDefId(SymbolName::new(tcx, sym));
- (exported_symbol, SymbolExportLevel::C)
+ (
+ exported_symbol,
+ SymbolExportInfo {
+ level: SymbolExportLevel::C,
+ kind: SymbolExportKind::Data,
+ used: false,
+ },
+ )
}));
}
let symbol_name = metadata_symbol_name(tcx);
let exported_symbol = ExportedSymbol::NoDefId(SymbolName::new(tcx, &symbol_name));
- symbols.push((exported_symbol, SymbolExportLevel::Rust));
+ symbols.push((
+ exported_symbol,
+ SymbolExportInfo {
+ level: SymbolExportLevel::Rust,
+ kind: SymbolExportKind::Data,
+ used: false,
+ },
+ ));
}
if tcx.sess.opts.share_generics() && tcx.local_crate_exports_generics() {
MonoItem::Fn(Instance { def: InstanceDef::Item(def), substs }) => {
if substs.non_erasable_generics().next().is_some() {
let symbol = ExportedSymbol::Generic(def.did, substs);
- symbols.push((symbol, SymbolExportLevel::Rust));
+ symbols.push((
+ symbol,
+ SymbolExportInfo {
+ level: SymbolExportLevel::Rust,
+ kind: SymbolExportKind::Text,
+ used: false,
+ },
+ ));
}
}
MonoItem::Fn(Instance { def: InstanceDef::DropGlue(_, Some(ty)), substs }) => {
substs.non_erasable_generics().next(),
Some(GenericArgKind::Type(ty))
);
- symbols.push((ExportedSymbol::DropGlue(ty), SymbolExportLevel::Rust));
+ symbols.push((
+ ExportedSymbol::DropGlue(ty),
+ SymbolExportInfo {
+ level: SymbolExportLevel::Rust,
+ kind: SymbolExportKind::Text,
+ used: false,
+ },
+ ));
}
_ => {
// Any other symbols don't qualify for sharing
}
}
+/// This is the symbol name of the given instance as seen by the linker.
+///
+/// On 32-bit Windows symbols are decorated according to their calling conventions.
+pub fn linking_symbol_name_for_instance_in_crate<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ symbol: ExportedSymbol<'tcx>,
+ instantiating_crate: CrateNum,
+) -> String {
+ use rustc_target::abi::call::Conv;
+
+ let mut undecorated = symbol_name_for_instance_in_crate(tcx, symbol, instantiating_crate);
+
+ let target = &tcx.sess.target;
+ if !target.is_like_windows {
+ // Mach-O has a global "_" suffix and `object` crate will handle it.
+ // ELF does not have any symbol decorations.
+ return undecorated;
+ }
+
+ let x86 = match &target.arch[..] {
+ "x86" => true,
+ "x86_64" => false,
+ // Only x86/64 use symbol decorations.
+ _ => return undecorated,
+ };
+
+ let instance = match symbol {
+ ExportedSymbol::NonGeneric(def_id) | ExportedSymbol::Generic(def_id, _)
+ if tcx.is_static(def_id) =>
+ {
+ None
+ }
+ ExportedSymbol::NonGeneric(def_id) => Some(Instance::mono(tcx, def_id)),
+ ExportedSymbol::Generic(def_id, substs) => Some(Instance::new(def_id, substs)),
+ // DropGlue always use the Rust calling convention and thus follow the target's default
+ // symbol decoration scheme.
+ ExportedSymbol::DropGlue(..) => None,
+ // NoDefId always follow the target's default symbol decoration scheme.
+ ExportedSymbol::NoDefId(..) => None,
+ };
+
+ let (conv, args) = instance
+ .map(|i| {
+ tcx.fn_abi_of_instance(ty::ParamEnv::reveal_all().and((i, ty::List::empty())))
+ .unwrap_or_else(|_| bug!("fn_abi_of_instance({i:?}) failed"))
+ })
+ .map(|fnabi| (fnabi.conv, &fnabi.args[..]))
+ .unwrap_or((Conv::Rust, &[]));
+
+ // Decorate symbols with prefices, suffices and total number of bytes of arguments.
+ // Reference: https://docs.microsoft.com/en-us/cpp/build/reference/decorated-names?view=msvc-170
+ let (prefix, suffix) = match conv {
+ Conv::X86Fastcall => ("@", "@"),
+ Conv::X86Stdcall => ("_", "@"),
+ Conv::X86VectorCall => ("", "@@"),
+ _ => {
+ if x86 {
+ undecorated.insert(0, '_');
+ }
+ return undecorated;
+ }
+ };
+
+ let args_in_bytes: u64 = args
+ .iter()
+ .map(|abi| abi.layout.size.bytes().next_multiple_of(target.pointer_width as u64 / 8))
+ .sum();
+ format!("{prefix}{undecorated}{suffix}{args_in_bytes}")
+}
+
fn wasm_import_module_map(tcx: TyCtxt<'_>, cnum: CrateNum) -> FxHashMap<DefId, String> {
// Build up a map from DefId to a `NativeLib` structure, where
// `NativeLib` internally contains information about
};
use rustc_metadata::EncodedMetadata;
use rustc_middle::dep_graph::{WorkProduct, WorkProductId};
-use rustc_middle::middle::exported_symbols::SymbolExportLevel;
+use rustc_middle::middle::exported_symbols::SymbolExportInfo;
use rustc_middle::ty::TyCtxt;
use rustc_session::cgu_reuse_tracker::CguReuseTracker;
use rustc_session::config::{self, CrateType, Lto, OutputFilenames, OutputType};
+ Sync,
>;
-pub type ExportedSymbols = FxHashMap<CrateNum, Arc<Vec<(String, SymbolExportLevel)>>>;
+pub type ExportedSymbols = FxHashMap<CrateNum, Arc<Vec<(String, SymbolExportInfo)>>>;
/// Additional resources used by optimize_and_codegen (not module specific)
#[derive(Clone)]
fn execute_lto_work_item<B: ExtraBackendMethods>(
cgcx: &CodegenContext<B>,
- mut module: lto::LtoModuleCodegen<B>,
+ module: lto::LtoModuleCodegen<B>,
module_config: &ModuleConfig,
) -> Result<WorkItemResult<B>, FatalError> {
let module = unsafe { module.optimize(cgcx)? };
} else if let Some(kind) = tcx.allocator_kind(()) {
let llmod_id =
cgu_name_builder.build_cgu_name(LOCAL_CRATE, &["crate"], Some("allocator")).to_string();
- let mut module_llvm = backend.new_metadata(tcx, &llmod_id);
- tcx.sess.time("write_allocator_module", || {
- backend.codegen_allocator(
- tcx,
- &mut module_llvm,
- &llmod_id,
- kind,
- tcx.lang_items().oom().is_some(),
- )
+ let module_llvm = tcx.sess.time("write_allocator_module", || {
+ backend.codegen_allocator(tcx, &llmod_id, kind, tcx.lang_items().oom().is_some())
});
Some(ModuleCodegen { name: llmod_id, module_llvm, kind: ModuleKind::Allocator })
.iter()
.map(|&c| (c, crate::back::linker::exported_symbols(tcx, c)))
.collect();
+ let linked_symbols = tcx
+ .sess
+ .crate_types()
+ .iter()
+ .map(|&c| (c, crate::back::linker::linked_symbols(tcx, c)))
+ .collect();
let local_crate_name = tcx.crate_name(LOCAL_CRATE);
let crate_attrs = tcx.hir().attrs(rustc_hir::CRATE_HIR_ID);
let subsystem = tcx.sess.first_attr_value_str_by_name(crate_attrs, sym::windows_subsystem);
let mut info = CrateInfo {
target_cpu,
exported_symbols,
+ linked_symbols,
local_crate_name,
compiler_builtins: None,
profiler_runtime: None,
use rustc_errors::struct_span_err;
use rustc_hir as hir;
-use rustc_hir::def_id::DefId;
use rustc_hir::LangItem;
use rustc_middle::mir::interpret::ConstValue;
use rustc_middle::ty::{self, layout::TyAndLayout, Ty, TyCtxt};
use rustc_span::Span;
use crate::base;
-use crate::traits::BuilderMethods;
use crate::traits::*;
pub enum IntPredicate {
}
}
-pub fn langcall(tcx: TyCtxt<'_>, span: Option<Span>, msg: &str, li: LangItem) -> DefId {
- tcx.lang_items().require(li).unwrap_or_else(|s| {
- let msg = format!("{} {}", msg, s);
- match span {
- Some(span) => tcx.sess.span_fatal(span, &msg),
- None => tcx.sess.fatal(&msg),
- }
- })
+pub fn build_langcall<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>>(
+ bx: &Bx,
+ span: Option<Span>,
+ li: LangItem,
+) -> (Bx::FnAbiOfResult, Bx::Value) {
+ let tcx = bx.tcx();
+ let def_id = tcx.require_lang_item(li, span);
+ let instance = ty::Instance::mono(tcx, def_id);
+ (bx.fn_abi_of_instance(instance, ty::List::empty()), bx.get_fn_addr(instance))
}
// To avoid UB from LLVM, these two functions mask RHS with an
#![feature(nll)]
#![feature(associated_type_bounds)]
#![feature(strict_provenance)]
+#![feature(int_roundings)]
#![recursion_limit = "256"]
#![allow(rustc::potential_query_instability)]
use rustc_hir::LangItem;
use rustc_middle::dep_graph::WorkProduct;
use rustc_middle::middle::dependency_format::Dependencies;
+use rustc_middle::middle::exported_symbols::SymbolExportKind;
use rustc_middle::ty::query::{ExternProviders, Providers};
use rustc_serialize::{opaque, Decodable, Decoder, Encoder};
use rustc_session::config::{CrateType, OutputFilenames, OutputType, RUST_CGU_EXT};
pub struct CrateInfo {
pub target_cpu: String,
pub exported_symbols: FxHashMap<CrateType, Vec<String>>,
+ pub linked_symbols: FxHashMap<CrateType, Vec<(String, SymbolExportKind)>>,
pub local_crate_name: Symbol,
pub compiler_builtins: Option<CrateNum>,
pub profiler_runtime: Option<CrateNum>,
}
// If there exists a local definition that dominates all uses of that local,
- // the definition should be visited first. Traverse blocks in preorder which
+ // the definition should be visited first. Traverse blocks in an order that
// is a topological sort of dominance partial order.
- for (bb, data) in traversal::preorder(&mir) {
+ for (bb, data) in traversal::reverse_postorder(&mir) {
analyzer.visit_basic_block_data(bb, data);
}
}
};
- // Obtain the panic entry point.
- let def_id = common::langcall(bx.tcx(), Some(span), "", lang_item);
- let instance = ty::Instance::mono(bx.tcx(), def_id);
- let fn_abi = bx.fn_abi_of_instance(instance, ty::List::empty());
- let llfn = bx.get_fn_addr(instance);
+ let (fn_abi, llfn) = common::build_langcall(&bx, Some(span), lang_item);
// Codegen the actual panic invoke/call.
helper.do_call(self, &mut bx, fn_abi, llfn, &args, None, cleanup);
self.set_debug_loc(&mut bx, terminator.source_info);
// Obtain the panic entry point.
- let def_id = common::langcall(bx.tcx(), Some(span), "", LangItem::PanicNoUnwind);
- let instance = ty::Instance::mono(bx.tcx(), def_id);
- let fn_abi = bx.fn_abi_of_instance(instance, ty::List::empty());
- let llfn = bx.get_fn_addr(instance);
+ let (fn_abi, llfn) = common::build_langcall(&bx, Some(span), LangItem::PanicNoUnwind);
// Codegen the actual panic invoke/call.
helper.do_call(self, &mut bx, fn_abi, llfn, &[], None, None);
let location = self.get_caller_location(bx, source_info).immediate();
// Obtain the panic entry point.
- // FIXME: dedup this with `codegen_assert_terminator` above.
- let def_id =
- common::langcall(bx.tcx(), Some(source_info.span), "", LangItem::Panic);
- let instance = ty::Instance::mono(bx.tcx(), def_id);
- let fn_abi = bx.fn_abi_of_instance(instance, ty::List::empty());
- let llfn = bx.get_fn_addr(instance);
+ let (fn_abi, llfn) =
+ common::build_langcall(bx, Some(source_info.span), LangItem::Panic);
// Codegen the actual panic invoke/call.
helper.do_call(
let llretty = self.landing_pad_type();
bx.cleanup_landing_pad(llretty, llpersonality);
- let def_id = common::langcall(bx.tcx(), None, "", LangItem::PanicNoUnwind);
- let instance = ty::Instance::mono(bx.tcx(), def_id);
- let fn_abi = bx.fn_abi_of_instance(instance, ty::List::empty());
- let fn_ptr = bx.get_fn_addr(instance);
+ let (fn_abi, fn_ptr) = common::build_langcall(&bx, None, LangItem::PanicNoUnwind);
let fn_ty = bx.fn_decl_backend_type(&fn_abi);
let llret = bx.call(fn_ty, fn_ptr, &[], None);
#[derive(Clone, Copy, Debug)]
pub struct DebugScope<S, L> {
- // FIXME(eddyb) this should never be `None`, after initialization.
- pub dbg_scope: Option<S>,
+ pub dbg_scope: S,
/// Call site location, if this scope was inlined from another function.
pub inlined_at: Option<L>,
cx: &Cx,
span: Span,
) -> S {
- // FIXME(eddyb) this should never be `None`.
- let dbg_scope = self
- .dbg_scope
- .unwrap_or_else(|| bug!("`dbg_scope` is only `None` during initialization"));
-
let pos = span.lo();
if pos < self.file_start_pos || pos >= self.file_end_pos {
let sm = cx.sess().source_map();
- cx.extend_scope_to_file(dbg_scope, &sm.lookup_char_pos(pos).file)
+ cx.extend_scope_to_file(self.dbg_scope, &sm.lookup_char_pos(pos).file)
} else {
- dbg_scope
+ self.dbg_scope
}
}
}
fx.debug_introduce_locals(&mut bx);
// Codegen the body of each block using reverse postorder
- // FIXME(eddyb) reuse RPO iterator between `analysis` and this.
for (bb, _) in traversal::reverse_postorder(&mir) {
fx.codegen_block(bb);
}
}
pub trait ExtraBackendMethods: CodegenBackend + WriteBackendMethods + Sized + Send + Sync {
- fn new_metadata(&self, sess: TyCtxt<'_>, mod_name: &str) -> Self::Module;
fn codegen_allocator<'tcx>(
&self,
tcx: TyCtxt<'tcx>,
- module_llvm: &mut Self::Module,
module_name: &str,
kind: AllocatorKind,
has_alloc_error_handler: bool,
- );
+ ) -> Self::Module;
/// This generates the codegen unit and returns it along with
/// a `u64` giving an estimate of the unit's processing cost.
fn compile_codegen_unit(
module: &ModuleCodegen<Self::Module>,
config: &ModuleConfig,
) -> Result<(), FatalError>;
+ fn optimize_fat(
+ cgcx: &CodegenContext<Self>,
+ llmod: &mut ModuleCodegen<Self::Module>,
+ ) -> Result<(), FatalError>;
unsafe fn optimize_thin(
cgcx: &CodegenContext<Self>,
- thin: &mut ThinModule<Self>,
+ thin: ThinModule<Self>,
) -> Result<ModuleCodegen<Self::Module>, FatalError>;
unsafe fn codegen(
cgcx: &CodegenContext<Self>,
) -> Result<CompiledModule, FatalError>;
fn prepare_thin(module: ModuleCodegen<Self::Module>) -> (String, Self::ThinBuffer);
fn serialize_module(module: ModuleCodegen<Self::Module>) -> (String, Self::ModuleBuffer);
- fn run_lto_pass_manager(
- cgcx: &CodegenContext<Self>,
- llmod: &ModuleCodegen<Self::Module>,
- config: &ModuleConfig,
- thin: bool,
- ) -> Result<(), FatalError>;
}
pub trait ThinBufferMethods: Send + Sync {
/// This function converts an interpreter value into a constant that is meant for use in the
/// type system.
+#[instrument(skip(ecx), level = "debug")]
pub(super) fn op_to_const<'tcx>(
ecx: &CompileTimeEvalContext<'_, 'tcx>,
op: &OpTy<'tcx>,
op.try_as_mplace()
};
+ debug!(?immediate);
+
// We know `offset` is relative to the allocation, so we can use `into_parts`.
- let to_const_value = |mplace: &MPlaceTy<'_>| match mplace.ptr.into_parts() {
- (Some(alloc_id), offset) => {
- let alloc = ecx.tcx.global_alloc(alloc_id).unwrap_memory();
- ConstValue::ByRef { alloc, offset }
- }
- (None, offset) => {
- assert!(mplace.layout.is_zst());
- assert_eq!(
- offset.bytes() % mplace.layout.align.abi.bytes(),
- 0,
- "this MPlaceTy must come from a validated constant, thus we can assume the \
+ let to_const_value = |mplace: &MPlaceTy<'_>| {
+ debug!("to_const_value(mplace: {:?})", mplace);
+ match mplace.ptr.into_parts() {
+ (Some(alloc_id), offset) => {
+ let alloc = ecx.tcx.global_alloc(alloc_id).unwrap_memory();
+ ConstValue::ByRef { alloc, offset }
+ }
+ (None, offset) => {
+ assert!(mplace.layout.is_zst());
+ assert_eq!(
+ offset.bytes() % mplace.layout.align.abi.bytes(),
+ 0,
+ "this MPlaceTy must come from a validated constant, thus we can assume the \
alignment is correct",
- );
- ConstValue::Scalar(Scalar::ZST)
+ );
+ ConstValue::Scalar(Scalar::ZST)
+ }
}
};
match immediate {
ScalarMaybeUninit::Uninit => to_const_value(&op.assert_mem_place()),
},
Immediate::ScalarPair(a, b) => {
+ debug!("ScalarPair(a: {:?}, b: {:?})", a, b);
// We know `offset` is relative to the allocation, so we can use `into_parts`.
let (data, start) =
match ecx.scalar_to_ptr(a.check_init().unwrap()).unwrap().into_parts() {
);
// Turn this into a proper constant.
- op_to_const(&ecx, &mplace.into())
+ let const_val = op_to_const(&ecx, &mplace.into());
+ debug!(?const_val);
+
+ const_val
}
pub fn eval_to_const_value_raw_provider<'tcx>(
}
pub fn is_parent_const_impl_raw(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
- let parent_id = tcx.local_parent(def_id).unwrap();
+ let parent_id = tcx.local_parent(def_id);
tcx.def_kind(parent_id) == DefKind::Impl
&& tcx.impl_constness(parent_id) == hir::Constness::Const
}
use std::convert::TryFrom;
use rustc_hir::Mutability;
-use rustc_middle::ty::layout::HasTyCtxt;
+use rustc_middle::mir;
use rustc_middle::ty::{self, TyCtxt};
-use rustc_middle::{
- mir::{self, interpret::ConstAlloc},
- ty::ScalarInt,
-};
use rustc_span::{source_map::DUMMY_SP, symbol::Symbol};
-use rustc_target::abi::VariantIdx;
use crate::interpret::{
- intern_const_alloc_recursive, ConstValue, InternKind, InterpCx, InterpResult, MPlaceTy,
- MemPlaceMeta, Scalar,
+ intern_const_alloc_recursive, ConstValue, InternKind, InterpCx, InterpResult, MemPlaceMeta,
+ Scalar,
};
mod error;
mod eval_queries;
mod fn_queries;
mod machine;
+mod valtrees;
pub use error::*;
pub use eval_queries::*;
pub use fn_queries::*;
pub use machine::*;
+pub(crate) use valtrees::{const_to_valtree, valtree_to_const_value};
pub(crate) fn const_caller_location(
tcx: TyCtxt<'_>,
ConstValue::Scalar(Scalar::from_maybe_pointer(loc_place.ptr, &tcx))
}
-/// Convert an evaluated constant to a type level constant
-pub(crate) fn const_to_valtree<'tcx>(
- tcx: TyCtxt<'tcx>,
- param_env: ty::ParamEnv<'tcx>,
- raw: ConstAlloc<'tcx>,
-) -> Option<ty::ValTree<'tcx>> {
- let ecx = mk_eval_cx(
- tcx, DUMMY_SP, param_env,
- // It is absolutely crucial for soundness that
- // we do not read from static items or other mutable memory.
- false,
- );
- let place = ecx.raw_const_to_mplace(raw).unwrap();
- const_to_valtree_inner(&ecx, &place)
-}
-
-#[instrument(skip(ecx), level = "debug")]
-fn branches<'tcx>(
- ecx: &CompileTimeEvalContext<'tcx, 'tcx>,
- place: &MPlaceTy<'tcx>,
- n: usize,
- variant: Option<VariantIdx>,
-) -> Option<ty::ValTree<'tcx>> {
- let place = match variant {
- Some(variant) => ecx.mplace_downcast(&place, variant).unwrap(),
- None => *place,
- };
- let variant = variant.map(|variant| Some(ty::ValTree::Leaf(ScalarInt::from(variant.as_u32()))));
- debug!(?place, ?variant);
-
- let fields = (0..n).map(|i| {
- let field = ecx.mplace_field(&place, i).unwrap();
- const_to_valtree_inner(ecx, &field)
- });
- // For enums, we prepend their variant index before the variant's fields so we can figure out
- // the variant again when just seeing a valtree.
- let branches = variant.into_iter().chain(fields);
- Some(ty::ValTree::Branch(ecx.tcx.arena.alloc_from_iter(branches.collect::<Option<Vec<_>>>()?)))
-}
-
-fn slice_branches<'tcx>(
- ecx: &CompileTimeEvalContext<'tcx, 'tcx>,
- place: &MPlaceTy<'tcx>,
-) -> Option<ty::ValTree<'tcx>> {
- let n = place.len(&ecx.tcx()).expect(&format!("expected to use len of place {:?}", place));
- let branches = (0..n).map(|i| {
- let place_elem = ecx.mplace_index(place, i).unwrap();
- const_to_valtree_inner(ecx, &place_elem)
- });
-
- Some(ty::ValTree::Branch(ecx.tcx.arena.alloc_from_iter(branches.collect::<Option<Vec<_>>>()?)))
-}
-
-#[instrument(skip(ecx), level = "debug")]
-fn const_to_valtree_inner<'tcx>(
- ecx: &CompileTimeEvalContext<'tcx, 'tcx>,
- place: &MPlaceTy<'tcx>,
-) -> Option<ty::ValTree<'tcx>> {
- match place.layout.ty.kind() {
- ty::FnDef(..) => Some(ty::ValTree::zst()),
- ty::Bool | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Char => {
- let val = ecx.read_immediate(&place.into()).unwrap();
- let val = val.to_scalar().unwrap();
- Some(ty::ValTree::Leaf(val.assert_int()))
- }
-
- // Raw pointers are not allowed in type level constants, as we cannot properly test them for
- // equality at compile-time (see `ptr_guaranteed_eq`/`_ne`).
- // Technically we could allow function pointers (represented as `ty::Instance`), but this is not guaranteed to
- // agree with runtime equality tests.
- ty::FnPtr(_) | ty::RawPtr(_) => None,
-
- ty::Ref(_, _, _) => {
- let derefd_place = ecx.deref_operand(&place.into()).unwrap_or_else(|e| bug!("couldn't deref {:?}, error: {:?}", place, e));
- debug!(?derefd_place);
-
- const_to_valtree_inner(ecx, &derefd_place)
- }
-
- ty::Str | ty::Slice(_) | ty::Array(_, _) => {
- let valtree = slice_branches(ecx, place);
- debug!(?valtree);
-
- valtree
- }
- // Trait objects are not allowed in type level constants, as we have no concept for
- // resolving their backing type, even if we can do that at const eval time. We may
- // hypothetically be able to allow `dyn StructuralEq` trait objects in the future,
- // but it is unclear if this is useful.
- ty::Dynamic(..) => None,
-
- ty::Tuple(substs) => branches(ecx, place, substs.len(), None),
-
- ty::Adt(def, _) => {
- if def.variants().is_empty() {
- bug!("uninhabited types should have errored and never gotten converted to valtree")
- }
-
- let variant = ecx.read_discriminant(&place.into()).unwrap().1;
-
- branches(ecx, place, def.variant(variant).fields.len(), def.is_enum().then_some(variant))
- }
-
- ty::Never
- | ty::Error(_)
- | ty::Foreign(..)
- | ty::Infer(ty::FreshIntTy(_))
- | ty::Infer(ty::FreshFloatTy(_))
- | ty::Projection(..)
- | ty::Param(_)
- | ty::Bound(..)
- | ty::Placeholder(..)
- // FIXME(oli-obk): we could look behind opaque types
- | ty::Opaque(..)
- | ty::Infer(_)
- // FIXME(oli-obk): we can probably encode closures just like structs
- | ty::Closure(..)
- | ty::Generator(..)
- | ty::GeneratorWitness(..) => None,
- }
-}
-
/// This function should never fail for validated constants. However, it is also invoked from the
/// pretty printer which might attempt to format invalid constants and in that case it might fail.
pub(crate) fn try_destructure_const<'tcx>(
Ok(mir::DestructuredConst { variant, fields })
}
+#[instrument(skip(tcx), level = "debug")]
pub(crate) fn deref_const<'tcx>(
tcx: TyCtxt<'tcx>,
param_env: ty::ParamEnv<'tcx>,
--- /dev/null
+use super::eval_queries::{mk_eval_cx, op_to_const};
+use super::machine::CompileTimeEvalContext;
+use crate::interpret::{
+ intern_const_alloc_recursive, ConstValue, ImmTy, Immediate, InternKind, MemPlaceMeta,
+ MemoryKind, PlaceTy, Scalar, ScalarMaybeUninit,
+};
+use rustc_middle::mir::interpret::ConstAlloc;
+use rustc_middle::ty::{self, ScalarInt, Ty, TyCtxt};
+use rustc_span::source_map::DUMMY_SP;
+use rustc_target::abi::{Align, VariantIdx};
+
+use crate::interpret::MPlaceTy;
+use crate::interpret::Value;
+
+/// Convert an evaluated constant to a type level constant
+#[instrument(skip(tcx), level = "debug")]
+pub(crate) fn const_to_valtree<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ param_env: ty::ParamEnv<'tcx>,
+ raw: ConstAlloc<'tcx>,
+) -> Option<ty::ValTree<'tcx>> {
+ let ecx = mk_eval_cx(
+ tcx, DUMMY_SP, param_env,
+ // It is absolutely crucial for soundness that
+ // we do not read from static items or other mutable memory.
+ false,
+ );
+ let place = ecx.raw_const_to_mplace(raw).unwrap();
+ const_to_valtree_inner(&ecx, &place)
+}
+
+#[instrument(skip(ecx), level = "debug")]
+fn branches<'tcx>(
+ ecx: &CompileTimeEvalContext<'tcx, 'tcx>,
+ place: &MPlaceTy<'tcx>,
+ n: usize,
+ variant: Option<VariantIdx>,
+) -> Option<ty::ValTree<'tcx>> {
+ let place = match variant {
+ Some(variant) => ecx.mplace_downcast(&place, variant).unwrap(),
+ None => *place,
+ };
+ let variant = variant.map(|variant| Some(ty::ValTree::Leaf(ScalarInt::from(variant.as_u32()))));
+ debug!(?place, ?variant);
+
+ let fields = (0..n).map(|i| {
+ let field = ecx.mplace_field(&place, i).unwrap();
+ const_to_valtree_inner(ecx, &field)
+ });
+ // For enums, we preped their variant index before the variant's fields so we can figure out
+ // the variant again when just seeing a valtree.
+ let branches = variant.into_iter().chain(fields);
+ Some(ty::ValTree::Branch(ecx.tcx.arena.alloc_from_iter(branches.collect::<Option<Vec<_>>>()?)))
+}
+
+#[instrument(skip(ecx), level = "debug")]
+fn slice_branches<'tcx>(
+ ecx: &CompileTimeEvalContext<'tcx, 'tcx>,
+ place: &MPlaceTy<'tcx>,
+) -> Option<ty::ValTree<'tcx>> {
+ let n = place.len(&ecx.tcx.tcx).expect(&format!("expected to use len of place {:?}", place));
+ let branches = (0..n).map(|i| {
+ let place_elem = ecx.mplace_index(place, i).unwrap();
+ const_to_valtree_inner(ecx, &place_elem)
+ });
+
+ Some(ty::ValTree::Branch(ecx.tcx.arena.alloc_from_iter(branches.collect::<Option<Vec<_>>>()?)))
+}
+
+#[instrument(skip(ecx), level = "debug")]
+fn const_to_valtree_inner<'tcx>(
+ ecx: &CompileTimeEvalContext<'tcx, 'tcx>,
+ place: &MPlaceTy<'tcx>,
+) -> Option<ty::ValTree<'tcx>> {
+ match place.layout.ty.kind() {
+ ty::FnDef(..) => Some(ty::ValTree::zst()),
+ ty::Bool | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Char => {
+ let val = ecx.read_immediate(&place.into()).unwrap();
+ let val = val.to_scalar().unwrap();
+ Some(ty::ValTree::Leaf(val.assert_int()))
+ }
+
+ // Raw pointers are not allowed in type level constants, as we cannot properly test them for
+ // equality at compile-time (see `ptr_guaranteed_eq`/`_ne`).
+ // Technically we could allow function pointers (represented as `ty::Instance`), but this is not guaranteed to
+ // agree with runtime equality tests.
+ ty::FnPtr(_) | ty::RawPtr(_) => None,
+
+ ty::Ref(_, _, _) => {
+ let derefd_place = ecx.deref_operand(&place.into()).unwrap_or_else(|e| bug!("couldn't deref {:?}, error: {:?}", place, e));
+ debug!(?derefd_place);
+
+ const_to_valtree_inner(ecx, &derefd_place)
+ }
+
+ ty::Str | ty::Slice(_) | ty::Array(_, _) => {
+ let valtree = slice_branches(ecx, place);
+ debug!(?valtree);
+
+ valtree
+ }
+ // Trait objects are not allowed in type level constants, as we have no concept for
+ // resolving their backing type, even if we can do that at const eval time. We may
+ // hypothetically be able to allow `dyn StructuralEq` trait objects in the future,
+ // but it is unclear if this is useful.
+ ty::Dynamic(..) => None,
+
+ ty::Tuple(substs) => branches(ecx, place, substs.len(), None),
+
+ ty::Adt(def, _) => {
+ if def.is_union() {
+ return None
+ } else if def.variants().is_empty() {
+ bug!("uninhabited types should have errored and never gotten converted to valtree")
+ }
+
+ let variant = ecx.read_discriminant(&place.into()).unwrap().1;
+
+ branches(ecx, place, def.variant(variant).fields.len(), def.is_enum().then_some(variant))
+ }
+
+ ty::Never
+ | ty::Error(_)
+ | ty::Foreign(..)
+ | ty::Infer(ty::FreshIntTy(_))
+ | ty::Infer(ty::FreshFloatTy(_))
+ | ty::Projection(..)
+ | ty::Param(_)
+ | ty::Bound(..)
+ | ty::Placeholder(..)
+ // FIXME(oli-obk): we could look behind opaque types
+ | ty::Opaque(..)
+ | ty::Infer(_)
+ // FIXME(oli-obk): we can probably encode closures just like structs
+ | ty::Closure(..)
+ | ty::Generator(..)
+ | ty::GeneratorWitness(..) => None,
+ }
+}
+
+#[instrument(skip(ecx), level = "debug")]
+fn create_mplace_from_layout<'tcx>(
+ ecx: &mut CompileTimeEvalContext<'tcx, 'tcx>,
+ ty: Ty<'tcx>,
+) -> MPlaceTy<'tcx> {
+ let tcx = ecx.tcx;
+ let param_env = ecx.param_env;
+ let layout = tcx.layout_of(param_env.and(ty)).unwrap();
+ debug!(?layout);
+
+ ecx.allocate(layout, MemoryKind::Stack).unwrap()
+}
+
+// Walks custom DSTs and gets the type of the unsized field and the number of elements
+// in the unsized field.
+fn get_info_on_unsized_field<'tcx>(
+ ty: Ty<'tcx>,
+ valtree: ty::ValTree<'tcx>,
+ tcx: TyCtxt<'tcx>,
+) -> (Ty<'tcx>, usize) {
+ let mut last_valtree = valtree;
+ let tail = tcx.struct_tail_with_normalize(
+ ty,
+ |ty| ty,
+ || {
+ let branches = last_valtree.unwrap_branch();
+ last_valtree = branches[branches.len() - 1];
+ debug!(?branches, ?last_valtree);
+ },
+ );
+ let unsized_inner_ty = match tail.kind() {
+ ty::Slice(t) => *t,
+ ty::Str => tail,
+ _ => bug!("expected Slice or Str"),
+ };
+
+ // Have to adjust type for ty::Str
+ let unsized_inner_ty = match unsized_inner_ty.kind() {
+ ty::Str => tcx.mk_ty(ty::Uint(ty::UintTy::U8)),
+ _ => unsized_inner_ty,
+ };
+
+ // Get the number of elements in the unsized field
+ let num_elems = last_valtree.unwrap_branch().len();
+
+ (unsized_inner_ty, num_elems)
+}
+
+#[instrument(skip(ecx), level = "debug")]
+fn create_pointee_place<'tcx>(
+ ecx: &mut CompileTimeEvalContext<'tcx, 'tcx>,
+ ty: Ty<'tcx>,
+ valtree: ty::ValTree<'tcx>,
+) -> MPlaceTy<'tcx> {
+ let tcx = ecx.tcx.tcx;
+
+ if !ty.is_sized(ecx.tcx, ty::ParamEnv::empty()) {
+ // We need to create `Allocation`s for custom DSTs
+
+ let (unsized_inner_ty, num_elems) = get_info_on_unsized_field(ty, valtree, tcx);
+ let unsized_inner_ty = match unsized_inner_ty.kind() {
+ ty::Str => tcx.mk_ty(ty::Uint(ty::UintTy::U8)),
+ _ => unsized_inner_ty,
+ };
+ let unsized_inner_ty_size =
+ tcx.layout_of(ty::ParamEnv::empty().and(unsized_inner_ty)).unwrap().layout.size();
+ debug!(?unsized_inner_ty, ?unsized_inner_ty_size, ?num_elems);
+
+ // for custom DSTs only the last field/element is unsized, but we need to also allocate
+ // space for the other fields/elements
+ let layout = tcx.layout_of(ty::ParamEnv::empty().and(ty)).unwrap();
+ let size_of_sized_part = layout.layout.size();
+
+ // Get the size of the memory behind the DST
+ let dst_size = unsized_inner_ty_size.checked_mul(num_elems as u64, &tcx).unwrap();
+
+ let ptr = ecx
+ .allocate_ptr(
+ size_of_sized_part.checked_add(dst_size, &tcx).unwrap(),
+ Align::from_bytes(1).unwrap(),
+ MemoryKind::Stack,
+ )
+ .unwrap();
+ debug!(?ptr);
+
+ let mut place = MPlaceTy::from_aligned_ptr(ptr.into(), layout);
+ place.meta = MemPlaceMeta::Meta(Scalar::from_u64(num_elems as u64));
+ debug!(?place);
+
+ place
+ } else {
+ create_mplace_from_layout(ecx, ty)
+ }
+}
+
+/// Converts a `ValTree` to a `ConstValue`, which is needed after mir
+/// construction has finished.
+// FIXME Merge `valtree_to_const_value` and `fill_place_recursively` into one function
+#[instrument(skip(tcx), level = "debug")]
+pub fn valtree_to_const_value<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ param_env_ty: ty::ParamEnvAnd<'tcx, Ty<'tcx>>,
+ valtree: ty::ValTree<'tcx>,
+) -> ConstValue<'tcx> {
+ // Basic idea: We directly construct `Scalar` values from trivial `ValTree`s
+ // (those for constants with type bool, int, uint, float or char).
+ // For all other types we create an `MPlace` and fill that by walking
+ // the `ValTree` and using `place_projection` and `place_field` to
+ // create inner `MPlace`s which are filled recursively.
+ // FIXME Does this need an example?
+
+ let (param_env, ty) = param_env_ty.into_parts();
+ let mut ecx = mk_eval_cx(tcx, DUMMY_SP, param_env, false);
+
+ match ty.kind() {
+ ty::FnDef(..) => {
+ assert!(valtree.unwrap_branch().is_empty());
+ ConstValue::Scalar(Scalar::ZST)
+ }
+ ty::Bool | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Char => match valtree {
+ ty::ValTree::Leaf(scalar_int) => ConstValue::Scalar(Scalar::Int(scalar_int)),
+ ty::ValTree::Branch(_) => bug!(
+ "ValTrees for Bool, Int, Uint, Float or Char should have the form ValTree::Leaf"
+ ),
+ },
+ ty::Ref(_, _, _) | ty::Tuple(_) | ty::Array(_, _) | ty::Adt(..) => {
+ let mut place = match ty.kind() {
+ ty::Ref(_, inner_ty, _) => {
+ // Need to create a place for the pointee to fill for Refs
+ create_pointee_place(&mut ecx, *inner_ty, valtree)
+ }
+ _ => create_mplace_from_layout(&mut ecx, ty),
+ };
+ debug!(?place);
+
+ fill_place_recursively(&mut ecx, &mut place, valtree);
+ dump_place(&ecx, place.into());
+ intern_const_alloc_recursive(&mut ecx, InternKind::Constant, &place).unwrap();
+
+ let const_val = match ty.kind() {
+ ty::Ref(_, _, _) => {
+ let ref_place = place.to_ref(&tcx);
+ let imm =
+ ImmTy::from_immediate(ref_place, tcx.layout_of(param_env_ty).unwrap());
+
+ op_to_const(&ecx, &imm.into())
+ }
+ _ => op_to_const(&ecx, &place.into()),
+ };
+ debug!(?const_val);
+
+ const_val
+ }
+ ty::Never
+ | ty::Error(_)
+ | ty::Foreign(..)
+ | ty::Infer(ty::FreshIntTy(_))
+ | ty::Infer(ty::FreshFloatTy(_))
+ | ty::Projection(..)
+ | ty::Param(_)
+ | ty::Bound(..)
+ | ty::Placeholder(..)
+ | ty::Opaque(..)
+ | ty::Infer(_)
+ | ty::Closure(..)
+ | ty::Generator(..)
+ | ty::GeneratorWitness(..)
+ | ty::FnPtr(_)
+ | ty::RawPtr(_)
+ | ty::Str
+ | ty::Slice(_)
+ | ty::Dynamic(..) => bug!("no ValTree should have been created for type {:?}", ty.kind()),
+ }
+}
+
+// FIXME Needs a better/correct name
+#[instrument(skip(ecx), level = "debug")]
+fn fill_place_recursively<'tcx>(
+ ecx: &mut CompileTimeEvalContext<'tcx, 'tcx>,
+ place: &mut MPlaceTy<'tcx>,
+ valtree: ty::ValTree<'tcx>,
+) {
+ // This will match on valtree and write the value(s) corresponding to the ValTree
+ // inside the place recursively.
+
+ let tcx = ecx.tcx.tcx;
+ let ty = place.layout.ty;
+
+ match ty.kind() {
+ ty::FnDef(_, _) => {
+ ecx.write_immediate(
+ Immediate::Scalar(ScalarMaybeUninit::Scalar(Scalar::ZST)),
+ &(*place).into(),
+ )
+ .unwrap();
+ }
+ ty::Bool | ty::Int(_) | ty::Uint(_) | ty::Float(_) | ty::Char => {
+ let scalar_int = valtree.unwrap_leaf();
+ debug!("writing trivial valtree {:?} to place {:?}", scalar_int, place);
+ ecx.write_immediate(
+ Immediate::Scalar(ScalarMaybeUninit::Scalar(scalar_int.into())),
+ &(*place).into(),
+ )
+ .unwrap();
+ }
+ ty::Ref(_, inner_ty, _) => {
+ let mut pointee_place = create_pointee_place(ecx, *inner_ty, valtree);
+ debug!(?pointee_place);
+
+ fill_place_recursively(ecx, &mut pointee_place, valtree);
+ dump_place(ecx, pointee_place.into());
+ intern_const_alloc_recursive(ecx, InternKind::Constant, &pointee_place).unwrap();
+
+ let imm = match inner_ty.kind() {
+ ty::Slice(_) | ty::Str => {
+ let len = valtree.unwrap_branch().len();
+ let len_scalar = ScalarMaybeUninit::Scalar(Scalar::from_u64(len as u64));
+
+ Immediate::ScalarPair(
+ ScalarMaybeUninit::from_maybe_pointer((*pointee_place).ptr, &tcx),
+ len_scalar,
+ )
+ }
+ _ => pointee_place.to_ref(&tcx),
+ };
+ debug!(?imm);
+
+ ecx.write_immediate(imm, &(*place).into()).unwrap();
+ }
+ ty::Adt(_, _) | ty::Tuple(_) | ty::Array(_, _) | ty::Str | ty::Slice(_) => {
+ let branches = valtree.unwrap_branch();
+
+ // Need to downcast place for enums
+ let (place_adjusted, branches, variant_idx) = match ty.kind() {
+ ty::Adt(def, _) if def.is_enum() => {
+ // First element of valtree corresponds to variant
+ let scalar_int = branches[0].unwrap_leaf();
+ let variant_idx = VariantIdx::from_u32(scalar_int.try_to_u32().unwrap());
+ let variant = def.variant(variant_idx);
+ debug!(?variant);
+
+ (
+ place.project_downcast(ecx, variant_idx).unwrap(),
+ &branches[1..],
+ Some(variant_idx),
+ )
+ }
+ _ => (*place, branches, None),
+ };
+ debug!(?place_adjusted, ?branches);
+
+ // Create the places (by indexing into `place`) for the fields and fill
+ // them recursively
+ for (i, inner_valtree) in branches.iter().enumerate() {
+ debug!(?i, ?inner_valtree);
+
+ let mut place_inner = match ty.kind() {
+ ty::Str | ty::Slice(_) => ecx.mplace_index(&place, i as u64).unwrap(),
+ _ if !ty.is_sized(ecx.tcx, ty::ParamEnv::empty())
+ && i == branches.len() - 1 =>
+ {
+ // Note: For custom DSTs we need to manually process the last unsized field.
+ // We created a `Pointer` for the `Allocation` of the complete sized version of
+ // the Adt in `create_pointee_place` and now we fill that `Allocation` with the
+ // values in the ValTree. For the unsized field we have to additionally add the meta
+ // data.
+
+ let (unsized_inner_ty, num_elems) =
+ get_info_on_unsized_field(ty, valtree, tcx);
+ debug!(?unsized_inner_ty);
+
+ let inner_ty = match ty.kind() {
+ ty::Adt(def, substs) => {
+ def.variant(VariantIdx::from_u32(0)).fields[i].ty(tcx, substs)
+ }
+ ty::Tuple(inner_tys) => inner_tys[i],
+ _ => bug!("unexpected unsized type {:?}", ty),
+ };
+
+ let inner_layout =
+ tcx.layout_of(ty::ParamEnv::empty().and(inner_ty)).unwrap();
+ debug!(?inner_layout);
+
+ let offset = place_adjusted.layout.fields.offset(i);
+ place
+ .offset(
+ offset,
+ MemPlaceMeta::Meta(Scalar::from_u64(num_elems as u64)),
+ inner_layout,
+ &tcx,
+ )
+ .unwrap()
+ }
+ _ => ecx.mplace_field(&place_adjusted, i).unwrap(),
+ };
+
+ debug!(?place_inner);
+ fill_place_recursively(ecx, &mut place_inner, *inner_valtree);
+ dump_place(&ecx, place_inner.into());
+ }
+
+ debug!("dump of place_adjusted:");
+ dump_place(ecx, place_adjusted.into());
+
+ if let Some(variant_idx) = variant_idx {
+ // don't forget filling the place with the discriminant of the enum
+ ecx.write_discriminant(variant_idx, &(*place).into()).unwrap();
+ }
+
+ debug!("dump of place after writing discriminant:");
+ dump_place(ecx, (*place).into());
+ }
+ _ => bug!("shouldn't have created a ValTree for {:?}", ty),
+ }
+}
+
+fn dump_place<'tcx>(ecx: &CompileTimeEvalContext<'tcx, 'tcx>, place: PlaceTy<'tcx>) {
+ trace!("{:?}", ecx.dump_place(*place));
+}
pub use self::operand::{ImmTy, Immediate, OpTy, Operand};
pub use self::place::{MPlaceTy, MemPlace, MemPlaceMeta, Place, PlaceTy};
pub use self::validity::{CtfeValidationMode, RefTracking};
-pub use self::visitor::{MutValueVisitor, ValueVisitor};
+pub use self::visitor::{MutValueVisitor, Value, ValueVisitor};
crate use self::intrinsics::eval_nullary_intrinsic;
use eval_context::{from_known_layout, mir_assign_valid_types};
})
}
+ #[instrument(skip(self), level = "debug")]
pub fn operand_projection(
&self,
base: &OpTy<'tcx, M::PointerTag>,
}
}
+impl<'tcx, Tag: Provenance> std::ops::DerefMut for MPlaceTy<'tcx, Tag> {
+ fn deref_mut(&mut self) -> &mut Self::Target {
+ &mut self.mplace
+ }
+}
+
impl<'tcx, Tag: Provenance> From<MPlaceTy<'tcx, Tag>> for PlaceTy<'tcx, Tag> {
#[inline(always)]
fn from(mplace: MPlaceTy<'tcx, Tag>) -> Self {
/// Take an operand, representing a pointer, and dereference it to a place -- that
/// will always be a MemPlace. Lives in `place.rs` because it creates a place.
+ #[instrument(skip(self), level = "debug")]
pub fn deref_operand(
&self,
src: &OpTy<'tcx, M::PointerTag>,
}
/// Project into an mplace
- pub(super) fn mplace_projection(
+ #[instrument(skip(self), level = "debug")]
+ pub(crate) fn mplace_projection(
&self,
base: &MPlaceTy<'tcx, M::PointerTag>,
proj_elem: mir::PlaceElem<'tcx>,
/// Just a convenience function, but used quite a bit.
/// This is the only projection that might have a side-effect: We cannot project
/// into the field of a local `ScalarPair`, we have to first allocate it.
+ #[instrument(skip(self), level = "debug")]
pub fn place_field(
&mut self,
base: &PlaceTy<'tcx, M::PointerTag>,
/// Computes a place. You should only use this if you intend to write into this
/// place; for reading, a more efficient alternative is `eval_place_for_read`.
+ #[instrument(skip(self), level = "debug")]
pub fn eval_place(
&mut self,
place: mir::Place<'tcx>,
/// Write an immediate to a place
#[inline(always)]
+ #[instrument(skip(self), level = "debug")]
pub fn write_immediate(
&mut self,
src: Immediate<M::PointerTag>,
/// Copies the data from an operand to a place. This does not support transmuting!
/// Use `copy_op_transmute` if the layouts could disagree.
#[inline(always)]
+ #[instrument(skip(self), level = "debug")]
pub fn copy_op(
&mut self,
src: &OpTy<'tcx, M::PointerTag>,
/// Use `copy_op_transmute` if the layouts could disagree.
/// Also, if you use this you are responsible for validating that things get copied at the
/// right type.
+ #[instrument(skip(self), level = "debug")]
fn copy_op_no_validate(
&mut self,
src: &OpTy<'tcx, M::PointerTag>,
/// This supports unsized types and returns the computed size to avoid some
/// redundant computation when copying; use `force_allocation` for a simpler, sized-only
/// version.
+ #[instrument(skip(self), level = "debug")]
pub fn force_allocation_maybe_sized(
&mut self,
place: &PlaceTy<'tcx, M::PointerTag>,
}
/// Writes the discriminant of the given variant.
+ #[instrument(skip(self), level = "debug")]
pub fn write_discriminant(
&mut self,
variant_index: VariantIdx,
pub mod util;
use rustc_middle::ty::query::Providers;
+use rustc_middle::ty::ParamEnv;
pub fn provide(providers: &mut Providers) {
const_eval::provide(providers);
let (param_env, raw) = param_env_and_value.into_parts();
const_eval::const_to_valtree(tcx, param_env, raw)
};
+ providers.valtree_to_const_val = |tcx, (ty, valtree)| {
+ const_eval::valtree_to_const_value(tcx, ParamEnv::empty().and(ty), valtree)
+ };
providers.deref_const = |tcx, param_env_and_value| {
let (param_env, value) = param_env_and_value.into_parts();
const_eval::deref_const(tcx, param_env, value)
//! move analysis runs after promotion on broken MIR.
use rustc_hir as hir;
-use rustc_middle::mir::traversal::ReversePostorder;
+use rustc_middle::mir::traversal::ReversePostorderIter;
use rustc_middle::mir::visit::{MutVisitor, MutatingUseContext, PlaceContext, Visitor};
use rustc_middle::mir::*;
use rustc_middle::ty::cast::CastTy;
pub fn collect_temps_and_candidates<'tcx>(
ccx: &ConstCx<'_, 'tcx>,
- rpo: &mut ReversePostorder<'_, 'tcx>,
+ rpo: &mut ReversePostorderIter<'_, 'tcx>,
) -> (IndexVec<Local, TempState>, Vec<Candidate>) {
let mut collector = Collector {
temps: IndexVec::from_elem(TempState::Undefined, &ccx.body.local_decls),
fn visit_operand(&mut self, operand: &Operand<'tcx>, location: Location) {
// This check is somewhat expensive, so only run it when -Zvalidate-mir is passed.
- if self.tcx.sess.opts.debugging_opts.validate_mir {
+ if self.tcx.sess.opts.debugging_opts.validate_mir && self.mir_phase < MirPhase::DropsLowered
+ {
// `Operand::Copy` is only supposed to be used with `Copy` types.
if let Operand::Copy(place) = operand {
let ty = place.ty(&self.body.local_decls, self.tcx).ty;
} else {
None
};
- let parent_self_ty = tcx
- .parent(method_did)
- .filter(|did| tcx.def_kind(*did) == rustc_hir::def::DefKind::Impl)
+ let parent_did = tcx.parent(method_did);
+ let parent_self_ty = (tcx.def_kind(parent_did) == rustc_hir::def::DefKind::Impl)
+ .then_some(parent_did)
.and_then(|did| match tcx.type_of(did).kind() {
ty::Adt(def, ..) => Some(def.did()),
_ => None,
Erroneous code example:
-```ignore (limited to a warning during 2018 edition development)
+```rust2018,compile_fail,E0705
#![feature(rust_2018_preview)]
#![feature(test_2018_feature)] // error: the feature
// `test_2018_feature` is
parser-struct-literal-body-without-path =
struct literal body without path
.suggestion = you might have forgotten to add the struct literal inside the block
+
+parser-maybe-report-ambiguous-plus =
+ ambiguous `+` in a type
+ .suggestion = use parentheses to disambiguate
typeck-address-of-temporary-taken = cannot take address of a temporary
.label = temporary value
+
+typeck-add-return-type-add = try adding a return type
+
+typeck-add-return-type-missing-here = a return type might be missing here
+
+typeck-expected-default-return-type = expected `()` because of default return type
+
+typeck-expected-return-type = expected `{$expected}` because of return type
}
}
+/// Trait implemented by error types. This should not be implemented manually. Instead, use
+/// `#[derive(SessionSubdiagnostic)]` -- see [rustc_macros::SessionSubdiagnostic].
+pub trait AddSubdiagnostic {
+ /// Add a subdiagnostic to an existing diagnostic.
+ fn add_to_diagnostic(self, diag: &mut Diagnostic);
+}
+
#[must_use]
#[derive(Clone, Debug, Encodable, Decodable)]
pub struct Diagnostic {
&mut self,
sp: Span,
msg: impl Into<DiagnosticMessage>,
- suggestion: String,
+ suggestion: impl ToString,
applicability: Applicability,
) -> &mut Self {
self.span_suggestion_with_style(
&mut self,
sp: Span,
msg: impl Into<DiagnosticMessage>,
- suggestion: String,
+ suggestion: impl ToString,
applicability: Applicability,
style: SuggestionStyle,
) -> &mut Self {
self.push_suggestion(CodeSuggestion {
substitutions: vec![Substitution {
- parts: vec![SubstitutionPart { snippet: suggestion, span: sp }],
+ parts: vec![SubstitutionPart { snippet: suggestion.to_string(), span: sp }],
}],
msg: msg.into(),
style,
&mut self,
sp: Span,
msg: impl Into<DiagnosticMessage>,
- suggestion: String,
+ suggestion: impl ToString,
applicability: Applicability,
) -> &mut Self {
self.span_suggestion_with_style(
&mut self,
sp: Span,
msg: impl Into<DiagnosticMessage>,
- suggestion: String,
+ suggestion: impl ToString,
applicability: Applicability,
) -> &mut Self {
self.span_suggestion_with_style(
&mut self,
sp: Span,
msg: impl Into<DiagnosticMessage>,
- suggestion: String,
+ suggestion: impl ToString,
applicability: Applicability,
) -> &mut Self {
self.span_suggestion_with_style(
&mut self,
sp: Span,
msg: impl Into<DiagnosticMessage>,
- suggestion: String,
+ suggestion: impl ToString,
applicability: Applicability,
) -> &mut Self {
self.span_suggestion_with_style(
self
}
+ /// Add a subdiagnostic from a type that implements `SessionSubdiagnostic` - see
+ /// [rustc_macros::SessionSubdiagnostic].
+ pub fn subdiagnostic(&mut self, subdiagnostic: impl AddSubdiagnostic) -> &mut Self {
+ subdiagnostic.add_to_diagnostic(self);
+ self
+ }
+
pub fn set_span<S: Into<MultiSpan>>(&mut self, sp: S) -> &mut Self {
self.span = sp.into();
if let Some(span) = self.span.primary_span() {
/// instead of a `&DiagnosticBuilder<'a>`. This `forward!` macro makes
/// it easy to declare such methods on the builder.
macro_rules! forward {
- // Forward pattern for &self -> &Self
- (
- $(#[$attrs:meta])*
- pub fn $n:ident(&self, $($name:ident: $ty:ty),* $(,)?) -> &Self
- ) => {
- $(#[$attrs])*
- #[doc = concat!("See [`Diagnostic::", stringify!($n), "()`].")]
- pub fn $n(&self, $($name: $ty),*) -> &Self {
- self.diagnostic.$n($($name),*);
- self
- }
- };
-
// Forward pattern for &mut self -> &mut Self
(
$(#[$attrs:meta])*
&mut self,
sp: Span,
msg: impl Into<DiagnosticMessage>,
- suggestion: String,
+ suggestion: impl ToString,
applicability: Applicability,
) -> &mut Self);
forward!(pub fn span_suggestions(
&mut self,
sp: Span,
msg: impl Into<DiagnosticMessage>,
- suggestion: String,
+ suggestion: impl ToString,
applicability: Applicability,
) -> &mut Self);
forward!(pub fn span_suggestion_verbose(
&mut self,
sp: Span,
msg: impl Into<DiagnosticMessage>,
- suggestion: String,
+ suggestion: impl ToString,
applicability: Applicability,
) -> &mut Self);
forward!(pub fn span_suggestion_hidden(
&mut self,
sp: Span,
msg: impl Into<DiagnosticMessage>,
- suggestion: String,
+ suggestion: impl ToString,
applicability: Applicability,
) -> &mut Self);
forward!(pub fn tool_only_span_suggestion(
&mut self,
sp: Span,
msg: impl Into<DiagnosticMessage>,
- suggestion: String,
+ suggestion: impl ToString,
applicability: Applicability,
) -> &mut Self);
name: impl Into<Cow<'static, str>>,
arg: DiagnosticArgValue<'static>,
) -> &mut Self);
+
+ forward!(pub fn subdiagnostic(
+ &mut self,
+ subdiagnostic: impl crate::AddSubdiagnostic
+ ) -> &mut Self);
}
impl<G: EmissionGuarantee> Debug for DiagnosticBuilder<'_, G> {
fn emit_future_breakage_report(&mut self, _diags: Vec<Diagnostic>) {}
/// Emit list of unused externs
- fn emit_unused_externs(&mut self, _lint_level: &str, _unused_externs: &[&str]) {}
+ fn emit_unused_externs(
+ &mut self,
+ _lint_level: rustc_lint_defs::Level,
+ _unused_externs: &[&str],
+ ) {
+ }
/// Checks if should show explanations about "rustc --explain"
fn should_show_explain(&self) -> bool {
}
}
- fn emit_unused_externs(&mut self, lint_level: &str, unused_externs: &[&str]) {
+ fn emit_unused_externs(&mut self, lint_level: rustc_lint_defs::Level, unused_externs: &[&str]) {
+ let lint_level = lint_level.as_str();
let data = UnusedExterns { lint_level, unused_extern_names: unused_externs };
let result = if self.pretty {
writeln!(&mut self.dst, "{}", as_pretty_json(&data))
impl error::Error for ExplicitBug {}
pub use diagnostic::{
- Diagnostic, DiagnosticArg, DiagnosticArgValue, DiagnosticId, DiagnosticStyledString,
- IntoDiagnosticArg, SubDiagnostic,
+ AddSubdiagnostic, Diagnostic, DiagnosticArg, DiagnosticArgValue, DiagnosticId,
+ DiagnosticStyledString, IntoDiagnosticArg, SubDiagnostic,
};
pub use diagnostic_builder::{DiagnosticBuilder, EmissionGuarantee};
use std::backtrace::Backtrace;
self.inner.borrow_mut().emitter.emit_future_breakage_report(diags)
}
- pub fn emit_unused_externs(&self, lint_level: &str, unused_externs: &[&str]) {
- self.inner.borrow_mut().emit_unused_externs(lint_level, unused_externs)
+ pub fn emit_unused_externs(
+ &self,
+ lint_level: rustc_lint_defs::Level,
+ loud: bool,
+ unused_externs: &[&str],
+ ) {
+ let mut inner = self.inner.borrow_mut();
+
+ if loud && lint_level.is_error() {
+ inner.bump_err_count();
+ }
+
+ inner.emit_unused_externs(lint_level, unused_externs)
}
pub fn update_unstable_expectation_id(
self.emitter.emit_artifact_notification(path, artifact_type);
}
- fn emit_unused_externs(&mut self, lint_level: &str, unused_externs: &[&str]) {
+ fn emit_unused_externs(&mut self, lint_level: rustc_lint_defs::Level, unused_externs: &[&str]) {
self.emitter.emit_unused_externs(lint_level, unused_externs);
}
// Once we've located the `#[proc_macro_derive]` attribute, verify
// that it's of the form `#[proc_macro_derive(Foo)]` or
// `#[proc_macro_derive(Foo, attributes(A, ..))]`
- let Some(list) = attr.meta_item_list() else {
- return None;
- };
+ let list = attr.meta_item_list()?;
if list.len() != 1 && list.len() != 2 {
diag.span_err(attr.span, "attribute must have either one or two arguments");
return None;
bounds,
kind: ast::GenericParamKind::Type { default },
is_placeholder: false,
+ colon_span: None,
}
}
//! Conditional compilation stripping.
use rustc_ast::ptr::P;
-use rustc_ast::token::{DelimToken, Token, TokenKind};
+use rustc_ast::token::{Delimiter, Token, TokenKind};
use rustc_ast::tokenstream::{AttrAnnotatedTokenStream, AttrAnnotatedTokenTree};
use rustc_ast::tokenstream::{DelimSpan, Spacing};
use rustc_ast::tokenstream::{LazyTokenStream, TokenTree};
// in `#[attr]`, so just use the span of the `#` token.
let bracket_group = AttrAnnotatedTokenTree::Delimited(
DelimSpan::from_single(pound_span),
- DelimToken::Bracket,
+ Delimiter::Bracket,
item.tokens
.as_ref()
.unwrap_or_else(|| panic!("Missing tokens for {:?}", item))
err.span_suggestion(
span,
"expected syntax is",
- suggestion.into(),
+ suggestion,
Applicability::HasPlaceholders,
);
}
use rustc_ast as ast;
use rustc_ast::mut_visit::*;
use rustc_ast::ptr::P;
-use rustc_ast::token;
+use rustc_ast::token::{self, Delimiter};
use rustc_ast::tokenstream::TokenStream;
use rustc_ast::visit::{self, AssocCtxt, Visitor};
use rustc_ast::{AssocItemKind, AstLike, AstLikeWrapper, AttrStyle, ExprKind, ForeignItemKind};
AstFragmentKind::Stmts => {
let mut stmts = SmallVec::new();
// Won't make progress on a `}`.
- while this.token != token::Eof && this.token != token::CloseDelim(token::Brace) {
+ while this.token != token::Eof && this.token != token::CloseDelim(Delimiter::Brace) {
if let Some(stmt) = this.parse_full_stmt(AttemptLocalParseRecovery::Yes)? {
stmts.push(stmt);
}
crate mod transcribe;
use metavar_expr::MetaVarExpr;
-use rustc_ast::token::{self, NonterminalKind, Token, TokenKind};
+use rustc_ast::token::{Delimiter, NonterminalKind, Token, TokenKind};
use rustc_ast::tokenstream::DelimSpan;
use rustc_span::symbol::Ident;
use rustc_span::Span;
-/// Contains the sub-token-trees of a "delimited" token tree such as `(a b c)`. The delimiters
-/// might be `NoDelim`, but they are not represented explicitly.
+/// Contains the sub-token-trees of a "delimited" token tree such as `(a b c)`.
+/// The delimiters are not represented explicitly in the `tts` vector.
#[derive(PartialEq, Encodable, Decodable, Debug)]
struct Delimited {
- delim: token::DelimToken,
+ delim: Delimiter,
/// FIXME: #67062 has details about why this is sub-optimal.
tts: Vec<TokenTree>,
}
//! bound.
use crate::mbe::{KleeneToken, TokenTree};
-use rustc_ast::token::{DelimToken, Token, TokenKind};
+use rustc_ast::token::{Delimiter, Token, TokenKind};
use rustc_ast::{NodeId, DUMMY_NODE_ID};
use rustc_data_structures::fx::FxHashMap;
use rustc_errors::MultiSpan;
}
(NestedMacroState::MacroRulesNotName, &TokenTree::Delimited(_, ref del))
| (NestedMacroState::MacroName, &TokenTree::Delimited(_, ref del))
- if del.delim == DelimToken::Brace =>
+ if del.delim == Delimiter::Brace =>
{
let macro_rules = state == NestedMacroState::MacroRulesNotName;
state = NestedMacroState::Empty;
check_occurrences(sess, node_id, tt, macros, binders, ops, valid);
}
(NestedMacroState::MacroName, &TokenTree::Delimited(_, ref del))
- if del.delim == DelimToken::Paren =>
+ if del.delim == Delimiter::Parenthesis =>
{
state = NestedMacroState::MacroNameParen;
nested_binders = Binders::default();
);
}
(NestedMacroState::MacroNameParen, &TokenTree::Delimited(_, ref del))
- if del.delim == DelimToken::Brace =>
+ if del.delim == Delimiter::Brace =>
{
state = NestedMacroState::Empty;
check_occurrences(
use crate::mbe::transcribe::transcribe;
use rustc_ast as ast;
-use rustc_ast::token::{self, NonterminalKind, Token, TokenKind, TokenKind::*};
+use rustc_ast::token::{self, Delimiter, NonterminalKind, Token, TokenKind, TokenKind::*};
use rustc_ast::tokenstream::{DelimSpan, TokenStream};
use rustc_ast::{NodeId, DUMMY_NODE_ID};
use rustc_ast_pretty::pprust;
// Merge the gated spans from parsing the matcher with the pre-existing ones.
sess.gated_spans.merge(gated_spans_snapshot);
- // Ignore the delimiters on the RHS.
- let rhs = match &rhses[i] {
- mbe::TokenTree::Delimited(_, delimited) => &delimited.tts,
+ let (rhs, rhs_span): (&mbe::Delimited, DelimSpan) = match &rhses[i] {
+ mbe::TokenTree::Delimited(span, delimited) => (&delimited, *span),
_ => cx.span_bug(sp, "malformed macro rhs"),
};
let arm_span = rhses[i].span();
- let rhs_spans = rhs.iter().map(|t| t.span()).collect::<Vec<_>>();
+ let rhs_spans = rhs.tts.iter().map(|t| t.span()).collect::<Vec<_>>();
// rhs has holes ( `$id` and `$(...)` that need filled)
- let mut tts = match transcribe(cx, &named_matches, &rhs, transparency) {
+ let mut tts = match transcribe(cx, &named_matches, &rhs, rhs_span, transparency) {
Ok(tts) => tts,
Err(mut err) => {
err.emit();
];
match tok {
TokenTree::Token(token) => match token.kind {
- OpenDelim(token::DelimToken::Brace)
- | OpenDelim(token::DelimToken::Bracket)
+ OpenDelim(Delimiter::Brace)
+ | OpenDelim(Delimiter::Bracket)
| Comma
| FatArrow
| Colon
-use rustc_ast::token;
+use rustc_ast::token::{self, Delimiter};
use rustc_ast::tokenstream::{Cursor, TokenStream, TokenTree};
use rustc_ast::{LitIntType, LitKind};
use rustc_ast_pretty::pprust;
) -> 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 Some(TokenTree::Delimited(_, Delimiter::Parenthesis, 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));
};
use crate::mbe::macro_parser::count_metavar_decls;
use crate::mbe::{Delimited, KleeneOp, KleeneToken, MetaVarExpr, SequenceRepetition, TokenTree};
-use rustc_ast::token::{self, Token};
+use rustc_ast::token::{self, Delimiter, Token};
use rustc_ast::{tokenstream, NodeId};
use rustc_ast_pretty::pprust;
use rustc_feature::Features;
match tree {
// `tree` is a `$` token. Look at the next token in `trees`
tokenstream::TokenTree::Token(Token { kind: token::Dollar, span }) => {
- // FIXME: Handle `None`-delimited groups in a more systematic way
+ // FIXME: Handle `Invisible`-delimited groups in a more systematic way
// during parsing.
let mut next = outer_trees.next();
let mut trees: Box<dyn Iterator<Item = tokenstream::TokenTree>>;
- if let Some(tokenstream::TokenTree::Delimited(_, token::NoDelim, tts)) = next {
+ if let Some(tokenstream::TokenTree::Delimited(_, Delimiter::Invisible, tts)) = next {
trees = Box::new(tts.into_trees());
next = trees.next();
} else {
// `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 {
+ if delim != Delimiter::Parenthesis {
span_dollar_dollar_or_metavar_in_the_lhs_err(
sess,
&Token { kind: token::OpenDelim(delim), span: delim_span.entire() },
}
} else {
match delim {
- token::Brace => {
+ Delimiter::Brace => {
// The delimiter is `{`. This indicates the beginning
// of a meta-variable expression (e.g. `${count(ident)}`).
// Try to parse the meta-variable expression.
}
}
}
- token::Paren => {}
+ Delimiter::Parenthesis => {}
_ => {
let tok = pprust::token_kind_to_string(&token::OpenDelim(delim));
let msg = format!("expected `(` or `{{`, found `{}`", tok);
use crate::mbe::macro_parser::{MatchedNonterminal, MatchedSeq, MatchedTokenTree, NamedMatch};
use crate::mbe::{self, MetaVarExpr};
use rustc_ast::mut_visit::{self, MutVisitor};
-use rustc_ast::token::{self, Token, TokenKind};
+use rustc_ast::token::{self, Delimiter, Token, TokenKind};
use rustc_ast::tokenstream::{DelimSpan, TokenStream, TokenTree, TreeAndSpacing};
use rustc_data_structures::fx::FxHashMap;
use rustc_errors::{pluralize, PResult};
/// An iterator over the token trees in a delimited token tree (`{ ... }`) or a sequence (`$(...)`).
enum Frame<'a> {
- Delimited {
- tts: &'a [mbe::TokenTree],
- delim_token: token::DelimToken,
- idx: usize,
- span: DelimSpan,
- },
- Sequence {
- tts: &'a [mbe::TokenTree],
- idx: usize,
- sep: Option<Token>,
- },
+ Delimited { tts: &'a [mbe::TokenTree], idx: usize, delim: Delimiter, span: DelimSpan },
+ Sequence { tts: &'a [mbe::TokenTree], idx: usize, sep: Option<Token> },
}
impl<'a> Frame<'a> {
/// Construct a new frame around the delimited set of tokens.
- fn new(tts: &'a [mbe::TokenTree]) -> Frame<'a> {
- Frame::Delimited { tts, delim_token: token::NoDelim, idx: 0, span: DelimSpan::dummy() }
+ fn new(src: &'a mbe::Delimited, span: DelimSpan) -> Frame<'a> {
+ Frame::Delimited { tts: &src.tts, idx: 0, delim: src.delim, span }
}
}
pub(super) fn transcribe<'a>(
cx: &ExtCtxt<'a>,
interp: &FxHashMap<MacroRulesNormalizedIdent, NamedMatch>,
- src: &[mbe::TokenTree],
+ src: &mbe::Delimited,
+ src_span: DelimSpan,
transparency: Transparency,
) -> PResult<'a, TokenStream> {
// Nothing for us to transcribe...
- if src.is_empty() {
+ if src.tts.is_empty() {
return Ok(TokenStream::default());
}
// We descend into the RHS (`src`), expanding things as we go. This stack contains the things
// we have yet to expand/are still expanding. We start the stack off with the whole RHS.
- let mut stack: SmallVec<[Frame<'_>; 1]> = smallvec![Frame::new(&src)];
+ let mut stack: SmallVec<[Frame<'_>; 1]> = smallvec![Frame::new(&src, src_span)];
// As we descend in the RHS, we will need to be able to match nested sequences of matchers.
// `repeats` keeps track of where we are in matching at each level, with the last element being
// We are done processing a Delimited. If this is the top-level delimited, we are
// done. Otherwise, we unwind the result_stack to append what we have produced to
// any previous results.
- Frame::Delimited { delim_token, span, .. } => {
+ Frame::Delimited { delim, span, .. } => {
if result_stack.is_empty() {
// No results left to compute! We are back at the top-level.
return Ok(TokenStream::new(result));
}
// Step back into the parent Delimited.
- let tree = TokenTree::Delimited(span, delim_token, TokenStream::new(result));
+ let tree = TokenTree::Delimited(span, delim, TokenStream::new(result));
result = result_stack.pop().unwrap();
result.push(tree.into());
}
}
MatchedNonterminal(ref nt) => {
// Other variables are emitted into the output stream as groups with
- // `Delimiter::None` to maintain parsing priorities.
+ // `Delimiter::Invisible` to maintain parsing priorities.
// `Interpolated` is currently used for such groups in rustc parser.
marker.visit_span(&mut sp);
let token = TokenTree::token(token::Interpolated(nt.clone()), sp);
mut_visit::visit_delim_span(&mut span, &mut marker);
stack.push(Frame::Delimited {
tts: &delimited.tts,
- delim_token: delimited.delim,
+ delim: delimited.delim,
idx: 0,
span,
});
use crate::tests::{matches_codepattern, string_to_stream, with_error_checking_parse};
use rustc_ast::ptr::P;
-use rustc_ast::token::{self, Token};
+use rustc_ast::token::{self, Delimiter, Token};
use rustc_ast::tokenstream::{DelimSpan, TokenStream, TokenTree};
use rustc_ast::visit;
use rustc_ast::{self as ast, PatKind};
TokenTree::Delimited(_, first_delim, first_tts),
TokenTree::Token(Token { kind: token::FatArrow, .. }),
TokenTree::Delimited(_, second_delim, second_tts),
- ] if macro_delim == &token::Paren => {
+ ] if macro_delim == &Delimiter::Parenthesis => {
let tts = &first_tts.trees().collect::<Vec<_>>();
match &tts[..] {
[
TokenTree::Token(Token { kind: token::Dollar, .. }),
TokenTree::Token(Token { kind: token::Ident(name, false), .. }),
- ] if first_delim == &token::Paren && name.as_str() == "a" => {}
+ ] if first_delim == &Delimiter::Parenthesis && name.as_str() == "a" => {
+ }
_ => panic!("value 3: {:?} {:?}", first_delim, first_tts),
}
let tts = &second_tts.trees().collect::<Vec<_>>();
[
TokenTree::Token(Token { kind: token::Dollar, .. }),
TokenTree::Token(Token { kind: token::Ident(name, false), .. }),
- ] if second_delim == &token::Paren && name.as_str() == "a" => {}
+ ] if second_delim == &Delimiter::Parenthesis
+ && name.as_str() == "a" => {}
_ => panic!("value 4: {:?} {:?}", second_delim, second_tts),
}
}
TokenTree::token(token::Ident(Symbol::intern("a"), false), sp(3, 4)).into(),
TokenTree::Delimited(
DelimSpan::from_pair(sp(5, 6), sp(13, 14)),
- token::DelimToken::Paren,
+ Delimiter::Parenthesis,
TokenStream::new(vec![
TokenTree::token(token::Ident(Symbol::intern("b"), false), sp(6, 7)).into(),
TokenTree::token(token::Colon, sp(8, 9)).into(),
.into(),
TokenTree::Delimited(
DelimSpan::from_pair(sp(15, 16), sp(20, 21)),
- token::DelimToken::Brace,
+ Delimiter::Brace,
TokenStream::new(vec![
TokenTree::token(token::Ident(Symbol::intern("b"), false), sp(17, 18)).into(),
TokenTree::token(token::Semi, sp(18, 19)).into(),
ident,
is_placeholder: true,
kind: ast::GenericParamKind::Lifetime,
+ colon_span: None,
}
}]),
AstFragmentKind::Params => AstFragment::Params(smallvec![ast::Param {
use rustc_errors::ErrorGuaranteed;
use rustc_parse::nt_to_tokenstream;
use rustc_parse::parser::ForceCollect;
+use rustc_span::profiling::SpannedEventArgRecorder;
use rustc_span::{Span, DUMMY_SP};
const EXEC_STRATEGY: pm::bridge::server::SameThread = pm::bridge::server::SameThread;
input: TokenStream,
) -> Result<TokenStream, ErrorGuaranteed> {
let _timer =
- ecx.sess.prof.generic_activity_with_arg("expand_proc_macro", ecx.expansion_descr());
+ ecx.sess.prof.generic_activity_with_arg_recorder("expand_proc_macro", |recorder| {
+ recorder.record_arg_with_span(ecx.expansion_descr(), span);
+ });
+
let proc_macro_backtrace = ecx.ecfg.proc_macro_backtrace;
let server = proc_macro_server::Rustc::new(ecx);
self.client.run(&EXEC_STRATEGY, server, input, proc_macro_backtrace).map_err(|e| {
annotated: TokenStream,
) -> Result<TokenStream, ErrorGuaranteed> {
let _timer =
- ecx.sess.prof.generic_activity_with_arg("expand_proc_macro", ecx.expansion_descr());
+ ecx.sess.prof.generic_activity_with_arg_recorder("expand_proc_macro", |recorder| {
+ recorder.record_arg_with_span(ecx.expansion_descr(), span);
+ });
+
let proc_macro_backtrace = ecx.ecfg.proc_macro_backtrace;
let server = proc_macro_server::Rustc::new(ecx);
self.client
let stream = {
let _timer =
- ecx.sess.prof.generic_activity_with_arg("expand_proc_macro", ecx.expansion_descr());
+ ecx.sess.prof.generic_activity_with_arg_recorder("expand_proc_macro", |recorder| {
+ recorder.record_arg_with_span(ecx.expansion_descr(), span);
+ });
let proc_macro_backtrace = ecx.ecfg.proc_macro_backtrace;
let server = proc_macro_server::Rustc::new(ecx);
match self.client.run(&EXEC_STRATEGY, server, input, proc_macro_backtrace) {
fn to_internal(self) -> T;
}
-impl FromInternal<token::DelimToken> for Delimiter {
- fn from_internal(delim: token::DelimToken) -> Delimiter {
+impl FromInternal<token::Delimiter> for Delimiter {
+ fn from_internal(delim: token::Delimiter) -> Delimiter {
match delim {
- token::Paren => Delimiter::Parenthesis,
- token::Brace => Delimiter::Brace,
- token::Bracket => Delimiter::Bracket,
- token::NoDelim => Delimiter::None,
+ token::Delimiter::Parenthesis => Delimiter::Parenthesis,
+ token::Delimiter::Brace => Delimiter::Brace,
+ token::Delimiter::Bracket => Delimiter::Bracket,
+ token::Delimiter::Invisible => Delimiter::None,
}
}
}
-impl ToInternal<token::DelimToken> for Delimiter {
- fn to_internal(self) -> token::DelimToken {
+impl ToInternal<token::Delimiter> for Delimiter {
+ fn to_internal(self) -> token::Delimiter {
match self {
- Delimiter::Parenthesis => token::Paren,
- Delimiter::Brace => token::Brace,
- Delimiter::Bracket => token::Bracket,
- Delimiter::None => token::NoDelim,
+ Delimiter::Parenthesis => token::Delimiter::Parenthesis,
+ Delimiter::Brace => token::Delimiter::Brace,
+ Delimiter::Bracket => token::Delimiter::Bracket,
+ Delimiter::None => token::Delimiter::Invisible,
}
}
}
let joint = spacing == Joint;
let Token { kind, span } = match tree {
tokenstream::TokenTree::Delimited(span, delim, tts) => {
- let delimiter = Delimiter::from_internal(delim);
+ let delimiter = pm::Delimiter::from_internal(delim);
return TokenTree::Group(Group { delimiter, stream: tts, span, flatten: false });
}
tokenstream::TokenTree::Token(token) => token,
.map(|kind| tokenstream::TokenTree::token(kind, span))
.collect();
stack.push(TokenTree::Group(Group {
- delimiter: Delimiter::Bracket,
+ delimiter: pm::Delimiter::Bracket,
stream,
span: DelimSpan::from_single(span),
flatten: false,
Interpolated(nt) => {
let stream = nt_to_tokenstream(&nt, rustc.sess(), CanSynthesizeMissingTokens::No);
TokenTree::Group(Group {
- delimiter: Delimiter::None,
+ delimiter: pm::Delimiter::None,
stream,
span: DelimSpan::from_single(span),
flatten: crate::base::pretty_printing_compatibility_hack(&nt, rustc.sess()),
(active, used_with_arg, "1.60.0", Some(93798), None),
/// Allows `extern "wasm" fn`
(active, wasm_abi, "1.53.0", Some(83788), None),
+ /// Allows `do yeet` expressions
+ (active, yeet_expr, "1.62.0", Some(96373), None),
// !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!!
// Features are listed in alphabetical order. Tidy will fail if you don't keep it this way.
// !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!! !!!!
[] block: rustc_hir::Block<'tcx>,
[] bare_fn_ty: rustc_hir::BareFnTy<'tcx>,
[] body: rustc_hir::Body<'tcx>,
+ [] generics: rustc_hir::Generics<'tcx>,
[] generic_arg: rustc_hir::GenericArg<'tcx>,
[] generic_args: rustc_hir::GenericArgs<'tcx>,
[] generic_bound: rustc_hir::GenericBound<'tcx>,
[] generic_param: rustc_hir::GenericParam<'tcx>,
[] expr: rustc_hir::Expr<'tcx>,
+ [] impl_: rustc_hir::Impl<'tcx>,
[] let_expr: rustc_hir::Let<'tcx>,
[] expr_field: rustc_hir::ExprField<'tcx>,
[] pat_field: rustc_hir::PatField<'tcx>,
use crate::intravisit::FnKind;
use crate::LangItem;
+use rustc_ast as ast;
use rustc_ast::util::parser::ExprPrecedence;
-use rustc_ast::{self as ast, CrateSugar};
use rustc_ast::{Attribute, FloatTy, IntTy, Label, LitKind, TraitObjectSyntax, UintTy};
pub use rustc_ast::{BorrowKind, ImplPolarity, IsAuto};
pub use rustc_ast::{CaptureBy, Movability, Mutability};
use rustc_index::vec::IndexVec;
use rustc_macros::HashStable_Generic;
use rustc_span::hygiene::MacroKind;
-use rustc_span::source_map::{SourceMap, Spanned};
+use rustc_span::source_map::Spanned;
use rustc_span::symbol::{kw, sym, Ident, Symbol};
use rustc_span::{def_id::LocalDefId, BytePos, Span, DUMMY_SP};
use rustc_target::asm::InlineAsmRegOrRegClass;
Param(ParamName),
/// User wrote nothing (e.g., the lifetime in `&u32`).
- ///
- /// The bool indicates whether the user should have written something.
Implicit,
/// Implicit lifetime in a context like `dyn Foo`. This is
Outlives(Lifetime),
}
-#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
-rustc_data_structures::static_assert_size!(GenericBound<'_>, 48);
-
impl GenericBound<'_> {
pub fn trait_ref(&self) -> Option<&TraitRef<'_>> {
match self {
pub struct GenericParam<'hir> {
pub hir_id: HirId,
pub name: ParamName,
- pub bounds: GenericBounds<'hir>,
pub span: Span,
pub pure_wrt_drop: bool,
pub kind: GenericParamKind<'hir>,
+ pub colon_span: Option<Span>,
}
impl<'hir> GenericParam<'hir> {
- pub fn bounds_span_for_suggestions(&self) -> Option<Span> {
- self.bounds
- .iter()
- .fold(None, |span: Option<Span>, bound| {
- // We include bounds that come from a `#[derive(_)]` but point at the user's code,
- // as we use this method to get a span appropriate for suggestions.
- if !bound.span().can_be_used_for_suggestions() {
- None
- } else {
- let span = span.map(|s| s.to(bound.span())).unwrap_or_else(|| bound.span());
- Some(span)
- }
- })
- .map(|sp| sp.shrink_to_hi())
+ /// Synthetic type-parameters are inserted after normal ones.
+ /// In order for normal parameters to be able to refer to synthetic ones,
+ /// scans them first.
+ pub fn is_impl_trait(&self) -> bool {
+ matches!(self.kind, GenericParamKind::Type { synthetic: true, .. })
}
- /// Returns the span of `:` after a generic parameter.
- ///
- /// For example:
- ///
- /// ```text
- /// fn a<T:>()
- /// ^
- /// | here
- /// here |
- /// v
- /// fn b<T :>()
+ /// This can happen for `async fn`, e.g. `async fn f<'_>(&'_ self)`.
///
- /// fn c<T
- ///
- /// :>()
- /// ^
- /// |
- /// here
- /// ```
- pub fn colon_span_for_suggestions(&self, source_map: &SourceMap) -> Option<Span> {
- let sp = source_map
- .span_extend_while(self.span.shrink_to_hi(), |c| c.is_whitespace() || c == ':')
- .ok()?;
-
- let snippet = source_map.span_to_snippet(sp).ok()?;
- let offset = snippet.find(':')?;
-
- let colon_sp = sp
- .with_lo(BytePos(sp.lo().0 + offset as u32))
- .with_hi(BytePos(sp.lo().0 + (offset + ':'.len_utf8()) as u32));
-
- Some(colon_sp)
+ /// See `lifetime_to_generic_param` in `rustc_ast_lowering` for more information.
+ pub fn is_elided_lifetime(&self) -> bool {
+ matches!(self.kind, GenericParamKind::Lifetime { kind: LifetimeParamKind::Elided })
}
}
#[derive(Debug, HashStable_Generic)]
pub struct Generics<'hir> {
pub params: &'hir [GenericParam<'hir>],
- pub where_clause: WhereClause<'hir>,
+ pub predicates: &'hir [WherePredicate<'hir>],
+ pub has_where_clause: bool,
+ pub where_clause_span: Span,
pub span: Span,
}
impl<'hir> Generics<'hir> {
- pub const fn empty() -> Generics<'hir> {
- Generics {
+ pub const fn empty() -> &'hir Generics<'hir> {
+ const NOPE: Generics<'_> = Generics {
params: &[],
- where_clause: WhereClause { predicates: &[], span: DUMMY_SP },
+ predicates: &[],
+ has_where_clause: false,
+ where_clause_span: DUMMY_SP,
span: DUMMY_SP,
- }
+ };
+ &NOPE
}
pub fn get_named(&self, name: Symbol) -> Option<&GenericParam<'_>> {
self.params.iter().map(|p| p.span).collect::<Vec<Span>>().into()
}
}
-}
-/// A where-clause in a definition.
-#[derive(Debug, HashStable_Generic)]
-pub struct WhereClause<'hir> {
- pub predicates: &'hir [WherePredicate<'hir>],
- // Only valid if predicates aren't empty.
- pub span: Span,
-}
+ /// If there are generic parameters, return where to introduce a new one.
+ pub fn span_for_param_suggestion(&self) -> Option<Span> {
+ if self.params.iter().any(|p| self.span.contains(p.span)) {
+ // `fn foo<A>(t: impl Trait)`
+ // ^ suggest `, T: Trait` here
+ let span = self.span.with_lo(self.span.hi() - BytePos(1)).shrink_to_lo();
+ Some(span)
+ } else {
+ None
+ }
+ }
-impl WhereClause<'_> {
- pub fn span(&self) -> Option<Span> {
- if self.predicates.is_empty() { None } else { Some(self.span) }
+ pub fn where_clause_span(&self) -> Option<Span> {
+ if self.predicates.is_empty() { None } else { Some(self.where_clause_span) }
}
- /// The `WhereClause` under normal circumstances points at either the predicates or the empty
+ /// The `where_span` under normal circumstances points at either the predicates or the empty
/// space where the `where` clause should be. Only of use for diagnostic suggestions.
pub fn span_for_predicates_or_empty_place(&self) -> Span {
- self.span
+ self.where_clause_span
}
/// `Span` where further predicates would be suggested, accounting for trailing commas, like
/// in `fn foo<T>(t: T) where T: Foo,` so we don't suggest two trailing commas.
- pub fn tail_span_for_suggestion(&self) -> Span {
+ pub fn tail_span_for_predicate_suggestion(&self) -> Span {
let end = self.span_for_predicates_or_empty_place().shrink_to_hi();
- self.predicates.last().map_or(end, |p| p.span()).shrink_to_hi().to(end)
+ if self.has_where_clause {
+ self.predicates
+ .iter()
+ .filter(|p| p.in_where_clause())
+ .last()
+ .map_or(end, |p| p.span())
+ .shrink_to_hi()
+ .to(end)
+ } else {
+ end
+ }
+ }
+
+ pub fn bounds_for_param(
+ &self,
+ param_def_id: LocalDefId,
+ ) -> impl Iterator<Item = &WhereBoundPredicate<'_>> {
+ self.predicates.iter().filter_map(move |pred| match pred {
+ WherePredicate::BoundPredicate(bp) if bp.is_param_bound(param_def_id.to_def_id()) => {
+ Some(bp)
+ }
+ _ => None,
+ })
+ }
+
+ pub fn bounds_span_for_suggestions(&self, param_def_id: LocalDefId) -> Option<Span> {
+ self.bounds_for_param(param_def_id).flat_map(|bp| bp.bounds.iter().rev()).find_map(
+ |bound| {
+ // We include bounds that come from a `#[derive(_)]` but point at the user's code,
+ // as we use this method to get a span appropriate for suggestions.
+ let bs = bound.span();
+ if bs.can_be_used_for_suggestions() { Some(bs.shrink_to_hi()) } else { None }
+ },
+ )
+ }
+
+ pub fn span_for_predicate_removal(&self, pos: usize) -> Span {
+ let predicate = &self.predicates[pos];
+ let span = predicate.span();
+
+ if !predicate.in_where_clause() {
+ // <T: ?Sized, U>
+ // ^^^^^^^^
+ return span;
+ }
+
+ // We need to find out which comma to remove.
+ if pos < self.predicates.len() - 1 {
+ let next_pred = &self.predicates[pos + 1];
+ if next_pred.in_where_clause() {
+ // where T: ?Sized, Foo: Bar,
+ // ^^^^^^^^^^^
+ return span.until(next_pred.span());
+ }
+ }
+
+ if pos > 0 {
+ let prev_pred = &self.predicates[pos - 1];
+ if prev_pred.in_where_clause() {
+ // where Foo: Bar, T: ?Sized,
+ // ^^^^^^^^^^^
+ return prev_pred.span().shrink_to_hi().to(span);
+ }
+ }
+
+ // This is the only predicate in the where clause.
+ // where T: ?Sized
+ // ^^^^^^^^^^^^^^^
+ self.where_clause_span
+ }
+
+ pub fn span_for_bound_removal(&self, predicate_pos: usize, bound_pos: usize) -> Span {
+ let predicate = &self.predicates[predicate_pos];
+ let bounds = predicate.bounds();
+
+ if bounds.len() == 1 {
+ return self.span_for_predicate_removal(predicate_pos);
+ }
+
+ let span = bounds[bound_pos].span();
+ if bound_pos == 0 {
+ // where T: ?Sized + Bar, Foo: Bar,
+ // ^^^^^^^^^
+ span.to(bounds[1].span().shrink_to_lo())
+ } else {
+ // where T: Bar + ?Sized, Foo: Bar,
+ // ^^^^^^^^^
+ bounds[bound_pos - 1].span().shrink_to_hi().to(span)
+ }
}
}
WherePredicate::EqPredicate(p) => p.span,
}
}
+
+ pub fn in_where_clause(&self) -> bool {
+ match self {
+ WherePredicate::BoundPredicate(p) => p.in_where_clause,
+ WherePredicate::RegionPredicate(p) => p.in_where_clause,
+ WherePredicate::EqPredicate(_) => false,
+ }
+ }
+
+ pub fn bounds(&self) -> GenericBounds<'hir> {
+ match self {
+ WherePredicate::BoundPredicate(p) => p.bounds,
+ WherePredicate::RegionPredicate(p) => p.bounds,
+ WherePredicate::EqPredicate(_) => &[],
+ }
+ }
}
/// A type bound (e.g., `for<'c> Foo: Send + Clone + 'c`).
#[derive(Debug, HashStable_Generic)]
pub struct WhereBoundPredicate<'hir> {
pub span: Span,
+ pub in_where_clause: bool,
/// Any generics from a `for` binding.
pub bound_generic_params: &'hir [GenericParam<'hir>],
/// The type being bounded.
impl<'hir> WhereBoundPredicate<'hir> {
/// Returns `true` if `param_def_id` matches the `bounded_ty` of this predicate.
pub fn is_param_bound(&self, param_def_id: DefId) -> bool {
- let TyKind::Path(QPath::Resolved(None, path)) = self.bounded_ty.kind else {
- return false;
- };
- match path.res {
- Res::Def(DefKind::TyParam, def_id)
- | Res::SelfTy { trait_: Some(def_id), alias_to: None } => def_id == param_def_id,
- _ => false,
- }
+ self.bounded_ty.as_generic_param().map_or(false, |(def_id, _)| def_id == param_def_id)
}
}
#[derive(Debug, HashStable_Generic)]
pub struct WhereRegionPredicate<'hir> {
pub span: Span,
+ pub in_where_clause: bool,
pub lifetime: Lifetime,
pub bounds: GenericBounds<'hir>,
}
pub struct TraitItem<'hir> {
pub ident: Ident,
pub def_id: LocalDefId,
- pub generics: Generics<'hir>,
+ pub generics: &'hir Generics<'hir>,
pub kind: TraitItemKind<'hir>,
pub span: Span,
}
pub struct ImplItem<'hir> {
pub ident: Ident,
pub def_id: LocalDefId,
- pub vis: Visibility<'hir>,
- pub generics: Generics<'hir>,
+ pub generics: &'hir Generics<'hir>,
pub kind: ImplItemKind<'hir>,
pub span: Span,
+ pub vis_span: Span,
}
impl ImplItem<'_> {
pub span: Span,
}
+impl<'hir> Ty<'hir> {
+ /// Returns `true` if `param_def_id` matches the `bounded_ty` of this predicate.
+ pub fn as_generic_param(&self) -> Option<(DefId, Ident)> {
+ let TyKind::Path(QPath::Resolved(None, path)) = self.kind else {
+ return None;
+ };
+ let [segment] = &path.segments else {
+ return None;
+ };
+ match path.res {
+ Res::Def(DefKind::TyParam, def_id)
+ | Res::SelfTy { trait_: Some(def_id), alias_to: None } => Some((def_id, segment.ident)),
+ _ => None,
+ }
+ }
+}
+
/// Not represented directly in the AST; referred to by name through a `ty_path`.
#[derive(Copy, Clone, PartialEq, Eq, Encodable, Decodable, Hash, Debug)]
#[derive(HashStable_Generic)]
#[derive(Debug, HashStable_Generic)]
pub struct OpaqueTy<'hir> {
- pub generics: Generics<'hir>,
+ pub generics: &'hir Generics<'hir>,
pub bounds: GenericBounds<'hir>,
pub origin: OpaqueTyOrigin,
}
pub span: Span,
}
-pub type Visibility<'hir> = Spanned<VisibilityKind<'hir>>;
-
-#[derive(Copy, Clone, Debug, HashStable_Generic)]
-pub enum VisibilityKind<'hir> {
- Public,
- Crate(CrateSugar),
- Restricted { path: &'hir Path<'hir>, hir_id: HirId },
- Inherited,
-}
-
-impl VisibilityKind<'_> {
- pub fn is_pub(&self) -> bool {
- matches!(*self, VisibilityKind::Public)
- }
-
- pub fn is_pub_restricted(&self) -> bool {
- match *self {
- VisibilityKind::Public | VisibilityKind::Inherited => false,
- VisibilityKind::Crate(..) | VisibilityKind::Restricted { .. } => true,
- }
- }
-}
-
#[derive(Debug, HashStable_Generic)]
pub struct FieldDef<'hir> {
pub span: Span,
+ pub vis_span: Span,
pub ident: Ident,
- pub vis: Visibility<'hir>,
pub hir_id: HirId,
pub ty: &'hir Ty<'hir>,
}
pub ident: Ident,
pub def_id: LocalDefId,
pub kind: ItemKind<'hir>,
- pub vis: Visibility<'hir>,
pub span: Span,
+ pub vis_span: Span,
}
impl Item<'_> {
/// A `const` item.
Const(&'hir Ty<'hir>, BodyId),
/// A function declaration.
- Fn(FnSig<'hir>, Generics<'hir>, BodyId),
+ Fn(FnSig<'hir>, &'hir Generics<'hir>, BodyId),
/// A MBE macro definition (`macro_rules!` or `macro`).
Macro(ast::MacroDef, MacroKind),
/// A module.
/// Module-level inline assembly (from `global_asm!`).
GlobalAsm(&'hir InlineAsm<'hir>),
/// A type alias, e.g., `type Foo = Bar<u8>`.
- TyAlias(&'hir Ty<'hir>, Generics<'hir>),
+ TyAlias(&'hir Ty<'hir>, &'hir Generics<'hir>),
/// An opaque `impl Trait` type alias, e.g., `type Foo = impl Bar;`.
OpaqueTy(OpaqueTy<'hir>),
/// An enum definition, e.g., `enum Foo<A, B> {C<A>, D<B>}`.
- Enum(EnumDef<'hir>, Generics<'hir>),
+ Enum(EnumDef<'hir>, &'hir Generics<'hir>),
/// A struct definition, e.g., `struct Foo<A> {x: A}`.
- Struct(VariantData<'hir>, Generics<'hir>),
+ Struct(VariantData<'hir>, &'hir Generics<'hir>),
/// A union definition, e.g., `union Foo<A, B> {x: A, y: B}`.
- Union(VariantData<'hir>, Generics<'hir>),
+ Union(VariantData<'hir>, &'hir Generics<'hir>),
/// A trait definition.
- Trait(IsAuto, Unsafety, Generics<'hir>, GenericBounds<'hir>, &'hir [TraitItemRef]),
+ Trait(IsAuto, Unsafety, &'hir Generics<'hir>, GenericBounds<'hir>, &'hir [TraitItemRef]),
/// A trait alias.
- TraitAlias(Generics<'hir>, GenericBounds<'hir>),
+ TraitAlias(&'hir Generics<'hir>, GenericBounds<'hir>),
/// An implementation, e.g., `impl<A> Trait for Foo { .. }`.
- Impl(Impl<'hir>),
+ Impl(&'hir Impl<'hir>),
}
#[derive(Debug, HashStable_Generic)]
// decoding as `Span`s cannot be decoded when a `Session` is not available.
pub defaultness_span: Option<Span>,
pub constness: Constness,
- pub generics: Generics<'hir>,
+ pub generics: &'hir Generics<'hir>,
/// The trait being implemented, if any.
pub of_trait: Option<TraitRef<'hir>>,
pub kind: ForeignItemKind<'hir>,
pub def_id: LocalDefId,
pub span: Span,
- pub vis: Visibility<'hir>,
+ pub vis_span: Span,
}
impl ForeignItem<'_> {
#[derive(Debug, HashStable_Generic)]
pub enum ForeignItemKind<'hir> {
/// A foreign function.
- Fn(&'hir FnDecl<'hir>, &'hir [Ident], Generics<'hir>),
+ Fn(&'hir FnDecl<'hir>, &'hir [Ident], &'hir Generics<'hir>),
/// A foreign static item (`static ext: u8`).
Static(&'hir Ty<'hir>, Mutability),
/// A foreign type.
Lifetime(&'hir Lifetime),
GenericParam(&'hir GenericParam<'hir>),
- Visibility(&'hir Visibility<'hir>),
Crate(&'hir Mod<'hir>),
| Node::Binding(..)
| Node::Arm(..)
| Node::Local(..)
- | Node::Visibility(..)
| Node::Crate(..)
| Node::Ty(..)
| Node::TraitRef(..)
match self {
Node::Item(i) => match i.kind {
ItemKind::Fn(ref sig, ref generics, _) => {
- Some(FnKind::ItemFn(i.ident, generics, sig.header, &i.vis))
+ Some(FnKind::ItemFn(i.ident, generics, sig.header))
}
_ => None,
},
Node::TraitItem(ti) => match ti.kind {
TraitItemKind::Fn(ref sig, TraitFn::Provided(_)) => {
- Some(FnKind::Method(ti.ident, sig, None))
+ Some(FnKind::Method(ti.ident, sig))
}
_ => None,
},
Node::ImplItem(ii) => match ii.kind {
- ImplItemKind::Fn(ref sig, _) => Some(FnKind::Method(ii.ident, sig, Some(&ii.vis))),
+ ImplItemKind::Fn(ref sig, _) => Some(FnKind::Method(ii.ident, sig)),
_ => None,
},
Node::Expr(e) => match e.kind {
_ => None,
}
}
+
+ /// Get the fields for the tuple-constructor,
+ /// if this node is a tuple constructor, otherwise None
+ pub fn tuple_fields(&self) -> Option<&'hir [FieldDef<'hir>]> {
+ if let Node::Ctor(&VariantData::Tuple(fields, _)) = self { Some(fields) } else { None }
+ }
}
// Some nodes are used a lot. Make sure they don't unintentionally get bigger.
rustc_data_structures::static_assert_size!(super::Pat<'static>, 88);
rustc_data_structures::static_assert_size!(super::QPath<'static>, 24);
rustc_data_structures::static_assert_size!(super::Ty<'static>, 72);
-
- rustc_data_structures::static_assert_size!(super::Item<'static>, 184);
- rustc_data_structures::static_assert_size!(super::TraitItem<'static>, 128);
- rustc_data_structures::static_assert_size!(super::ImplItem<'static>, 144);
- rustc_data_structures::static_assert_size!(super::ForeignItem<'static>, 136);
+ rustc_data_structures::static_assert_size!(super::GenericBound<'_>, 48);
+ rustc_data_structures::static_assert_size!(super::Generics<'static>, 56);
+ rustc_data_structures::static_assert_size!(super::Impl<'static>, 80);
+
+ rustc_data_structures::static_assert_size!(super::Item<'static>, 80);
+ rustc_data_structures::static_assert_size!(super::TraitItem<'static>, 88);
+ rustc_data_structures::static_assert_size!(super::ImplItem<'static>, 80);
+ rustc_data_structures::static_assert_size!(super::ForeignItem<'static>, 72);
}
#[derive(Copy, Clone, Debug)]
pub enum FnKind<'a> {
/// `#[xxx] pub async/const/extern "Abi" fn foo()`
- ItemFn(Ident, &'a Generics<'a>, FnHeader, &'a Visibility<'a>),
+ ItemFn(Ident, &'a Generics<'a>, FnHeader),
/// `fn foo(&self)`
- Method(Ident, &'a FnSig<'a>, Option<&'a Visibility<'a>>),
+ Method(Ident, &'a FnSig<'a>),
/// `|x, y| {}`
Closure,
impl<'a> FnKind<'a> {
pub fn header(&self) -> Option<&FnHeader> {
match *self {
- FnKind::ItemFn(_, _, ref header, _) => Some(header),
- FnKind::Method(_, ref sig, _) => Some(&sig.header),
+ FnKind::ItemFn(_, _, ref header) => Some(header),
+ FnKind::Method(_, ref sig) => Some(&sig.header),
FnKind::Closure => None,
}
}
pub mod nested_filter {
use super::Map;
- /// Specifies what nested things a visitor wants to visit. The most
- /// common choice is `OnlyBodies`, which will cause the visitor to
- /// visit fn bodies for fns that it encounters, but skip over nested
- /// item-like things.
+ /// Specifies what nested things a visitor wants to visit. By "nested
+ /// things", we are referring to bits of HIR that are not directly embedded
+ /// within one another but rather indirectly, through a table in the crate.
+ /// This is done to control dependencies during incremental compilation: the
+ /// non-inline bits of HIR can be tracked and hashed separately.
+ ///
+ /// The most common choice is `OnlyBodies`, which will cause the visitor to
+ /// visit fn bodies for fns that it encounters, and closure bodies, but
+ /// skip over nested item-like things.
///
/// See the comments on `ItemLikeVisitor` for more details on the overall
/// visit strategy.
pub trait Visitor<'v>: Sized {
// this type should not be overridden, it exists for convenient usage as `Self::Map`
type Map: Map<'v> = <Self::NestedFilter as NestedFilter<'v>>::Map;
- type NestedFilter: NestedFilter<'v> = nested_filter::None;
///////////////////////////////////////////////////////////////////////////
// Nested items.
- /// The default versions of the `visit_nested_XXX` routines invoke
- /// this method to get a map to use. By selecting an enum variant,
- /// you control which kinds of nested HIR are visited; see
- /// `NestedVisitorMap` for details. By "nested HIR", we are
- /// referring to bits of HIR that are not directly embedded within
- /// one another but rather indirectly, through a table in the
- /// crate. This is done to control dependencies during incremental
- /// compilation: the non-inline bits of HIR can be tracked and
- /// hashed separately.
+ /// Override this type to control which nested HIR are visited; see
+ /// [`NestedFilter`] for details. If you override this type, you
+ /// must also override [`nested_visit_map`](Self::nested_visit_map).
///
/// **If for some reason you want the nested behavior, but don't
- /// have a `Map` at your disposal:** then you should override the
- /// `visit_nested_XXX` methods, and override this method to
- /// `panic!()`. This way, if a new `visit_nested_XXX` variant is
- /// added in the future, we will see the panic in your code and
- /// fix it appropriately.
+ /// have a `Map` at your disposal:** then override the
+ /// `visit_nested_XXX` methods. If a new `visit_nested_XXX` variant is
+ /// added in the future, it will cause a panic which can be detected
+ /// and fixed appropriately.
+ type NestedFilter: NestedFilter<'v> = nested_filter::None;
+
+ /// If `type NestedFilter` is set to visit nested items, this method
+ /// must also be overridden to provide a map to retrieve nested items.
fn nested_visit_map(&mut self) -> Self::Map {
panic!(
"nested_visit_map must be implemented or consider using \
);
}
- /// Invoked when a nested item is encountered. By default does
- /// nothing unless you override `nested_visit_map` to return other than
- /// `None`, in which case it will walk the item. **You probably
- /// don't want to override this method** -- instead, override
- /// `nested_visit_map` or use the "shallow" or "deep" visit
- /// patterns described on `itemlikevisit::ItemLikeVisitor`. The only
- /// reason to override this method is if you want a nested pattern
- /// but cannot supply a `Map`; see `nested_visit_map` for advice.
+ /// Invoked when a nested item is encountered. By default, when
+ /// `Self::NestedFilter` is `nested_filter::None`, this method does
+ /// nothing. **You probably don't want to override this method** --
+ /// instead, override [`Self::NestedFilter`] or use the "shallow" or
+ /// "deep" visit patterns described on
+ /// `itemlikevisit::ItemLikeVisitor`. The only reason to override
+ /// this method is if you want a nested pattern but cannot supply a
+ /// [`Map`]; see `nested_visit_map` for advice.
fn visit_nested_item(&mut self, id: ItemId) {
if Self::NestedFilter::INTER {
let item = self.nested_visit_map().item(id);
}
/// Invoked to visit the body of a function, method or closure. Like
- /// visit_nested_item, does nothing by default unless you override
- /// `nested_visit_map` to return other than `None`, in which case it will walk
- /// the body.
+ /// `visit_nested_item`, does nothing by default unless you override
+ /// `Self::NestedFilter`.
fn visit_nested_body(&mut self, id: BodyId) {
if Self::NestedFilter::INTRA {
let body = self.nested_visit_map().body(id);
walk_assoc_type_binding(self, type_binding)
}
fn visit_attribute(&mut self, _id: HirId, _attr: &'v Attribute) {}
- fn visit_vis(&mut self, vis: &'v Visibility<'v>) {
- walk_vis(self, vis)
- }
fn visit_associated_item_kind(&mut self, kind: &'v AssocItemKind) {
walk_associated_item_kind(self, kind);
}
}
pub fn walk_item<'v, V: Visitor<'v>>(visitor: &mut V, item: &'v Item<'v>) {
- visitor.visit_vis(&item.vis);
visitor.visit_ident(item.ident);
match item.kind {
ItemKind::ExternCrate(orig_name) => {
visitor.visit_nested_body(body);
}
ItemKind::Fn(ref sig, ref generics, body_id) => visitor.visit_fn(
- FnKind::ItemFn(item.ident, generics, sig.header, &item.vis),
+ FnKind::ItemFn(item.ident, generics, sig.header),
&sig.decl,
body_id,
item.span,
visitor.visit_generics(generics);
walk_list!(visitor, visit_trait_ref, of_trait);
visitor.visit_ty(self_ty);
- walk_list!(visitor, visit_impl_item_ref, items);
+ walk_list!(visitor, visit_impl_item_ref, *items);
}
ItemKind::Struct(ref struct_definition, ref generics)
| ItemKind::Union(ref struct_definition, ref generics) => {
pub fn walk_foreign_item<'v, V: Visitor<'v>>(visitor: &mut V, foreign_item: &'v ForeignItem<'v>) {
visitor.visit_id(foreign_item.hir_id());
- visitor.visit_vis(&foreign_item.vis);
visitor.visit_ident(foreign_item.ident);
match foreign_item.kind {
}
}
}
- walk_list!(visitor, visit_param_bound, param.bounds);
}
pub fn walk_const_param_default<'v, V: Visitor<'v>>(visitor: &mut V, ct: &'v AnonConst) {
pub fn walk_generics<'v, V: Visitor<'v>>(visitor: &mut V, generics: &'v Generics<'v>) {
walk_list!(visitor, visit_generic_param, generics.params);
- walk_list!(visitor, visit_where_predicate, generics.where_clause.predicates);
+ walk_list!(visitor, visit_where_predicate, generics.predicates);
}
pub fn walk_where_predicate<'v, V: Visitor<'v>>(
}
TraitItemKind::Fn(ref sig, TraitFn::Provided(body_id)) => {
visitor.visit_fn(
- FnKind::Method(trait_item.ident, sig, None),
+ FnKind::Method(trait_item.ident, sig),
&sig.decl,
body_id,
trait_item.span,
pub fn walk_impl_item<'v, V: Visitor<'v>>(visitor: &mut V, impl_item: &'v ImplItem<'v>) {
// N.B., deliberately force a compilation error if/when new fields are added.
- let ImplItem { def_id: _, ident, ref vis, ref generics, ref kind, span: _ } = *impl_item;
+ let ImplItem { def_id: _, ident, ref generics, ref kind, span: _, vis_span: _ } = *impl_item;
visitor.visit_ident(ident);
- visitor.visit_vis(vis);
visitor.visit_generics(generics);
match *kind {
ImplItemKind::Const(ref ty, body) => {
}
ImplItemKind::Fn(ref sig, body_id) => {
visitor.visit_fn(
- FnKind::Method(impl_item.ident, sig, Some(&impl_item.vis)),
+ FnKind::Method(impl_item.ident, sig),
&sig.decl,
body_id,
impl_item.span,
pub fn walk_field_def<'v, V: Visitor<'v>>(visitor: &mut V, field: &'v FieldDef<'v>) {
visitor.visit_id(field.hir_id);
- visitor.visit_vis(&field.vis);
visitor.visit_ident(field.ident);
visitor.visit_ty(&field.ty);
}
visitor.visit_expr(&arm.body);
}
-pub fn walk_vis<'v, V: Visitor<'v>>(visitor: &mut V, vis: &'v Visibility<'v>) {
- if let VisibilityKind::Restricted { ref path, hir_id } = vis.node {
- visitor.visit_id(hir_id);
- visitor.visit_path(path, hir_id)
- }
-}
-
pub fn walk_associated_item_kind<'v, V: Visitor<'v>>(_: &mut V, _: &'v AssocItemKind) {
// No visitable content here: this fn exists so you can call it if
// the right thing to do, should content be added in the future,
/// an item, but don't care about how item-like things are nested
/// within one another.
/// - Example: Examine each expression to look for its type and do some check or other.
-/// - How: Implement `intravisit::Visitor` and override the `nested_visit_map()` method
-/// to return `NestedVisitorMap::OnlyBodies` and use
+/// - How: Implement `intravisit::Visitor` and override the `NestedFilter` type to
+/// `nested_filter::OnlyBodies` (and implement `nested_visit_map`), and use
/// `tcx.hir().visit_all_item_likes(&mut visitor.as_deep_visitor())`. Within your
/// `intravisit::Visitor` impl, implement methods like `visit_expr()` (don't forget to invoke
/// `intravisit::walk_expr()` to keep walking the subparts).
/// item-like things.
/// - Example: Lifetime resolution, which wants to bring lifetimes declared on the
/// impl into scope while visiting the impl-items, and then back out again.
-/// - How: Implement `intravisit::Visitor` and override the `nested_visit_map()` method
-/// to return `NestedVisitorMap::All`. Walk your crate with `intravisit::walk_crate()`
-/// invoked on `tcx.hir().krate()`.
+/// - How: Implement `intravisit::Visitor` and override the `NestedFilter` type to
+/// `nested_filter::All` (and implement `nested_visit_map`). Walk your crate with
+/// `tcx.hir().walk_toplevel_module(visitor)` invoked on `tcx.hir().krate()`.
/// - Pro: Visitor methods for any kind of HIR node, not just item-like things.
/// - Pro: Preserves nesting information
/// - Con: Does not integrate well into dependency tracking.
TryTraitFromResidual, sym::from_residual, from_residual_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
TryTraitFromOutput, sym::from_output, from_output_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
TryTraitBranch, sym::branch, branch_fn, Target::Method(MethodKind::Trait { body: false }), GenericRequirement::None;
+ TryTraitFromYeet, sym::from_yeet, from_yeet_fn, Target::Fn, GenericRequirement::None;
PollReady, sym::Ready, poll_ready_variant, Target::Variant, GenericRequirement::None;
PollPending, sym::Pending, poll_pending_variant, Target::Variant, GenericRequirement::None;
use rustc_hir as hir;
use rustc_hir::{GenericArg, GenericParam, GenericParamKind, Node, Term};
use rustc_hir::{GenericBound, PatKind, RangeEnd, TraitBoundModifier};
-use rustc_span::source_map::{SourceMap, Spanned};
+use rustc_span::source_map::SourceMap;
use rustc_span::symbol::{kw, Ident, IdentPrinter, Symbol};
use rustc_span::{self, FileName};
use rustc_target::spec::abi::Abi;
-use std::borrow::Cow;
use std::cell::Cell;
use std::vec;
self.print_block(&a)
}
Node::Lifetime(a) => self.print_lifetime(&a),
- Node::Visibility(a) => self.print_visibility(&a),
Node::GenericParam(_) => panic!("cannot print Node::GenericParam"),
Node::Field(_) => panic!("cannot print Node::Field"),
// These cases do not carry enough information in the
printer.s.eof()
}
-pub fn visibility_qualified<S: Into<Cow<'static, str>>>(vis: &hir::Visibility<'_>, w: S) -> String {
- to_string(NO_ANN, |s| {
- s.print_visibility(vis);
- s.word(w)
- })
-}
-
pub fn generic_params_to_string(generic_params: &[GenericParam<'_>]) -> String {
to_string(NO_ANN, |s| s.print_generic_params(generic_params))
}
header: hir::FnHeader,
name: Option<Symbol>,
generics: &hir::Generics<'_>,
- vis: &hir::Visibility<'_>,
arg_names: &[Ident],
body_id: Option<hir::BodyId>,
) -> String {
- to_string(NO_ANN, |s| s.print_fn(decl, header, name, generics, vis, arg_names, body_id))
+ to_string(NO_ANN, |s| s.print_fn(decl, header, name, generics, arg_names, body_id))
}
pub fn enum_def_to_string(
generics: &hir::Generics<'_>,
name: Symbol,
span: rustc_span::Span,
- visibility: &hir::Visibility<'_>,
) -> String {
- to_string(NO_ANN, |s| s.print_enum_def(enum_definition, generics, name, span, visibility))
+ to_string(NO_ANN, |s| s.print_enum_def(enum_definition, generics, name, span))
}
impl<'a> State<'a> {
},
Some(item.ident.name),
generics,
- &item.vis,
arg_names,
None,
);
self.end() // end the outer fn box
}
hir::ForeignItemKind::Static(ref t, m) => {
- self.head(visibility_qualified(&item.vis, "static"));
+ self.head("static");
if m == hir::Mutability::Mut {
self.word_space("mut");
}
self.end() // end the outer cbox
}
hir::ForeignItemKind::Type => {
- self.head(visibility_qualified(&item.vis, "type"));
+ self.head("type");
self.print_ident(item.ident);
self.word(";");
self.end(); // end the head-ibox
ident: Ident,
ty: &hir::Ty<'_>,
default: Option<hir::BodyId>,
- vis: &hir::Visibility<'_>,
) {
- self.word(visibility_qualified(vis, ""));
+ self.head("");
self.word_space("const");
self.print_ident(ident);
self.word_space(":");
if let Some(bounds) = bounds {
self.print_bounds(":", bounds);
}
- self.print_where_clause(&generics.where_clause);
+ self.print_where_clause(generics);
if let Some(ty) = ty {
self.space();
self.word_space("=");
generics: &hir::Generics<'_>,
inner: impl Fn(&mut Self),
) {
- self.head(visibility_qualified(&item.vis, "type"));
+ self.head("type");
self.print_ident(item.ident);
self.print_generic_params(&generics.params);
self.end(); // end the inner ibox
- self.print_where_clause(&generics.where_clause);
+ self.print_where_clause(generics);
self.space();
inner(self);
self.word(";");
self.ann.pre(self, AnnNode::Item(item));
match item.kind {
hir::ItemKind::ExternCrate(orig_name) => {
- self.head(visibility_qualified(&item.vis, "extern crate"));
+ self.head("extern crate");
if let Some(orig_name) = orig_name {
self.print_name(orig_name);
self.space();
self.end(); // end outer head-block
}
hir::ItemKind::Use(ref path, kind) => {
- self.head(visibility_qualified(&item.vis, "use"));
+ self.head("use");
self.print_path(path, false);
match kind {
self.end(); // end outer head-block
}
hir::ItemKind::Static(ref ty, m, expr) => {
- self.head(visibility_qualified(&item.vis, "static"));
+ self.head("static");
if m == hir::Mutability::Mut {
self.word_space("mut");
}
self.end(); // end the outer cbox
}
hir::ItemKind::Const(ref ty, expr) => {
- self.head(visibility_qualified(&item.vis, "const"));
+ self.head("const");
self.print_ident(item.ident);
self.word_space(":");
self.print_type(&ty);
sig.header,
Some(item.ident.name),
param_names,
- &item.vis,
&[],
Some(body),
);
self.ann.nested(self, Nested::Body(body));
}
hir::ItemKind::Macro(ref macro_def, _) => {
- self.print_mac_def(macro_def, &item.ident, item.span, |state| {
- state.print_visibility(&item.vis)
- });
+ self.print_mac_def(macro_def, &item.ident, item.span, |_| {});
}
hir::ItemKind::Mod(ref _mod) => {
- self.head(visibility_qualified(&item.vis, "mod"));
+ self.head("mod");
self.print_ident(item.ident);
self.nbsp();
self.bopen();
self.bclose(item.span);
}
hir::ItemKind::GlobalAsm(ref asm) => {
- self.head(visibility_qualified(&item.vis, "global_asm!"));
+ self.head("global_asm!");
self.print_inline_asm(asm);
self.end()
}
});
}
hir::ItemKind::Enum(ref enum_definition, ref params) => {
- self.print_enum_def(enum_definition, params, item.ident.name, item.span, &item.vis);
+ self.print_enum_def(enum_definition, params, item.ident.name, item.span);
}
hir::ItemKind::Struct(ref struct_def, ref generics) => {
- self.head(visibility_qualified(&item.vis, "struct"));
+ self.head("struct");
self.print_struct(struct_def, generics, item.ident.name, item.span, true);
}
hir::ItemKind::Union(ref struct_def, ref generics) => {
- self.head(visibility_qualified(&item.vis, "union"));
+ self.head("union");
self.print_struct(struct_def, generics, item.ident.name, item.span, true);
}
hir::ItemKind::Impl(hir::Impl {
items,
}) => {
self.head("");
- self.print_visibility(&item.vis);
- self.print_defaultness(defaultness);
- self.print_unsafety(unsafety);
+ self.print_defaultness(*defaultness);
+ self.print_unsafety(*unsafety);
self.word_nbsp("impl");
if !generics.params.is_empty() {
self.space();
}
- if constness == hir::Constness::Const {
+ if *constness == hir::Constness::Const {
self.word_nbsp("const");
}
}
self.print_type(&self_ty);
- self.print_where_clause(&generics.where_clause);
+ self.print_where_clause(generics);
self.space();
self.bopen();
self.print_inner_attributes(attrs);
- for impl_item in items {
+ for impl_item in *items {
self.ann.nested(self, Nested::ImplItem(impl_item.id));
}
self.bclose(item.span);
}
hir::ItemKind::Trait(is_auto, unsafety, ref generics, ref bounds, trait_items) => {
self.head("");
- self.print_visibility(&item.vis);
self.print_is_auto(is_auto);
self.print_unsafety(unsafety);
self.word_nbsp("trait");
}
}
self.print_bounds(":", real_bounds);
- self.print_where_clause(&generics.where_clause);
+ self.print_where_clause(generics);
self.word(" ");
self.bopen();
for trait_item in trait_items {
self.bclose(item.span);
}
hir::ItemKind::TraitAlias(ref generics, ref bounds) => {
- self.head(visibility_qualified(&item.vis, "trait"));
+ self.head("trait");
self.print_ident(item.ident);
self.print_generic_params(&generics.params);
let mut real_bounds = Vec::with_capacity(bounds.len());
}
self.nbsp();
self.print_bounds("=", real_bounds);
- self.print_where_clause(&generics.where_clause);
+ self.print_where_clause(generics);
self.word(";");
self.end(); // end inner head-block
self.end(); // end outer head-block
generics: &hir::Generics<'_>,
name: Symbol,
span: rustc_span::Span,
- visibility: &hir::Visibility<'_>,
) {
- self.head(visibility_qualified(visibility, "enum"));
+ self.head("enum");
self.print_name(name);
self.print_generic_params(&generics.params);
- self.print_where_clause(&generics.where_clause);
+ self.print_where_clause(generics);
self.space();
self.print_variants(&enum_definition.variants, span)
}
self.bclose(span)
}
- pub fn print_visibility(&mut self, vis: &hir::Visibility<'_>) {
- match vis.node {
- hir::VisibilityKind::Public => self.word_nbsp("pub"),
- hir::VisibilityKind::Crate(ast::CrateSugar::JustCrate) => self.word_nbsp("crate"),
- hir::VisibilityKind::Crate(ast::CrateSugar::PubCrate) => self.word_nbsp("pub(crate)"),
- hir::VisibilityKind::Restricted { ref path, .. } => {
- self.word("pub(");
- if path.segments.len() == 1 && path.segments[0].ident.name == kw::Super {
- // Special case: `super` can print like `pub(super)`.
- self.word("super");
- } else {
- // Everything else requires `in` at present.
- self.word_nbsp("in");
- self.print_path(path, false);
- }
- self.word_nbsp(")");
- }
- hir::VisibilityKind::Inherited => (),
- }
- }
-
pub fn print_defaultness(&mut self, defaultness: hir::Defaultness) {
match defaultness {
hir::Defaultness::Default { .. } => self.word_nbsp("default"),
self.commasep(Inconsistent, struct_def.fields(), |s, field| {
s.maybe_print_comment(field.span.lo());
s.print_outer_attributes(s.attrs(field.hir_id));
- s.print_visibility(&field.vis);
s.print_type(&field.ty)
});
self.pclose();
}
- self.print_where_clause(&generics.where_clause);
+ self.print_where_clause(generics);
if print_finalizer {
self.word(";");
}
self.end() // close the outer-box
}
hir::VariantData::Struct(..) => {
- self.print_where_clause(&generics.where_clause);
+ self.print_where_clause(generics);
self.nbsp();
self.bopen();
self.hardbreak_if_not_bol();
self.hardbreak_if_not_bol();
self.maybe_print_comment(field.span.lo());
self.print_outer_attributes(self.attrs(field.hir_id));
- self.print_visibility(&field.vis);
self.print_ident(field.ident);
self.word_nbsp(":");
self.print_type(&field.ty);
ident: Ident,
m: &hir::FnSig<'_>,
generics: &hir::Generics<'_>,
- vis: &hir::Visibility<'_>,
arg_names: &[Ident],
body_id: Option<hir::BodyId>,
) {
- self.print_fn(&m.decl, m.header, Some(ident.name), generics, vis, arg_names, body_id)
+ self.print_fn(&m.decl, m.header, Some(ident.name), generics, arg_names, body_id)
}
pub fn print_trait_item(&mut self, ti: &hir::TraitItem<'_>) {
self.print_outer_attributes(self.attrs(ti.hir_id()));
match ti.kind {
hir::TraitItemKind::Const(ref ty, default) => {
- let vis =
- Spanned { span: rustc_span::DUMMY_SP, node: hir::VisibilityKind::Inherited };
- self.print_associated_const(ti.ident, &ty, default, &vis);
+ self.print_associated_const(ti.ident, &ty, default);
}
hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Required(ref arg_names)) => {
- let vis =
- Spanned { span: rustc_span::DUMMY_SP, node: hir::VisibilityKind::Inherited };
- self.print_method_sig(ti.ident, sig, &ti.generics, &vis, arg_names, None);
+ self.print_method_sig(ti.ident, sig, &ti.generics, arg_names, None);
self.word(";");
}
hir::TraitItemKind::Fn(ref sig, hir::TraitFn::Provided(body)) => {
- let vis =
- Spanned { span: rustc_span::DUMMY_SP, node: hir::VisibilityKind::Inherited };
self.head("");
- self.print_method_sig(ti.ident, sig, &ti.generics, &vis, &[], Some(body));
+ self.print_method_sig(ti.ident, sig, &ti.generics, &[], Some(body));
self.nbsp();
self.end(); // need to close a box
self.end(); // need to close a box
match ii.kind {
hir::ImplItemKind::Const(ref ty, expr) => {
- self.print_associated_const(ii.ident, &ty, Some(expr), &ii.vis);
+ self.print_associated_const(ii.ident, &ty, Some(expr));
}
hir::ImplItemKind::Fn(ref sig, body) => {
self.head("");
- self.print_method_sig(ii.ident, sig, &ii.generics, &ii.vis, &[], Some(body));
+ self.print_method_sig(ii.ident, sig, &ii.generics, &[], Some(body));
self.nbsp();
self.end(); // need to close a box
self.end(); // need to close a box
header: hir::FnHeader,
name: Option<Symbol>,
generics: &hir::Generics<'_>,
- vis: &hir::Visibility<'_>,
arg_names: &[Ident],
body_id: Option<hir::BodyId>,
) {
- self.print_fn_header_info(header, vis);
+ self.print_fn_header_info(header);
if let Some(name) = name {
self.nbsp();
self.pclose();
self.print_fn_output(decl);
- self.print_where_clause(&generics.where_clause)
+ self.print_where_clause(generics)
}
fn print_closure_params(&mut self, decl: &hir::FnDecl<'_>, body_id: hir::BodyId) {
self.print_ident(param.name.ident());
match param.kind {
- GenericParamKind::Lifetime { .. } => {
- let mut sep = ":";
- for bound in param.bounds {
- match bound {
- GenericBound::Outlives(ref lt) => {
- self.word(sep);
- self.print_lifetime(lt);
- sep = "+";
- }
- _ => panic!(),
- }
- }
- }
+ GenericParamKind::Lifetime { .. } => {}
GenericParamKind::Type { ref default, .. } => {
- self.print_bounds(":", param.bounds);
if let Some(default) = default {
self.space();
self.word_space("=");
self.print_ident(lifetime.name.ident())
}
- pub fn print_where_clause(&mut self, where_clause: &hir::WhereClause<'_>) {
- if where_clause.predicates.is_empty() {
+ pub fn print_where_clause(&mut self, generics: &hir::Generics<'_>) {
+ if generics.predicates.is_empty() {
return;
}
self.space();
self.word_space("where");
- for (i, predicate) in where_clause.predicates.iter().enumerate() {
+ for (i, predicate) in generics.predicates.iter().enumerate() {
if i != 0 {
self.word_space(",");
}
) {
self.ibox(INDENT_UNIT);
self.print_formal_generic_params(generic_params);
- let generics = hir::Generics {
- params: &[],
- where_clause: hir::WhereClause { predicates: &[], span: rustc_span::DUMMY_SP },
- span: rustc_span::DUMMY_SP,
- };
+ let generics = hir::Generics::empty();
self.print_fn(
decl,
hir::FnHeader {
},
name,
&generics,
- &Spanned { span: rustc_span::DUMMY_SP, node: hir::VisibilityKind::Inherited },
arg_names,
None,
);
self.end();
}
- pub fn print_fn_header_info(&mut self, header: hir::FnHeader, vis: &hir::Visibility<'_>) {
- self.word(visibility_qualified(vis, ""));
-
+ pub fn print_fn_header_info(&mut self, header: hir::FnHeader) {
match header.constness {
hir::Constness::NotConst => {}
hir::Constness::Const => self.word_nbsp("const"),
let targets = node_set(&query, &edge_filter.target);
filter_nodes(&query, &sources, &targets)
}
- Err(_) => query.nodes().into_iter().collect(),
+ Err(_) => query.nodes().into_iter().map(|n| n.kind).collect(),
};
let edges = filter_edges(&query, &nodes);
}
#[allow(missing_docs)]
-pub struct GraphvizDepGraph<'q>(FxHashSet<&'q DepNode>, Vec<(&'q DepNode, &'q DepNode)>);
+pub struct GraphvizDepGraph(FxHashSet<DepKind>, Vec<(DepKind, DepKind)>);
-impl<'a, 'q> dot::GraphWalk<'a> for GraphvizDepGraph<'q> {
- type Node = &'q DepNode;
- type Edge = (&'q DepNode, &'q DepNode);
- fn nodes(&self) -> dot::Nodes<'_, &'q DepNode> {
+impl<'a> dot::GraphWalk<'a> for GraphvizDepGraph {
+ type Node = DepKind;
+ type Edge = (DepKind, DepKind);
+ fn nodes(&self) -> dot::Nodes<'_, DepKind> {
let nodes: Vec<_> = self.0.iter().cloned().collect();
nodes.into()
}
- fn edges(&self) -> dot::Edges<'_, (&'q DepNode, &'q DepNode)> {
+ fn edges(&self) -> dot::Edges<'_, (DepKind, DepKind)> {
self.1[..].into()
}
- fn source(&self, edge: &(&'q DepNode, &'q DepNode)) -> &'q DepNode {
+ fn source(&self, edge: &(DepKind, DepKind)) -> DepKind {
edge.0
}
- fn target(&self, edge: &(&'q DepNode, &'q DepNode)) -> &'q DepNode {
+ fn target(&self, edge: &(DepKind, DepKind)) -> DepKind {
edge.1
}
}
-impl<'a, 'q> dot::Labeller<'a> for GraphvizDepGraph<'q> {
- type Node = &'q DepNode;
- type Edge = (&'q DepNode, &'q DepNode);
+impl<'a> dot::Labeller<'a> for GraphvizDepGraph {
+ type Node = DepKind;
+ type Edge = (DepKind, DepKind);
fn graph_id(&self) -> dot::Id<'_> {
dot::Id::new("DependencyGraph").unwrap()
}
- fn node_id(&self, n: &&'q DepNode) -> dot::Id<'_> {
+ fn node_id(&self, n: &DepKind) -> dot::Id<'_> {
let s: String = format!("{:?}", n)
.chars()
.map(|c| if c == '_' || c.is_alphanumeric() { c } else { '_' })
debug!("n={:?} s={:?}", n, s);
dot::Id::new(s).unwrap()
}
- fn node_label(&self, n: &&'q DepNode) -> dot::LabelText<'_> {
+ fn node_label(&self, n: &DepKind) -> dot::LabelText<'_> {
dot::LabelText::label(format!("{:?}", n))
}
}
query: &'q DepGraphQuery,
sources: &Option<FxHashSet<&'q DepNode>>,
targets: &Option<FxHashSet<&'q DepNode>>,
-) -> FxHashSet<&'q DepNode> {
+) -> FxHashSet<DepKind> {
if let Some(sources) = sources {
if let Some(targets) = targets {
walk_between(query, sources, targets)
} else if let Some(targets) = targets {
walk_nodes(query, targets, INCOMING)
} else {
- query.nodes().into_iter().collect()
+ query.nodes().into_iter().map(|n| n.kind).collect()
}
}
query: &'q DepGraphQuery,
starts: &FxHashSet<&'q DepNode>,
direction: Direction,
-) -> FxHashSet<&'q DepNode> {
+) -> FxHashSet<DepKind> {
let mut set = FxHashSet::default();
for &start in starts {
debug!("walk_nodes: start={:?} outgoing?={:?}", start, direction == OUTGOING);
- if set.insert(start) {
+ if set.insert(start.kind) {
let mut stack = vec![query.indices[start]];
while let Some(index) = stack.pop() {
for (_, edge) in query.graph.adjacent_edges(index, direction) {
let neighbor_index = edge.source_or_target(direction);
let neighbor = query.graph.node_data(neighbor_index);
- if set.insert(neighbor) {
+ if set.insert(neighbor.kind) {
stack.push(neighbor_index);
}
}
query: &'q DepGraphQuery,
sources: &FxHashSet<&'q DepNode>,
targets: &FxHashSet<&'q DepNode>,
-) -> FxHashSet<&'q DepNode> {
+) -> FxHashSet<DepKind> {
// This is a bit tricky. We want to include a node only if it is:
// (a) reachable from a source and (b) will reach a target. And we
// have to be careful about cycles etc. Luckily efficiency is not
let index = query.indices[n];
node_states[index.0] == State::Included
})
+ .map(|n| n.kind)
.collect();
fn recurse(query: &DepGraphQuery, node_states: &mut [State], node: NodeIndex) -> bool {
fn filter_edges<'q>(
query: &'q DepGraphQuery,
- nodes: &FxHashSet<&'q DepNode>,
-) -> Vec<(&'q DepNode, &'q DepNode)> {
- query
+ nodes: &FxHashSet<DepKind>,
+) -> Vec<(DepKind, DepKind)> {
+ let uniq: FxHashSet<_> = query
.edges()
.into_iter()
- .filter(|&(source, target)| nodes.contains(source) && nodes.contains(target))
- .collect()
+ .map(|(s, t)| (s.kind, t.kind))
+ .filter(|(source, target)| nodes.contains(source) && nodes.contains(target))
+ .collect();
+ uniq.into_iter().collect()
}
}
}
+ #[inline]
+ pub fn iter(&self) -> ChunkedBitIter<'_, T> {
+ ChunkedBitIter::new(self)
+ }
+
/// Insert `elem`. Returns whether the set has changed.
pub fn insert(&mut self, elem: T) -> bool {
assert!(elem.index() < self.domain_size);
}
}
+pub struct ChunkedBitIter<'a, T: Idx> {
+ index: usize,
+ bitset: &'a ChunkedBitSet<T>,
+}
+
+impl<'a, T: Idx> ChunkedBitIter<'a, T> {
+ #[inline]
+ fn new(bitset: &'a ChunkedBitSet<T>) -> ChunkedBitIter<'a, T> {
+ ChunkedBitIter { index: 0, bitset }
+ }
+}
+
+impl<'a, T: Idx> Iterator for ChunkedBitIter<'a, T> {
+ type Item = T;
+ fn next(&mut self) -> Option<T> {
+ while self.index < self.bitset.domain_size() {
+ let elem = T::new(self.index);
+ let chunk = &self.bitset.chunks[chunk_index(elem)];
+ match &chunk {
+ Zeros(chunk_domain_size) => {
+ self.index += *chunk_domain_size as usize;
+ }
+ Ones(_chunk_domain_size) => {
+ self.index += 1;
+ return Some(elem);
+ }
+ Mixed(_chunk_domain_size, _, words) => loop {
+ let elem = T::new(self.index);
+ self.index += 1;
+ let (word_index, mask) = chunk_word_index_and_mask(elem);
+ if (words[word_index] & mask) != 0 {
+ return Some(elem);
+ }
+ if self.index % CHUNK_BITS == 0 {
+ break;
+ }
+ },
+ }
+ }
+ None
+ }
+}
+
impl Chunk {
#[cfg(test)]
fn assert_valid(&self) {
}
pub fn row(&self, row: R) -> Option<&HybridBitSet<C>> {
- if let Some(Some(row)) = self.rows.get(row) { Some(row) } else { None }
+ self.rows.get(row)?.as_ref()
}
/// Intersects `row` with `set`. `set` can be either `BitSet` or
b10000b.assert_valid();
}
+#[test]
+fn chunked_bitset_iter() {
+ fn with_elements(elements: &[usize], domain_size: usize) -> ChunkedBitSet<usize> {
+ let mut s = ChunkedBitSet::new_empty(domain_size);
+ for &e in elements {
+ s.insert(e);
+ }
+ s
+ }
+
+ // Empty
+ let vec: Vec<usize> = Vec::new();
+ let bit = with_elements(&vec, 9000);
+ assert_eq!(vec, bit.iter().collect::<Vec<_>>());
+
+ // Filled
+ let n = 10000;
+ let vec: Vec<usize> = (0..n).collect();
+ let bit = with_elements(&vec, n);
+ assert_eq!(vec, bit.iter().collect::<Vec<_>>());
+
+ // Filled with trailing zeros
+ let n = 10000;
+ let vec: Vec<usize> = (0..n).collect();
+ let bit = with_elements(&vec, 2 * n);
+ assert_eq!(vec, bit.iter().collect::<Vec<_>>());
+
+ // Mixed
+ let n = 12345;
+ let vec: Vec<usize> = vec![0, 1, 2, 2010, 2047, 2099, 6000, 6002, 6004];
+ let bit = with_elements(&vec, n);
+ assert_eq!(vec, bit.iter().collect::<Vec<_>>());
+}
+
#[test]
fn grow() {
let mut set: GrowableBitSet<usize> = GrowableBitSet::with_capacity(65);
use rustc_errors::{pluralize, struct_span_err, Diagnostic, ErrorGuaranteed};
use rustc_errors::{Applicability, DiagnosticBuilder, DiagnosticStyledString, MultiSpan};
use rustc_hir as hir;
-use rustc_hir::def_id::DefId;
+use rustc_hir::def_id::{DefId, LocalDefId};
use rustc_hir::lang_items::LangItem;
use rustc_hir::{Item, ItemKind, Node};
use rustc_middle::dep_graph::DepContext;
err.span_suggestion(
span.with_hi(before_close).shrink_to_hi(),
msg,
- ",".into(),
+ ",",
Applicability::MachineApplicable,
);
} else {
bound_kind: GenericKind<'tcx>,
sub: Region<'tcx>,
) {
- self.construct_generic_bound_failure(span, origin, bound_kind, sub).emit();
+ let owner =
+ self.in_progress_typeck_results.map(|typeck_results| typeck_results.borrow().hir_owner);
+ self.construct_generic_bound_failure(span, origin, bound_kind, sub, owner).emit();
}
pub fn construct_generic_bound_failure(
origin: Option<SubregionOrigin<'tcx>>,
bound_kind: GenericKind<'tcx>,
sub: Region<'tcx>,
+ owner: Option<LocalDefId>,
) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
let hir = self.tcx.hir();
// Attempt to obtain the span of the parameter so we can
// suggest adding an explicit lifetime bound to it.
- let generics = self
- .in_progress_typeck_results
- .map(|typeck_results| typeck_results.borrow().hir_owner)
- .map(|owner| {
- let hir_id = hir.local_def_id_to_hir_id(owner);
- let parent_id = hir.get_parent_item(hir_id);
- (
- // Parent item could be a `mod`, so we check the HIR before calling:
- if let Some(Node::Item(Item {
- kind: ItemKind::Trait(..) | ItemKind::Impl { .. },
- ..
- })) = hir.find_by_def_id(parent_id)
- {
- Some(self.tcx.generics_of(parent_id))
- } else {
- None
- },
- self.tcx.generics_of(owner.to_def_id()),
- hir.span(hir_id),
- )
- });
+ let generics = owner.map(|owner| {
+ let hir_id = hir.local_def_id_to_hir_id(owner);
+ let parent_id = hir.get_parent_item(hir_id);
+ (
+ // Parent item could be a `mod`, so we check the HIR before calling:
+ if let Some(Node::Item(Item {
+ kind: ItemKind::Trait(..) | ItemKind::Impl { .. },
+ ..
+ })) = hir.find_by_def_id(parent_id)
+ {
+ Some(self.tcx.generics_of(parent_id))
+ } else {
+ None
+ },
+ self.tcx.generics_of(owner.to_def_id()),
+ hir.span(hir_id),
+ )
+ });
let span = match generics {
// This is to get around the trait identity obligation, that has a `DUMMY_SP` as signal
_ => span,
};
+ // type_param_span is (span, has_bounds)
let type_param_span = match (generics, bound_kind) {
(Some((_, ref generics, _)), GenericKind::Param(ref param)) => {
// Account for the case where `param` corresponds to `Self`,
// Get the `hir::Param` to verify whether it already has any bounds.
// We do this to avoid suggesting code that ends up as `T: 'a'b`,
// instead we suggest `T: 'a + 'b` in that case.
- let id = hir.local_def_id_to_hir_id(def_id);
- let mut has_bounds = false;
- if let Node::GenericParam(param) = hir.get(id) {
- has_bounds = !param.bounds.is_empty();
- }
- let sp = self.tcx.def_span(def_id);
+ let hir_id = self.tcx.hir().local_def_id_to_hir_id(def_id);
+ let ast_generics = self.tcx.hir().get_generics(hir_id.owner);
+ let bounds =
+ ast_generics.and_then(|g| g.bounds_span_for_suggestions(def_id));
// `sp` only covers `T`, change it so that it covers
// `T:` when appropriate
- let is_impl_trait = bound_kind.to_string().starts_with("impl ");
- let sp = if has_bounds && !is_impl_trait {
- sp.to(self
- .tcx
- .sess
- .source_map()
- .next_point(self.tcx.sess.source_map().next_point(sp)))
+ if let Some(span) = bounds {
+ (span, true)
} else {
- sp
- };
- (sp, has_bounds, is_impl_trait)
+ let sp = self.tcx.def_span(def_id);
+ (sp.shrink_to_hi(), false)
+ }
})
} else {
None
fn binding_suggestion<'tcx, S: fmt::Display>(
err: &mut Diagnostic,
- type_param_span: Option<(Span, bool, bool)>,
+ type_param_span: Option<(Span, bool)>,
bound_kind: GenericKind<'tcx>,
sub: S,
) {
let msg = "consider adding an explicit lifetime bound";
- if let Some((sp, has_lifetimes, is_impl_trait)) = type_param_span {
- let suggestion = if is_impl_trait {
- format!("{} + {}", bound_kind, sub)
- } else {
- let tail = if has_lifetimes { " + " } else { "" };
- format!("{}: {}{}", bound_kind, sub, tail)
- };
- err.span_suggestion(
+ if let Some((sp, has_lifetimes)) = type_param_span {
+ let suggestion =
+ if has_lifetimes { format!(" + {}", sub) } else { format!(": {}", sub) };
+ err.span_suggestion_verbose(
sp,
&format!("{}...", msg),
suggestion,
Applicability::MaybeIncorrect, // Issue #41966
);
} else {
- let consider = format!(
- "{} {}...",
- msg,
- if type_param_span.map_or(false, |(_, _, is_impl_trait)| is_impl_trait) {
- format!(" `{}` to `{}`", sub, bound_kind)
- } else {
- format!("`{}: {}`", bound_kind, sub)
- },
- );
+ let consider = format!("{} `{}: {}`...", msg, bound_kind, sub,);
err.help(&consider);
}
}
let new_binding_suggestion =
- |err: &mut Diagnostic,
- type_param_span: Option<(Span, bool, bool)>,
- bound_kind: GenericKind<'tcx>| {
+ |err: &mut Diagnostic, type_param_span: Option<(Span, bool)>| {
let msg = "consider introducing an explicit lifetime bound";
- if let Some((sp, has_lifetimes, is_impl_trait)) = type_param_span {
- let suggestion = if is_impl_trait {
- (sp.shrink_to_hi(), format!(" + {}", new_lt))
+ if let Some((sp, has_lifetimes)) = type_param_span {
+ let suggestion = if has_lifetimes {
+ format!(" + {}", new_lt)
} else {
- let tail = if has_lifetimes { " +" } else { "" };
- (sp, format!("{}: {}{}", bound_kind, new_lt, tail))
+ format!(": {}", new_lt)
};
let mut sugg =
- vec![suggestion, (span.shrink_to_hi(), format!(" + {}", new_lt))];
+ vec![(sp, suggestion), (span.shrink_to_hi(), format!(" + {}", new_lt))];
if let Some(lt) = add_lt_sugg {
sugg.push(lt);
sugg.rotate_right(1);
let pred = format!("{}: {}", bound_kind, sub);
let suggestion = format!(
"{} {}",
- if !generics.where_clause.predicates.is_empty() { "," } else { " where" },
+ if !generics.predicates.is_empty() { "," } else { " where" },
pred,
);
err.span_suggestion(
- generics.where_clause.tail_span_for_suggestion(),
+ generics.tail_span_for_predicate_suggestion(),
"consider adding a where clause",
suggestion,
Applicability::MaybeIncorrect,
None,
);
if let Some(infer::RelateParamBound(_, t, _)) = origin {
- let return_impl_trait = self
- .in_progress_typeck_results
- .map(|typeck_results| typeck_results.borrow().hir_owner)
- .and_then(|owner| self.tcx.return_type_impl_trait(owner))
- .is_some();
+ let return_impl_trait =
+ owner.and_then(|owner| self.tcx.return_type_impl_trait(owner)).is_some();
let t = self.resolve_vars_if_possible(t);
match t.kind() {
// We've got:
// suggest:
// fn get_later<'a, G: 'a, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ + 'a
ty::Closure(_, _substs) | ty::Opaque(_, _substs) if return_impl_trait => {
- new_binding_suggestion(&mut err, type_param_span, bound_kind);
+ new_binding_suggestion(&mut err, type_param_span);
}
_ => {
binding_suggestion(&mut err, type_param_span, bound_kind, new_lt);
impl InferenceDiagnosticsParentData {
fn for_def_id(tcx: TyCtxt<'_>, def_id: DefId) -> Option<InferenceDiagnosticsParentData> {
- let parent_def_id = tcx.parent(def_id)?;
+ let parent_def_id = tcx.parent(def_id);
let parent_name =
tcx.def_key(parent_def_id).disambiguated_data.data.get_opt_name()?.to_string();
if !impl_candidates.is_empty() && e.span.contains(span)
&& let Some(expr) = exprs.first()
&& let ExprKind::Path(hir::QPath::Resolved(_, path)) = expr.kind
- && let [path_segment] = path.segments
+ && let [_] = path.segments
{
+ let mut eraser = TypeParamEraser(self.tcx);
let candidate_len = impl_candidates.len();
- let suggestions = impl_candidates.iter().map(|candidate| {
- format!(
- "{}::{}({})",
- candidate, segment.ident, path_segment.ident
- )
- });
- err.span_suggestions(
- e.span,
+ let mut suggestions: Vec<_> = impl_candidates.iter().map(|candidate| {
+ let candidate = candidate.super_fold_with(&mut eraser);
+ vec![
+ (expr.span.shrink_to_lo(), format!("{}::{}(", candidate, segment.ident)),
+ if exprs.len() == 1 {
+ (expr.span.shrink_to_hi().with_hi(e.span.hi()), ")".to_string())
+ } else {
+ (expr.span.shrink_to_hi().with_hi(exprs[1].span.lo()), ", ".to_string())
+ },
+ ]
+ }).collect();
+ suggestions.sort_by(|a, b| a[0].1.cmp(&b[0].1));
+ err.multipart_suggestions(
&format!(
"use the fully qualified path for the potential candidate{}",
pluralize!(candidate_len),
),
- suggestions,
+ suggestions.into_iter(),
Applicability::MaybeIncorrect,
);
}
if let Some((DefKind::AssocFn, def_id)) =
self.in_progress_typeck_results?.borrow().type_dependent_def(hir_id)
{
- return self
- .tcx
- .parent(def_id)
- .filter(|&parent_def_id| self.tcx.is_trait(parent_def_id));
+ let parent_def_id = self.tcx.parent(def_id);
+ return self.tcx.is_trait(parent_def_id).then_some(parent_def_id);
}
None
}
}
}
+
+/// Replace type parameters with `ty::Infer(ty::Var)` to display `_`.
+struct TypeParamEraser<'tcx>(TyCtxt<'tcx>);
+
+impl<'tcx> TypeFolder<'tcx> for TypeParamEraser<'tcx> {
+ fn tcx<'a>(&'a self) -> TyCtxt<'tcx> {
+ self.0
+ }
+ fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
+ match t.kind() {
+ ty::Param(_) | ty::Error(_) => self.tcx().mk_ty_var(ty::TyVid::from_u32(0)),
+ _ => t.super_fold_with(self),
+ }
+ }
+}
use crate::infer::error_reporting::nice_region_error::NiceRegionError;
use crate::infer::lexical_region_resolve::RegionResolutionError;
use crate::infer::SubregionOrigin;
+use crate::infer::TyCtxt;
use rustc_errors::{struct_span_err, Applicability, Diagnostic, ErrorGuaranteed};
use rustc_hir as hir;
}
}
- self.suggest_adding_lifetime_params(sub, ty_sup, ty_sub, &mut err);
+ if suggest_adding_lifetime_params(self.tcx(), sub, ty_sup, ty_sub, &mut err) {
+ err.note("each elided lifetime in input position becomes a distinct lifetime");
+ }
let reported = err.emit();
Some(reported)
}
+}
- fn suggest_adding_lifetime_params(
- &self,
- sub: Region<'tcx>,
- ty_sup: &Ty<'_>,
- ty_sub: &Ty<'_>,
- err: &mut Diagnostic,
- ) {
- if let (
- hir::Ty { kind: hir::TyKind::Rptr(lifetime_sub, _), .. },
- hir::Ty { kind: hir::TyKind::Rptr(lifetime_sup, _), .. },
- ) = (ty_sub, ty_sup)
- {
- if lifetime_sub.name.is_elided() && lifetime_sup.name.is_elided() {
- if let Some(anon_reg) = self.tcx().is_suitable_region(sub) {
- let hir_id = self.tcx().hir().local_def_id_to_hir_id(anon_reg.def_id);
-
- let node = self.tcx().hir().get(hir_id);
- let is_impl = matches!(&node, hir::Node::ImplItem(_));
- let generics = match node {
- hir::Node::Item(&hir::Item {
- kind: hir::ItemKind::Fn(_, ref generics, ..),
- ..
- })
- | hir::Node::TraitItem(&hir::TraitItem { ref generics, .. })
- | hir::Node::ImplItem(&hir::ImplItem { ref generics, .. }) => generics,
- _ => return,
- };
-
- let (suggestion_param_name, introduce_new) = generics
- .params
- .iter()
- .find(|p| matches!(p.kind, GenericParamKind::Lifetime { .. }))
- .and_then(|p| self.tcx().sess.source_map().span_to_snippet(p.span).ok())
- .map(|name| (name, false))
- .unwrap_or_else(|| ("'a".to_string(), true));
-
- let mut suggestions = vec![
- if let hir::LifetimeName::Underscore = lifetime_sub.name {
- (lifetime_sub.span, suggestion_param_name.clone())
- } else {
- (lifetime_sub.span.shrink_to_hi(), suggestion_param_name.clone() + " ")
- },
- if let hir::LifetimeName::Underscore = lifetime_sup.name {
- (lifetime_sup.span, suggestion_param_name.clone())
- } else {
- (lifetime_sup.span.shrink_to_hi(), suggestion_param_name.clone() + " ")
- },
- ];
-
- if introduce_new {
- let new_param_suggestion = match &generics.params {
- [] => (generics.span, format!("<{}>", suggestion_param_name)),
- [first, ..] => {
- (first.span.shrink_to_lo(), format!("{}, ", suggestion_param_name))
- }
- };
-
- suggestions.push(new_param_suggestion);
- }
-
- let mut sugg = String::from("consider introducing a named lifetime parameter");
- if is_impl {
- sugg.push_str(" and update trait if needed");
- }
- err.multipart_suggestion(
- sugg.as_str(),
- suggestions,
- Applicability::MaybeIncorrect,
- );
- err.note("each elided lifetime in input position becomes a distinct lifetime");
- }
- }
- }
+pub fn suggest_adding_lifetime_params<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ sub: Region<'tcx>,
+ ty_sup: &Ty<'_>,
+ ty_sub: &Ty<'_>,
+ err: &mut Diagnostic,
+) -> bool {
+ let (
+ hir::Ty { kind: hir::TyKind::Rptr(lifetime_sub, _), .. },
+ hir::Ty { kind: hir::TyKind::Rptr(lifetime_sup, _), .. },
+ ) = (ty_sub, ty_sup) else {
+ return false;
+ };
+
+ if !lifetime_sub.name.is_elided() || !lifetime_sup.name.is_elided() {
+ return false;
+ };
+
+ let Some(anon_reg) = tcx.is_suitable_region(sub) else {
+ return false;
+ };
+
+ let hir_id = tcx.hir().local_def_id_to_hir_id(anon_reg.def_id);
+
+ let node = tcx.hir().get(hir_id);
+ let is_impl = matches!(&node, hir::Node::ImplItem(_));
+ let generics = match node {
+ hir::Node::Item(&hir::Item { kind: hir::ItemKind::Fn(_, ref generics, ..), .. })
+ | hir::Node::TraitItem(&hir::TraitItem { ref generics, .. })
+ | hir::Node::ImplItem(&hir::ImplItem { ref generics, .. }) => generics,
+ _ => return false,
+ };
+
+ let (suggestion_param_name, introduce_new) = generics
+ .params
+ .iter()
+ .find(|p| matches!(p.kind, GenericParamKind::Lifetime { .. }))
+ .and_then(|p| tcx.sess.source_map().span_to_snippet(p.span).ok())
+ .map(|name| (name, false))
+ .unwrap_or_else(|| ("'a".to_string(), true));
+
+ let mut suggestions = vec![
+ if let hir::LifetimeName::Underscore = lifetime_sub.name {
+ (lifetime_sub.span, suggestion_param_name.clone())
+ } else {
+ (lifetime_sub.span.shrink_to_hi(), suggestion_param_name.clone() + " ")
+ },
+ if let hir::LifetimeName::Underscore = lifetime_sup.name {
+ (lifetime_sup.span, suggestion_param_name.clone())
+ } else {
+ (lifetime_sup.span.shrink_to_hi(), suggestion_param_name.clone() + " ")
+ },
+ ];
+
+ if introduce_new {
+ let new_param_suggestion = match &generics.params {
+ [] => (generics.span, format!("<{}>", suggestion_param_name)),
+ [first, ..] => (first.span.shrink_to_lo(), format!("{}, ", suggestion_param_name)),
+ };
+
+ suggestions.push(new_param_suggestion);
}
+
+ let mut sugg = String::from("consider introducing a named lifetime parameter");
+ if is_impl {
+ sugg.push_str(" and update trait if needed");
+ }
+ err.multipart_suggestion(sugg.as_str(), suggestions, Applicability::MaybeIncorrect);
+
+ true
}
/// ```
/// The function returns the nested type corresponding to the anonymous region
/// for e.g., `&u8` and `Vec<&u8>`.
-pub(crate) fn find_anon_type<'tcx>(
+pub fn find_anon_type<'tcx>(
tcx: TyCtxt<'tcx>,
region: Region<'tcx>,
br: &ty::BoundRegionKind,
) -> Option<(&'tcx hir::Ty<'tcx>, &'tcx hir::FnSig<'tcx>)> {
- if let Some(anon_reg) = tcx.is_suitable_region(region) {
- let hir_id = tcx.hir().local_def_id_to_hir_id(anon_reg.def_id);
- let Some(fn_sig) = tcx.hir().get(hir_id).fn_sig() else {
- return None
- };
+ let anon_reg = tcx.is_suitable_region(region)?;
+ let hir_id = tcx.hir().local_def_id_to_hir_id(anon_reg.def_id);
+ let fn_sig = tcx.hir().get(hir_id).fn_sig()?;
- fn_sig
- .decl
- .inputs
- .iter()
- .find_map(|arg| find_component_for_bound_region(tcx, arg, br))
- .map(|ty| (ty, fn_sig))
- } else {
- None
- }
+ fn_sig
+ .decl
+ .inputs
+ .iter()
+ .find_map(|arg| find_component_for_bound_region(tcx, arg, br))
+ .map(|ty| (ty, fn_sig))
}
// This method creates a FindNestedTypeVisitor which returns the type corresponding
mod trait_impl_difference;
mod util;
+pub use different_lifetimes::suggest_adding_lifetime_params;
+pub use find_anon_type::find_anon_type;
pub use static_impl_trait::suggest_new_region_bound;
+pub use util::find_param_with_region;
impl<'cx, 'tcx> InferCtxt<'cx, 'tcx> {
pub fn try_report_nice_region_error(&self, error: &RegionResolutionError<'tcx>) -> bool {
//! anonymous regions.
use crate::infer::error_reporting::nice_region_error::NiceRegionError;
+use crate::infer::TyCtxt;
use rustc_hir as hir;
use rustc_hir::def_id::LocalDefId;
use rustc_middle::ty::{self, Binder, DefIdTree, Region, Ty, TypeFoldable};
/// Information about the anonymous region we are searching for.
#[derive(Debug)]
-pub(super) struct AnonymousParamInfo<'tcx> {
+pub struct AnonymousParamInfo<'tcx> {
/// The parameter corresponding to the anonymous region.
pub param: &'tcx hir::Param<'tcx>,
/// The type corresponding to the anonymous region parameter.
pub is_first: bool,
}
+// This method walks the Type of the function body parameters using
+// `fold_regions()` function and returns the
+// &hir::Param of the function parameter corresponding to the anonymous
+// region and the Ty corresponding to the named region.
+// Currently only the case where the function declaration consists of
+// one named region and one anonymous region is handled.
+// Consider the example `fn foo<'a>(x: &'a i32, y: &i32) -> &'a i32`
+// Here, we would return the hir::Param for y, we return the type &'a
+// i32, which is the type of y but with the anonymous region replaced
+// with 'a, the corresponding bound region and is_first which is true if
+// the hir::Param is the first parameter in the function declaration.
+pub fn find_param_with_region<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ anon_region: Region<'tcx>,
+ replace_region: Region<'tcx>,
+) -> Option<AnonymousParamInfo<'tcx>> {
+ let (id, bound_region) = match *anon_region {
+ ty::ReFree(ref free_region) => (free_region.scope, free_region.bound_region),
+ ty::ReEarlyBound(ebr) => {
+ (tcx.parent(ebr.def_id), ty::BoundRegionKind::BrNamed(ebr.def_id, ebr.name))
+ }
+ _ => return None, // not a free region
+ };
+
+ let hir = &tcx.hir();
+ let hir_id = hir.local_def_id_to_hir_id(id.as_local()?);
+ let body_id = hir.maybe_body_owned_by(hir_id)?;
+ let body = hir.body(body_id);
+ let owner_id = hir.body_owner(body_id);
+ let fn_decl = hir.fn_decl_by_hir_id(owner_id).unwrap();
+ let poly_fn_sig = tcx.fn_sig(id);
+ let fn_sig = tcx.liberate_late_bound_regions(id, poly_fn_sig);
+ body.params
+ .iter()
+ .take(if fn_sig.c_variadic {
+ fn_sig.inputs().len()
+ } else {
+ assert_eq!(fn_sig.inputs().len(), body.params.len());
+ body.params.len()
+ })
+ .enumerate()
+ .find_map(|(index, param)| {
+ // May return None; sometimes the tables are not yet populated.
+ let ty = fn_sig.inputs()[index];
+ let mut found_anon_region = false;
+ let new_param_ty = tcx.fold_regions(ty, &mut false, |r, _| {
+ if r == anon_region {
+ found_anon_region = true;
+ replace_region
+ } else {
+ r
+ }
+ });
+ if found_anon_region {
+ let ty_hir_id = fn_decl.inputs[index].hir_id;
+ let param_ty_span = hir.span(ty_hir_id);
+ let is_first = index == 0;
+ Some(AnonymousParamInfo {
+ param,
+ param_ty: new_param_ty,
+ param_ty_span,
+ bound_region,
+ is_first,
+ })
+ } else {
+ None
+ }
+ })
+}
+
impl<'a, 'tcx> NiceRegionError<'a, 'tcx> {
- // This method walks the Type of the function body parameters using
- // `fold_regions()` function and returns the
- // &hir::Param of the function parameter corresponding to the anonymous
- // region and the Ty corresponding to the named region.
- // Currently only the case where the function declaration consists of
- // one named region and one anonymous region is handled.
- // Consider the example `fn foo<'a>(x: &'a i32, y: &i32) -> &'a i32`
- // Here, we would return the hir::Param for y, we return the type &'a
- // i32, which is the type of y but with the anonymous region replaced
- // with 'a, the corresponding bound region and is_first which is true if
- // the hir::Param is the first parameter in the function declaration.
pub(super) fn find_param_with_region(
&self,
anon_region: Region<'tcx>,
replace_region: Region<'tcx>,
) -> Option<AnonymousParamInfo<'_>> {
- let (id, bound_region) = match *anon_region {
- ty::ReFree(ref free_region) => (free_region.scope, free_region.bound_region),
- ty::ReEarlyBound(ebr) => (
- self.tcx().parent(ebr.def_id).unwrap(),
- ty::BoundRegionKind::BrNamed(ebr.def_id, ebr.name),
- ),
- _ => return None, // not a free region
- };
-
- let hir = &self.tcx().hir();
- let hir_id = hir.local_def_id_to_hir_id(id.as_local()?);
- let body_id = hir.maybe_body_owned_by(hir_id)?;
- let body = hir.body(body_id);
- let owner_id = hir.body_owner(body_id);
- let fn_decl = hir.fn_decl_by_hir_id(owner_id).unwrap();
- let poly_fn_sig = self.tcx().fn_sig(id);
- let fn_sig = self.tcx().liberate_late_bound_regions(id, poly_fn_sig);
- body.params
- .iter()
- .take(if fn_sig.c_variadic {
- fn_sig.inputs().len()
- } else {
- assert_eq!(fn_sig.inputs().len(), body.params.len());
- body.params.len()
- })
- .enumerate()
- .find_map(|(index, param)| {
- // May return None; sometimes the tables are not yet populated.
- let ty = fn_sig.inputs()[index];
- let mut found_anon_region = false;
- let new_param_ty = self.tcx().fold_regions(ty, &mut false, |r, _| {
- if r == anon_region {
- found_anon_region = true;
- replace_region
- } else {
- r
- }
- });
- if found_anon_region {
- let ty_hir_id = fn_decl.inputs[index].hir_id;
- let param_ty_span = hir.span(ty_hir_id);
- let is_first = index == 0;
- Some(AnonymousParamInfo {
- param,
- param_ty: new_param_ty,
- param_ty_span,
- bound_region,
- is_first,
- })
- } else {
- None
- }
- })
+ find_param_with_region(self.tcx(), anon_region, replace_region)
}
// Here, we check for the case where the anonymous region
.hir()
.get_generics(impl_item_def_id)
.unwrap()
- .where_clause
- .tail_span_for_suggestion();
+ .where_clause_span
+ .shrink_to_hi();
let suggestion = format!(
"{} {}",
location: ExternLocation::ExactPaths(locations),
is_private_dep: false,
add_prelude: true,
+ nounused_dep: false,
}
}
tracked!(location_detail, LocationDetail { file: true, line: false, column: false });
tracked!(merge_functions, Some(MergeFunctions::Disabled));
tracked!(mir_emit_retag, true);
+ tracked!(mir_enable_passes, vec![("DestProp".to_string(), false)]);
tracked!(mir_opt_level, Some(4));
tracked!(move_size_limit, Some(4096));
tracked!(mutable_noalias, Some(true));
diag.span_suggestion(
call.ident.span,
"use `.iter()` instead of `.into_iter()` to avoid ambiguity",
- "iter".into(),
+ "iter",
Applicability::MachineApplicable,
);
if self.for_expr_span == expr.span {
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::{DefId, LocalDefId, LocalDefIdSet, CRATE_DEF_ID};
-use rustc_hir::{ForeignItemKind, GenericParamKind, PatKind};
-use rustc_hir::{HirId, Node};
+use rustc_hir::{ForeignItemKind, GenericParamKind, HirId, PatKind};
use rustc_index::vec::Idx;
use rustc_middle::lint::LintDiagnosticBuilder;
use rustc_middle::ty::layout::{LayoutError, LayoutOf};
pub struct MissingDoc {
/// Stack of whether `#[doc(hidden)]` is set at each level which has lint attributes.
doc_hidden_stack: Vec<bool>,
-
- /// Private traits or trait items that leaked through. Don't check their methods.
- private_traits: FxHashSet<hir::HirId>,
}
impl_lint_pass!(MissingDoc => [MISSING_DOCS]);
impl MissingDoc {
pub fn new() -> MissingDoc {
- MissingDoc { doc_hidden_stack: vec![false], private_traits: FxHashSet::default() }
+ MissingDoc { doc_hidden_stack: vec![false] }
}
fn doc_hidden(&self) -> bool {
fn check_item(&mut self, cx: &LateContext<'_>, it: &hir::Item<'_>) {
match it.kind {
- hir::ItemKind::Trait(.., trait_item_refs) => {
+ hir::ItemKind::Trait(..) => {
// Issue #11592: traits are always considered exported, even when private.
- if let hir::VisibilityKind::Inherited = it.vis.node {
- self.private_traits.insert(it.hir_id());
- for trait_item_ref in trait_item_refs {
- self.private_traits.insert(trait_item_ref.id.hir_id());
- }
+ if cx.tcx.visibility(it.def_id)
+ == ty::Visibility::Restricted(
+ cx.tcx.parent_module_from_def_id(it.def_id).to_def_id(),
+ )
+ {
return;
}
}
- hir::ItemKind::Impl(hir::Impl { of_trait: Some(ref trait_ref), items, .. }) => {
- // If the trait is private, add the impl items to `private_traits` so they don't get
- // reported for missing docs.
- let real_trait = trait_ref.path.res.def_id();
- let Some(def_id) = real_trait.as_local() else { return };
- let Some(Node::Item(item)) = cx.tcx.hir().find_by_def_id(def_id) else { return };
- if let hir::VisibilityKind::Inherited = item.vis.node {
- for impl_item_ref in items {
- self.private_traits.insert(impl_item_ref.id.hir_id());
- }
- }
- return;
- }
-
hir::ItemKind::TyAlias(..)
| hir::ItemKind::Fn(..)
| hir::ItemKind::Macro(..)
}
fn check_trait_item(&mut self, cx: &LateContext<'_>, trait_item: &hir::TraitItem<'_>) {
- if self.private_traits.contains(&trait_item.hir_id()) {
- return;
- }
-
let (article, desc) = cx.tcx.article_and_description(trait_item.def_id.to_def_id());
self.check_missing_docs_attrs(cx, trait_item.def_id, trait_item.span, article, desc);
});
}
}
- hir::ItemKind::Impl(hir::Impl { ref generics, items, .. }) => {
- for it in items {
+ hir::ItemKind::Impl(hir::Impl { generics, items, .. }) => {
+ for it in *items {
if let hir::AssocItemKind::Fn { .. } = it.kind {
if let Some(no_mangle_attr) = cx
.sess()
cx: &LateContext<'_>,
what: &str,
def_id: LocalDefId,
- vis: &hir::Visibility<'_>,
span: Span,
+ vis_span: Span,
exportable: bool,
) {
let mut applicability = Applicability::MachineApplicable;
- match vis.node {
- hir::VisibilityKind::Public if !cx.access_levels.is_reachable(def_id) => {
- if span.from_expansion() {
- applicability = Applicability::MaybeIncorrect;
+ if cx.tcx.visibility(def_id).is_public() && !cx.access_levels.is_reachable(def_id) {
+ if vis_span.from_expansion() {
+ applicability = Applicability::MaybeIncorrect;
+ }
+ let def_span = cx.tcx.sess.source_map().guess_head_span(span);
+ cx.struct_span_lint(UNREACHABLE_PUB, def_span, |lint| {
+ let mut err = lint.build(&format!("unreachable `pub` {}", what));
+ let replacement = if cx.tcx.features().crate_visibility_modifier {
+ "crate"
+ } else {
+ "pub(crate)"
}
- let def_span = cx.tcx.sess.source_map().guess_head_span(span);
- cx.struct_span_lint(UNREACHABLE_PUB, def_span, |lint| {
- let mut err = lint.build(&format!("unreachable `pub` {}", what));
- let replacement = if cx.tcx.features().crate_visibility_modifier {
- "crate"
- } else {
- "pub(crate)"
- }
- .to_owned();
+ .to_owned();
- err.span_suggestion(
- vis.span,
- "consider restricting its visibility",
- replacement,
- applicability,
- );
- if exportable {
- err.help("or consider exporting it for use by other crates");
- }
- err.emit();
- });
- }
- _ => {}
+ err.span_suggestion(
+ vis_span,
+ "consider restricting its visibility",
+ replacement,
+ applicability,
+ );
+ if exportable {
+ err.help("or consider exporting it for use by other crates");
+ }
+ err.emit();
+ });
}
}
}
impl<'tcx> LateLintPass<'tcx> for UnreachablePub {
fn check_item(&mut self, cx: &LateContext<'_>, item: &hir::Item<'_>) {
- self.perform_lint(cx, "item", item.def_id, &item.vis, item.span, true);
+ // Do not warn for fake `use` statements.
+ if let hir::ItemKind::Use(_, hir::UseKind::ListStem) = &item.kind {
+ return;
+ }
+ self.perform_lint(cx, "item", item.def_id, item.span, item.vis_span, true);
}
fn check_foreign_item(&mut self, cx: &LateContext<'_>, foreign_item: &hir::ForeignItem<'tcx>) {
cx,
"item",
foreign_item.def_id,
- &foreign_item.vis,
foreign_item.span,
+ foreign_item.vis_span,
true,
);
}
fn check_field_def(&mut self, cx: &LateContext<'_>, field: &hir::FieldDef<'_>) {
let def_id = cx.tcx.hir().local_def_id(field.hir_id);
- self.perform_lint(cx, "field", def_id, &field.vis, field.span, false);
+ self.perform_lint(cx, "field", def_id, field.span, field.vis_span, false);
}
fn check_impl_item(&mut self, cx: &LateContext<'_>, impl_item: &hir::ImplItem<'_>) {
- self.perform_lint(cx, "item", impl_item.def_id, &impl_item.vis, impl_item.span, false);
+ // Only lint inherent impl items.
+ if cx.tcx.associated_item(impl_item.def_id).trait_item_def_id.is_none() {
+ self.perform_lint(
+ cx,
+ "item",
+ impl_item.def_id,
+ impl_item.span,
+ impl_item.vis_span,
+ false,
+ );
+ }
}
}
// Bounds are respected for `type X = impl Trait`
return;
}
- let mut suggested_changing_assoc_types = false;
// There must not be a where clause
- if !type_alias_generics.where_clause.predicates.is_empty() {
- cx.lint(
- TYPE_ALIAS_BOUNDS,
- |lint| {
- let mut err = lint.build("where clauses are not enforced in type aliases");
- let spans: Vec<_> = type_alias_generics
- .where_clause
- .predicates
- .iter()
- .map(|pred| pred.span())
- .collect();
- err.set_span(spans);
- err.span_suggestion(
- type_alias_generics.where_clause.span_for_predicates_or_empty_place(),
- "the clause will not be checked when the type alias is used, and should be removed",
- String::new(),
- Applicability::MachineApplicable,
- );
- if !suggested_changing_assoc_types {
- TypeAliasBounds::suggest_changing_assoc_types(ty, &mut err);
- suggested_changing_assoc_types = true;
- }
- err.emit();
- },
- );
+ if type_alias_generics.predicates.is_empty() {
+ return;
}
- // The parameters must not have bounds
- for param in type_alias_generics.params.iter() {
- let spans: Vec<_> = param.bounds.iter().map(|b| b.span()).collect();
- let suggestion = spans
- .iter()
- .map(|sp| {
- let start = param.span.between(*sp); // Include the `:` in `T: Bound`.
- (start.to(*sp), String::new())
- })
- .collect();
- if !spans.is_empty() {
- cx.struct_span_lint(TYPE_ALIAS_BOUNDS, spans, |lint| {
- let mut err =
- lint.build("bounds on generic parameters are not enforced in type aliases");
- let msg = "the bound will not be checked when the type alias is used, \
- and should be removed";
- err.multipart_suggestion(msg, suggestion, Applicability::MachineApplicable);
- if !suggested_changing_assoc_types {
- TypeAliasBounds::suggest_changing_assoc_types(ty, &mut err);
- suggested_changing_assoc_types = true;
- }
- err.emit();
- });
+
+ let mut where_spans = Vec::new();
+ let mut inline_spans = Vec::new();
+ let mut inline_sugg = Vec::new();
+ for p in type_alias_generics.predicates {
+ let span = p.span();
+ if p.in_where_clause() {
+ where_spans.push(span);
+ } else {
+ for b in p.bounds() {
+ inline_spans.push(b.span());
+ }
+ inline_sugg.push((span, String::new()));
}
}
+
+ let mut suggested_changing_assoc_types = false;
+ if !where_spans.is_empty() {
+ cx.lint(TYPE_ALIAS_BOUNDS, |lint| {
+ let mut err = lint.build("where clauses are not enforced in type aliases");
+ err.set_span(where_spans);
+ err.span_suggestion(
+ type_alias_generics.where_clause_span,
+ "the clause will not be checked when the type alias is used, and should be removed",
+ String::new(),
+ Applicability::MachineApplicable,
+ );
+ if !suggested_changing_assoc_types {
+ TypeAliasBounds::suggest_changing_assoc_types(ty, &mut err);
+ suggested_changing_assoc_types = true;
+ }
+ err.emit();
+ });
+ }
+
+ if !inline_spans.is_empty() {
+ cx.lint(TYPE_ALIAS_BOUNDS, |lint| {
+ let mut err =
+ lint.build("bounds on generic parameters are not enforced in type aliases");
+ err.set_span(inline_spans);
+ err.multipart_suggestion(
+ "the bound will not be checked when the type alias is used, and should be removed",
+ inline_sugg,
+ Applicability::MachineApplicable,
+ );
+ if !suggested_changing_assoc_types {
+ TypeAliasBounds::suggest_changing_assoc_types(ty, &mut err);
+ }
+ err.emit();
+ });
+ }
}
}
.collect()
}
- fn collect_outlived_lifetimes<'tcx>(
- &self,
- param: &'tcx hir::GenericParam<'tcx>,
- tcx: TyCtxt<'tcx>,
- inferred_outlives: &'tcx [(ty::Predicate<'tcx>, Span)],
- ty_generics: &'tcx ty::Generics,
- ) -> Vec<ty::Region<'tcx>> {
- let index =
- ty_generics.param_def_id_to_index[&tcx.hir().local_def_id(param.hir_id).to_def_id()];
-
- match param.kind {
- hir::GenericParamKind::Lifetime { .. } => {
- Self::lifetimes_outliving_lifetime(inferred_outlives, index)
- }
- hir::GenericParamKind::Type { .. } => {
- Self::lifetimes_outliving_type(inferred_outlives, index)
- }
- hir::GenericParamKind::Const { .. } => Vec::new(),
- }
- }
-
fn collect_outlives_bound_spans<'tcx>(
&self,
tcx: TyCtxt<'tcx>,
let mut bound_count = 0;
let mut lint_spans = Vec::new();
-
- for param in hir_generics.params {
- let has_lifetime_bounds = param
- .bounds
- .iter()
- .any(|bound| matches!(bound, hir::GenericBound::Outlives(_)));
- if !has_lifetime_bounds {
- continue;
- }
-
- let relevant_lifetimes =
- self.collect_outlived_lifetimes(param, cx.tcx, inferred_outlives, ty_generics);
- if relevant_lifetimes.is_empty() {
- continue;
- }
-
- let bound_spans = self.collect_outlives_bound_spans(
- cx.tcx,
- ¶m.bounds,
- &relevant_lifetimes,
- infer_static,
- );
- bound_count += bound_spans.len();
- lint_spans.extend(self.consolidate_outlives_bound_spans(
- param.span.shrink_to_hi(),
- ¶m.bounds,
- bound_spans,
- ));
- }
-
let mut where_lint_spans = Vec::new();
let mut dropped_predicate_count = 0;
- let num_predicates = hir_generics.where_clause.predicates.len();
- for (i, where_predicate) in hir_generics.where_clause.predicates.iter().enumerate() {
- let (relevant_lifetimes, bounds, span) = match where_predicate {
+ let num_predicates = hir_generics.predicates.len();
+ for (i, where_predicate) in hir_generics.predicates.iter().enumerate() {
+ let (relevant_lifetimes, bounds, span, in_where_clause) = match where_predicate {
hir::WherePredicate::RegionPredicate(predicate) => {
if let Some(Region::EarlyBound(index, ..)) =
cx.tcx.named_region(predicate.lifetime.hir_id)
Self::lifetimes_outliving_lifetime(inferred_outlives, index),
&predicate.bounds,
predicate.span,
+ predicate.in_where_clause,
)
} else {
continue;
Self::lifetimes_outliving_type(inferred_outlives, index),
&predicate.bounds,
predicate.span,
+ predicate.in_where_clause,
)
}
_ => {
dropped_predicate_count += 1;
}
- // If all the bounds on a predicate were inferable and there are
- // further predicates, we want to eat the trailing comma.
- if drop_predicate && i + 1 < num_predicates {
- let next_predicate_span = hir_generics.where_clause.predicates[i + 1].span();
+ if drop_predicate && !in_where_clause {
+ lint_spans.push(span);
+ } else if drop_predicate && i + 1 < num_predicates {
+ // If all the bounds on a predicate were inferable and there are
+ // further predicates, we want to eat the trailing comma.
+ let next_predicate_span = hir_generics.predicates[i + 1].span();
where_lint_spans.push(span.to(next_predicate_span.shrink_to_lo()));
} else {
where_lint_spans.extend(self.consolidate_outlives_bound_spans(
// If all predicates are inferable, drop the entire clause
// (including the `where`)
- if num_predicates > 0 && dropped_predicate_count == num_predicates {
+ if hir_generics.has_where_clause && dropped_predicate_count == num_predicates {
let where_span = hir_generics
- .where_clause
- .span()
+ .where_clause_span()
.expect("span of (nonempty) where clause should exist");
// Extend the where clause back to the closing `>` of the
// generics, except for tuple struct, which have the `where`
},
lint_spans
.into_iter()
- .map(|span| (span, "".to_owned()))
+ .map(|span| (span, String::new()))
.collect::<Vec<_>>(),
Applicability::MachineApplicable,
)
db.span_suggestion_verbose(
span.shrink_to_hi(),
"insert whitespace here to avoid this being parsed as a prefix in Rust 2021",
- " ".into(),
+ " ",
Applicability::MachineApplicable,
);
}
l.span_suggestion_verbose(
arg_span.shrink_to_lo(),
"add a \"{}\" format string to Display the message",
- "\"{}\", ".into(),
+ "\"{}\", ",
fmt_applicability,
);
} else if suggest_debug {
"add a \"{{:?}}\" format string to use the Debug implementation of `{}`",
ty,
),
- "\"{:?}\", ".into(),
+ "\"{:?}\", ",
fmt_applicability,
);
}
l.span_suggestion(
arg.span.shrink_to_hi(),
&format!("add the missing argument{}", pluralize!(n_arguments)),
- ", ...".into(),
+ ", ...",
Applicability::HasPlaceholders,
);
l.span_suggestion(
arg.span.shrink_to_lo(),
"or add a \"{}\" format string to use the message literally",
- "\"{}\", ".into(),
+ "\"{}\", ",
Applicability::MachineApplicable,
);
}
l.span_suggestion(
arg.span.shrink_to_lo(),
"add a \"{}\" format string to use the message literally",
- "\"{}\", ".into(),
+ "\"{}\", ",
Applicability::MachineApplicable,
);
}
}
_ => (),
},
- FnKind::ItemFn(ident, _, header, _) => {
+ FnKind::ItemFn(ident, _, header) => {
// Skip foreign-ABI #[no_mangle] functions (Issue #31924)
if header.abi != Abi::Rust && cx.sess().contains_name(attrs, sym::no_mangle) {
return;
&& let Some(adt) = cx.tcx.type_of(impl_did).ty_adt_def()
// skip extension traits, only lint functions from the standard library
&& cx.tcx.trait_id_of_impl(impl_did).is_none()
- && let Some(parent) = cx.tcx.parent(adt.did())
+ && let parent = cx.tcx.parent(adt.did())
&& cx.tcx.is_diagnostic_item(sym::atomic_mod, parent)
&& ATOMIC_TYPES.contains(&cx.tcx.item_name(adt.did()))
{
orderings.iter().any(|ordering| {
tcx.item_name(did) == *ordering && {
let parent = tcx.parent(did);
- parent == atomic_ordering
+ Some(parent) == atomic_ordering
// needed in case this is a ctor, not a variant
- || parent.map_or(false, |parent| tcx.parent(parent) == atomic_ordering)
+ || tcx.opt_parent(parent) == atomic_ordering
}
})
}
_ => None,
}
}
+
+ pub fn is_error(self) -> bool {
+ match self {
+ Level::Allow | Level::Expect(_) | Level::Warn | Level::ForceWarn => false,
+ Level::Deny | Level::Forbid => true,
+ }
+ }
}
/// Specification of a single lint.
}
extern "C" LLVMPassRef LLVMRustFindAndCreatePass(const char *PassName) {
+#if LLVM_VERSION_LT(15, 0)
StringRef SR(PassName);
PassRegistry *PR = PassRegistry::getPassRegistry();
return wrap(PI->createPass());
}
return nullptr;
+#else
+ report_fatal_error("Legacy PM not supported with LLVM 15");
+#endif
}
extern "C" LLVMPassRef LLVMRustCreateAddressSanitizerFunctionPass(bool Recover) {
+#if LLVM_VERSION_LT(15, 0)
const bool CompileKernel = false;
const bool UseAfterScope = true;
return wrap(createAddressSanitizerFunctionPass(CompileKernel, Recover, UseAfterScope));
+#else
+ report_fatal_error("Legacy PM not supported with LLVM 15");
+#endif
}
extern "C" LLVMPassRef LLVMRustCreateModuleAddressSanitizerPass(bool Recover) {
+#if LLVM_VERSION_LT(15, 0)
const bool CompileKernel = false;
return wrap(createModuleAddressSanitizerLegacyPassPass(CompileKernel, Recover));
+#else
+ report_fatal_error("Legacy PM not supported with LLVM 15");
+#endif
}
extern "C" LLVMPassRef LLVMRustCreateMemorySanitizerPass(int TrackOrigins, bool Recover) {
+#if LLVM_VERSION_LT(15, 0)
const bool CompileKernel = false;
return wrap(createMemorySanitizerLegacyPassPass(
MemorySanitizerOptions{TrackOrigins, Recover, CompileKernel}));
+#else
+ report_fatal_error("Legacy PM not supported with LLVM 15");
+#endif
}
extern "C" LLVMPassRef LLVMRustCreateThreadSanitizerPass() {
+#if LLVM_VERSION_LT(15, 0)
return wrap(createThreadSanitizerLegacyPassPass());
+#else
+ report_fatal_error("Legacy PM not supported with LLVM 15");
+#endif
}
extern "C" LLVMPassRef LLVMRustCreateHWAddressSanitizerPass(bool Recover) {
+#if LLVM_VERSION_LT(15, 0)
const bool CompileKernel = false;
return wrap(createHWAddressSanitizerLegacyPassPass(CompileKernel, Recover));
+#else
+ report_fatal_error("Legacy PM not supported with LLVM 15");
+#endif
}
extern "C" LLVMRustPassKind LLVMRustPassKind(LLVMPassRef RustPass) {
}
extern "C" void LLVMRustAddPass(LLVMPassManagerRef PMR, LLVMPassRef RustPass) {
+#if LLVM_VERSION_LT(15, 0)
assert(RustPass);
Pass *Pass = unwrap(RustPass);
PassManagerBase *PMB = unwrap(PMR);
PMB->add(Pass);
+#else
+ report_fatal_error("Legacy PM not supported with LLVM 15");
+#endif
+}
+
+extern "C" LLVMPassManagerBuilderRef LLVMRustPassManagerBuilderCreate() {
+#if LLVM_VERSION_LT(15, 0)
+ return LLVMPassManagerBuilderCreate();
+#else
+ report_fatal_error("Legacy PM not supported with LLVM 15");
+#endif
+}
+
+extern "C" void LLVMRustPassManagerBuilderDispose(LLVMPassManagerBuilderRef PMB) {
+#if LLVM_VERSION_LT(15, 0)
+ LLVMPassManagerBuilderDispose(PMB);
+#else
+ report_fatal_error("Legacy PM not supported with LLVM 15");
+#endif
+}
+
+extern "C" void LLVMRustPassManagerBuilderPopulateFunctionPassManager(
+ LLVMPassManagerBuilderRef PMB, LLVMPassManagerRef PM) {
+#if LLVM_VERSION_LT(15, 0)
+ LLVMPassManagerBuilderPopulateFunctionPassManager(PMB, PM);
+#else
+ report_fatal_error("Legacy PM not supported with LLVM 15");
+#endif
+}
+
+extern "C" void LLVMRustPassManagerBuilderPopulateModulePassManager(
+ LLVMPassManagerBuilderRef PMB, LLVMPassManagerRef PM) {
+#if LLVM_VERSION_LT(15, 0)
+ LLVMPassManagerBuilderPopulateModulePassManager(PMB, PM);
+#else
+ report_fatal_error("Legacy PM not supported with LLVM 15");
+#endif
+}
+
+extern "C" void LLVMRustPassManagerBuilderPopulateLTOPassManager(
+ LLVMPassManagerBuilderRef PMB, LLVMPassManagerRef PM, bool Internalize, bool RunInliner) {
+#if LLVM_VERSION_LT(15, 0)
+ LLVMPassManagerBuilderPopulateLTOPassManager(PMB, PM, Internalize, RunInliner);
+#else
+ report_fatal_error("Legacy PM not supported with LLVM 15");
+#endif
}
extern "C"
LLVMPassManagerBuilderRef PMBR,
LLVMPassManagerRef PMR
) {
+#if LLVM_VERSION_LT(15, 0)
unwrap(PMBR)->populateThinLTOPassManager(*unwrap(PMR));
+#else
+ report_fatal_error("Legacy PM not supported with LLVM 15");
+#endif
+}
+
+extern "C" void LLVMRustPassManagerBuilderUseInlinerWithThreshold(
+ LLVMPassManagerBuilderRef PMB, unsigned Threshold) {
+#if LLVM_VERSION_LT(15, 0)
+ LLVMPassManagerBuilderUseInlinerWithThreshold(PMB, Threshold);
+#else
+ report_fatal_error("Legacy PM not supported with LLVM 15");
+#endif
}
extern "C"
void LLVMRustAddLastExtensionPasses(
LLVMPassManagerBuilderRef PMBR, LLVMPassRef *Passes, size_t NumPasses) {
+#if LLVM_VERSION_LT(15, 0)
auto AddExtensionPasses = [Passes, NumPasses](
const PassManagerBuilder &Builder, PassManagerBase &PM) {
for (size_t I = 0; I < NumPasses; I++) {
AddExtensionPasses);
unwrap(PMBR)->addExtension(PassManagerBuilder::EP_EnabledOnOptLevel0,
AddExtensionPasses);
+#else
+ report_fatal_error("Legacy PM not supported with LLVM 15");
+#endif
}
#ifdef LLVM_COMPONENT_X86
extern "C" void LLVMRustConfigurePassManagerBuilder(
LLVMPassManagerBuilderRef PMBR, LLVMRustCodeGenOptLevel OptLevel,
bool MergeFunctions, bool SLPVectorize, bool LoopVectorize, bool PrepareForThinLTO,
- const char* PGOGenPath, const char* PGOUsePath, const char* PGOSampleUsePath) {
+ const char* PGOGenPath, const char* PGOUsePath, const char* PGOSampleUsePath,
+ int SizeLevel) {
+#if LLVM_VERSION_LT(15, 0)
unwrap(PMBR)->MergeFunctions = MergeFunctions;
unwrap(PMBR)->SLPVectorize = SLPVectorize;
unwrap(PMBR)->OptLevel = fromRust(OptLevel);
unwrap(PMBR)->LoopVectorize = LoopVectorize;
unwrap(PMBR)->PrepareForThinLTO = PrepareForThinLTO;
+ unwrap(PMBR)->SizeLevel = SizeLevel;
+ unwrap(PMBR)->DisableUnrollLoops = SizeLevel != 0;
if (PGOGenPath) {
assert(!PGOUsePath && !PGOSampleUsePath);
} else if (PGOSampleUsePath) {
unwrap(PMBR)->PGOSampleUse = PGOSampleUsePath;
}
+#else
+ report_fatal_error("Legacy PM not supported with LLVM 15");
+#endif
}
// Unfortunately, the LLVM C API doesn't provide a way to set the `LibraryInfo`
#include "llvm/IR/GlobalVariable.h"
#include "llvm/IR/Instructions.h"
#include "llvm/IR/Intrinsics.h"
+#include "llvm/IR/Mangler.h"
#include "llvm/Object/Archive.h"
#include "llvm/Object/COFFImportFile.h"
#include "llvm/Object/ObjectFile.h"
return LLVMBFloatTypeKind;
case Type::X86_AMXTyID:
return LLVMX86_AMXTypeKind;
+#if LLVM_VERSION_GE(15, 0)
+ case Type::DXILPointerTyID:
+ report_fatal_error("Rust does not support DirectX typed pointers.");
+ break;
+#endif
}
report_fatal_error("Unhandled TypeID.");
}
--- /dev/null
+#![deny(unused_must_use)]
+
+use crate::diagnostics::error::{
+ invalid_nested_attr, span_err, throw_invalid_attr, throw_invalid_nested_attr, throw_span_err,
+ SessionDiagnosticDeriveError,
+};
+use crate::diagnostics::utils::{
+ option_inner_ty, report_error_if_not_applied_to_span, type_matches_path, Applicability,
+ FieldInfo, HasFieldMap, SetOnce,
+};
+use proc_macro2::TokenStream;
+use quote::{format_ident, quote};
+use std::collections::HashMap;
+use std::str::FromStr;
+use syn::{spanned::Spanned, Attribute, Meta, MetaList, MetaNameValue, Type};
+use synstructure::Structure;
+
+/// The central struct for constructing the `into_diagnostic` method from an annotated struct.
+pub(crate) struct SessionDiagnosticDerive<'a> {
+ structure: Structure<'a>,
+ builder: SessionDiagnosticDeriveBuilder,
+}
+
+impl<'a> SessionDiagnosticDerive<'a> {
+ pub(crate) fn new(diag: syn::Ident, sess: syn::Ident, structure: Structure<'a>) -> Self {
+ // Build the mapping of field names to fields. This allows attributes to peek values from
+ // other fields.
+ let mut fields_map = HashMap::new();
+
+ // Convenience bindings.
+ let ast = structure.ast();
+
+ if let syn::Data::Struct(syn::DataStruct { fields, .. }) = &ast.data {
+ for field in fields.iter() {
+ if let Some(ident) = &field.ident {
+ fields_map.insert(ident.to_string(), quote! { &self.#ident });
+ }
+ }
+ }
+
+ Self {
+ builder: SessionDiagnosticDeriveBuilder {
+ diag,
+ sess,
+ fields: fields_map,
+ kind: None,
+ code: None,
+ slug: None,
+ },
+ structure,
+ }
+ }
+
+ pub(crate) fn into_tokens(self) -> TokenStream {
+ let SessionDiagnosticDerive { mut structure, mut builder } = self;
+
+ let ast = structure.ast();
+ let attrs = &ast.attrs;
+
+ let (implementation, param_ty) = {
+ if let syn::Data::Struct(..) = ast.data {
+ let preamble = {
+ let preamble = attrs.iter().map(|attr| {
+ builder
+ .generate_structure_code(attr)
+ .unwrap_or_else(|v| v.to_compile_error())
+ });
+
+ quote! {
+ #(#preamble)*;
+ }
+ };
+
+ // Generates calls to `span_label` and similar functions based on the attributes
+ // on fields. Code for suggestions uses formatting machinery and the value of
+ // other fields - because any given field can be referenced multiple times, it
+ // should be accessed through a borrow. When passing fields to `set_arg` (which
+ // happens below) for Fluent, we want to move the data, so that has to happen
+ // in a separate pass over the fields.
+ let attrs = structure.each(|field_binding| {
+ let field = field_binding.ast();
+ let result = field.attrs.iter().map(|attr| {
+ builder
+ .generate_field_attr_code(
+ attr,
+ FieldInfo {
+ vis: &field.vis,
+ binding: field_binding,
+ ty: &field.ty,
+ span: &field.span(),
+ },
+ )
+ .unwrap_or_else(|v| v.to_compile_error())
+ });
+
+ quote! { #(#result);* }
+ });
+
+ // When generating `set_arg` calls, move data rather than borrow it to avoid
+ // requiring clones - this must therefore be the last use of each field (for
+ // example, any formatting machinery that might refer to a field should be
+ // generated already).
+ structure.bind_with(|_| synstructure::BindStyle::Move);
+ let args = structure.each(|field_binding| {
+ let field = field_binding.ast();
+ // When a field has attributes like `#[label]` or `#[note]` then it doesn't
+ // need to be passed as an argument to the diagnostic. But when a field has no
+ // attributes then it must be passed as an argument to the diagnostic so that
+ // it can be referred to by Fluent messages.
+ if field.attrs.is_empty() {
+ let diag = &builder.diag;
+ let ident = field_binding.ast().ident.as_ref().unwrap();
+ quote! {
+ #diag.set_arg(
+ stringify!(#ident),
+ #field_binding.into_diagnostic_arg()
+ );
+ }
+ } else {
+ quote! {}
+ }
+ });
+
+ let span = ast.span().unwrap();
+ let (diag, sess) = (&builder.diag, &builder.sess);
+ let init = match (builder.kind, builder.slug) {
+ (None, _) => {
+ span_err(span, "diagnostic kind not specified")
+ .help("use the `#[error(...)]` attribute to create an error")
+ .emit();
+ return SessionDiagnosticDeriveError::ErrorHandled.to_compile_error();
+ }
+ (Some((kind, _)), None) => {
+ span_err(span, "`slug` not specified")
+ .help(&format!("use the `#[{}(slug = \"...\")]` attribute to set this diagnostic's slug", kind.descr()))
+ .emit();
+ return SessionDiagnosticDeriveError::ErrorHandled.to_compile_error();
+ }
+ (Some((SessionDiagnosticKind::Error, _)), Some((slug, _))) => {
+ quote! {
+ let mut #diag = #sess.struct_err(
+ rustc_errors::DiagnosticMessage::fluent(#slug),
+ );
+ }
+ }
+ (Some((SessionDiagnosticKind::Warn, _)), Some((slug, _))) => {
+ quote! {
+ let mut #diag = #sess.struct_warn(
+ rustc_errors::DiagnosticMessage::fluent(#slug),
+ );
+ }
+ }
+ };
+
+ let implementation = quote! {
+ #init
+ #preamble
+ match self {
+ #attrs
+ }
+ match self {
+ #args
+ }
+ #diag
+ };
+ let param_ty = match builder.kind {
+ Some((SessionDiagnosticKind::Error, _)) => {
+ quote! { rustc_errors::ErrorGuaranteed }
+ }
+ Some((SessionDiagnosticKind::Warn, _)) => quote! { () },
+ _ => unreachable!(),
+ };
+
+ (implementation, param_ty)
+ } else {
+ span_err(
+ ast.span().unwrap(),
+ "`#[derive(SessionDiagnostic)]` can only be used on structs",
+ )
+ .emit();
+
+ let implementation = SessionDiagnosticDeriveError::ErrorHandled.to_compile_error();
+ let param_ty = quote! { rustc_errors::ErrorGuaranteed };
+ (implementation, param_ty)
+ }
+ };
+
+ let sess = &builder.sess;
+ structure.gen_impl(quote! {
+ gen impl<'__session_diagnostic_sess> rustc_session::SessionDiagnostic<'__session_diagnostic_sess, #param_ty>
+ for @Self
+ {
+ fn into_diagnostic(
+ self,
+ #sess: &'__session_diagnostic_sess rustc_session::parse::ParseSess
+ ) -> rustc_errors::DiagnosticBuilder<'__session_diagnostic_sess, #param_ty> {
+ use rustc_errors::IntoDiagnosticArg;
+ #implementation
+ }
+ }
+ })
+ }
+}
+
+/// What kind of session diagnostic is being derived - an error or a warning?
+#[derive(Copy, Clone)]
+enum SessionDiagnosticKind {
+ /// `#[error(..)]`
+ Error,
+ /// `#[warn(..)]`
+ Warn,
+}
+
+impl SessionDiagnosticKind {
+ /// Returns human-readable string corresponding to the kind.
+ fn descr(&self) -> &'static str {
+ match self {
+ SessionDiagnosticKind::Error => "error",
+ SessionDiagnosticKind::Warn => "warning",
+ }
+ }
+}
+
+/// Tracks persistent information required for building up the individual calls to diagnostic
+/// methods for the final generated method. This is a separate struct to `SessionDiagnosticDerive`
+/// only to be able to destructure and split `self.builder` and the `self.structure` up to avoid a
+/// double mut borrow later on.
+struct SessionDiagnosticDeriveBuilder {
+ /// Name of the session parameter that's passed in to the `as_error` method.
+ sess: syn::Ident,
+ /// The identifier to use for the generated `DiagnosticBuilder` instance.
+ diag: syn::Ident,
+
+ /// Store a map of field name to its corresponding field. This is built on construction of the
+ /// derive builder.
+ fields: HashMap<String, TokenStream>,
+
+ /// Kind of diagnostic requested via the struct attribute.
+ kind: Option<(SessionDiagnosticKind, proc_macro::Span)>,
+ /// Slug is a mandatory part of the struct attribute as corresponds to the Fluent message that
+ /// has the actual diagnostic message.
+ slug: Option<(String, proc_macro::Span)>,
+ /// Error codes are a optional part of the struct attribute - this is only set to detect
+ /// multiple specifications.
+ code: Option<(String, proc_macro::Span)>,
+}
+
+impl HasFieldMap for SessionDiagnosticDeriveBuilder {
+ fn get_field_binding(&self, field: &String) -> Option<&TokenStream> {
+ self.fields.get(field)
+ }
+}
+
+impl SessionDiagnosticDeriveBuilder {
+ /// Establishes state in the `SessionDiagnosticDeriveBuilder` resulting from the struct
+ /// attributes like `#[error(..)#`, such as the diagnostic kind and slug. Generates
+ /// diagnostic builder calls for setting error code and creating note/help messages.
+ fn generate_structure_code(
+ &mut self,
+ attr: &Attribute,
+ ) -> Result<TokenStream, SessionDiagnosticDeriveError> {
+ let span = attr.span().unwrap();
+
+ let name = attr.path.segments.last().unwrap().ident.to_string();
+ let name = name.as_str();
+ let meta = attr.parse_meta()?;
+
+ if matches!(name, "help" | "note") && matches!(meta, Meta::Path(_) | Meta::NameValue(_)) {
+ let diag = &self.diag;
+ let slug = match &self.slug {
+ Some((slug, _)) => slug.as_str(),
+ None => throw_span_err!(
+ span,
+ &format!(
+ "`#[{}{}]` must come after `#[error(..)]` or `#[warn(..)]`",
+ name,
+ match meta {
+ Meta::Path(_) => "",
+ Meta::NameValue(_) => " = ...",
+ _ => unreachable!(),
+ }
+ )
+ ),
+ };
+ let id = match meta {
+ Meta::Path(..) => quote! { #name },
+ Meta::NameValue(MetaNameValue { lit: syn::Lit::Str(s), .. }) => {
+ quote! { #s }
+ }
+ _ => unreachable!(),
+ };
+ let fn_name = proc_macro2::Ident::new(name, attr.span());
+
+ return Ok(quote! {
+ #diag.#fn_name(rustc_errors::DiagnosticMessage::fluent_attr(#slug, #id));
+ });
+ }
+
+ let nested = match meta {
+ Meta::List(MetaList { ref nested, .. }) => nested,
+ _ => throw_invalid_attr!(attr, &meta),
+ };
+
+ let kind = match name {
+ "error" => SessionDiagnosticKind::Error,
+ "warning" => SessionDiagnosticKind::Warn,
+ _ => throw_invalid_attr!(attr, &meta, |diag| {
+ diag.help("only `error` and `warning` are valid attributes")
+ }),
+ };
+ self.kind.set_once((kind, span));
+
+ let mut tokens = Vec::new();
+ for nested_attr in nested {
+ let meta = match nested_attr {
+ syn::NestedMeta::Meta(meta) => meta,
+ _ => throw_invalid_nested_attr!(attr, &nested_attr),
+ };
+
+ let path = meta.path();
+ let nested_name = path.segments.last().unwrap().ident.to_string();
+ match &meta {
+ // Struct attributes are only allowed to be applied once, and the diagnostic
+ // changes will be set in the initialisation code.
+ Meta::NameValue(MetaNameValue { lit: syn::Lit::Str(s), .. }) => {
+ let span = s.span().unwrap();
+ match nested_name.as_str() {
+ "slug" => {
+ self.slug.set_once((s.value(), span));
+ }
+ "code" => {
+ self.code.set_once((s.value(), span));
+ let (diag, code) = (&self.diag, &self.code.as_ref().map(|(v, _)| v));
+ tokens.push(quote! {
+ #diag.code(rustc_errors::DiagnosticId::Error(#code.to_string()));
+ });
+ }
+ _ => invalid_nested_attr(attr, &nested_attr)
+ .help("only `slug` and `code` are valid nested attributes")
+ .emit(),
+ }
+ }
+ _ => invalid_nested_attr(attr, &nested_attr).emit(),
+ }
+ }
+
+ Ok(tokens.drain(..).collect())
+ }
+
+ fn generate_field_attr_code(
+ &mut self,
+ attr: &syn::Attribute,
+ info: FieldInfo<'_>,
+ ) -> Result<TokenStream, SessionDiagnosticDeriveError> {
+ let field_binding = &info.binding.binding;
+ let option_ty = option_inner_ty(&info.ty);
+ let generated_code = self.generate_non_option_field_code(
+ attr,
+ FieldInfo {
+ vis: info.vis,
+ binding: info.binding,
+ ty: option_ty.unwrap_or(&info.ty),
+ span: info.span,
+ },
+ )?;
+
+ if option_ty.is_none() {
+ Ok(quote! { #generated_code })
+ } else {
+ Ok(quote! {
+ if let Some(#field_binding) = #field_binding {
+ #generated_code
+ }
+ })
+ }
+ }
+
+ fn generate_non_option_field_code(
+ &mut self,
+ attr: &Attribute,
+ info: FieldInfo<'_>,
+ ) -> Result<TokenStream, SessionDiagnosticDeriveError> {
+ let diag = &self.diag;
+ let field_binding = &info.binding.binding;
+
+ let name = attr.path.segments.last().unwrap().ident.to_string();
+ let name = name.as_str();
+
+ let meta = attr.parse_meta()?;
+ match meta {
+ Meta::Path(_) => match name {
+ "skip_arg" => {
+ // Don't need to do anything - by virtue of the attribute existing, the
+ // `set_arg` call will not be generated.
+ Ok(quote! {})
+ }
+ "primary_span" => {
+ report_error_if_not_applied_to_span(attr, &info)?;
+ Ok(quote! {
+ #diag.set_span(*#field_binding);
+ })
+ }
+ "label" | "note" | "help" => {
+ report_error_if_not_applied_to_span(attr, &info)?;
+ Ok(self.add_subdiagnostic(field_binding, name, name))
+ }
+ "subdiagnostic" => Ok(quote! { #diag.subdiagnostic(*#field_binding); }),
+ _ => throw_invalid_attr!(attr, &meta, |diag| {
+ diag
+ .help("only `skip_arg`, `primary_span`, `label`, `note`, `help` and `subdiagnostic` are valid field attributes")
+ }),
+ },
+ Meta::NameValue(MetaNameValue { lit: syn::Lit::Str(ref s), .. }) => match name {
+ "label" | "note" | "help" => {
+ report_error_if_not_applied_to_span(attr, &info)?;
+ Ok(self.add_subdiagnostic(field_binding, name, &s.value()))
+ }
+ _ => throw_invalid_attr!(attr, &meta, |diag| {
+ diag.help("only `label`, `note` and `help` are valid field attributes")
+ }),
+ },
+ Meta::List(MetaList { ref path, ref nested, .. }) => {
+ let name = path.segments.last().unwrap().ident.to_string();
+ let name = name.as_ref();
+
+ match name {
+ "suggestion" | "suggestion_short" | "suggestion_hidden"
+ | "suggestion_verbose" => (),
+ _ => throw_invalid_attr!(attr, &meta, |diag| {
+ diag
+ .help("only `suggestion{,_short,_hidden,_verbose}` are valid field attributes")
+ }),
+ };
+
+ let (span_field, mut applicability) = self.span_and_applicability_of_ty(info)?;
+
+ let mut msg = None;
+ let mut code = None;
+
+ for nested_attr in nested {
+ let meta = match nested_attr {
+ syn::NestedMeta::Meta(ref meta) => meta,
+ syn::NestedMeta::Lit(_) => throw_invalid_nested_attr!(attr, &nested_attr),
+ };
+
+ let nested_name = meta.path().segments.last().unwrap().ident.to_string();
+ let nested_name = nested_name.as_str();
+ match meta {
+ Meta::NameValue(MetaNameValue { lit: syn::Lit::Str(s), .. }) => {
+ let span = meta.span().unwrap();
+ match nested_name {
+ "message" => {
+ msg = Some(s.value());
+ }
+ "code" => {
+ let formatted_str = self.build_format(&s.value(), s.span());
+ code = Some(formatted_str);
+ }
+ "applicability" => {
+ applicability = match applicability {
+ Some(v) => {
+ span_err(
+ span,
+ "applicability cannot be set in both the field and attribute"
+ ).emit();
+ Some(v)
+ }
+ None => match Applicability::from_str(&s.value()) {
+ Ok(v) => Some(quote! { #v }),
+ Err(()) => {
+ span_err(span, "invalid applicability").emit();
+ None
+ }
+ },
+ }
+ }
+ _ => throw_invalid_nested_attr!(attr, &nested_attr, |diag| {
+ diag.help(
+ "only `message`, `code` and `applicability` are valid field attributes",
+ )
+ }),
+ }
+ }
+ _ => throw_invalid_nested_attr!(attr, &nested_attr),
+ }
+ }
+
+ let applicability = applicability
+ .unwrap_or_else(|| quote!(rustc_errors::Applicability::Unspecified));
+
+ let method = format_ident!("span_{}", name);
+
+ let slug = self
+ .slug
+ .as_ref()
+ .map(|(slug, _)| slug.as_str())
+ .unwrap_or_else(|| "missing-slug");
+ let msg = msg.as_deref().unwrap_or("suggestion");
+ let msg = quote! { rustc_errors::DiagnosticMessage::fluent_attr(#slug, #msg) };
+ let code = code.unwrap_or_else(|| quote! { String::new() });
+
+ Ok(quote! { #diag.#method(#span_field, #msg, #code, #applicability); })
+ }
+ _ => throw_invalid_attr!(attr, &meta),
+ }
+ }
+
+ /// Adds a subdiagnostic by generating a `diag.span_$kind` call with the current slug and
+ /// `fluent_attr_identifier`.
+ fn add_subdiagnostic(
+ &self,
+ field_binding: &proc_macro2::Ident,
+ kind: &str,
+ fluent_attr_identifier: &str,
+ ) -> TokenStream {
+ let diag = &self.diag;
+
+ let slug =
+ self.slug.as_ref().map(|(slug, _)| slug.as_str()).unwrap_or_else(|| "missing-slug");
+ let fn_name = format_ident!("span_{}", kind);
+ quote! {
+ #diag.#fn_name(
+ *#field_binding,
+ rustc_errors::DiagnosticMessage::fluent_attr(#slug, #fluent_attr_identifier)
+ );
+ }
+ }
+
+ fn span_and_applicability_of_ty(
+ &self,
+ info: FieldInfo<'_>,
+ ) -> Result<(TokenStream, Option<TokenStream>), SessionDiagnosticDeriveError> {
+ match &info.ty {
+ // If `ty` is `Span` w/out applicability, then use `Applicability::Unspecified`.
+ ty @ Type::Path(..) if type_matches_path(ty, &["rustc_span", "Span"]) => {
+ let binding = &info.binding.binding;
+ Ok((quote!(*#binding), None))
+ }
+ // If `ty` is `(Span, Applicability)` then return tokens accessing those.
+ Type::Tuple(tup) => {
+ let mut span_idx = None;
+ let mut applicability_idx = None;
+
+ for (idx, elem) in tup.elems.iter().enumerate() {
+ if type_matches_path(elem, &["rustc_span", "Span"]) {
+ if span_idx.is_none() {
+ span_idx = Some(syn::Index::from(idx));
+ } else {
+ throw_span_err!(
+ info.span.unwrap(),
+ "type of field annotated with `#[suggestion(...)]` contains more than one `Span`"
+ );
+ }
+ } else if type_matches_path(elem, &["rustc_errors", "Applicability"]) {
+ if applicability_idx.is_none() {
+ applicability_idx = Some(syn::Index::from(idx));
+ } else {
+ throw_span_err!(
+ info.span.unwrap(),
+ "type of field annotated with `#[suggestion(...)]` contains more than one Applicability"
+ );
+ }
+ }
+ }
+
+ if let Some(span_idx) = span_idx {
+ let binding = &info.binding.binding;
+ let span = quote!(#binding.#span_idx);
+ let applicability = applicability_idx
+ .map(|applicability_idx| quote!(#binding.#applicability_idx))
+ .unwrap_or_else(|| quote!(rustc_errors::Applicability::Unspecified));
+
+ return Ok((span, Some(applicability)));
+ }
+
+ throw_span_err!(info.span.unwrap(), "wrong types for suggestion", |diag| {
+ diag.help("`#[suggestion(...)]` on a tuple field must be applied to fields of type `(Span, Applicability)`")
+ });
+ }
+ // If `ty` isn't a `Span` or `(Span, Applicability)` then emit an error.
+ _ => throw_span_err!(info.span.unwrap(), "wrong field type for suggestion", |diag| {
+ diag.help("`#[suggestion(...)]` should be applied to fields of type `Span` or `(Span, Applicability)`")
+ }),
+ }
+ }
+}
--- /dev/null
+use proc_macro::{Diagnostic, Level, MultiSpan};
+use proc_macro2::TokenStream;
+use quote::quote;
+use syn::{spanned::Spanned, Attribute, Error as SynError, Meta, NestedMeta};
+
+#[derive(Debug)]
+pub(crate) enum SessionDiagnosticDeriveError {
+ SynError(SynError),
+ ErrorHandled,
+}
+
+impl SessionDiagnosticDeriveError {
+ pub(crate) fn to_compile_error(self) -> TokenStream {
+ match self {
+ SessionDiagnosticDeriveError::SynError(e) => e.to_compile_error(),
+ SessionDiagnosticDeriveError::ErrorHandled => {
+ // Return ! to avoid having to create a blank DiagnosticBuilder to return when an
+ // error has already been emitted to the compiler.
+ quote! {
+ { unreachable!(); }
+ }
+ }
+ }
+ }
+}
+
+impl From<SynError> for SessionDiagnosticDeriveError {
+ fn from(e: SynError) -> Self {
+ SessionDiagnosticDeriveError::SynError(e)
+ }
+}
+
+/// Helper function for use with `throw_*` macros - constraints `$f` to an `impl FnOnce`.
+pub(crate) fn _throw_err(
+ diag: Diagnostic,
+ f: impl FnOnce(Diagnostic) -> Diagnostic,
+) -> SessionDiagnosticDeriveError {
+ f(diag).emit();
+ SessionDiagnosticDeriveError::ErrorHandled
+}
+
+/// Returns an error diagnostic on span `span` with msg `msg`.
+pub(crate) fn span_err(span: impl MultiSpan, msg: &str) -> Diagnostic {
+ Diagnostic::spanned(span, Level::Error, msg)
+}
+
+/// Emit a diagnostic on span `$span` with msg `$msg` (optionally performing additional decoration
+/// using the `FnOnce` passed in `diag`) and return `Err(ErrorHandled)`.
+///
+/// For methods that return a `Result<_, SessionDiagnosticDeriveError>`:
+macro_rules! throw_span_err {
+ ($span:expr, $msg:expr) => {{ throw_span_err!($span, $msg, |diag| diag) }};
+ ($span:expr, $msg:expr, $f:expr) => {{
+ let diag = span_err($span, $msg);
+ return Err(crate::diagnostics::error::_throw_err(diag, $f));
+ }};
+}
+
+pub(crate) use throw_span_err;
+
+/// Returns an error diagnostic for an invalid attribute.
+pub(crate) fn invalid_attr(attr: &Attribute, meta: &Meta) -> Diagnostic {
+ let span = attr.span().unwrap();
+ let name = attr.path.segments.last().unwrap().ident.to_string();
+ let name = name.as_str();
+
+ match meta {
+ Meta::Path(_) => span_err(span, &format!("`#[{}]` is not a valid attribute", name)),
+ Meta::NameValue(_) => {
+ span_err(span, &format!("`#[{} = ...]` is not a valid attribute", name))
+ }
+ Meta::List(_) => span_err(span, &format!("`#[{}(...)]` is not a valid attribute", name)),
+ }
+}
+
+/// Emit a error diagnostic for an invalid attribute (optionally performing additional decoration
+/// using the `FnOnce` passed in `diag`) and return `Err(ErrorHandled)`.
+///
+/// For methods that return a `Result<_, SessionDiagnosticDeriveError>`:
+macro_rules! throw_invalid_attr {
+ ($attr:expr, $meta:expr) => {{ throw_invalid_attr!($attr, $meta, |diag| diag) }};
+ ($attr:expr, $meta:expr, $f:expr) => {{
+ let diag = crate::diagnostics::error::invalid_attr($attr, $meta);
+ return Err(crate::diagnostics::error::_throw_err(diag, $f));
+ }};
+}
+
+pub(crate) use throw_invalid_attr;
+
+/// Returns an error diagnostic for an invalid nested attribute.
+pub(crate) fn invalid_nested_attr(attr: &Attribute, nested: &NestedMeta) -> Diagnostic {
+ let name = attr.path.segments.last().unwrap().ident.to_string();
+ let name = name.as_str();
+
+ let span = nested.span().unwrap();
+ let meta = match nested {
+ syn::NestedMeta::Meta(meta) => meta,
+ syn::NestedMeta::Lit(_) => {
+ return span_err(span, &format!("`#[{}(\"...\")]` is not a valid attribute", name));
+ }
+ };
+
+ let span = meta.span().unwrap();
+ let nested_name = meta.path().segments.last().unwrap().ident.to_string();
+ let nested_name = nested_name.as_str();
+ match meta {
+ Meta::NameValue(..) => span_err(
+ span,
+ &format!("`#[{}({} = ...)]` is not a valid attribute", name, nested_name),
+ ),
+ Meta::Path(..) => {
+ span_err(span, &format!("`#[{}({})]` is not a valid attribute", name, nested_name))
+ }
+ Meta::List(..) => {
+ span_err(span, &format!("`#[{}({}(...))]` is not a valid attribute", name, nested_name))
+ }
+ }
+}
+
+/// Emit a error diagnostic for an invalid nested attribute (optionally performing additional
+/// decoration using the `FnOnce` passed in `diag`) and return `Err(ErrorHandled)`.
+///
+/// For methods that return a `Result<_, SessionDiagnosticDeriveError>`:
+macro_rules! throw_invalid_nested_attr {
+ ($attr:expr, $nested_attr:expr) => {{ throw_invalid_nested_attr!($attr, $nested_attr, |diag| diag) }};
+ ($attr:expr, $nested_attr:expr, $f:expr) => {{
+ let diag = crate::diagnostics::error::invalid_nested_attr($attr, $nested_attr);
+ return Err(crate::diagnostics::error::_throw_err(diag, $f));
+ }};
+}
+
+pub(crate) use throw_invalid_nested_attr;
--- /dev/null
+mod diagnostic;
+mod error;
+mod subdiagnostic;
+mod utils;
+
+use diagnostic::SessionDiagnosticDerive;
+use proc_macro2::TokenStream;
+use quote::format_ident;
+use subdiagnostic::SessionSubdiagnosticDerive;
+use synstructure::Structure;
+
+/// Implements `#[derive(SessionDiagnostic)]`, which allows for errors to be specified as a struct,
+/// independent from the actual diagnostics emitting code.
+///
+/// ```ignore (pseudo-rust)
+/// # extern crate rustc_errors;
+/// # use rustc_errors::Applicability;
+/// # extern crate rustc_span;
+/// # use rustc_span::{symbol::Ident, Span};
+/// # extern crate rust_middle;
+/// # use rustc_middle::ty::Ty;
+/// #[derive(SessionDiagnostic)]
+/// #[error(code = "E0505", slug = "borrowck-move-out-of-borrow")]
+/// pub struct MoveOutOfBorrowError<'tcx> {
+/// pub name: Ident,
+/// pub ty: Ty<'tcx>,
+/// #[primary_span]
+/// #[label]
+/// pub span: Span,
+/// #[label = "first-borrow-label"]
+/// pub first_borrow_span: Span,
+/// #[suggestion(code = "{name}.clone()")]
+/// pub clone_sugg: Option<(Span, Applicability)>
+/// }
+/// ```
+///
+/// ```fluent
+/// move-out-of-borrow = cannot move out of {$name} because it is borrowed
+/// .label = cannot move out of borrow
+/// .first-borrow-label = `{$ty}` first borrowed here
+/// .suggestion = consider cloning here
+/// ```
+///
+/// Then, later, to emit the error:
+///
+/// ```ignore (pseudo-rust)
+/// sess.emit_err(MoveOutOfBorrowError {
+/// expected,
+/// actual,
+/// span,
+/// first_borrow_span,
+/// clone_sugg: Some(suggestion, Applicability::MachineApplicable),
+/// });
+/// ```
+///
+/// See rustc dev guide for more examples on using the `#[derive(SessionDiagnostic)]`:
+/// <https://rustc-dev-guide.rust-lang.org/diagnostics/sessiondiagnostic.html>
+pub fn session_diagnostic_derive(s: Structure<'_>) -> TokenStream {
+ // Names for the diagnostic we build and the session we build it from.
+ let diag = format_ident!("diag");
+ let sess = format_ident!("sess");
+
+ SessionDiagnosticDerive::new(diag, sess, s).into_tokens()
+}
+
+/// Implements `#[derive(SessionSubdiagnostic)]`, which allows for labels, notes, helps and
+/// suggestions to be specified as a structs or enums, independent from the actual diagnostics
+/// emitting code or diagnostic derives.
+///
+/// ```ignore (pseudo-rust)
+/// #[derive(SessionSubdiagnostic)]
+/// pub enum ExpectedIdentifierLabel<'tcx> {
+/// #[label(slug = "parser-expected-identifier")]
+/// WithoutFound {
+/// #[primary_span]
+/// span: Span,
+/// }
+/// #[label(slug = "parser-expected-identifier-found")]
+/// WithFound {
+/// #[primary_span]
+/// span: Span,
+/// found: String,
+/// }
+/// }
+///
+/// #[derive(SessionSubdiagnostic)]
+/// #[suggestion_verbose(slug = "parser-raw-identifier")]
+/// pub struct RawIdentifierSuggestion<'tcx> {
+/// #[primary_span]
+/// span: Span,
+/// #[applicability]
+/// applicability: Applicability,
+/// ident: Ident,
+/// }
+/// ```
+///
+/// ```fluent
+/// parser-expected-identifier = expected identifier
+///
+/// parser-expected-identifier-found = expected identifier, found {$found}
+///
+/// parser-raw-identifier = escape `{$ident}` to use it as an identifier
+/// ```
+///
+/// Then, later, to add the subdiagnostic:
+///
+/// ```ignore (pseudo-rust)
+/// diag.subdiagnostic(ExpectedIdentifierLabel::WithoutFound { span });
+///
+/// diag.subdiagnostic(RawIdentifierSuggestion { span, applicability, ident });
+/// ```
+pub fn session_subdiagnostic_derive(s: Structure<'_>) -> TokenStream {
+ SessionSubdiagnosticDerive::new(s).into_tokens()
+}
--- /dev/null
+#![deny(unused_must_use)]
+
+use crate::diagnostics::error::{
+ span_err, throw_invalid_attr, throw_invalid_nested_attr, throw_span_err,
+ SessionDiagnosticDeriveError,
+};
+use crate::diagnostics::utils::{
+ option_inner_ty, report_error_if_not_applied_to_applicability,
+ report_error_if_not_applied_to_span, Applicability, FieldInfo, HasFieldMap, SetOnce,
+};
+use proc_macro2::TokenStream;
+use quote::{format_ident, quote};
+use std::collections::HashMap;
+use std::fmt;
+use std::str::FromStr;
+use syn::{spanned::Spanned, Meta, MetaList, MetaNameValue};
+use synstructure::{BindingInfo, Structure, VariantInfo};
+
+/// Which kind of suggestion is being created?
+#[derive(Clone, Copy)]
+enum SubdiagnosticSuggestionKind {
+ /// `#[suggestion]`
+ Normal,
+ /// `#[suggestion_short]`
+ Short,
+ /// `#[suggestion_hidden]`
+ Hidden,
+ /// `#[suggestion_verbose]`
+ Verbose,
+}
+
+/// Which kind of subdiagnostic is being created from a variant?
+#[derive(Clone, Copy)]
+enum SubdiagnosticKind {
+ /// `#[label(...)]`
+ Label,
+ /// `#[note(...)]`
+ Note,
+ /// `#[help(...)]`
+ Help,
+ /// `#[suggestion{,_short,_hidden,_verbose}]`
+ Suggestion(SubdiagnosticSuggestionKind),
+}
+
+impl FromStr for SubdiagnosticKind {
+ type Err = ();
+
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
+ match s {
+ "label" => Ok(SubdiagnosticKind::Label),
+ "note" => Ok(SubdiagnosticKind::Note),
+ "help" => Ok(SubdiagnosticKind::Help),
+ "suggestion" => Ok(SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Normal)),
+ "suggestion_short" => {
+ Ok(SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Short))
+ }
+ "suggestion_hidden" => {
+ Ok(SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Hidden))
+ }
+ "suggestion_verbose" => {
+ Ok(SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Verbose))
+ }
+ _ => Err(()),
+ }
+ }
+}
+
+impl quote::IdentFragment for SubdiagnosticKind {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self {
+ SubdiagnosticKind::Label => write!(f, "label"),
+ SubdiagnosticKind::Note => write!(f, "note"),
+ SubdiagnosticKind::Help => write!(f, "help"),
+ SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Normal) => {
+ write!(f, "suggestion")
+ }
+ SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Short) => {
+ write!(f, "suggestion_short")
+ }
+ SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Hidden) => {
+ write!(f, "suggestion_hidden")
+ }
+ SubdiagnosticKind::Suggestion(SubdiagnosticSuggestionKind::Verbose) => {
+ write!(f, "suggestion_verbose")
+ }
+ }
+ }
+
+ fn span(&self) -> Option<proc_macro2::Span> {
+ None
+ }
+}
+
+/// The central struct for constructing the `add_to_diagnostic` method from an annotated struct.
+pub(crate) struct SessionSubdiagnosticDerive<'a> {
+ structure: Structure<'a>,
+ diag: syn::Ident,
+}
+
+impl<'a> SessionSubdiagnosticDerive<'a> {
+ pub(crate) fn new(structure: Structure<'a>) -> Self {
+ let diag = format_ident!("diag");
+ Self { structure, diag }
+ }
+
+ pub(crate) fn into_tokens(self) -> TokenStream {
+ let SessionSubdiagnosticDerive { mut structure, diag } = self;
+ let implementation = {
+ let ast = structure.ast();
+ let span = ast.span().unwrap();
+ match ast.data {
+ syn::Data::Struct(..) | syn::Data::Enum(..) => (),
+ syn::Data::Union(..) => {
+ span_err(
+ span,
+ "`#[derive(SessionSubdiagnostic)]` can only be used on structs and enums",
+ );
+ }
+ }
+
+ if matches!(ast.data, syn::Data::Enum(..)) {
+ for attr in &ast.attrs {
+ span_err(
+ attr.span().unwrap(),
+ "unsupported type attribute for subdiagnostic enum",
+ )
+ .emit();
+ }
+ }
+
+ structure.bind_with(|_| synstructure::BindStyle::Move);
+ let variants_ = structure.each_variant(|variant| {
+ // Build the mapping of field names to fields. This allows attributes to peek
+ // values from other fields.
+ let mut fields_map = HashMap::new();
+ for binding in variant.bindings() {
+ let field = binding.ast();
+ if let Some(ident) = &field.ident {
+ fields_map.insert(ident.to_string(), quote! { #binding });
+ }
+ }
+
+ let mut builder = SessionSubdiagnosticDeriveBuilder {
+ diag: &diag,
+ variant,
+ span,
+ fields: fields_map,
+ kind: None,
+ slug: None,
+ code: None,
+ span_field: None,
+ applicability: None,
+ };
+ builder.into_tokens().unwrap_or_else(|v| v.to_compile_error())
+ });
+
+ quote! {
+ match self {
+ #variants_
+ }
+ }
+ };
+
+ let ret = structure.gen_impl(quote! {
+ gen impl rustc_errors::AddSubdiagnostic for @Self {
+ fn add_to_diagnostic(self, #diag: &mut rustc_errors::Diagnostic) {
+ use rustc_errors::{Applicability, IntoDiagnosticArg};
+ #implementation
+ }
+ }
+ });
+ ret
+ }
+}
+
+/// Tracks persistent information required for building up the call to add to the diagnostic
+/// for the final generated method. This is a separate struct to `SessionSubdiagnosticDerive`
+/// only to be able to destructure and split `self.builder` and the `self.structure` up to avoid a
+/// double mut borrow later on.
+struct SessionSubdiagnosticDeriveBuilder<'a> {
+ /// The identifier to use for the generated `DiagnosticBuilder` instance.
+ diag: &'a syn::Ident,
+
+ /// Info for the current variant (or the type if not an enum).
+ variant: &'a VariantInfo<'a>,
+ /// Span for the entire type.
+ span: proc_macro::Span,
+
+ /// Store a map of field name to its corresponding field. This is built on construction of the
+ /// derive builder.
+ fields: HashMap<String, TokenStream>,
+
+ /// Subdiagnostic kind of the type/variant.
+ kind: Option<(SubdiagnosticKind, proc_macro::Span)>,
+
+ /// Slug of the subdiagnostic - corresponds to the Fluent identifier for the message - from the
+ /// `#[kind(slug = "...")]` attribute on the type or variant.
+ slug: Option<(String, proc_macro::Span)>,
+ /// If a suggestion, the code to suggest as a replacement - from the `#[kind(code = "...")]`
+ /// attribute on the type or variant.
+ code: Option<(TokenStream, proc_macro::Span)>,
+
+ /// Identifier for the binding to the `#[primary_span]` field.
+ span_field: Option<(proc_macro2::Ident, proc_macro::Span)>,
+ /// If a suggestion, the identifier for the binding to the `#[applicability]` field or a
+ /// `rustc_errors::Applicability::*` variant directly.
+ applicability: Option<(TokenStream, proc_macro::Span)>,
+}
+
+impl<'a> HasFieldMap for SessionSubdiagnosticDeriveBuilder<'a> {
+ fn get_field_binding(&self, field: &String) -> Option<&TokenStream> {
+ self.fields.get(field)
+ }
+}
+
+impl<'a> SessionSubdiagnosticDeriveBuilder<'a> {
+ fn identify_kind(&mut self) -> Result<(), SessionDiagnosticDeriveError> {
+ for attr in self.variant.ast().attrs {
+ let span = attr.span().unwrap();
+
+ let name = attr.path.segments.last().unwrap().ident.to_string();
+ let name = name.as_str();
+
+ let meta = attr.parse_meta()?;
+ let kind = match meta {
+ Meta::List(MetaList { ref nested, .. }) => {
+ for nested_attr in nested {
+ let meta = match nested_attr {
+ syn::NestedMeta::Meta(ref meta) => meta,
+ _ => throw_invalid_nested_attr!(attr, &nested_attr),
+ };
+
+ let span = meta.span().unwrap();
+ let nested_name = meta.path().segments.last().unwrap().ident.to_string();
+ let nested_name = nested_name.as_str();
+
+ match meta {
+ Meta::NameValue(MetaNameValue { lit: syn::Lit::Str(s), .. }) => {
+ match nested_name {
+ "code" => {
+ let formatted_str = self.build_format(&s.value(), s.span());
+ self.code.set_once((formatted_str, span));
+ }
+ "slug" => self.slug.set_once((s.value(), span)),
+ "applicability" => {
+ let value = match Applicability::from_str(&s.value()) {
+ Ok(v) => v,
+ Err(()) => {
+ span_err(span, "invalid applicability").emit();
+ Applicability::Unspecified
+ }
+ };
+ self.applicability.set_once((quote! { #value }, span));
+ }
+ _ => throw_invalid_nested_attr!(attr, &nested_attr, |diag| {
+ diag.help("only `code`, `slug` and `applicability` are valid nested attributes")
+ }),
+ }
+ }
+ _ => throw_invalid_nested_attr!(attr, &nested_attr),
+ }
+ }
+
+ let Ok(kind) = SubdiagnosticKind::from_str(name) else {
+ throw_invalid_attr!(attr, &meta)
+ };
+
+ kind
+ }
+ _ => throw_invalid_attr!(attr, &meta),
+ };
+
+ if matches!(
+ kind,
+ SubdiagnosticKind::Label | SubdiagnosticKind::Help | SubdiagnosticKind::Note
+ ) && self.code.is_some()
+ {
+ throw_span_err!(
+ span,
+ &format!("`code` is not a valid nested attribute of a `{}` attribute", name)
+ );
+ }
+
+ if self.slug.is_none() {
+ throw_span_err!(
+ span,
+ &format!("`slug` must be set in a `#[{}(...)]` attribute", name)
+ );
+ }
+
+ self.kind.set_once((kind, span));
+ }
+
+ Ok(())
+ }
+
+ fn generate_field_code(
+ &mut self,
+ binding: &BindingInfo<'_>,
+ is_suggestion: bool,
+ ) -> Result<TokenStream, SessionDiagnosticDeriveError> {
+ let ast = binding.ast();
+
+ let option_ty = option_inner_ty(&ast.ty);
+ let info = FieldInfo {
+ vis: &ast.vis,
+ binding: binding,
+ ty: option_ty.unwrap_or(&ast.ty),
+ span: &ast.span(),
+ };
+
+ for attr in &ast.attrs {
+ let name = attr.path.segments.last().unwrap().ident.to_string();
+ let name = name.as_str();
+ let span = attr.span().unwrap();
+
+ let meta = attr.parse_meta()?;
+ match meta {
+ Meta::Path(_) => match name {
+ "primary_span" => {
+ report_error_if_not_applied_to_span(attr, &info)?;
+ self.span_field.set_once((binding.binding.clone(), span));
+ return Ok(quote! {});
+ }
+ "applicability" if is_suggestion => {
+ report_error_if_not_applied_to_applicability(attr, &info)?;
+ let binding = binding.binding.clone();
+ self.applicability.set_once((quote! { #binding }, span));
+ return Ok(quote! {});
+ }
+ "applicability" => {
+ span_err(span, "`#[applicability]` is only valid on suggestions").emit();
+ return Ok(quote! {});
+ }
+ "skip_arg" => {
+ return Ok(quote! {});
+ }
+ _ => throw_invalid_attr!(attr, &meta, |diag| {
+ diag.help("only `primary_span`, `applicability` and `skip_arg` are valid field attributes")
+ }),
+ },
+ _ => throw_invalid_attr!(attr, &meta),
+ }
+ }
+
+ let ident = ast.ident.as_ref().unwrap();
+
+ let diag = &self.diag;
+ let generated = quote! {
+ #diag.set_arg(
+ stringify!(#ident),
+ #binding.into_diagnostic_arg()
+ );
+ };
+
+ if option_ty.is_none() {
+ Ok(quote! { #generated })
+ } else {
+ Ok(quote! {
+ if let Some(#binding) = #binding {
+ #generated
+ }
+ })
+ }
+ }
+
+ fn into_tokens(&mut self) -> Result<TokenStream, SessionDiagnosticDeriveError> {
+ self.identify_kind()?;
+ let Some(kind) = self.kind.map(|(kind, _)| kind) else {
+ throw_span_err!(
+ self.variant.ast().ident.span().unwrap(),
+ "subdiagnostic kind not specified"
+ );
+ };
+
+ let is_suggestion = matches!(kind, SubdiagnosticKind::Suggestion(_));
+
+ let mut args = TokenStream::new();
+ for binding in self.variant.bindings() {
+ let arg = self
+ .generate_field_code(binding, is_suggestion)
+ .unwrap_or_else(|v| v.to_compile_error());
+ args.extend(arg);
+ }
+
+ // Missing slug errors will already have been reported.
+ let slug = self.slug.as_ref().map(|(slug, _)| &**slug).unwrap_or("missing-slug");
+ let code = match self.code.as_ref() {
+ Some((code, _)) => Some(quote! { #code }),
+ None if is_suggestion => {
+ span_err(self.span, "suggestion without `code = \"...\"`").emit();
+ Some(quote! { /* macro error */ "..." })
+ }
+ None => None,
+ };
+
+ let span_field = self.span_field.as_ref().map(|(span, _)| span);
+ let applicability = match self.applicability.clone() {
+ Some((applicability, _)) => Some(applicability),
+ None if is_suggestion => {
+ span_err(self.span, "suggestion without `applicability`").emit();
+ Some(quote! { rustc_errors::Applicability::Unspecified })
+ }
+ None => None,
+ };
+
+ let diag = &self.diag;
+ let name = format_ident!("{}{}", if span_field.is_some() { "span_" } else { "" }, kind);
+ let message = quote! { rustc_errors::DiagnosticMessage::fluent(#slug) };
+ let call = if matches!(kind, SubdiagnosticKind::Suggestion(..)) {
+ if let Some(span) = span_field {
+ quote! { #diag.#name(#span, #message, #code, #applicability); }
+ } else {
+ span_err(self.span, "suggestion without `#[primary_span]` field").emit();
+ quote! { unreachable!(); }
+ }
+ } else if matches!(kind, SubdiagnosticKind::Label) {
+ if let Some(span) = span_field {
+ quote! { #diag.#name(#span, #message); }
+ } else {
+ span_err(self.span, "label without `#[primary_span]` field").emit();
+ quote! { unreachable!(); }
+ }
+ } else {
+ if let Some(span) = span_field {
+ quote! { #diag.#name(#span, #message); }
+ } else {
+ quote! { #diag.#name(#message); }
+ }
+ };
+
+ Ok(quote! {
+ #call
+ #args
+ })
+ }
+}
--- /dev/null
+use crate::diagnostics::error::{span_err, throw_span_err, SessionDiagnosticDeriveError};
+use proc_macro::Span;
+use proc_macro2::TokenStream;
+use quote::{format_ident, quote};
+use std::collections::BTreeSet;
+use std::str::FromStr;
+use syn::{spanned::Spanned, Attribute, Meta, Type, Visibility};
+use synstructure::BindingInfo;
+
+/// Checks whether the type name of `ty` matches `name`.
+///
+/// Given some struct at `a::b::c::Foo`, this will return true for `c::Foo`, `b::c::Foo`, or
+/// `a::b::c::Foo`. This reasonably allows qualified names to be used in the macro.
+pub(crate) fn type_matches_path(ty: &Type, name: &[&str]) -> bool {
+ if let Type::Path(ty) = ty {
+ ty.path
+ .segments
+ .iter()
+ .map(|s| s.ident.to_string())
+ .rev()
+ .zip(name.iter().rev())
+ .all(|(x, y)| &x.as_str() == y)
+ } else {
+ false
+ }
+}
+
+/// Reports an error if the field's type is not `Applicability`.
+fn report_error_if_not_applied_to_ty(
+ attr: &Attribute,
+ info: &FieldInfo<'_>,
+ path: &[&str],
+ ty_name: &str,
+) -> Result<(), SessionDiagnosticDeriveError> {
+ if !type_matches_path(&info.ty, path) {
+ let name = attr.path.segments.last().unwrap().ident.to_string();
+ let name = name.as_str();
+ let meta = attr.parse_meta()?;
+
+ throw_span_err!(
+ attr.span().unwrap(),
+ &format!(
+ "the `#[{}{}]` attribute can only be applied to fields of type `{}`",
+ name,
+ match meta {
+ Meta::Path(_) => "",
+ Meta::NameValue(_) => " = ...",
+ Meta::List(_) => "(...)",
+ },
+ ty_name
+ )
+ );
+ }
+
+ Ok(())
+}
+
+/// Reports an error if the field's type is not `Applicability`.
+pub(crate) fn report_error_if_not_applied_to_applicability(
+ attr: &Attribute,
+ info: &FieldInfo<'_>,
+) -> Result<(), SessionDiagnosticDeriveError> {
+ report_error_if_not_applied_to_ty(
+ attr,
+ info,
+ &["rustc_errors", "Applicability"],
+ "Applicability",
+ )
+}
+
+/// Reports an error if the field's type is not `Span`.
+pub(crate) fn report_error_if_not_applied_to_span(
+ attr: &Attribute,
+ info: &FieldInfo<'_>,
+) -> Result<(), SessionDiagnosticDeriveError> {
+ report_error_if_not_applied_to_ty(attr, info, &["rustc_span", "Span"], "Span")
+}
+
+/// If `ty` is an Option, returns `Some(inner type)`, otherwise returns `None`.
+pub(crate) fn option_inner_ty(ty: &Type) -> Option<&Type> {
+ if type_matches_path(ty, &["std", "option", "Option"]) {
+ if let Type::Path(ty_path) = ty {
+ let path = &ty_path.path;
+ let ty = path.segments.iter().last().unwrap();
+ if let syn::PathArguments::AngleBracketed(bracketed) = &ty.arguments {
+ if bracketed.args.len() == 1 {
+ if let syn::GenericArgument::Type(ty) = &bracketed.args[0] {
+ return Some(ty);
+ }
+ }
+ }
+ }
+ }
+ None
+}
+
+/// Field information passed to the builder. Deliberately omits attrs to discourage the
+/// `generate_*` methods from walking the attributes themselves.
+pub(crate) struct FieldInfo<'a> {
+ pub(crate) vis: &'a Visibility,
+ pub(crate) binding: &'a BindingInfo<'a>,
+ pub(crate) ty: &'a Type,
+ pub(crate) span: &'a proc_macro2::Span,
+}
+
+/// Small helper trait for abstracting over `Option` fields that contain a value and a `Span`
+/// for error reporting if they are set more than once.
+pub(crate) trait SetOnce<T> {
+ fn set_once(&mut self, value: T);
+}
+
+impl<T> SetOnce<(T, Span)> for Option<(T, Span)> {
+ fn set_once(&mut self, (value, span): (T, Span)) {
+ match self {
+ None => {
+ *self = Some((value, span));
+ }
+ Some((_, prev_span)) => {
+ span_err(span, "specified multiple times")
+ .span_note(*prev_span, "previously specified here")
+ .emit();
+ }
+ }
+ }
+}
+
+pub(crate) trait HasFieldMap {
+ /// Returns the binding for the field with the given name, if it exists on the type.
+ fn get_field_binding(&self, field: &String) -> Option<&TokenStream>;
+
+ /// In the strings in the attributes supplied to this macro, we want callers to be able to
+ /// reference fields in the format string. For example:
+ ///
+ /// ```ignore (not-usage-example)
+ /// /// Suggest `==` when users wrote `===`.
+ /// #[suggestion(slug = "parser-not-javascript-eq", code = "{lhs} == {rhs}")]
+ /// struct NotJavaScriptEq {
+ /// #[primary_span]
+ /// span: Span,
+ /// lhs: Ident,
+ /// rhs: Ident,
+ /// }
+ /// ```
+ ///
+ /// We want to automatically pick up that `{lhs}` refers `self.lhs` and `{rhs}` refers to
+ /// `self.rhs`, then generate this call to `format!`:
+ ///
+ /// ```ignore (not-usage-example)
+ /// format!("{lhs} == {rhs}", lhs = self.lhs, rhs = self.rhs)
+ /// ```
+ ///
+ /// This function builds the entire call to `format!`.
+ fn build_format(&self, input: &str, span: proc_macro2::Span) -> TokenStream {
+ // This set is used later to generate the final format string. To keep builds reproducible,
+ // the iteration order needs to be deterministic, hence why we use a `BTreeSet` here
+ // instead of a `HashSet`.
+ let mut referenced_fields: BTreeSet<String> = BTreeSet::new();
+
+ // At this point, we can start parsing the format string.
+ let mut it = input.chars().peekable();
+
+ // Once the start of a format string has been found, process the format string and spit out
+ // the referenced fields. Leaves `it` sitting on the closing brace of the format string, so
+ // the next call to `it.next()` retrieves the next character.
+ while let Some(c) = it.next() {
+ if c == '{' && *it.peek().unwrap_or(&'\0') != '{' {
+ let mut eat_argument = || -> Option<String> {
+ let mut result = String::new();
+ // Format specifiers look like:
+ //
+ // format := '{' [ argument ] [ ':' format_spec ] '}' .
+ //
+ // Therefore, we only need to eat until ':' or '}' to find the argument.
+ while let Some(c) = it.next() {
+ result.push(c);
+ let next = *it.peek().unwrap_or(&'\0');
+ if next == '}' {
+ break;
+ } else if next == ':' {
+ // Eat the ':' character.
+ assert_eq!(it.next().unwrap(), ':');
+ break;
+ }
+ }
+ // Eat until (and including) the matching '}'
+ while it.next()? != '}' {
+ continue;
+ }
+ Some(result)
+ };
+
+ if let Some(referenced_field) = eat_argument() {
+ referenced_fields.insert(referenced_field);
+ }
+ }
+ }
+
+ // At this point, `referenced_fields` contains a set of the unique fields that were
+ // referenced in the format string. Generate the corresponding "x = self.x" format
+ // string parameters:
+ let args = referenced_fields.into_iter().map(|field: String| {
+ let field_ident = format_ident!("{}", field);
+ let value = match self.get_field_binding(&field) {
+ Some(value) => value.clone(),
+ // This field doesn't exist. Emit a diagnostic.
+ None => {
+ span_err(
+ span.unwrap(),
+ &format!("`{}` doesn't refer to a field on this type", field),
+ )
+ .emit();
+ quote! {
+ "{#field}"
+ }
+ }
+ };
+ quote! {
+ #field_ident = #value
+ }
+ });
+ quote! {
+ format!(#input #(,#args)*)
+ }
+ }
+}
+
+/// `Applicability` of a suggestion - mirrors `rustc_errors::Applicability` - and used to represent
+/// the user's selection of applicability if specified in an attribute.
+pub(crate) enum Applicability {
+ MachineApplicable,
+ MaybeIncorrect,
+ HasPlaceholders,
+ Unspecified,
+}
+
+impl FromStr for Applicability {
+ type Err = ();
+
+ fn from_str(s: &str) -> Result<Self, Self::Err> {
+ match s {
+ "machine-applicable" => Ok(Applicability::MachineApplicable),
+ "maybe-incorrect" => Ok(Applicability::MaybeIncorrect),
+ "has-placeholders" => Ok(Applicability::HasPlaceholders),
+ "unspecified" => Ok(Applicability::Unspecified),
+ _ => Err(()),
+ }
+ }
+}
+
+impl quote::ToTokens for Applicability {
+ fn to_tokens(&self, tokens: &mut TokenStream) {
+ tokens.extend(match self {
+ Applicability::MachineApplicable => {
+ quote! { rustc_errors::Applicability::MachineApplicable }
+ }
+ Applicability::MaybeIncorrect => {
+ quote! { rustc_errors::Applicability::MaybeIncorrect }
+ }
+ Applicability::HasPlaceholders => {
+ quote! { rustc_errors::Applicability::HasPlaceholders }
+ }
+ Applicability::Unspecified => {
+ quote! { rustc_errors::Applicability::Unspecified }
+ }
+ });
+ }
+}
-#![feature(proc_macro_diagnostic)]
#![feature(allow_internal_unstable)]
+#![feature(let_else)]
+#![feature(proc_macro_diagnostic)]
#![allow(rustc::default_hash_types)]
#![recursion_limit = "128"]
use proc_macro::TokenStream;
+mod diagnostics;
mod hash_stable;
mod lift;
mod newtype;
mod query;
mod serialize;
-mod session_diagnostic;
mod symbols;
mod type_foldable;
skip_arg,
primary_span,
label,
+ subdiagnostic,
suggestion,
suggestion_short,
suggestion_hidden,
- suggestion_verbose)] => session_diagnostic::session_diagnostic_derive
+ suggestion_verbose)] => diagnostics::session_diagnostic_derive
+);
+decl_derive!(
+ [SessionSubdiagnostic, attributes(
+ // struct/variant attributes
+ label,
+ help,
+ note,
+ suggestion,
+ suggestion_short,
+ suggestion_hidden,
+ suggestion_verbose,
+ // field attributes
+ skip_arg,
+ primary_span,
+ applicability)] => diagnostics::session_subdiagnostic_derive
);
+++ /dev/null
-#![deny(unused_must_use)]
-use proc_macro::Diagnostic;
-use quote::{format_ident, quote};
-use syn::spanned::Spanned;
-
-use std::collections::{BTreeSet, HashMap};
-
-/// Implements `#[derive(SessionDiagnostic)]`, which allows for errors to be specified as a struct,
-/// independent from the actual diagnostics emitting code.
-///
-/// ```ignore (pseudo-rust)
-/// # extern crate rustc_errors;
-/// # use rustc_errors::Applicability;
-/// # extern crate rustc_span;
-/// # use rustc_span::{symbol::Ident, Span};
-/// # extern crate rust_middle;
-/// # use rustc_middle::ty::Ty;
-/// #[derive(SessionDiagnostic)]
-/// #[error(code = "E0505", slug = "move-out-of-borrow-error")]
-/// pub struct MoveOutOfBorrowError<'tcx> {
-/// pub name: Ident,
-/// pub ty: Ty<'tcx>,
-/// #[primary_span]
-/// #[label = "cannot move out of borrow"]
-/// pub span: Span,
-/// #[label = "`{ty}` first borrowed here"]
-/// pub other_span: Span,
-/// #[suggestion(message = "consider cloning here", code = "{name}.clone()")]
-/// pub opt_sugg: Option<(Span, Applicability)>
-/// }
-/// ```
-///
-/// Then, later, to emit the error:
-///
-/// ```ignore (pseudo-rust)
-/// sess.emit_err(MoveOutOfBorrowError {
-/// expected,
-/// actual,
-/// span,
-/// other_span,
-/// opt_sugg: Some(suggestion, Applicability::MachineApplicable),
-/// });
-/// ```
-pub fn session_diagnostic_derive(s: synstructure::Structure<'_>) -> proc_macro2::TokenStream {
- // Names for the diagnostic we build and the session we build it from.
- let diag = format_ident!("diag");
- let sess = format_ident!("sess");
-
- SessionDiagnosticDerive::new(diag, sess, s).into_tokens()
-}
-
-/// Checks whether the type name of `ty` matches `name`.
-///
-/// Given some struct at `a::b::c::Foo`, this will return true for `c::Foo`, `b::c::Foo`, or
-/// `a::b::c::Foo`. This reasonably allows qualified names to be used in the macro.
-fn type_matches_path(ty: &syn::Type, name: &[&str]) -> bool {
- if let syn::Type::Path(ty) = ty {
- ty.path
- .segments
- .iter()
- .map(|s| s.ident.to_string())
- .rev()
- .zip(name.iter().rev())
- .all(|(x, y)| &x.as_str() == y)
- } else {
- false
- }
-}
-
-/// The central struct for constructing the `as_error` method from an annotated struct.
-struct SessionDiagnosticDerive<'a> {
- structure: synstructure::Structure<'a>,
- builder: SessionDiagnosticDeriveBuilder<'a>,
-}
-
-impl std::convert::From<syn::Error> for SessionDiagnosticDeriveError {
- fn from(e: syn::Error) -> Self {
- SessionDiagnosticDeriveError::SynError(e)
- }
-}
-
-#[derive(Debug)]
-enum SessionDiagnosticDeriveError {
- SynError(syn::Error),
- ErrorHandled,
-}
-
-impl SessionDiagnosticDeriveError {
- fn to_compile_error(self) -> proc_macro2::TokenStream {
- match self {
- SessionDiagnosticDeriveError::SynError(e) => e.to_compile_error(),
- SessionDiagnosticDeriveError::ErrorHandled => {
- // Return ! to avoid having to create a blank DiagnosticBuilder to return when an
- // error has already been emitted to the compiler.
- quote! {
- { unreachable!(); }
- }
- }
- }
- }
-}
-
-fn span_err(span: impl proc_macro::MultiSpan, msg: &str) -> proc_macro::Diagnostic {
- Diagnostic::spanned(span, proc_macro::Level::Error, msg)
-}
-
-/// For methods that return a `Result<_, SessionDiagnosticDeriveError>`:
-///
-/// Emit a diagnostic on span `$span` with msg `$msg` (optionally performing additional decoration
-/// using the `FnOnce` passed in `diag`) and return `Err(ErrorHandled)`.
-macro_rules! throw_span_err {
- ($span:expr, $msg:expr) => {{ throw_span_err!($span, $msg, |diag| diag) }};
- ($span:expr, $msg:expr, $f:expr) => {{
- return Err(_throw_span_err($span, $msg, $f));
- }};
-}
-
-/// When possible, prefer using `throw_span_err!` over using this function directly. This only
-/// exists as a function to constrain `f` to an `impl FnOnce`.
-fn _throw_span_err(
- span: impl proc_macro::MultiSpan,
- msg: &str,
- f: impl FnOnce(proc_macro::Diagnostic) -> proc_macro::Diagnostic,
-) -> SessionDiagnosticDeriveError {
- let diag = span_err(span, msg);
- f(diag).emit();
- SessionDiagnosticDeriveError::ErrorHandled
-}
-
-impl<'a> SessionDiagnosticDerive<'a> {
- fn new(diag: syn::Ident, sess: syn::Ident, structure: synstructure::Structure<'a>) -> Self {
- // Build the mapping of field names to fields. This allows attributes to peek values from
- // other fields.
- let mut fields_map = HashMap::new();
-
- // Convenience bindings.
- let ast = structure.ast();
-
- if let syn::Data::Struct(syn::DataStruct { fields, .. }) = &ast.data {
- for field in fields.iter() {
- if let Some(ident) = &field.ident {
- fields_map.insert(ident.to_string(), field);
- }
- }
- }
-
- Self {
- builder: SessionDiagnosticDeriveBuilder {
- diag,
- sess,
- fields: fields_map,
- kind: None,
- code: None,
- slug: None,
- },
- structure,
- }
- }
-
- fn into_tokens(self) -> proc_macro2::TokenStream {
- let SessionDiagnosticDerive { mut structure, mut builder } = self;
-
- let ast = structure.ast();
- let attrs = &ast.attrs;
-
- let (implementation, param_ty) = {
- if let syn::Data::Struct(..) = ast.data {
- let preamble = {
- let preamble = attrs.iter().map(|attr| {
- builder
- .generate_structure_code(attr)
- .unwrap_or_else(|v| v.to_compile_error())
- });
-
- quote! {
- #(#preamble)*;
- }
- };
-
- // Generates calls to `span_label` and similar functions based on the attributes
- // on fields. Code for suggestions uses formatting machinery and the value of
- // other fields - because any given field can be referenced multiple times, it
- // should be accessed through a borrow. When passing fields to `set_arg` (which
- // happens below) for Fluent, we want to move the data, so that has to happen
- // in a separate pass over the fields.
- let attrs = structure.each(|field_binding| {
- let field = field_binding.ast();
- let result = field.attrs.iter().map(|attr| {
- builder
- .generate_field_attr_code(
- attr,
- FieldInfo {
- vis: &field.vis,
- binding: field_binding,
- ty: &field.ty,
- span: &field.span(),
- },
- )
- .unwrap_or_else(|v| v.to_compile_error())
- });
-
- quote! { #(#result);* }
- });
-
- // When generating `set_arg` calls, move data rather than borrow it to avoid
- // requiring clones - this must therefore be the last use of each field (for
- // example, any formatting machinery that might refer to a field should be
- // generated already).
- structure.bind_with(|_| synstructure::BindStyle::Move);
- let args = structure.each(|field_binding| {
- let field = field_binding.ast();
- // When a field has attributes like `#[label]` or `#[note]` then it doesn't
- // need to be passed as an argument to the diagnostic. But when a field has no
- // attributes then it must be passed as an argument to the diagnostic so that
- // it can be referred to by Fluent messages.
- if field.attrs.is_empty() {
- let diag = &builder.diag;
- let ident = field_binding.ast().ident.as_ref().unwrap();
- quote! {
- #diag.set_arg(
- stringify!(#ident),
- #field_binding.into_diagnostic_arg()
- );
- }
- } else {
- quote! {}
- }
- });
-
- let span = ast.span().unwrap();
- let (diag, sess) = (&builder.diag, &builder.sess);
- let init = match (builder.kind, builder.slug) {
- (None, _) => {
- span_err(span, "diagnostic kind not specified")
- .help("use the `#[error(...)]` attribute to create an error")
- .emit();
- return SessionDiagnosticDeriveError::ErrorHandled.to_compile_error();
- }
- (Some((kind, _)), None) => {
- span_err(span, "`slug` not specified")
- .help(&format!("use the `#[{}(slug = \"...\")]` attribute to set this diagnostic's slug", kind.descr()))
- .emit();
- return SessionDiagnosticDeriveError::ErrorHandled.to_compile_error();
- }
- (Some((SessionDiagnosticKind::Error, _)), Some((slug, _))) => {
- quote! {
- let mut #diag = #sess.struct_err(
- rustc_errors::DiagnosticMessage::fluent(#slug),
- );
- }
- }
- (Some((SessionDiagnosticKind::Warn, _)), Some((slug, _))) => {
- quote! {
- let mut #diag = #sess.struct_warn(
- rustc_errors::DiagnosticMessage::fluent(#slug),
- );
- }
- }
- };
-
- let implementation = quote! {
- #init
- #preamble
- match self {
- #attrs
- }
- match self {
- #args
- }
- #diag
- };
- let param_ty = match builder.kind {
- Some((SessionDiagnosticKind::Error, _)) => {
- quote! { rustc_errors::ErrorGuaranteed }
- }
- Some((SessionDiagnosticKind::Warn, _)) => quote! { () },
- _ => unreachable!(),
- };
-
- (implementation, param_ty)
- } else {
- span_err(
- ast.span().unwrap(),
- "`#[derive(SessionDiagnostic)]` can only be used on structs",
- )
- .emit();
-
- let implementation = SessionDiagnosticDeriveError::ErrorHandled.to_compile_error();
- let param_ty = quote! { rustc_errors::ErrorGuaranteed };
- (implementation, param_ty)
- }
- };
-
- let sess = &builder.sess;
- structure.gen_impl(quote! {
- gen impl<'__session_diagnostic_sess> rustc_session::SessionDiagnostic<'__session_diagnostic_sess, #param_ty>
- for @Self
- {
- fn into_diagnostic(
- self,
- #sess: &'__session_diagnostic_sess rustc_session::Session
- ) -> rustc_errors::DiagnosticBuilder<'__session_diagnostic_sess, #param_ty> {
- use rustc_errors::IntoDiagnosticArg;
- #implementation
- }
- }
- })
- }
-}
-
-/// Field information passed to the builder. Deliberately omits attrs to discourage the
-/// `generate_*` methods from walking the attributes themselves.
-struct FieldInfo<'a> {
- vis: &'a syn::Visibility,
- binding: &'a synstructure::BindingInfo<'a>,
- ty: &'a syn::Type,
- span: &'a proc_macro2::Span,
-}
-
-/// What kind of session diagnostic is being derived - an error or a warning?
-#[derive(Copy, Clone)]
-enum SessionDiagnosticKind {
- /// `#[error(..)]`
- Error,
- /// `#[warn(..)]`
- Warn,
-}
-
-impl SessionDiagnosticKind {
- /// Returns human-readable string corresponding to the kind.
- fn descr(&self) -> &'static str {
- match self {
- SessionDiagnosticKind::Error => "error",
- SessionDiagnosticKind::Warn => "warning",
- }
- }
-}
-
-/// Tracks persistent information required for building up the individual calls to diagnostic
-/// methods for the final generated method. This is a separate struct to `SessionDiagnosticDerive`
-/// only to be able to destructure and split `self.builder` and the `self.structure` up to avoid a
-/// double mut borrow later on.
-struct SessionDiagnosticDeriveBuilder<'a> {
- /// Name of the session parameter that's passed in to the `as_error` method.
- sess: syn::Ident,
- /// The identifier to use for the generated `DiagnosticBuilder` instance.
- diag: syn::Ident,
-
- /// Store a map of field name to its corresponding field. This is built on construction of the
- /// derive builder.
- fields: HashMap<String, &'a syn::Field>,
-
- /// Kind of diagnostic requested via the struct attribute.
- kind: Option<(SessionDiagnosticKind, proc_macro::Span)>,
- /// Slug is a mandatory part of the struct attribute as corresponds to the Fluent message that
- /// has the actual diagnostic message.
- slug: Option<(String, proc_macro::Span)>,
- /// Error codes are a optional part of the struct attribute - this is only set to detect
- /// multiple specifications.
- code: Option<proc_macro::Span>,
-}
-
-impl<'a> SessionDiagnosticDeriveBuilder<'a> {
- /// Establishes state in the `SessionDiagnosticDeriveBuilder` resulting from the struct
- /// attributes like `#[error(..)#`, such as the diagnostic kind and slug. Generates
- /// diagnostic builder calls for setting error code and creating note/help messages.
- fn generate_structure_code(
- &mut self,
- attr: &syn::Attribute,
- ) -> Result<proc_macro2::TokenStream, SessionDiagnosticDeriveError> {
- let span = attr.span().unwrap();
-
- let name = attr.path.segments.last().unwrap().ident.to_string();
- let name = name.as_str();
- let meta = attr.parse_meta()?;
-
- if matches!(name, "help" | "note")
- && matches!(meta, syn::Meta::Path(_) | syn::Meta::NameValue(_))
- {
- let diag = &self.diag;
- let slug = match &self.slug {
- Some((slug, _)) => slug.as_str(),
- None => throw_span_err!(
- span,
- &format!(
- "`#[{}{}]` must come after `#[error(..)]` or `#[warn(..)]`",
- name,
- match meta {
- syn::Meta::Path(_) => "",
- syn::Meta::NameValue(_) => " = ...",
- _ => unreachable!(),
- }
- )
- ),
- };
- let id = match meta {
- syn::Meta::Path(..) => quote! { #name },
- syn::Meta::NameValue(syn::MetaNameValue { lit: syn::Lit::Str(s), .. }) => {
- quote! { #s }
- }
- _ => unreachable!(),
- };
- let fn_name = proc_macro2::Ident::new(name, attr.span());
-
- return Ok(quote! {
- #diag.#fn_name(rustc_errors::DiagnosticMessage::fluent_attr(#slug, #id));
- });
- }
-
- let nested = match meta {
- syn::Meta::List(syn::MetaList { nested, .. }) => nested,
- syn::Meta::Path(..) => throw_span_err!(
- span,
- &format!("`#[{}]` is not a valid `SessionDiagnostic` struct attribute", name)
- ),
- syn::Meta::NameValue(..) => throw_span_err!(
- span,
- &format!("`#[{} = ...]` is not a valid `SessionDiagnostic` struct attribute", name)
- ),
- };
-
- let kind = match name {
- "error" => SessionDiagnosticKind::Error,
- "warning" => SessionDiagnosticKind::Warn,
- other => throw_span_err!(
- span,
- &format!("`#[{}(...)]` is not a valid `SessionDiagnostic` struct attribute", other)
- ),
- };
- self.set_kind_once(kind, span)?;
-
- let mut tokens = Vec::new();
- for attr in nested {
- let span = attr.span().unwrap();
- let meta = match attr {
- syn::NestedMeta::Meta(meta) => meta,
- syn::NestedMeta::Lit(_) => throw_span_err!(
- span,
- &format!(
- "`#[{}(\"...\")]` is not a valid `SessionDiagnostic` struct attribute",
- name
- )
- ),
- };
-
- let path = meta.path();
- let nested_name = path.segments.last().unwrap().ident.to_string();
- match &meta {
- // Struct attributes are only allowed to be applied once, and the diagnostic
- // changes will be set in the initialisation code.
- syn::Meta::NameValue(syn::MetaNameValue { lit: syn::Lit::Str(s), .. }) => {
- match nested_name.as_str() {
- "slug" => {
- self.set_slug_once(s.value(), s.span().unwrap());
- }
- "code" => {
- tokens.push(self.set_code_once(s.value(), s.span().unwrap()));
- }
- other => {
- let diag = span_err(
- span,
- &format!(
- "`#[{}({} = ...)]` is not a valid `SessionDiagnostic` struct attribute",
- name, other
- ),
- );
- diag.emit();
- }
- }
- }
- syn::Meta::NameValue(..) => {
- span_err(
- span,
- &format!(
- "`#[{}({} = ...)]` is not a valid `SessionDiagnostic` struct attribute",
- name, nested_name
- ),
- )
- .help("value must be a string")
- .emit();
- }
- syn::Meta::Path(..) => {
- span_err(
- span,
- &format!(
- "`#[{}({})]` is not a valid `SessionDiagnostic` struct attribute",
- name, nested_name
- ),
- )
- .emit();
- }
- syn::Meta::List(..) => {
- span_err(
- span,
- &format!(
- "`#[{}({}(...))]` is not a valid `SessionDiagnostic` struct attribute",
- name, nested_name
- ),
- )
- .emit();
- }
- }
- }
-
- Ok(tokens.drain(..).collect())
- }
-
- #[must_use]
- fn set_kind_once(
- &mut self,
- kind: SessionDiagnosticKind,
- span: proc_macro::Span,
- ) -> Result<(), SessionDiagnosticDeriveError> {
- match self.kind {
- None => {
- self.kind = Some((kind, span));
- Ok(())
- }
- Some((prev_kind, prev_span)) => {
- let existing = prev_kind.descr();
- let current = kind.descr();
-
- let msg = if current == existing {
- format!("`{}` specified multiple times", existing)
- } else {
- format!("`{}` specified when `{}` was already specified", current, existing)
- };
- throw_span_err!(span, &msg, |diag| diag
- .span_note(prev_span, "previously specified here"));
- }
- }
- }
-
- fn set_code_once(&mut self, code: String, span: proc_macro::Span) -> proc_macro2::TokenStream {
- match self.code {
- None => {
- self.code = Some(span);
- }
- Some(prev_span) => {
- span_err(span, "`code` specified multiple times")
- .span_note(prev_span, "previously specified here")
- .emit();
- }
- }
-
- let diag = &self.diag;
- quote! { #diag.code(rustc_errors::DiagnosticId::Error(#code.to_string())); }
- }
-
- fn set_slug_once(&mut self, slug: String, span: proc_macro::Span) {
- match self.slug {
- None => {
- self.slug = Some((slug, span));
- }
- Some((_, prev_span)) => {
- span_err(span, "`slug` specified multiple times")
- .span_note(prev_span, "previously specified here")
- .emit();
- }
- }
- }
-
- fn generate_field_attr_code(
- &mut self,
- attr: &syn::Attribute,
- info: FieldInfo<'_>,
- ) -> Result<proc_macro2::TokenStream, SessionDiagnosticDeriveError> {
- let field_binding = &info.binding.binding;
- let option_ty = option_inner_ty(&info.ty);
- let generated_code = self.generate_non_option_field_code(
- attr,
- FieldInfo {
- vis: info.vis,
- binding: info.binding,
- ty: option_ty.unwrap_or(&info.ty),
- span: info.span,
- },
- )?;
-
- if option_ty.is_none() {
- Ok(quote! { #generated_code })
- } else {
- Ok(quote! {
- if let Some(#field_binding) = #field_binding {
- #generated_code
- }
- })
- }
- }
-
- fn generate_non_option_field_code(
- &mut self,
- attr: &syn::Attribute,
- info: FieldInfo<'_>,
- ) -> Result<proc_macro2::TokenStream, SessionDiagnosticDeriveError> {
- let diag = &self.diag;
- let span = attr.span().unwrap();
- let field_binding = &info.binding.binding;
-
- let name = attr.path.segments.last().unwrap().ident.to_string();
- let name = name.as_str();
-
- let meta = attr.parse_meta()?;
- match meta {
- syn::Meta::Path(_) => match name {
- "skip_arg" => {
- // Don't need to do anything - by virtue of the attribute existing, the
- // `set_arg` call will not be generated.
- Ok(quote! {})
- }
- "primary_span" => {
- self.report_error_if_not_applied_to_span(attr, info)?;
- Ok(quote! {
- #diag.set_span(*#field_binding);
- })
- }
- "label" | "note" | "help" => {
- self.report_error_if_not_applied_to_span(attr, info)?;
- Ok(self.add_subdiagnostic(field_binding, name, name))
- }
- other => throw_span_err!(
- span,
- &format!("`#[{}]` is not a valid `SessionDiagnostic` field attribute", other)
- ),
- },
- syn::Meta::NameValue(syn::MetaNameValue { lit: syn::Lit::Str(s), .. }) => match name {
- "label" | "note" | "help" => {
- self.report_error_if_not_applied_to_span(attr, info)?;
- Ok(self.add_subdiagnostic(field_binding, name, &s.value()))
- }
- other => throw_span_err!(
- span,
- &format!(
- "`#[{} = ...]` is not a valid `SessionDiagnostic` field attribute",
- other
- )
- ),
- },
- syn::Meta::NameValue(_) => throw_span_err!(
- span,
- &format!("`#[{} = ...]` is not a valid `SessionDiagnostic` field attribute", name),
- |diag| diag.help("value must be a string")
- ),
- syn::Meta::List(syn::MetaList { path, nested, .. }) => {
- let name = path.segments.last().unwrap().ident.to_string();
- let name = name.as_ref();
-
- match name {
- "suggestion" | "suggestion_short" | "suggestion_hidden"
- | "suggestion_verbose" => (),
- other => throw_span_err!(
- span,
- &format!(
- "`#[{}(...)]` is not a valid `SessionDiagnostic` field attribute",
- other
- )
- ),
- };
-
- let (span_, applicability) = self.span_and_applicability_of_ty(info)?;
-
- let mut msg = None;
- let mut code = None;
-
- for attr in nested {
- let meta = match attr {
- syn::NestedMeta::Meta(meta) => meta,
- syn::NestedMeta::Lit(_) => throw_span_err!(
- span,
- &format!(
- "`#[{}(\"...\")]` is not a valid `SessionDiagnostic` field attribute",
- name
- )
- ),
- };
-
- let span = meta.span().unwrap();
- let nested_name = meta.path().segments.last().unwrap().ident.to_string();
- let nested_name = nested_name.as_str();
-
- match meta {
- syn::Meta::NameValue(syn::MetaNameValue {
- lit: syn::Lit::Str(s), ..
- }) => match nested_name {
- "message" => {
- msg = Some(s.value());
- }
- "code" => {
- let formatted_str = self.build_format(&s.value(), s.span());
- code = Some(formatted_str);
- }
- other => throw_span_err!(
- span,
- &format!(
- "`#[{}({} = ...)]` is not a valid `SessionDiagnostic` field attribute",
- name, other
- )
- ),
- },
- syn::Meta::NameValue(..) => throw_span_err!(
- span,
- &format!(
- "`#[{}({} = ...)]` is not a valid `SessionDiagnostic` struct attribute",
- name, nested_name
- ),
- |diag| diag.help("value must be a string")
- ),
- syn::Meta::Path(..) => throw_span_err!(
- span,
- &format!(
- "`#[{}({})]` is not a valid `SessionDiagnostic` struct attribute",
- name, nested_name
- )
- ),
- syn::Meta::List(..) => throw_span_err!(
- span,
- &format!(
- "`#[{}({}(...))]` is not a valid `SessionDiagnostic` struct attribute",
- name, nested_name
- )
- ),
- }
- }
-
- let method = format_ident!("span_{}", name);
-
- let slug = self
- .slug
- .as_ref()
- .map(|(slug, _)| slug.as_str())
- .unwrap_or_else(|| "missing-slug");
- let msg = msg.as_deref().unwrap_or("suggestion");
- let msg = quote! { rustc_errors::DiagnosticMessage::fluent_attr(#slug, #msg) };
- let code = code.unwrap_or_else(|| quote! { String::new() });
-
- Ok(quote! { #diag.#method(#span_, #msg, #code, #applicability); })
- }
- }
- }
-
- /// Reports an error if the field's type is not `Span`.
- fn report_error_if_not_applied_to_span(
- &self,
- attr: &syn::Attribute,
- info: FieldInfo<'_>,
- ) -> Result<(), SessionDiagnosticDeriveError> {
- if !type_matches_path(&info.ty, &["rustc_span", "Span"]) {
- let name = attr.path.segments.last().unwrap().ident.to_string();
- let name = name.as_str();
- let meta = attr.parse_meta()?;
-
- throw_span_err!(
- attr.span().unwrap(),
- &format!(
- "the `#[{}{}]` attribute can only be applied to fields of type `Span`",
- name,
- match meta {
- syn::Meta::Path(_) => "",
- syn::Meta::NameValue(_) => " = ...",
- syn::Meta::List(_) => "(...)",
- }
- )
- );
- }
-
- Ok(())
- }
-
- /// Adds a subdiagnostic by generating a `diag.span_$kind` call with the current slug and
- /// `fluent_attr_identifier`.
- fn add_subdiagnostic(
- &self,
- field_binding: &proc_macro2::Ident,
- kind: &str,
- fluent_attr_identifier: &str,
- ) -> proc_macro2::TokenStream {
- let diag = &self.diag;
-
- let slug =
- self.slug.as_ref().map(|(slug, _)| slug.as_str()).unwrap_or_else(|| "missing-slug");
- let fn_name = format_ident!("span_{}", kind);
- quote! {
- #diag.#fn_name(
- *#field_binding,
- rustc_errors::DiagnosticMessage::fluent_attr(#slug, #fluent_attr_identifier)
- );
- }
- }
-
- fn span_and_applicability_of_ty(
- &self,
- info: FieldInfo<'_>,
- ) -> Result<(proc_macro2::TokenStream, proc_macro2::TokenStream), SessionDiagnosticDeriveError>
- {
- match &info.ty {
- // If `ty` is `Span` w/out applicability, then use `Applicability::Unspecified`.
- ty @ syn::Type::Path(..) if type_matches_path(ty, &["rustc_span", "Span"]) => {
- let binding = &info.binding.binding;
- Ok((quote!(*#binding), quote!(rustc_errors::Applicability::Unspecified)))
- }
- // If `ty` is `(Span, Applicability)` then return tokens accessing those.
- syn::Type::Tuple(tup) => {
- let mut span_idx = None;
- let mut applicability_idx = None;
-
- for (idx, elem) in tup.elems.iter().enumerate() {
- if type_matches_path(elem, &["rustc_span", "Span"]) {
- if span_idx.is_none() {
- span_idx = Some(syn::Index::from(idx));
- } else {
- throw_span_err!(
- info.span.unwrap(),
- "type of field annotated with `#[suggestion(...)]` contains more than one `Span`"
- );
- }
- } else if type_matches_path(elem, &["rustc_errors", "Applicability"]) {
- if applicability_idx.is_none() {
- applicability_idx = Some(syn::Index::from(idx));
- } else {
- throw_span_err!(
- info.span.unwrap(),
- "type of field annotated with `#[suggestion(...)]` contains more than one Applicability"
- );
- }
- }
- }
-
- if let Some(span_idx) = span_idx {
- let binding = &info.binding.binding;
- let span = quote!(#binding.#span_idx);
- let applicability = applicability_idx
- .map(|applicability_idx| quote!(#binding.#applicability_idx))
- .unwrap_or_else(|| quote!(rustc_errors::Applicability::Unspecified));
-
- return Ok((span, applicability));
- }
-
- throw_span_err!(info.span.unwrap(), "wrong types for suggestion", |diag| {
- diag.help("`#[suggestion(...)]` on a tuple field must be applied to fields of type `(Span, Applicability)`")
- });
- }
- // If `ty` isn't a `Span` or `(Span, Applicability)` then emit an error.
- _ => throw_span_err!(info.span.unwrap(), "wrong field type for suggestion", |diag| {
- diag.help("`#[suggestion(...)]` should be applied to fields of type `Span` or `(Span, Applicability)`")
- }),
- }
- }
-
- /// In the strings in the attributes supplied to this macro, we want callers to be able to
- /// reference fields in the format string. For example:
- ///
- /// ```ignore (not-usage-example)
- /// struct Point {
- /// #[error = "Expected a point greater than ({x}, {y})"]
- /// x: i32,
- /// y: i32,
- /// }
- /// ```
- ///
- /// We want to automatically pick up that `{x}` refers `self.x` and `{y}` refers to `self.y`,
- /// then generate this call to `format!`:
- ///
- /// ```ignore (not-usage-example)
- /// format!("Expected a point greater than ({x}, {y})", x = self.x, y = self.y)
- /// ```
- ///
- /// This function builds the entire call to `format!`.
- fn build_format(&self, input: &str, span: proc_macro2::Span) -> proc_macro2::TokenStream {
- // This set is used later to generate the final format string. To keep builds reproducible,
- // the iteration order needs to be deterministic, hence why we use a BTreeSet here instead
- // of a HashSet.
- let mut referenced_fields: BTreeSet<String> = BTreeSet::new();
-
- // At this point, we can start parsing the format string.
- let mut it = input.chars().peekable();
- // Once the start of a format string has been found, process the format string and spit out
- // the referenced fields. Leaves `it` sitting on the closing brace of the format string, so the
- // next call to `it.next()` retrieves the next character.
- while let Some(c) = it.next() {
- if c == '{' && *it.peek().unwrap_or(&'\0') != '{' {
- let mut eat_argument = || -> Option<String> {
- let mut result = String::new();
- // Format specifiers look like
- // format := '{' [ argument ] [ ':' format_spec ] '}' .
- // Therefore, we only need to eat until ':' or '}' to find the argument.
- while let Some(c) = it.next() {
- result.push(c);
- let next = *it.peek().unwrap_or(&'\0');
- if next == '}' {
- break;
- } else if next == ':' {
- // Eat the ':' character.
- assert_eq!(it.next().unwrap(), ':');
- break;
- }
- }
- // Eat until (and including) the matching '}'
- while it.next()? != '}' {
- continue;
- }
- Some(result)
- };
-
- if let Some(referenced_field) = eat_argument() {
- referenced_fields.insert(referenced_field);
- }
- }
- }
- // At this point, `referenced_fields` contains a set of the unique fields that were
- // referenced in the format string. Generate the corresponding "x = self.x" format
- // string parameters:
- let args = referenced_fields.into_iter().map(|field: String| {
- let field_ident = format_ident!("{}", field);
- let value = if self.fields.contains_key(&field) {
- quote! {
- &self.#field_ident
- }
- } else {
- // This field doesn't exist. Emit a diagnostic.
- Diagnostic::spanned(
- span.unwrap(),
- proc_macro::Level::Error,
- format!("`{}` doesn't refer to a field on this type", field),
- )
- .emit();
- quote! {
- "{#field}"
- }
- };
- quote! {
- #field_ident = #value
- }
- });
- quote! {
- format!(#input #(,#args)*)
- }
- }
-}
-
-/// If `ty` is an Option, returns `Some(inner type)`, otherwise returns `None`.
-fn option_inner_ty(ty: &syn::Type) -> Option<&syn::Type> {
- if type_matches_path(ty, &["std", "option", "Option"]) {
- if let syn::Type::Path(ty_path) = ty {
- let path = &ty_path.path;
- let ty = path.segments.iter().last().unwrap();
- if let syn::PathArguments::AngleBracketed(bracketed) = &ty.arguments {
- if bracketed.args.len() == 1 {
- if let syn::GenericArgument::Type(ty) = &bracketed.args[0] {
- return Some(ty);
- }
- }
- }
- }
- }
- None
-}
}
pub fn report_unused_deps(&self, tcx: TyCtxt<'_>) {
+ let json_unused_externs = tcx.sess.opts.json_unused_externs;
+
// We put the check for the option before the lint_level_at_node call
// because the call mutates internal state and introducing it
// leads to some ui tests failing.
- if !tcx.sess.opts.json_unused_externs {
+ if !json_unused_externs.is_enabled() {
return;
}
let level = tcx
let unused_externs =
self.unused_externs.iter().map(|ident| ident.to_ident_string()).collect::<Vec<_>>();
let unused_externs = unused_externs.iter().map(String::as_str).collect::<Vec<&str>>();
- tcx.sess
- .parse_sess
- .span_diagnostic
- .emit_unused_externs(level.as_str(), &unused_externs);
+ tcx.sess.parse_sess.span_diagnostic.emit_unused_externs(
+ level,
+ json_unused_externs.is_loud(),
+ &unused_externs,
+ );
}
}
}
// Don't worry about pathless `--extern foo` sysroot references
continue;
}
+ if entry.nounused_dep {
+ // We're not worried about this one
+ continue;
+ }
let name_interned = Symbol::intern(name);
if self.used_extern_options.contains(&name_interned) {
continue;
}
// Got a real unused --extern
- if self.sess.opts.json_unused_externs {
+ if self.sess.opts.json_unused_externs.is_enabled() {
self.cstore.unused_externs.push(name_interned);
continue;
}
use rustc_index::vec::{Idx, IndexVec};
use rustc_middle::arena::ArenaAllocatable;
use rustc_middle::metadata::ModChild;
-use rustc_middle::middle::exported_symbols::{ExportedSymbol, SymbolExportLevel};
+use rustc_middle::middle::exported_symbols::{ExportedSymbol, SymbolExportInfo};
use rustc_middle::middle::stability::DeprecationEntry;
use rustc_middle::mir::interpret::{AllocDecodingSession, AllocDecodingState};
use rustc_middle::thir;
fn exported_symbols(
self,
tcx: TyCtxt<'tcx>,
- ) -> &'tcx [(ExportedSymbol<'tcx>, SymbolExportLevel)] {
+ ) -> &'tcx [(ExportedSymbol<'tcx>, SymbolExportInfo)] {
tcx.arena.alloc_from_iter(self.root.exported_symbols.decode((self, tcx)))
}
let reachable_non_generics = tcx
.exported_symbols(cdata.cnum)
.iter()
- .filter_map(|&(exported_symbol, export_level)| {
+ .filter_map(|&(exported_symbol, export_info)| {
if let ExportedSymbol::NonGeneric(def_id) = exported_symbol {
- Some((def_id, export_level))
+ Some((def_id, export_info))
} else {
None
}
self.get_crate_data(cnum).get_all_incoherent_impls()
}
+ pub fn associated_item_def_ids_untracked<'a>(
+ &'a self,
+ def_id: DefId,
+ sess: &'a Session,
+ ) -> impl Iterator<Item = DefId> + 'a {
+ self.get_crate_data(def_id.krate).get_associated_item_def_ids(def_id.index, sess)
+ }
+
pub fn may_have_doc_links_untracked(&self, def_id: DefId) -> bool {
self.get_crate_data(def_id.krate).get_may_have_doc_links(def_id.index)
}
use rustc_middle::hir::nested_filter;
use rustc_middle::middle::dependency_format::Linkage;
use rustc_middle::middle::exported_symbols::{
- metadata_symbol_name, ExportedSymbol, SymbolExportLevel,
+ metadata_symbol_name, ExportedSymbol, SymbolExportInfo,
};
use rustc_middle::mir::interpret;
use rustc_middle::thir;
}))
}
hir::ItemKind::Impl(hir::Impl { defaultness, constness, .. }) => {
- self.tables.impl_defaultness.set(def_id.index, defaultness);
- self.tables.impl_constness.set(def_id.index, constness);
+ self.tables.impl_defaultness.set(def_id.index, *defaultness);
+ self.tables.impl_constness.set(def_id.index, *constness);
let trait_ref = self.tcx.impl_trait_ref(def_id);
if let Some(trait_ref) = trait_ref {
// definition (as that's not defined in this crate).
fn encode_exported_symbols(
&mut self,
- exported_symbols: &[(ExportedSymbol<'tcx>, SymbolExportLevel)],
- ) -> Lazy<[(ExportedSymbol<'tcx>, SymbolExportLevel)]> {
+ exported_symbols: &[(ExportedSymbol<'tcx>, SymbolExportInfo)],
+ ) -> Lazy<[(ExportedSymbol<'tcx>, SymbolExportInfo)]> {
empty_proc_macro!(self);
// The metadata symbol name is special. It should not show up in
// downstream crates.
use rustc_hir::lang_items;
use rustc_index::{bit_set::FiniteBitSet, vec::IndexVec};
use rustc_middle::metadata::ModChild;
-use rustc_middle::middle::exported_symbols::{ExportedSymbol, SymbolExportLevel};
+use rustc_middle::middle::exported_symbols::{ExportedSymbol, SymbolExportInfo};
use rustc_middle::mir;
use rustc_middle::thir;
use rustc_middle::ty::fast_reject::SimplifiedType;
tables: LazyTables<'tcx>,
- exported_symbols: Lazy!([(ExportedSymbol<'tcx>, SymbolExportLevel)]),
+ exported_symbols: Lazy!([(ExportedSymbol<'tcx>, SymbolExportInfo)]),
syntax_contexts: SyntaxContextTable,
expn_data: ExpnDataTable,
use rustc_index::vec::Idx;
use rustc_middle::hir::nested_filter;
use rustc_span::def_id::StableCrateId;
-use rustc_span::source_map::Spanned;
use rustc_span::symbol::{kw, sym, Ident, Symbol};
use rustc_span::Span;
use rustc_target::spec::abi::Abi;
| Node::Param(_)
| Node::Arm(_)
| Node::Lifetime(_)
- | Node::Visibility(_)
| Node::Block(_) => return None,
};
Some(def_kind)
let def_kind = self.tcx.def_kind(def_id);
match def_kind {
DefKind::Trait | DefKind::TraitAlias => def_id,
- DefKind::TyParam | DefKind::ConstParam => self.tcx.local_parent(def_id).unwrap(),
+ DefKind::TyParam | DefKind::ConstParam => self.tcx.local_parent(def_id),
_ => bug!("ty_param_owner: {:?} is a {:?} not a type parameter", def_id, def_kind),
}
}
},
Node::Lifetime(lifetime) => lifetime.span,
Node::GenericParam(param) => param.span,
- Node::Visibility(&Spanned {
- node: VisibilityKind::Restricted { ref path, .. },
- ..
- }) => path.span,
Node::Infer(i) => i.span,
- Node::Visibility(v) => bug!("unexpected Visibility {:?}", v),
Node::Local(local) => local.span,
Node::Crate(item) => item.spans.inner_span,
};
}
tcx.sess.opts.dep_tracking_hash(true).hash_stable(&mut hcx, &mut stable_hasher);
tcx.sess.local_stable_crate_id().hash_stable(&mut hcx, &mut stable_hasher);
+ // Hash visibility information since it does not appear in HIR.
+ let resolutions = tcx.resolutions(());
+ resolutions.visibilities.hash_stable(&mut hcx, &mut stable_hasher);
+ resolutions.has_pub_restricted.hash_stable(&mut hcx, &mut stable_hasher);
let crate_hash: Fingerprint = stable_hasher.finish();
Svh::new(crate_hash.to_smaller_hash())
Some(Node::Ctor(..)) => format!("ctor {}{}", path_str(), id_str),
Some(Node::Lifetime(_)) => node_str("lifetime"),
Some(Node::GenericParam(ref param)) => format!("generic_param {:?}{}", param, id_str),
- Some(Node::Visibility(ref vis)) => format!("visibility {:?}{}", vis, id_str),
Some(Node::Crate(..)) => String::from("root_crate"),
None => format!("unknown node{}", id_str),
}
}
}
+/// Kind of exported symbols.
+#[derive(Eq, PartialEq, Debug, Copy, Clone, Encodable, Decodable, HashStable)]
+pub enum SymbolExportKind {
+ Text,
+ Data,
+ Tls,
+}
+
+/// The `SymbolExportInfo` of a symbols specifies symbol-related information
+/// that is relevant to code generation and linking.
+#[derive(Eq, PartialEq, Debug, Copy, Clone, TyEncodable, TyDecodable, HashStable)]
+pub struct SymbolExportInfo {
+ pub level: SymbolExportLevel,
+ pub kind: SymbolExportKind,
+ pub used: bool,
+}
+
#[derive(Eq, PartialEq, Debug, Copy, Clone, TyEncodable, TyDecodable, HashStable)]
pub enum ExportedSymbol<'tcx> {
NonGeneric(DefId),
feature: Symbol,
) -> Option<(Span, String, String, Applicability)> {
if feature == sym::allocator_api {
- if let Some(trait_) = tcx.parent(def_id) {
+ if let Some(trait_) = tcx.opt_parent(def_id) {
if tcx.is_diagnostic_item(sym::Vec, trait_) {
let sm = tcx.sess.parse_sess.source_map();
let inner_types = sm.span_extend_to_prev_char(span, '<', true);
///
/// It is the caller's responsibility to check bounds and alignment beforehand.
/// Most likely, you want to call `InterpCx::write_scalar` instead of this method.
+ #[instrument(skip(self, cx), level = "debug")]
pub fn write_scalar(
&mut self,
cx: &impl HasDataLayout,
mod switch_sources;
pub mod tcx;
pub mod terminator;
+use crate::mir::traversal::PostorderCache;
pub use terminator::*;
+
pub mod traversal;
mod type_foldable;
pub mod visit;
/// * [`StatementKind::Retag`]
///
/// Furthermore, `Drop` now uses explicit drop flags visible in the MIR and reaching a `Drop`
- /// terminator means that the auto-generated drop glue will be invoked.
+ /// terminator means that the auto-generated drop glue will be invoked. Also, `Copy` operands
+ /// are allowed for non-`Copy` types.
DropsLowered = 3,
/// Beginning with this phase, the following variant is disallowed:
/// * [`Rvalue::Aggregate`] for any `AggregateKind` except `Array`
predecessor_cache: PredecessorCache,
switch_source_cache: SwitchSourceCache,
is_cyclic: GraphIsCyclicCache,
+ postorder_cache: PostorderCache,
pub tainted_by_errors: Option<ErrorGuaranteed>,
}
predecessor_cache: PredecessorCache::new(),
switch_source_cache: SwitchSourceCache::new(),
is_cyclic: GraphIsCyclicCache::new(),
+ postorder_cache: PostorderCache::new(),
tainted_by_errors,
};
body.is_polymorphic = body.has_param_types_or_consts();
predecessor_cache: PredecessorCache::new(),
switch_source_cache: SwitchSourceCache::new(),
is_cyclic: GraphIsCyclicCache::new(),
+ postorder_cache: PostorderCache::new(),
tainted_by_errors: None,
};
body.is_polymorphic = body.has_param_types_or_consts();
self.predecessor_cache.invalidate();
self.switch_source_cache.invalidate();
self.is_cyclic.invalidate();
+ self.postorder_cache.invalidate();
&mut self.basic_blocks
}
self.predecessor_cache.invalidate();
self.switch_source_cache.invalidate();
self.is_cyclic.invalidate();
+ self.postorder_cache.invalidate();
(&mut self.basic_blocks, &mut self.local_decls)
}
self.predecessor_cache.invalidate();
self.switch_source_cache.invalidate();
self.is_cyclic.invalidate();
+ self.postorder_cache.invalidate();
(&mut self.basic_blocks, &mut self.local_decls, &mut self.var_debug_info)
}
/// A temporary created during the creation of an aggregate
/// (e.g. a temporary for `foo` in `MyStruct { my_field: foo }`)
AggregateTemp,
+ /// A temporary created during the pass `Derefer` to avoid it's retagging
+ DerefTemp,
}
impl<'tcx> LocalDecl<'tcx> {
/// validator.
#[derive(Clone, PartialEq, TyEncodable, TyDecodable, Hash, HashStable)]
pub enum Operand<'tcx> {
- /// Creates a value by loading the given place. The type of the place must be `Copy`
+ /// Creates a value by loading the given place.
+ ///
+ /// Before drop elaboration, the type of the place must be `Copy`. After drop elaboration there
+ /// is no such requirement.
Copy(Place<'tcx>),
/// Creates a value by performing loading the place, just like the `Copy` operand.
UnaryOp(UnOp, Operand<'tcx>),
/// Computes the discriminant of the place, returning it as an integer of type
- /// [`discriminant_ty`].
+ /// [`discriminant_ty`]. Returns zero for types without discriminant.
///
/// The validity requirements for the underlying value are undecided for this rvalue, see
/// [#91095]. Note too that the value of the discriminant is not the same thing as the
/// variant index; use [`discriminant_for_variant`] to convert.
///
- /// For types defined in the source code as enums, this is well behaved. This is also well
- /// formed for other types, but yields no particular value - there is no reason it couldn't be
- /// defined to yield eg zero though.
- ///
/// [`discriminant_ty`]: crate::ty::Ty::discriminant_ty
/// [#91095]: https://github.com/rust-lang/rust/issues/91095
/// [`discriminant_for_variant`]: crate::ty::Ty::discriminant_for_variant
/// This is different from a normal transmute because dataflow analysis will treat the box as
/// initialized but its content as uninitialized. Like other pointer casts, this in general
/// affects alias analysis.
- ///
- /// Disallowed after drop elaboration.
ShallowInitBox(Operand<'tcx>, Ty<'tcx>),
}
Location { block: bb, statement_index: offset }
}
- pub fn new_temp(&mut self, ty: Ty<'tcx>, span: Span) -> Local {
+ pub fn new_local_with_info(
+ &mut self,
+ ty: Ty<'tcx>,
+ span: Span,
+ local_info: Option<Box<LocalInfo<'tcx>>>,
+ ) -> Local {
let index = self.next_local;
self.next_local += 1;
- self.new_locals.push(LocalDecl::new(ty, span));
+ let mut new_decl = LocalDecl::new(ty, span);
+ new_decl.local_info = local_info;
+ self.new_locals.push(new_decl);
Local::new(index as usize)
}
+ pub fn new_temp(&mut self, ty: Ty<'tcx>, span: Span) -> Local {
+ self.new_local_with_info(ty, span, None)
+ }
+
pub fn new_internal(&mut self, ty: Ty<'tcx>, span: Span) -> Local {
let index = self.next_local;
self.next_local += 1;
let mut delta = 0;
let mut last_bb = START_BLOCK;
+ let mut stmts_and_targets: Vec<(Statement<'_>, BasicBlock)> = Vec::new();
for (mut loc, stmt) in new_statements {
if loc.block != last_bb {
delta = 0;
debug!("MirPatch: adding statement {:?} at loc {:?}+{}", stmt, loc, delta);
loc.statement_index += delta;
let source_info = Self::source_info_for_index(&body[loc.block], loc);
+
+ // For mir-opt `Derefer` to work in all cases we need to
+ // get terminator's targets and apply the statement to all of them.
+ if loc.statement_index > body[loc.block].statements.len() {
+ let term = body[loc.block].terminator();
+ let successors = term.successors().clone();
+
+ for i in successors {
+ stmts_and_targets
+ .push((Statement { source_info, kind: stmt.clone() }, i.clone()));
+ }
+ delta += 1;
+ continue;
+ }
+
body[loc.block]
.statements
.insert(loc.statement_index, Statement { source_info, kind: stmt });
delta += 1;
}
+
+ for (stmt, target) in stmts_and_targets.into_iter().rev() {
+ body[target].statements.insert(0, stmt);
+ }
}
pub fn source_info_for_index(data: &BasicBlockData<'_>, loc: Location) -> SourceInfo {
+use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
+use rustc_data_structures::sync::OnceCell;
use rustc_index::bit_set::BitSet;
+use rustc_serialize as serialize;
use super::*;
}
}
-pub fn reverse_postorder<'a, 'tcx>(body: &'a Body<'tcx>) -> ReversePostorder<'a, 'tcx> {
- ReversePostorder::new(body, START_BLOCK)
-}
-
impl<'a, 'tcx> Iterator for ReversePostorder<'a, 'tcx> {
type Item = (BasicBlock, &'a BasicBlockData<'tcx>);
(&mut iter).for_each(drop);
iter.visited
}
+
+#[derive(Clone)]
+pub struct ReversePostorderIter<'a, 'tcx> {
+ body: &'a Body<'tcx>,
+ blocks: &'a Vec<BasicBlock>,
+ idx: usize,
+}
+
+impl<'a, 'tcx> Iterator for ReversePostorderIter<'a, 'tcx> {
+ type Item = (BasicBlock, &'a BasicBlockData<'tcx>);
+
+ fn next(&mut self) -> Option<(BasicBlock, &'a BasicBlockData<'tcx>)> {
+ if self.idx == 0 {
+ return None;
+ }
+ self.idx -= 1;
+
+ self.blocks.get(self.idx).map(|&bb| (bb, &self.body[bb]))
+ }
+
+ fn size_hint(&self) -> (usize, Option<usize>) {
+ (self.idx, Some(self.idx))
+ }
+}
+
+impl<'a, 'tcx> ExactSizeIterator for ReversePostorderIter<'a, 'tcx> {}
+
+pub fn reverse_postorder<'a, 'tcx>(body: &'a Body<'tcx>) -> ReversePostorderIter<'a, 'tcx> {
+ let blocks = body.postorder_cache.compute(body);
+
+ let len = blocks.len();
+
+ ReversePostorderIter { body, blocks, idx: len }
+}
+
+#[derive(Clone, Debug)]
+pub(super) struct PostorderCache {
+ cache: OnceCell<Vec<BasicBlock>>,
+}
+
+impl PostorderCache {
+ #[inline]
+ pub(super) fn new() -> Self {
+ PostorderCache { cache: OnceCell::new() }
+ }
+
+ /// Invalidates the postorder cache.
+ #[inline]
+ pub(super) fn invalidate(&mut self) {
+ self.cache = OnceCell::new();
+ }
+
+ /// Returns the &Vec<BasicBlocks> represents the postorder graph for this MIR.
+ #[inline]
+ pub(super) fn compute(&self, body: &Body<'_>) -> &Vec<BasicBlock> {
+ self.cache.get_or_init(|| Postorder::new(body, START_BLOCK).map(|(bb, _)| bb).collect())
+ }
+}
+
+impl<S: serialize::Encoder> serialize::Encodable<S> for PostorderCache {
+ #[inline]
+ fn encode(&self, s: &mut S) -> Result<(), S::Error> {
+ s.emit_unit()
+ }
+}
+
+impl<D: serialize::Decoder> serialize::Decodable<D> for PostorderCache {
+ #[inline]
+ fn decode(_: &mut D) -> Self {
+ Self::new()
+ }
+}
+
+impl<CTX> HashStable<CTX> for PostorderCache {
+ #[inline]
+ fn hash_stable(&self, _: &mut CTX, _: &mut StableHasher) {
+ // do nothing
+ }
+}
+
+TrivialTypeFoldableAndLiftImpls! {
+ PostorderCache,
+}
remap_env_constness
}
+ /// Converts a type level constant value into `ConstValue`
+ query valtree_to_const_val(key: (Ty<'tcx>, ty::ValTree<'tcx>)) -> ConstValue<'tcx> {
+ desc { "convert type-level constant value to mir constant value"}
+ }
+
/// Destructure a constant ADT or array into its variant index and its
/// field values or return `None` if constant is invalid.
///
// Does not include external symbols that don't have a corresponding DefId,
// like the compiler-generated `main` function and so on.
query reachable_non_generics(_: CrateNum)
- -> DefIdMap<SymbolExportLevel> {
+ -> DefIdMap<SymbolExportInfo> {
storage(ArenaCacheSelector<'tcx>)
desc { "looking up the exported symbols of a crate" }
separate_provide_extern
/// correspond to a publicly visible symbol in `cnum` machine code.
/// - The `exported_symbols` sets of different crates do not intersect.
query exported_symbols(_: CrateNum)
- -> &'tcx [(ExportedSymbol<'tcx>, SymbolExportLevel)] {
+ -> &'tcx [(ExportedSymbol<'tcx>, SymbolExportInfo)] {
desc { "exported_symbols" }
separate_provide_extern
}
/// A container for a THIR body.
///
/// This can be indexed directly by any THIR index (e.g. [`ExprId`]).
- #[derive(Debug, HashStable)]
+ #[derive(Debug, HashStable, Clone)]
pub struct Thir<'tcx> {
$(
pub $name: IndexVec<$id, $value>,
Explicit(hir::HirId),
}
-#[derive(Debug, HashStable)]
+#[derive(Clone, Debug, HashStable)]
pub struct Block {
/// Whether the block itself has a label. Used by `label: {}`
/// and `try` blocks.
pub safety_mode: BlockSafety,
}
-#[derive(Debug, HashStable)]
+#[derive(Clone, Debug, HashStable)]
pub struct Adt<'tcx> {
/// The ADT we're constructing.
pub adt_def: AdtDef<'tcx>,
ExplicitUnsafe(hir::HirId),
}
-#[derive(Debug, HashStable)]
+#[derive(Clone, Debug, HashStable)]
pub struct Stmt<'tcx> {
pub kind: StmtKind<'tcx>,
pub opt_destruction_scope: Option<region::Scope>,
}
-#[derive(Debug, HashStable)]
+#[derive(Clone, Debug, HashStable)]
pub enum StmtKind<'tcx> {
/// An expression with a trailing semicolon.
Expr {
rustc_data_structures::static_assert_size!(Expr<'_>, 104);
/// A THIR expression.
-#[derive(Debug, HashStable)]
+#[derive(Clone, Debug, HashStable)]
pub struct Expr<'tcx> {
/// The type of this expression
pub ty: Ty<'tcx>,
pub kind: ExprKind<'tcx>,
}
-#[derive(Debug, HashStable)]
+#[derive(Clone, Debug, HashStable)]
pub enum ExprKind<'tcx> {
/// `Scope`s are used to explicitly mark destruction scopes,
/// and to track the `HirId` of the expressions within the scope.
/// Represents the association of a field identifier and an expression.
///
/// This is used in struct constructors.
-#[derive(Debug, HashStable)]
+#[derive(Clone, Debug, HashStable)]
pub struct FieldExpr {
pub name: Field,
pub expr: ExprId,
}
-#[derive(Debug, HashStable)]
+#[derive(Clone, Debug, HashStable)]
pub struct FruInfo<'tcx> {
pub base: ExprId,
pub field_types: Box<[Ty<'tcx>]>,
}
/// A `match` arm.
-#[derive(Debug, HashStable)]
+#[derive(Clone, Debug, HashStable)]
pub struct Arm<'tcx> {
pub pattern: Pat<'tcx>,
pub guard: Option<Guard<'tcx>>,
}
/// A `match` guard.
-#[derive(Debug, HashStable)]
+#[derive(Clone, Debug, HashStable)]
pub enum Guard<'tcx> {
If(ExprId),
IfLet(Pat<'tcx>, ExprId),
Or,
}
-#[derive(Debug, HashStable)]
+#[derive(Clone, Debug, HashStable)]
pub enum InlineAsmOperand<'tcx> {
In {
reg: InlineAsmRegOrRegClass,
}
}
};
- ([$ignore:ident $(, $attrs:ident)*]$args:tt) => {
- impl_arena_allocatable_decoder!([$($attrs),*]$args);
- };
}
macro_rules! impl_arena_allocatable_decoders {
pub fn try_to_machine_usize<'tcx>(&self, tcx: TyCtxt<'tcx>) -> Result<u64, Size> {
Ok(self.to_bits(tcx.data_layout.pointer_size)? as u64)
}
+
+ /// Tries to convert the `ScalarInt` to an unsigned integer of the given size.
+ /// Fails if the size of the `ScalarInt` is unequal to `size` and returns the
+ /// `ScalarInt`s size in that case.
+ #[inline]
+ pub fn try_to_uint(self, size: Size) -> Result<u128, Size> {
+ self.to_bits(size)
+ }
+
+ // Tries to convert the `ScalarInt` to `u8`. Fails if the `size` of the `ScalarInt`
+ // in not equal to `Size { raw: 1 }` and returns the `size` value of the `ScalarInt` in
+ // that case.
+ #[inline]
+ pub fn try_to_u8(self) -> Result<u8, Size> {
+ self.to_bits(Size::from_bits(8)).map(|v| u8::try_from(v).unwrap())
+ }
+
+ /// Tries to convert the `ScalarInt` to `u16`. Fails if the size of the `ScalarInt`
+ /// in not equal to `Size { raw: 2 }` and returns the `size` value of the `ScalarInt` in
+ /// that case.
+ #[inline]
+ pub fn try_to_u16(self) -> Result<u16, Size> {
+ self.to_bits(Size::from_bits(16)).map(|v| u16::try_from(v).unwrap())
+ }
+
+ /// Tries to convert the `ScalarInt` to `u32`. Fails if the `size` of the `ScalarInt`
+ /// in not equal to `Size { raw: 4 }` and returns the `size` value of the `ScalarInt` in
+ /// that case.
+ #[inline]
+ pub fn try_to_u32(self) -> Result<u32, Size> {
+ self.to_bits(Size::from_bits(32)).map(|v| u32::try_from(v).unwrap())
+ }
+
+ /// Tries to convert the `ScalarInt` to `u64`. Fails if the `size` of the `ScalarInt`
+ /// in not equal to `Size { raw: 8 }` and returns the `size` value of the `ScalarInt` in
+ /// that case.
+ #[inline]
+ pub fn try_to_u64(self) -> Result<u64, Size> {
+ self.to_bits(Size::from_bits(64)).map(|v| u64::try_from(v).unwrap())
+ }
+
+ /// Tries to convert the `ScalarInt` to `u128`. Fails if the `size` of the `ScalarInt`
+ /// in not equal to `Size { raw: 16 }` and returns the `size` value of the `ScalarInt` in
+ /// that case.
+ #[inline]
+ pub fn try_to_u128(self) -> Result<u128, Size> {
+ self.to_bits(Size::from_bits(128))
+ }
+
+ /// Tries to convert the `ScalarInt` to a signed integer of the given size.
+ /// Fails if the size of the `ScalarInt` is unequal to `size` and returns the
+ /// `ScalarInt`s size in that case.
+ #[inline]
+ pub fn try_to_int(self, size: Size) -> Result<i128, Size> {
+ let b = self.to_bits(size)?;
+ Ok(size.sign_extend(b) as i128)
+ }
+
+ /// Tries to convert the `ScalarInt` to i8.
+ /// Fails if the size of the `ScalarInt` is unequal to `Size { raw: 1 }`
+ /// and returns the `ScalarInt`s size in that case.
+ pub fn try_to_i8(self) -> Result<i8, Size> {
+ self.try_to_int(Size::from_bits(8)).map(|v| i8::try_from(v).unwrap())
+ }
+
+ /// Tries to convert the `ScalarInt` to i16.
+ /// Fails if the size of the `ScalarInt` is unequal to `Size { raw: 2 }`
+ /// and returns the `ScalarInt`s size in that case.
+ pub fn try_to_i16(self) -> Result<i16, Size> {
+ self.try_to_int(Size::from_bits(16)).map(|v| i16::try_from(v).unwrap())
+ }
+
+ /// Tries to convert the `ScalarInt` to i32.
+ /// Fails if the size of the `ScalarInt` is unequal to `Size { raw: 4 }`
+ /// and returns the `ScalarInt`s size in that case.
+ pub fn try_to_i32(self) -> Result<i32, Size> {
+ self.try_to_int(Size::from_bits(32)).map(|v| i32::try_from(v).unwrap())
+ }
+
+ /// Tries to convert the `ScalarInt` to i64.
+ /// Fails if the size of the `ScalarInt` is unequal to `Size { raw: 8 }`
+ /// and returns the `ScalarInt`s size in that case.
+ pub fn try_to_i64(self) -> Result<i64, Size> {
+ self.try_to_int(Size::from_bits(64)).map(|v| i64::try_from(v).unwrap())
+ }
+
+ /// Tries to convert the `ScalarInt` to i128.
+ /// Fails if the size of the `ScalarInt` is unequal to `Size { raw: 16 }`
+ /// and returns the `ScalarInt`s size in that case.
+ pub fn try_to_i128(self) -> Result<i128, Size> {
+ self.try_to_int(Size::from_bits(128)).map(|v| i128::try_from(v).unwrap())
+ }
}
macro_rules! from {
/// See the `ScalarInt` documentation for how `ScalarInt` guarantees that equal values
/// of these types have the same representation.
Leaf(ScalarInt),
+
+ //SliceOrStr(ValSlice<'tcx>),
+ // dont use SliceOrStr for now
/// The fields of any kind of aggregate. Structs, tuples and arrays are represented by
/// listing their fields' values in order.
/// Enums are represented by storing their discriminant as a field, followed by all
pub fn zst() -> Self {
Self::Branch(&[])
}
+
+ #[inline]
+ pub fn unwrap_leaf(self) -> ScalarInt {
+ match self {
+ Self::Leaf(s) => s,
+ _ => bug!("expected leaf, got {:?}", self),
+ }
+ }
+
+ #[inline]
+ pub fn unwrap_branch(self) -> &'tcx [Self] {
+ match self {
+ Self::Branch(branch) => branch,
+ _ => bug!("expected branch, got {:?}", self),
+ }
+ }
}
(free_region.scope.expect_local(), free_region.bound_region)
}
ty::ReEarlyBound(ref ebr) => (
- self.parent(ebr.def_id).unwrap().expect_local(),
+ self.local_parent(ebr.def_id.expect_local()),
ty::BoundRegionKind::BrNamed(ebr.def_id, ebr.name),
),
_ => return None, // not a free region
use crate::ty::subst::{GenericArg, GenericArgKind};
use crate::ty::TyKind::*;
use crate::ty::{
- ConstKind, ExistentialPredicate, ExistentialProjection, ExistentialTraitRef, InferTy,
- ProjectionTy, Term, Ty, TyCtxt, TypeAndMut,
+ ConstKind, DefIdTree, ExistentialPredicate, ExistentialProjection, ExistentialTraitRef,
+ InferTy, ProjectionTy, Term, Ty, TyCtxt, TypeAndMut,
};
use rustc_data_structures::fx::FxHashMap;
use rustc_errors::{Applicability, Diagnostic, DiagnosticArgValue, IntoDiagnosticArg};
use rustc_hir as hir;
use rustc_hir::def_id::DefId;
-use rustc_hir::{QPath, TyKind, WhereBoundPredicate, WherePredicate};
+use rustc_hir::WherePredicate;
use rustc_span::Span;
impl<'tcx> IntoDiagnosticArg for Ty<'tcx> {
}
/// Whether the type can be safely suggested during error recovery.
- pub fn is_suggestable(self) -> bool {
- fn generic_arg_is_suggestible(arg: GenericArg<'_>) -> bool {
+ pub fn is_suggestable(self, tcx: TyCtxt<'tcx>) -> bool {
+ fn generic_arg_is_suggestible<'tcx>(arg: GenericArg<'tcx>, tcx: TyCtxt<'tcx>) -> bool {
match arg.unpack() {
- GenericArgKind::Type(ty) => ty.is_suggestable(),
+ GenericArgKind::Type(ty) => ty.is_suggestable(tcx),
GenericArgKind::Const(c) => const_is_suggestable(c.val()),
_ => true,
}
// temporary, so I'll leave this as a fixme.
match self.kind() {
- Opaque(..)
- | FnDef(..)
+ FnDef(..)
| Closure(..)
| Infer(..)
| Generator(..)
| Bound(_, _)
| Placeholder(_)
| Error(_) => false,
+ Opaque(did, substs) => {
+ let parent = tcx.parent(*did);
+ if let hir::def::DefKind::TyAlias | hir::def::DefKind::AssocTy = tcx.def_kind(parent)
+ && let Opaque(parent_did, _) = tcx.type_of(parent).kind()
+ && parent_did == did
+ {
+ substs.iter().all(|a| generic_arg_is_suggestible(a, tcx))
+ } else {
+ false
+ }
+ }
Dynamic(dty, _) => dty.iter().all(|pred| match pred.skip_binder() {
ExistentialPredicate::Trait(ExistentialTraitRef { substs, .. }) => {
- substs.iter().all(generic_arg_is_suggestible)
+ substs.iter().all(|a| generic_arg_is_suggestible(a, tcx))
}
ExistentialPredicate::Projection(ExistentialProjection {
substs, term, ..
}) => {
let term_is_suggestable = match term {
- Term::Ty(ty) => ty.is_suggestable(),
+ Term::Ty(ty) => ty.is_suggestable(tcx),
Term::Const(c) => const_is_suggestable(c.val()),
};
- term_is_suggestable && substs.iter().all(generic_arg_is_suggestible)
+ term_is_suggestable && substs.iter().all(|a| generic_arg_is_suggestible(a, tcx))
}
_ => true,
}),
Projection(ProjectionTy { substs: args, .. }) | Adt(_, args) => {
- args.iter().all(generic_arg_is_suggestible)
+ args.iter().all(|a| generic_arg_is_suggestible(a, tcx))
}
- Tuple(args) => args.iter().all(|ty| ty.is_suggestable()),
- Slice(ty) | RawPtr(TypeAndMut { ty, .. }) | Ref(_, ty, _) => ty.is_suggestable(),
- Array(ty, c) => ty.is_suggestable() && const_is_suggestable(c.val()),
+ Tuple(args) => args.iter().all(|ty| ty.is_suggestable(tcx)),
+ Slice(ty) | RawPtr(TypeAndMut { ty, .. }) | Ref(_, ty, _) => ty.is_suggestable(tcx),
+ Array(ty, c) => ty.is_suggestable(tcx) && const_is_suggestable(c.val()),
_ => true,
}
}
_ => {}
}
// Suggest a where clause bound for a non-type parameter.
- let (action, prefix) = if generics.where_clause.predicates.is_empty() {
- ("introducing a", " where ")
- } else {
+ let (action, prefix) = if generics.has_where_clause {
("extending the", ", ")
+ } else {
+ ("introducing a", " where ")
};
err.span_suggestion_verbose(
- generics.where_clause.tail_span_for_suggestion(),
+ generics.tail_span_for_predicate_suggestion(),
&format!(
"consider {} `where` bound, but there might be an alternative better way to express \
this requirement",
}
fn suggest_removing_unsized_bound(
+ tcx: TyCtxt<'_>,
generics: &hir::Generics<'_>,
suggestions: &mut Vec<(Span, String, SuggestChangingConstraintsMessage<'_>)>,
- param_name: &str,
param: &hir::GenericParam<'_>,
def_id: Option<DefId>,
) {
// See if there's a `?Sized` bound that can be removed to suggest that.
// First look at the `where` clause because we can have `where T: ?Sized`,
// then look at params.
- for (where_pos, predicate) in generics.where_clause.predicates.iter().enumerate() {
- match predicate {
- WherePredicate::BoundPredicate(WhereBoundPredicate {
- bounded_ty:
- hir::Ty {
- kind:
- hir::TyKind::Path(hir::QPath::Resolved(
- None,
- hir::Path {
- segments: [segment],
- res: hir::def::Res::Def(hir::def::DefKind::TyParam, _),
- ..
- },
- )),
- ..
- },
- bounds,
- span,
- ..
- }) if segment.ident.as_str() == param_name => {
- for (pos, bound) in bounds.iter().enumerate() {
- match bound {
- hir::GenericBound::Trait(poly, hir::TraitBoundModifier::Maybe)
- if poly.trait_ref.trait_def_id() == def_id => {}
- _ => continue,
- }
- let sp = match (
- bounds.len(),
- pos,
- generics.where_clause.predicates.len(),
- where_pos,
- ) {
- // where T: ?Sized
- // ^^^^^^^^^^^^^^^
- (1, _, 1, _) => generics.where_clause.span,
- // where Foo: Bar, T: ?Sized,
- // ^^^^^^^^^^^
- (1, _, len, pos) if pos == len - 1 => generics.where_clause.predicates
- [pos - 1]
- .span()
- .shrink_to_hi()
- .to(*span),
- // where T: ?Sized, Foo: Bar,
- // ^^^^^^^^^^^
- (1, _, _, pos) => {
- span.until(generics.where_clause.predicates[pos + 1].span())
- }
- // where T: ?Sized + Bar, Foo: Bar,
- // ^^^^^^^^^
- (_, 0, _, _) => bound.span().to(bounds[1].span().shrink_to_lo()),
- // where T: Bar + ?Sized, Foo: Bar,
- // ^^^^^^^^^
- (_, pos, _, _) => bounds[pos - 1].span().shrink_to_hi().to(bound.span()),
- };
+ let param_def_id = tcx.hir().local_def_id(param.hir_id);
+ for (where_pos, predicate) in generics.predicates.iter().enumerate() {
+ let WherePredicate::BoundPredicate(predicate) = predicate else {
+ continue;
+ };
+ if !predicate.is_param_bound(param_def_id.to_def_id()) {
+ continue;
+ };
- suggestions.push((
- sp,
- String::new(),
- SuggestChangingConstraintsMessage::RemovingQSized,
- ));
- }
- }
- _ => {}
- }
- }
- for (pos, bound) in param.bounds.iter().enumerate() {
- match bound {
- hir::GenericBound::Trait(poly, hir::TraitBoundModifier::Maybe)
- if poly.trait_ref.trait_def_id() == def_id =>
- {
- let sp = match (param.bounds.len(), pos) {
- // T: ?Sized,
- // ^^^^^^^^
- (1, _) => param.span.shrink_to_hi().to(bound.span()),
- // T: ?Sized + Bar,
- // ^^^^^^^^^
- (_, 0) => bound.span().to(param.bounds[1].span().shrink_to_lo()),
- // T: Bar + ?Sized,
- // ^^^^^^^^^
- (_, pos) => param.bounds[pos - 1].span().shrink_to_hi().to(bound.span()),
- };
-
- suggestions.push((
- sp,
- String::new(),
- SuggestChangingConstraintsMessage::RemovingQSized,
- ));
+ for (pos, bound) in predicate.bounds.iter().enumerate() {
+ let hir::GenericBound::Trait(poly, hir::TraitBoundModifier::Maybe) = bound else {
+ continue;
+ };
+ if poly.trait_ref.trait_def_id() != def_id {
+ continue;
}
- _ => {}
+ let sp = generics.span_for_bound_removal(where_pos, pos);
+ suggestions.push((
+ sp,
+ String::new(),
+ SuggestChangingConstraintsMessage::RemovingQSized,
+ ));
}
}
}
param.span,
&format!("this type parameter needs to be `{}`", constraint),
);
- suggest_removing_unsized_bound(
- generics,
- &mut suggestions,
- param_name,
- param,
- def_id,
- );
+ suggest_removing_unsized_bound(tcx, generics, &mut suggestions, param, def_id);
}
}
))
};
- if param_name.starts_with("impl ") {
- // If there's an `impl Trait` used in argument position, suggest
- // restricting it:
- //
- // fn foo(t: impl Foo) { ... }
- // --------
- // |
- // help: consider further restricting this bound with `+ Bar`
- //
- // Suggestion for tools in this case is:
- //
- // fn foo(t: impl Foo) { ... }
- // --------
- // |
- // replace with: `impl Foo + Bar`
-
- // `impl Trait` must have at least one trait in the list
- let bound_list_non_empty = true;
-
- suggest_restrict(param.span.shrink_to_hi(), bound_list_non_empty);
+ // When the type parameter has been provided bounds
+ //
+ // Message:
+ // fn foo<T>(t: T) where T: Foo { ... }
+ // ^^^^^^
+ // |
+ // help: consider further restricting this bound with `+ Bar`
+ //
+ // Suggestion:
+ // fn foo<T>(t: T) where T: Foo { ... }
+ // ^
+ // |
+ // replace with: ` + Bar`
+ //
+ // Or, if user has provided some bounds, suggest restricting them:
+ //
+ // fn foo<T: Foo>(t: T) { ... }
+ // ---
+ // |
+ // help: consider further restricting this bound with `+ Bar`
+ //
+ // Suggestion for tools in this case is:
+ //
+ // fn foo<T: Foo>(t: T) { ... }
+ // --
+ // |
+ // replace with: `T: Bar +`
+ let param_def_id = tcx.hir().local_def_id(param.hir_id);
+ if let Some(span) = generics.bounds_span_for_suggestions(param_def_id) {
+ suggest_restrict(span, true);
continue;
}
- if generics.where_clause.predicates.is_empty()
- // Given `trait Base<T = String>: Super<T>` where `T: Copy`, suggest restricting in the
- // `where` clause instead of `trait Base<T: Copy = String>: Super<T>`.
- && !matches!(param.kind, hir::GenericParamKind::Type { default: Some(_), .. })
- {
- if let Some(span) = param.bounds_span_for_suggestions() {
- // If user has provided some bounds, suggest restricting them:
- //
- // fn foo<T: Foo>(t: T) { ... }
- // ---
- // |
- // help: consider further restricting this bound with `+ Bar`
- //
- // Suggestion for tools in this case is:
- //
- // fn foo<T: Foo>(t: T) { ... }
- // --
- // |
- // replace with: `T: Bar +`
-
- // `bounds_span_for_suggestions` returns `None` if the list is empty
- let bound_list_non_empty = true;
-
- suggest_restrict(span, bound_list_non_empty);
- } else {
- let (colon, span) = match param.colon_span_for_suggestions(tcx.sess.source_map()) {
- // If there is already a colon after generic, do not suggest adding it again
- Some(sp) => ("", sp.shrink_to_hi()),
- None => (":", param.span.shrink_to_hi()),
- };
-
- // If user hasn't provided any bounds, suggest adding a new one:
- //
- // fn foo<T>(t: T) { ... }
- // - help: consider restricting this type parameter with `T: Foo`
- suggestions.push((
- span,
- format!("{colon} {constraint}"),
- SuggestChangingConstraintsMessage::RestrictType { ty: param_name },
- ));
- }
- } else {
+ if generics.has_where_clause {
// This part is a bit tricky, because using the `where` clause user can
// provide zero, one or many bounds for the same type parameter, so we
// have following cases to consider:
//
- // 1) When the type parameter has been provided zero bounds
+ // When the type parameter has been provided zero bounds
//
// Message:
// fn foo<X, Y>(x: X, y: Y) where Y: Foo { ... }
// Suggestion:
// fn foo<X, Y>(x: X, y: Y) where Y: Foo { ... }
// - insert: `, X: Bar`
- //
- //
- // 2) When the type parameter has been provided one bound
- //
- // Message:
- // fn foo<T>(t: T) where T: Foo { ... }
- // ^^^^^^
- // |
- // help: consider further restricting this bound with `+ Bar`
- //
- // Suggestion:
- // fn foo<T>(t: T) where T: Foo { ... }
- // ^^
- // |
- // replace with: `T: Bar +`
- //
- //
- // 3) When the type parameter has been provided many bounds
- //
- // Message:
- // fn foo<T>(t: T) where T: Foo, T: Bar {... }
- // - help: consider further restricting this type parameter with `where T: Zar`
- //
- // Suggestion:
- // fn foo<T>(t: T) where T: Foo, T: Bar {... }
- // - insert: `, T: Zar`
- //
- // Additionally, there may be no `where` clause whatsoever in the case that this was
- // reached because the generic parameter has a default:
- //
- // Message:
- // trait Foo<T=()> {... }
- // - help: consider further restricting this type parameter with `where T: Zar`
- //
- // Suggestion:
- // trait Foo<T=()> where T: Zar {... }
- // - insert: `where T: Zar`
-
- if matches!(param.kind, hir::GenericParamKind::Type { default: Some(_), .. })
- && generics.where_clause.predicates.len() == 0
- {
- // Suggest a bound, but there is no existing `where` clause *and* the type param has a
- // default (`<T=Foo>`), so we suggest adding `where T: Bar`.
- suggestions.push((
- generics.where_clause.tail_span_for_suggestion(),
- format!(" where {}: {}", param_name, constraint),
- SuggestChangingConstraintsMessage::RestrictTypeFurther { ty: param_name },
- ));
- } else {
- let mut param_spans = Vec::new();
- let mut non_empty = false;
-
- for predicate in generics.where_clause.predicates {
- if let WherePredicate::BoundPredicate(WhereBoundPredicate {
- span,
- bounded_ty,
- bounds,
- ..
- }) = predicate
- {
- if let TyKind::Path(QPath::Resolved(_, path)) = &bounded_ty.kind {
- if let Some(segment) = path.segments.first() {
- if segment.ident.to_string() == param_name {
- non_empty = !bounds.is_empty();
-
- param_spans.push(span);
- }
- }
- }
- }
- }
+ suggestions.push((
+ generics.tail_span_for_predicate_suggestion(),
+ constraints
+ .iter()
+ .map(|&(constraint, _)| format!(", {}: {}", param_name, constraint))
+ .collect::<String>(),
+ SuggestChangingConstraintsMessage::RestrictTypeFurther { ty: param_name },
+ ));
+ continue;
+ }
- match param_spans[..] {
- [¶m_span] => suggest_restrict(param_span.shrink_to_hi(), non_empty),
- _ => {
- suggestions.push((
- generics.where_clause.tail_span_for_suggestion(),
- constraints
- .iter()
- .map(|&(constraint, _)| format!(", {}: {}", param_name, constraint))
- .collect::<String>(),
- SuggestChangingConstraintsMessage::RestrictTypeFurther {
- ty: param_name,
- },
- ));
- }
- }
- }
+ // Additionally, there may be no `where` clause but the generic parameter has a default:
+ //
+ // Message:
+ // trait Foo<T=()> {... }
+ // - help: consider further restricting this type parameter with `where T: Zar`
+ //
+ // Suggestion:
+ // trait Foo<T=()> {... }
+ // - insert: `where T: Zar`
+ if matches!(param.kind, hir::GenericParamKind::Type { default: Some(_), .. }) {
+ // Suggest a bound, but there is no existing `where` clause *and* the type param has a
+ // default (`<T=Foo>`), so we suggest adding `where T: Bar`.
+ suggestions.push((
+ generics.tail_span_for_predicate_suggestion(),
+ format!(" where {}: {}", param_name, constraint),
+ SuggestChangingConstraintsMessage::RestrictTypeFurther { ty: param_name },
+ ));
+ continue;
}
+
+ // If user has provided a colon, don't suggest adding another:
+ //
+ // fn foo<T:>(t: T) { ... }
+ // - insert: consider restricting this type parameter with `T: Foo`
+ if let Some(colon_span) = param.colon_span {
+ suggestions.push((
+ colon_span.shrink_to_hi(),
+ format!(" {}", constraint),
+ SuggestChangingConstraintsMessage::RestrictType { ty: param_name },
+ ));
+ continue;
+ }
+
+ // If user hasn't provided any bounds, suggest adding a new one:
+ //
+ // fn foo<T>(t: T) { ... }
+ // - help: consider restricting this type parameter with `T: Foo`
+ suggestions.push((
+ param.span.shrink_to_hi(),
+ format!(": {}", constraint),
+ SuggestChangingConstraintsMessage::RestrictType { ty: param_name },
+ ));
}
if suggestions.len() == 1 {
} else {
return false;
};
+ let Some(def_id) = def_id.as_local() else {
+ return false;
+ };
// First look in the `where` clause, as this might be
// `fn foo<T>(x: T) where T: Trait`.
- for predicate in hir_generics.where_clause.predicates {
- if let hir::WherePredicate::BoundPredicate(pred) = predicate {
- if let hir::TyKind::Path(hir::QPath::Resolved(None, path)) =
- pred.bounded_ty.kind
- {
- if path.res.opt_def_id() == Some(def_id) {
- // This predicate is binding type param `A` in `<A as T>::Foo` to
- // something, potentially `T`.
- } else {
- continue;
- }
- } else {
- continue;
- }
-
- if self.constrain_generic_bound_associated_type_structured_suggestion(
- diag,
- &trait_ref,
- pred.bounds,
- &assoc,
- assoc_substs,
- ty,
- msg,
- false,
- ) {
- return true;
- }
- }
- }
- for param in hir_generics.params {
- if self.hir().opt_local_def_id(param.hir_id).map(|id| id.to_def_id())
- == Some(def_id)
- {
- // This is type param `A` in `<A as T>::Foo`.
- return self.constrain_generic_bound_associated_type_structured_suggestion(
- diag,
- &trait_ref,
- param.bounds,
- &assoc,
- assoc_substs,
- ty,
- msg,
- false,
- );
+ for pred in hir_generics.bounds_for_param(def_id) {
+ if self.constrain_generic_bound_associated_type_structured_suggestion(
+ diag,
+ &trait_ref,
+ pred.bounds,
+ &assoc,
+ assoc_substs,
+ ty,
+ msg,
+ false,
+ ) {
+ return true;
}
}
}
match st[i].abi() {
Abi::Scalar(_) => Abi::Scalar(niche_scalar),
Abi::ScalarPair(first, second) => {
- // We need to use scalar_unit to reset the
- // valid range to the maximal one for that
- // primitive, because only the niche is
- // guaranteed to be initialised, not the
- // other primitive.
+ // Only the niche is guaranteed to be initialised,
+ // so use union layout for the other primitive.
if offset.bytes() == 0 {
- Abi::ScalarPair(
- niche_scalar,
- scalar_unit(second.primitive()),
- )
+ Abi::ScalarPair(niche_scalar, second.to_union())
} else {
- Abi::ScalarPair(
- scalar_unit(first.primitive()),
- niche_scalar,
- )
+ Abi::ScalarPair(first.to_union(), niche_scalar)
}
}
_ => Abi::Aggregate { sized: true },
} else {
// Try to use a ScalarPair for all tagged enums.
let mut common_prim = None;
+ let mut common_prim_initialized_in_all_variants = true;
for (field_layouts, layout_variant) in iter::zip(&variants, &layout_variants) {
let FieldsShape::Arbitrary { ref offsets, .. } = layout_variant.fields else {
bug!();
let mut fields =
iter::zip(field_layouts, offsets).filter(|p| !p.0.is_zst());
let (field, offset) = match (fields.next(), fields.next()) {
- (None, None) => continue,
+ (None, None) => {
+ common_prim_initialized_in_all_variants = false;
+ continue;
+ }
(Some(pair), None) => pair,
_ => {
common_prim = None;
}
};
let prim = match field.abi {
- Abi::Scalar(scalar) => scalar.primitive(),
+ Abi::Scalar(scalar) => {
+ common_prim_initialized_in_all_variants &=
+ matches!(scalar, Scalar::Initialized { .. });
+ scalar.primitive()
+ }
_ => {
common_prim = None;
break;
}
}
if let Some((prim, offset)) = common_prim {
- let pair = self.scalar_pair(tag, scalar_unit(prim));
+ let prim_scalar = if common_prim_initialized_in_all_variants {
+ scalar_unit(prim)
+ } else {
+ // Common prim might be uninit.
+ Scalar::Union { value: prim }
+ };
+ let pair = self.scalar_pair(tag, prim_scalar);
let pair_offsets = match pair.fields {
FieldsShape::Arbitrary { ref offsets, ref memory_index } => {
assert_eq!(memory_index, &[0, 1]);
pointee_info
}
+
+ fn is_adt(this: TyAndLayout<'tcx>) -> bool {
+ matches!(this.ty.kind(), ty::Adt(..))
+ }
+
+ fn is_never(this: TyAndLayout<'tcx>) -> bool {
+ this.ty.kind() == &ty::Never
+ }
+
+ fn is_tuple(this: TyAndLayout<'tcx>) -> bool {
+ matches!(this.ty.kind(), ty::Tuple(..))
+ }
+
+ fn is_unit(this: TyAndLayout<'tcx>) -> bool {
+ matches!(this.ty.kind(), ty::Tuple(list) if list.len() == 0)
+ }
}
impl<'tcx> ty::Instance<'tcx> {
static EMPTY_SLICE: InOrder<usize, MaxAlign> = InOrder(0, MaxAlign);
unsafe { &*(&EMPTY_SLICE as *const _ as *const List<T>) }
}
+
+ pub fn len(&self) -> usize {
+ self.len
+ }
}
impl<T: Copy> List<T> {
use rustc_data_structures::tagged_ptr::CopyTaggedPtr;
use rustc_hir as hir;
use rustc_hir::def::{CtorKind, CtorOf, DefKind, Res};
-use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, LocalDefIdMap, CRATE_DEF_ID};
+use rustc_hir::def_id::{CrateNum, DefId, LocalDefId, LocalDefIdMap};
use rustc_hir::Node;
use rustc_macros::HashStable;
use rustc_query_system::ich::StableHashingContext;
pub definitions: rustc_hir::definitions::Definitions,
pub cstore: Box<CrateStoreDyn>,
pub visibilities: FxHashMap<LocalDefId, Visibility>,
+ /// This field is used to decide whether we should make `PRIVATE_IN_PUBLIC` a hard error.
+ pub has_pub_restricted: bool,
pub access_levels: AccessLevels,
pub extern_crate_map: FxHashMap<LocalDefId, CrateNum>,
pub maybe_unused_trait_imports: FxHashSet<LocalDefId>,
}
pub trait DefIdTree: Copy {
- fn parent(self, id: DefId) -> Option<DefId>;
+ fn opt_parent(self, id: DefId) -> Option<DefId>;
#[inline]
- fn local_parent(self, id: LocalDefId) -> Option<LocalDefId> {
- Some(self.parent(id.to_def_id())?.expect_local())
+ #[track_caller]
+ fn parent(self, id: DefId) -> DefId {
+ match self.opt_parent(id) {
+ Some(id) => id,
+ // not `unwrap_or_else` to avoid breaking caller tracking
+ None => bug!("{id:?} doesn't have a parent"),
+ }
+ }
+
+ #[inline]
+ #[track_caller]
+ fn opt_local_parent(self, id: LocalDefId) -> Option<LocalDefId> {
+ self.opt_parent(id.to_def_id()).map(DefId::expect_local)
+ }
+
+ #[inline]
+ #[track_caller]
+ fn local_parent(self, id: LocalDefId) -> LocalDefId {
+ self.parent(id.to_def_id()).expect_local()
}
fn is_descendant_of(self, mut descendant: DefId, ancestor: DefId) -> bool {
}
while descendant != ancestor {
- match self.parent(descendant) {
+ match self.opt_parent(descendant) {
Some(parent) => descendant = parent,
None => return false,
}
}
impl<'tcx> DefIdTree for TyCtxt<'tcx> {
- fn parent(self, id: DefId) -> Option<DefId> {
+ #[inline]
+ fn opt_parent(self, id: DefId) -> Option<DefId> {
self.def_key(id).parent.map(|index| DefId { index, ..id })
}
}
impl Visibility {
- pub fn from_hir(visibility: &hir::Visibility<'_>, id: hir::HirId, tcx: TyCtxt<'_>) -> Self {
- match visibility.node {
- hir::VisibilityKind::Public => Visibility::Public,
- hir::VisibilityKind::Crate(_) => Visibility::Restricted(CRATE_DEF_ID.to_def_id()),
- hir::VisibilityKind::Restricted { ref path, .. } => match path.res {
- // If there is no resolution, `resolve` will have already reported an error, so
- // assume that the visibility is public to avoid reporting more privacy errors.
- Res::Err => Visibility::Public,
- def => Visibility::Restricted(def.def_id()),
- },
- hir::VisibilityKind::Inherited => {
- Visibility::Restricted(tcx.parent_module(id).to_def_id())
- }
- }
- }
-
/// Returns `true` if an item with this visibility is accessible from the given block.
pub fn is_accessible_from<T: DefIdTree>(self, module: DefId, tree: T) -> bool {
let restriction = match self {
pub fn expect_variant_res(self, res: Res) -> &'tcx VariantDef {
match res {
Res::Def(DefKind::Variant, did) => {
- let enum_did = self.parent(did).unwrap();
+ let enum_did = self.parent(did);
self.adt_def(enum_did).variant_with_id(did)
}
Res::Def(DefKind::Struct | DefKind::Union, did) => self.adt_def(did).non_enum_variant(),
Res::Def(DefKind::Ctor(CtorOf::Variant, ..), variant_ctor_did) => {
- let variant_did = self.parent(variant_ctor_did).unwrap();
- let enum_did = self.parent(variant_did).unwrap();
+ let variant_did = self.parent(variant_ctor_did);
+ let enum_did = self.parent(variant_did);
self.adt_def(enum_did).variant_with_ctor_id(variant_ctor_did)
}
Res::Def(DefKind::Ctor(CtorOf::Struct, ..), ctor_did) => {
- let struct_did = self.parent(ctor_did).expect("struct ctor has no parent");
+ let struct_did = self.parent(ctor_did);
self.adt_def(struct_did).non_enum_variant()
}
_ => bug!("expect_variant_res used with unexpected res {:?}", res),
// as the trait.
let in_self_mod = match characteristic_def_id_of_type(self_ty) {
None => false,
- Some(ty_def_id) => self.tcx().parent(ty_def_id) == Some(parent_def_id),
+ Some(ty_def_id) => self.tcx().parent(ty_def_id) == parent_def_id,
};
let in_trait_mod = match impl_trait_ref {
None => false,
- Some(trait_ref) => self.tcx().parent(trait_ref.def_id) == Some(parent_def_id),
+ Some(trait_ref) => self.tcx().parent(trait_ref.def_id) == parent_def_id,
};
if !in_self_mod && !in_trait_mod {
return Ok((self, false));
};
- let actual_parent = self.tcx().parent(def_id);
+ let actual_parent = self.tcx().opt_parent(def_id);
debug!(
"try_print_visible_def_path: visible_parent={:?} actual_parent={:?}",
visible_parent, actual_parent,
return Ok(self);
}
- let parent = self.tcx().parent(def_id).expect("opaque types always have a parent");
+ let parent = self.tcx().parent(def_id);
match self.tcx().def_kind(parent) {
DefKind::TyAlias | DefKind::AssocTy => {
if let ty::Opaque(d, _) = *self.tcx().type_of(parent).kind() {
use crate::lint::LintLevelMap;
use crate::metadata::ModChild;
use crate::middle::codegen_fn_attrs::CodegenFnAttrs;
-use crate::middle::exported_symbols::{ExportedSymbol, SymbolExportLevel};
+use crate::middle::exported_symbols::{ExportedSymbol, SymbolExportInfo};
use crate::middle::lib_features::LibFeatures;
use crate::middle::privacy::AccessLevels;
use crate::middle::region;
/// function might return the `DefId` of a closure.
pub fn free_region_binding_scope(self, tcx: TyCtxt<'_>) -> DefId {
match *self {
- ty::ReEarlyBound(br) => tcx.parent(br.def_id).unwrap(),
+ ty::ReEarlyBound(br) => tcx.parent(br.def_id),
ty::ReFree(fr) => fr.scope,
_ => bug!("free_region_binding_scope invoked on inappropriate region: {:?}", self),
}
tcx: TyCtxt<'tcx>,
normalize: impl FnMut(Ty<'tcx>) -> Ty<'tcx>,
) -> (Ty<'tcx>, bool) {
- let tail = tcx.struct_tail_with_normalize(self, normalize);
+ let tail = tcx.struct_tail_with_normalize(self, normalize, || {});
match tail.kind() {
// Sized types
ty::Infer(ty::IntVar(_) | ty::FloatVar(_))
pub fn res_generics_def_id(self, res: Res) -> Option<DefId> {
match res {
Res::Def(DefKind::Ctor(CtorOf::Variant, _), def_id) => {
- Some(self.parent(def_id).and_then(|def_id| self.parent(def_id)).unwrap())
+ Some(self.parent(self.parent(def_id)))
}
Res::Def(DefKind::Variant | DefKind::Ctor(CtorOf::Struct, _), def_id) => {
- Some(self.parent(def_id).unwrap())
+ Some(self.parent(def_id))
}
// Other `DefKind`s don't have generics and would ICE when calling
// `generics_of`.
/// if input `ty` is not a structure at all.
pub fn struct_tail_without_normalization(self, ty: Ty<'tcx>) -> Ty<'tcx> {
let tcx = self;
- tcx.struct_tail_with_normalize(ty, |ty| ty)
+ tcx.struct_tail_with_normalize(ty, |ty| ty, || {})
}
/// Returns the deeply last field of nested structures, or the same type if
param_env: ty::ParamEnv<'tcx>,
) -> Ty<'tcx> {
let tcx = self;
- tcx.struct_tail_with_normalize(ty, |ty| tcx.normalize_erasing_regions(param_env, ty))
+ tcx.struct_tail_with_normalize(ty, |ty| tcx.normalize_erasing_regions(param_env, ty), || {})
}
/// Returns the deeply last field of nested structures, or the same type if
self,
mut ty: Ty<'tcx>,
mut normalize: impl FnMut(Ty<'tcx>) -> Ty<'tcx>,
+ // This is currently used to allow us to walk a ValTree
+ // in lockstep with the type in order to get the ValTree branch that
+ // corresponds to an unsized field.
+ mut f: impl FnMut() -> (),
) -> Ty<'tcx> {
let recursion_limit = self.recursion_limit();
for iteration in 0.. {
break;
}
match def.non_enum_variant().fields.last() {
- Some(f) => ty = f.ty(self, substs),
+ Some(field) => {
+ f();
+ ty = field.ty(self, substs);
+ }
None => break,
}
}
ty::Tuple(tys) if let Some((&last_ty, _)) = tys.split_last() => {
+ f();
ty = last_ty;
}
pub fn typeck_root_def_id(self, def_id: DefId) -> DefId {
let mut def_id = def_id;
while self.is_typeck_child(def_id) {
- def_id = self.parent(def_id).unwrap_or_else(|| {
- bug!("closure {:?} has no parent", def_id);
- });
+ def_id = self.parent(def_id);
}
def_id
}
use rustc_span::symbol::Symbol;
use rustc_span::Span;
+use std::borrow::Cow;
use std::ops::Bound;
struct UnsafetyVisitor<'a, 'tcx> {
}
fn requires_unsafe(&mut self, span: Span, kind: UnsafeOpKind) {
- let (description, note) = kind.description_and_note();
let unsafe_op_in_unsafe_fn_allowed = self.unsafe_op_in_unsafe_fn_allowed();
match self.safety_context {
SafetyContext::BuiltinUnsafeBlock => {}
}
SafetyContext::UnsafeFn if unsafe_op_in_unsafe_fn_allowed => {}
SafetyContext::UnsafeFn => {
+ let (description, note) = kind.description_and_note(self.tcx);
// unsafe_op_in_unsafe_fn is disallowed
self.tcx.struct_span_lint_hir(
UNSAFE_OP_IN_UNSAFE_FN,
"{} is unsafe and requires unsafe block (error E0133)",
description,
))
- .span_label(span, description)
+ .span_label(span, kind.simple_description())
.note(note)
.emit();
},
)
}
SafetyContext::Safe => {
+ let (description, note) = kind.description_and_note(self.tcx);
let fn_sugg = if unsafe_op_in_unsafe_fn_allowed { " function or" } else { "" };
struct_span_err!(
self.tcx.sess,
description,
fn_sugg,
)
- .span_label(span, description)
+ .span_label(span, kind.simple_description())
.note(note)
.emit();
}
}
ExprKind::Call { fun, ty: _, args: _, from_hir_call: _, fn_span: _ } => {
if self.thir[fun].ty.fn_sig(self.tcx).unsafety() == hir::Unsafety::Unsafe {
- self.requires_unsafe(expr.span, CallToUnsafeFunction);
+ let func_id = if let ty::FnDef(func_id, _) = self.thir[fun].ty.kind() {
+ Some(*func_id)
+ } else {
+ None
+ };
+ self.requires_unsafe(expr.span, CallToUnsafeFunction(func_id));
} else if let &ty::FnDef(func_did, _) = self.thir[fun].ty.kind() {
// If the called function has target features the calling function hasn't,
// the call requires `unsafe`. Don't check this on wasm
.iter()
.all(|feature| self.body_target_features.contains(feature))
{
- self.requires_unsafe(expr.span, CallToFunctionWith);
+ self.requires_unsafe(expr.span, CallToFunctionWith(func_did));
}
}
}
#[derive(Clone, Copy, PartialEq)]
enum UnsafeOpKind {
- CallToUnsafeFunction,
+ CallToUnsafeFunction(Option<DefId>),
UseOfInlineAssembly,
InitializingTypeWith,
UseOfMutableStatic,
AccessToUnionField,
MutationOfLayoutConstrainedField,
BorrowOfLayoutConstrainedField,
- CallToFunctionWith,
+ CallToFunctionWith(DefId),
}
use UnsafeOpKind::*;
impl UnsafeOpKind {
- pub fn description_and_note(&self) -> (&'static str, &'static str) {
+ pub fn simple_description(&self) -> &'static str {
match self {
- CallToUnsafeFunction => (
- "call to unsafe function",
+ CallToUnsafeFunction(..) => "call to unsafe function",
+ UseOfInlineAssembly => "use of inline assembly",
+ InitializingTypeWith => "initializing type with `rustc_layout_scalar_valid_range` attr",
+ UseOfMutableStatic => "use of mutable static",
+ UseOfExternStatic => "use of extern static",
+ DerefOfRawPointer => "dereference of raw pointer",
+ AssignToDroppingUnionField => "assignment to union field that might need dropping",
+ AccessToUnionField => "access to union field",
+ MutationOfLayoutConstrainedField => "mutation of layout constrained field",
+ BorrowOfLayoutConstrainedField => {
+ "borrow of layout constrained field with interior mutability"
+ }
+ CallToFunctionWith(..) => "call to function with `#[target_feature]`",
+ }
+ }
+
+ pub fn description_and_note(&self, tcx: TyCtxt<'_>) -> (Cow<'static, str>, &'static str) {
+ match self {
+ CallToUnsafeFunction(did) => (
+ if let Some(did) = did {
+ Cow::from(format!("call to unsafe function `{}`", tcx.def_path_str(*did)))
+ } else {
+ Cow::Borrowed(self.simple_description())
+ },
"consult the function's documentation for information on how to avoid undefined \
behavior",
),
UseOfInlineAssembly => (
- "use of inline assembly",
+ Cow::Borrowed(self.simple_description()),
"inline assembly is entirely unchecked and can cause undefined behavior",
),
InitializingTypeWith => (
- "initializing type with `rustc_layout_scalar_valid_range` attr",
+ Cow::Borrowed(self.simple_description()),
"initializing a layout restricted type's field with a value outside the valid \
range is undefined behavior",
),
UseOfMutableStatic => (
- "use of mutable static",
+ Cow::Borrowed(self.simple_description()),
"mutable statics can be mutated by multiple threads: aliasing violations or data \
races will cause undefined behavior",
),
UseOfExternStatic => (
- "use of extern static",
+ Cow::Borrowed(self.simple_description()),
"extern statics are not controlled by the Rust type system: invalid data, \
aliasing violations or data races will cause undefined behavior",
),
DerefOfRawPointer => (
- "dereference of raw pointer",
+ Cow::Borrowed(self.simple_description()),
"raw pointers may be null, dangling or unaligned; they can violate aliasing rules \
and cause data races: all of these are undefined behavior",
),
AssignToDroppingUnionField => (
- "assignment to union field that might need dropping",
+ Cow::Borrowed(self.simple_description()),
"the previous content of the field will be dropped, which causes undefined \
behavior if the field was not properly initialized",
),
AccessToUnionField => (
- "access to union field",
+ Cow::Borrowed(self.simple_description()),
"the field may not be properly initialized: using uninitialized data will cause \
undefined behavior",
),
MutationOfLayoutConstrainedField => (
- "mutation of layout constrained field",
+ Cow::Borrowed(self.simple_description()),
"mutating layout constrained fields cannot statically be checked for valid values",
),
BorrowOfLayoutConstrainedField => (
- "borrow of layout constrained field with interior mutability",
+ Cow::Borrowed(self.simple_description()),
"references to fields of layout constrained fields lose the constraints. Coupled \
with interior mutability, the field can be changed to invalid values",
),
- CallToFunctionWith => (
- "call to function with `#[target_feature]`",
+ CallToFunctionWith(did) => (
+ Cow::from(format!(
+ "call to function `{}` with `#[target_feature]`",
+ tcx.def_path_str(*did)
+ )),
"can only be called if the required target features are available",
),
}
) -> PatKind<'tcx> {
let res = match res {
Res::Def(DefKind::Ctor(CtorOf::Variant, ..), variant_ctor_id) => {
- let variant_id = self.tcx.parent(variant_ctor_id).unwrap();
+ let variant_id = self.tcx.parent(variant_ctor_id);
Res::Def(DefKind::Variant, variant_id)
}
res => res,
let mut kind = match res {
Res::Def(DefKind::Variant, variant_id) => {
- let enum_id = self.tcx.parent(variant_id).unwrap();
+ let enum_id = self.tcx.parent(variant_id);
let adt_def = self.tcx.adt_def(enum_id);
if adt_def.is_enum() {
let substs = match ty.kind() {
};
}
- let mut first = true;
- for idx in set_in_self.iter() {
- let delim = if first {
- "\u{001f}+"
- } else if f.alternate() {
- "\n\u{001f}+"
- } else {
- ", "
- };
+ fmt_diff(&set_in_self, &cleared_in_self, ctxt, f)
+ }
+}
- write!(f, "{}", delim)?;
- idx.fmt_with(ctxt, f)?;
- first = false;
- }
+impl<T, C> DebugWithContext<C> for ChunkedBitSet<T>
+where
+ T: Idx + DebugWithContext<C>,
+{
+ fn fmt_with(&self, ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.debug_set().entries(self.iter().map(|i| DebugWithAdapter { this: i, ctxt })).finish()
+ }
- if !f.alternate() {
- first = true;
- if !set_in_self.is_empty() && !cleared_in_self.is_empty() {
- write!(f, "\t")?;
- }
- }
+ fn fmt_diff_with(&self, old: &Self, ctxt: &C, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ let size = self.domain_size();
+ assert_eq!(size, old.domain_size());
- for idx in cleared_in_self.iter() {
- let delim = if first {
- "\u{001f}-"
- } else if f.alternate() {
- "\n\u{001f}-"
- } else {
- ", "
- };
+ let mut set_in_self = HybridBitSet::new_empty(size);
+ let mut cleared_in_self = HybridBitSet::new_empty(size);
- write!(f, "{}", delim)?;
- idx.fmt_with(ctxt, f)?;
- first = false;
+ for i in (0..size).map(T::new) {
+ match (self.contains(i), old.contains(i)) {
+ (true, false) => set_in_self.insert(i),
+ (false, true) => cleared_in_self.insert(i),
+ _ => continue,
+ };
}
- Ok(())
+ fmt_diff(&set_in_self, &cleared_in_self, ctxt, f)
}
}
-impl<T, C> DebugWithContext<C> for ChunkedBitSet<T>
+fn fmt_diff<T, C>(
+ inserted: &HybridBitSet<T>,
+ removed: &HybridBitSet<T>,
+ ctxt: &C,
+ f: &mut fmt::Formatter<'_>,
+) -> fmt::Result
where
T: Idx + DebugWithContext<C>,
{
- fn fmt_with(&self, _ctxt: &C, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
- unimplemented!("implement when/if needed");
+ let mut first = true;
+ for idx in inserted.iter() {
+ let delim = if first {
+ "\u{001f}+"
+ } else if f.alternate() {
+ "\n\u{001f}+"
+ } else {
+ ", "
+ };
+
+ write!(f, "{}", delim)?;
+ idx.fmt_with(ctxt, f)?;
+ first = false;
+ }
+
+ if !f.alternate() {
+ first = true;
+ if !inserted.is_empty() && !removed.is_empty() {
+ write!(f, "\t")?;
+ }
}
- fn fmt_diff_with(&self, _old: &Self, _ctxt: &C, _f: &mut fmt::Formatter<'_>) -> fmt::Result {
- unimplemented!("implement when/if needed");
+ for idx in removed.iter() {
+ let delim = if first {
+ "\u{001f}-"
+ } else if f.alternate() {
+ "\n\u{001f}-"
+ } else {
+ ", "
+ };
+
+ write!(f, "{}", delim)?;
+ idx.fmt_with(ctxt, f)?;
+ first = false;
}
+
+ Ok(())
}
impl<T, C> DebugWithContext<C> for &'_ T
}
}
+/// Determines whether or not this LocalDecl is temp, if not it needs retagging.
+fn is_not_temp<'tcx>(local_decl: &LocalDecl<'tcx>) -> bool {
+ if let Some(local_info) = &local_decl.local_info {
+ match local_info.as_ref() {
+ LocalInfo::DerefTemp => return false,
+ _ => (),
+ };
+ }
+ return true;
+}
+
impl<'tcx> MirPass<'tcx> for AddRetag {
fn is_enabled(&self, sess: &rustc_session::Session) -> bool {
sess.opts.debugging_opts.mir_emit_retag
let needs_retag = |place: &Place<'tcx>| {
// FIXME: Instead of giving up for unstable places, we should introduce
// a temporary and retag on that.
- is_stable(place.as_ref()) && may_be_reference(place.ty(&*local_decls, tcx).ty)
+ is_stable(place.as_ref())
+ && may_be_reference(place.ty(&*local_decls, tcx).ty)
+ && is_not_temp(&local_decls[place.local])
};
let place_base_raw = |place: &Place<'tcx>| {
// If this is a `Deref`, get the type of what we are deref'ing.
TerminatorKind::Call { ref func, .. } => {
let func_ty = func.ty(self.body, self.tcx);
+ let func_id =
+ if let ty::FnDef(func_id, _) = func_ty.kind() { Some(func_id) } else { None };
let sig = func_ty.fn_sig(self.tcx);
if let hir::Unsafety::Unsafe = sig.unsafety() {
self.require_unsafe(
)
}
- if let ty::FnDef(func_id, _) = func_ty.kind() {
+ if let Some(func_id) = func_id {
self.check_target_features(*func_id);
}
}
ecx: InterpCx<'mir, 'tcx, ConstPropMachine<'mir, 'tcx>>,
tcx: TyCtxt<'tcx>,
param_env: ParamEnv<'tcx>,
- // FIXME(eddyb) avoid cloning this field more than once,
- // by accessing it through `ecx` instead.
- local_decls: IndexVec<Local, LocalDecl<'tcx>>,
+ local_decls: &'mir IndexVec<Local, LocalDecl<'tcx>>,
// Because we have `MutVisitor` we can't obtain the `SourceInfo` from a `Location`. So we store
// the last known `SourceInfo` here and just keep revisiting it.
source_info: Option<SourceInfo>,
let substs = &InternalSubsts::identity_for_item(tcx, def_id);
let param_env = tcx.param_env_reveal_all_normalized(def_id);
- let span = tcx.def_span(def_id);
- // FIXME: `CanConstProp::check` computes the layout of all locals, return those layouts
- // so we can write them to `ecx.frame_mut().locals.layout, reducing the duplication in
- // `layout_of` query invocations.
let can_const_prop = CanConstProp::check(tcx, param_env, body);
let mut only_propagate_inside_block_locals = BitSet::new_empty(can_const_prop.len());
for (l, mode) in can_const_prop.iter_enumerated() {
}
let mut ecx = InterpCx::new(
tcx,
- span,
+ tcx.def_span(def_id),
param_env,
ConstPropMachine::new(only_propagate_inside_block_locals, can_const_prop),
);
ecx,
tcx,
param_env,
- // FIXME(eddyb) avoid cloning this field more than once,
- // by accessing it through `ecx` instead.
- //FIXME(wesleywiser) we can't steal this because `Visitor::super_visit_body()` needs it
- local_decls: body.local_decls.clone(),
+ local_decls: &dummy_body.local_decls,
source_info: None,
}
}
let r = r?;
// We need the type of the LHS. We cannot use `place_layout` as that is the type
// of the result, which for checked binops is not the same!
- let left_ty = left.ty(&self.local_decls, self.tcx);
+ let left_ty = left.ty(self.local_decls, self.tcx);
let left_size = self.ecx.layout_of(left_ty).ok()?.size;
let right_size = r.layout.size;
let r_bits = r.to_scalar().ok();
ecx: InterpCx<'mir, 'tcx, ConstPropMachine<'mir, 'tcx>>,
tcx: TyCtxt<'tcx>,
param_env: ParamEnv<'tcx>,
- // FIXME(eddyb) avoid cloning these two fields more than once,
- // by accessing them through `ecx` instead.
- source_scopes: IndexVec<SourceScope, SourceScopeData<'tcx>>,
- local_decls: IndexVec<Local, LocalDecl<'tcx>>,
+ source_scopes: &'mir IndexVec<SourceScope, SourceScopeData<'tcx>>,
+ local_decls: &'mir IndexVec<Local, LocalDecl<'tcx>>,
// Because we have `MutVisitor` we can't obtain the `SourceInfo` from a `Location`. So we store
// the last known `SourceInfo` here and just keep revisiting it.
source_info: Option<SourceInfo>,
let substs = &InternalSubsts::identity_for_item(tcx, def_id);
let param_env = tcx.param_env_reveal_all_normalized(def_id);
- let span = tcx.def_span(def_id);
- // FIXME: `CanConstProp::check` computes the layout of all locals, return those layouts
- // so we can write them to `ecx.frame_mut().locals.layout, reducing the duplication in
- // `layout_of` query invocations.
let can_const_prop = CanConstProp::check(tcx, param_env, body);
let mut only_propagate_inside_block_locals = BitSet::new_empty(can_const_prop.len());
for (l, mode) in can_const_prop.iter_enumerated() {
}
let mut ecx = InterpCx::new(
tcx,
- span,
+ tcx.def_span(def_id),
param_env,
ConstPropMachine::new(only_propagate_inside_block_locals, can_const_prop),
);
ecx,
tcx,
param_env,
- // FIXME(eddyb) avoid cloning these two fields more than once,
- // by accessing them through `ecx` instead.
- source_scopes: body.source_scopes.clone(),
- //FIXME(wesleywiser) we can't steal this because `Visitor::super_visit_body()` needs it
- local_decls: body.local_decls.clone(),
+ source_scopes: &dummy_body.source_scopes,
+ local_decls: &dummy_body.local_decls,
source_info: None,
}
}
}
fn lint_root(&self, source_info: SourceInfo) -> Option<HirId> {
- source_info.scope.lint_root(&self.source_scopes)
+ source_info.scope.lint_root(self.source_scopes)
}
fn use_ecx<F, T>(&mut self, f: F) -> Option<T>
let r = r?;
// We need the type of the LHS. We cannot use `place_layout` as that is the type
// of the result, which for checked binops is not the same!
- let left_ty = left.ty(&self.local_decls, self.tcx);
+ let left_ty = left.ty(self.local_decls, self.tcx);
let left_size = self.ecx.layout_of(left_ty).ok()?.size;
let right_size = r.layout.size;
let r_bits = r.to_scalar().ok();
use crate::MirPass;
+use rustc_index::vec::IndexVec;
use rustc_middle::mir::patch::MirPatch;
+use rustc_middle::mir::visit::{MutVisitor, PlaceContext};
use rustc_middle::mir::*;
use rustc_middle::ty::TyCtxt;
pub struct Derefer;
-pub fn deref_finder<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
- let mut patch = MirPatch::new(body);
- let (basic_blocks, local_decl) = body.basic_blocks_and_local_decls_mut();
- for (block, data) in basic_blocks.iter_enumerated_mut() {
- for (i, stmt) in data.statements.iter_mut().enumerate() {
- match stmt.kind {
- StatementKind::Assign(box (og_place, Rvalue::Ref(region, borrow_knd, place))) => {
- let mut place_local = place.local;
- let mut last_len = 0;
- for (idx, (p_ref, p_elem)) in place.iter_projections().enumerate() {
- if p_elem == ProjectionElem::Deref && !p_ref.projection.is_empty() {
- // The type that we are derefing.
- let ty = p_ref.ty(local_decl, tcx).ty;
- let temp = patch.new_temp(ty, stmt.source_info.span);
-
- // Because we are assigning this right before original statement
- // we are using index i of statement.
- let loc = Location { block: block, statement_index: i };
- patch.add_statement(loc, StatementKind::StorageLive(temp));
-
- // We are adding current p_ref's projections to our
- // temp value, excluding projections we already covered.
- let deref_place = Place::from(place_local)
- .project_deeper(&p_ref.projection[last_len..], tcx);
- patch.add_assign(
- loc,
- Place::from(temp),
- Rvalue::Use(Operand::Move(deref_place)),
- );
-
- place_local = temp;
- last_len = p_ref.projection.len();
-
- // We are creating a place by using our temp value's location
- // and copying derefed values which we need to create new statement.
- let temp_place =
- Place::from(temp).project_deeper(&place.projection[idx..], tcx);
- let new_stmt = Statement {
- source_info: stmt.source_info,
- kind: StatementKind::Assign(Box::new((
- og_place,
- Rvalue::Ref(region, borrow_knd, temp_place),
- ))),
- };
-
- // Replace current statement with newly created one.
- *stmt = new_stmt;
-
- // Since our job with the temp is done it should be gone
- let loc = Location { block: block, statement_index: i + 1 };
- patch.add_statement(loc, StatementKind::StorageDead(temp));
- }
- }
+pub struct DerefChecker<'tcx> {
+ tcx: TyCtxt<'tcx>,
+ patcher: MirPatch<'tcx>,
+ local_decls: IndexVec<Local, LocalDecl<'tcx>>,
+}
+
+impl<'tcx> MutVisitor<'tcx> for DerefChecker<'tcx> {
+ fn tcx(&self) -> TyCtxt<'tcx> {
+ self.tcx
+ }
+
+ fn visit_place(&mut self, place: &mut Place<'tcx>, _: PlaceContext, loc: Location) {
+ let mut place_local = place.local;
+ let mut last_len = 0;
+ let mut last_deref_idx = 0;
+
+ let mut prev_temp: Option<Local> = None;
+
+ for (idx, (p_ref, p_elem)) in place.iter_projections().enumerate() {
+ if p_elem == ProjectionElem::Deref && !p_ref.projection.is_empty() {
+ last_deref_idx = idx;
+ }
+ }
+
+ for (idx, (p_ref, p_elem)) in place.iter_projections().enumerate() {
+ if p_elem == ProjectionElem::Deref && !p_ref.projection.is_empty() {
+ let ty = p_ref.ty(&self.local_decls, self.tcx).ty;
+ let temp = self.patcher.new_local_with_info(
+ ty,
+ self.local_decls[p_ref.local].source_info.span,
+ Some(Box::new(LocalInfo::DerefTemp)),
+ );
+
+ self.patcher.add_statement(loc, StatementKind::StorageLive(temp));
+
+ // We are adding current p_ref's projections to our
+ // temp value, excluding projections we already covered.
+ let deref_place = Place::from(place_local)
+ .project_deeper(&p_ref.projection[last_len..], self.tcx);
+
+ self.patcher.add_assign(
+ loc,
+ Place::from(temp),
+ Rvalue::Use(Operand::Move(deref_place)),
+ );
+ place_local = temp;
+ last_len = p_ref.projection.len();
+
+ // Change `Place` only if we are actually at the Place's last deref
+ if idx == last_deref_idx {
+ let temp_place =
+ Place::from(temp).project_deeper(&place.projection[idx..], self.tcx);
+ *place = temp_place;
+ }
+
+ // We are destroying the previous temp since it's no longer used.
+ if let Some(prev_temp) = prev_temp {
+ self.patcher.add_statement(loc, StatementKind::StorageDead(prev_temp));
}
- _ => (),
+
+ prev_temp = Some(temp);
}
}
+
+ // Since we won't be able to reach final temp, we destroy it outside the loop.
+ if let Some(prev_temp) = prev_temp {
+ let last_loc = Location { block: loc.block, statement_index: loc.statement_index + 1 };
+ self.patcher.add_statement(last_loc, StatementKind::StorageDead(prev_temp));
+ }
}
- patch.apply(body);
+}
+
+pub fn deref_finder<'tcx>(tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
+ let patch = MirPatch::new(body);
+ let mut checker = DerefChecker { tcx, patcher: patch, local_decls: body.local_decls.clone() };
+
+ for (bb, data) in body.basic_blocks_mut().iter_enumerated_mut() {
+ checker.visit_basic_block_data(bb, data);
+ }
+
+ checker.patcher.apply(body);
}
impl<'tcx> MirPass<'tcx> for Derefer {
Some(poss)
}
};
- let Some((_, child)) = targets.iter().next() else {
- return None
- };
+ let (_, child) = targets.iter().next()?;
let child_terminator = &bbs[child].terminator();
let TerminatorKind::SwitchInt {
switch_ty: child_ty,
&add_moves_for_packed_drops::AddMovesForPackedDrops,
// `AddRetag` needs to run after `ElaborateDrops`. Otherwise it should run fairly late,
// but before optimizations begin.
+ &deref_separator::Derefer,
&add_retag::AddRetag,
&lower_intrinsics::LowerIntrinsics,
&simplify::SimplifyCfg::new("elaborate-drops"),
// `Deaggregator` is conceptually part of MIR building, some backends rely on it happening
// and it can help optimizations.
&deaggregator::Deaggregator,
- &deref_separator::Derefer,
&Lint(const_prop_lint::ConstProp),
];
let mut cnt = 0;
let validate = tcx.sess.opts.debugging_opts.validate_mir;
+ let overridden_passes = &tcx.sess.opts.debugging_opts.mir_enable_passes;
+ trace!(?overridden_passes);
if validate {
validate_body(tcx, body, format!("start of phase transition from {:?}", start_phase));
}
for pass in passes {
- if !pass.is_enabled(&tcx.sess) {
- continue;
- }
-
let name = pass.name();
+
+ if let Some((_, polarity)) = overridden_passes.iter().rev().find(|(s, _)| s == &*name) {
+ trace!(
+ pass = %name,
+ "{} as requested by flag",
+ if *polarity { "Running" } else { "Not running" },
+ );
+ if !polarity {
+ continue;
+ }
+ } else {
+ if !pass.is_enabled(&tcx.sess) {
+ continue;
+ }
+ }
let dump_enabled = pass.is_mir_dump_enabled();
if dump_enabled {
use rustc_data_structures::sync::{par_iter, MTLock, MTRef, ParallelIterator};
use rustc_hir as hir;
use rustc_hir::def::DefKind;
-use rustc_hir::def_id::{DefId, DefIdMap, LocalDefId, LOCAL_CRATE};
+use rustc_hir::def_id::{DefId, DefIdMap, LocalDefId};
use rustc_hir::lang_items::LangItem;
use rustc_index::bit_set::GrowableBitSet;
use rustc_middle::mir::interpret::{AllocId, ConstValue};
// error count. If it has changed, a PME occurred, and we trigger some diagnostics about the
// current step of mono items collection.
//
+ // FIXME: don't rely on global state, instead bubble up errors. Note: this is very hard to do.
let error_count = tcx.sess.diagnostic().err_count();
match starting_point.node {
}
// Check for PMEs and emit a diagnostic if one happened. To try to show relevant edges of the
- // mono item graph where the PME diagnostics are currently the most problematic (e.g. ones
- // involving a dependency, and the lack of context is confusing) in this MVP, we focus on
- // diagnostics on edges crossing a crate boundary: the collected mono items which are not
- // defined in the local crate.
+ // mono item graph.
if tcx.sess.diagnostic().err_count() > error_count
- && starting_point.node.krate() != LOCAL_CRATE
+ && starting_point.node.is_generic_fn()
&& starting_point.node.is_user_defined()
{
let formatted_item = with_no_trimmed_paths!(starting_point.node.to_string());
use rustc_hir::def_id::{DefId, LOCAL_CRATE};
use rustc_hir::definitions::DefPathDataName;
use rustc_middle::middle::codegen_fn_attrs::CodegenFnAttrFlags;
-use rustc_middle::middle::exported_symbols::SymbolExportLevel;
+use rustc_middle::middle::exported_symbols::{SymbolExportInfo, SymbolExportLevel};
use rustc_middle::mir::mono::{CodegenUnit, CodegenUnitNameBuilder, Linkage, Visibility};
use rustc_middle::mir::mono::{InstantiationMode, MonoItem};
use rustc_middle::ty::print::characteristic_def_id_of_type;
cgu_def_id = None;
}
- current_def_id = tcx.parent(current_def_id).unwrap();
+ current_def_id = tcx.parent(current_def_id);
}
let cgu_def_id = cgu_def_id.unwrap();
// C-export level items remain at `Default`, all other internal
// items become `Hidden`.
match tcx.reachable_non_generics(id.krate).get(&id) {
- Some(SymbolExportLevel::C) => Visibility::Default,
+ Some(SymbolExportInfo { level: SymbolExportLevel::C, .. }) => Visibility::Default,
_ => Visibility::Hidden,
}
}
rustc_data_structures = { path = "../rustc_data_structures" }
rustc_feature = { path = "../rustc_feature" }
rustc_lexer = { path = "../rustc_lexer" }
+rustc_macros = { path = "../rustc_macros" }
rustc_errors = { path = "../rustc_errors" }
rustc_session = { path = "../rustc_session" }
rustc_span = { path = "../rustc_span" }
use crate::lexer::unicode_chars::UNICODE_ARRAY;
use rustc_ast::ast::{self, AttrStyle};
-use rustc_ast::token::{self, CommentKind, Token, TokenKind};
+use rustc_ast::token::{self, CommentKind, Delimiter, Token, TokenKind};
use rustc_ast::tokenstream::{Spacing, TokenStream};
use rustc_ast::util::unicode::contains_text_flow_control_chars;
use rustc_errors::{error_code, Applicability, DiagnosticBuilder, ErrorGuaranteed, PResult};
#[derive(Clone, Debug)]
pub struct UnmatchedBrace {
- pub expected_delim: token::DelimToken,
- pub found_delim: Option<token::DelimToken>,
+ pub expected_delim: Delimiter,
+ pub found_delim: Option<Delimiter>,
pub found_span: Span,
pub unclosed_span: Option<Span>,
pub candidate_span: Option<Span>,
rustc_lexer::TokenKind::Semi => token::Semi,
rustc_lexer::TokenKind::Comma => token::Comma,
rustc_lexer::TokenKind::Dot => token::Dot,
- rustc_lexer::TokenKind::OpenParen => token::OpenDelim(token::Paren),
- rustc_lexer::TokenKind::CloseParen => token::CloseDelim(token::Paren),
- rustc_lexer::TokenKind::OpenBrace => token::OpenDelim(token::Brace),
- rustc_lexer::TokenKind::CloseBrace => token::CloseDelim(token::Brace),
- rustc_lexer::TokenKind::OpenBracket => token::OpenDelim(token::Bracket),
- rustc_lexer::TokenKind::CloseBracket => token::CloseDelim(token::Bracket),
+ rustc_lexer::TokenKind::OpenParen => token::OpenDelim(Delimiter::Parenthesis),
+ rustc_lexer::TokenKind::CloseParen => token::CloseDelim(Delimiter::Parenthesis),
+ rustc_lexer::TokenKind::OpenBrace => token::OpenDelim(Delimiter::Brace),
+ rustc_lexer::TokenKind::CloseBrace => token::CloseDelim(Delimiter::Brace),
+ rustc_lexer::TokenKind::OpenBracket => token::OpenDelim(Delimiter::Bracket),
+ rustc_lexer::TokenKind::CloseBracket => token::CloseDelim(Delimiter::Bracket),
rustc_lexer::TokenKind::At => token::At,
rustc_lexer::TokenKind::Pound => token::Pound,
rustc_lexer::TokenKind::Tilde => token::Tilde,
err.span_suggestion_verbose(
prefix_span,
"use `br` for a raw byte string",
- "br".to_string(),
+ "br",
Applicability::MaybeIncorrect,
);
} else if expn_data.is_root() {
err.span_suggestion_verbose(
prefix_span.shrink_to_hi(),
"consider inserting whitespace here",
- " ".into(),
+ " ",
Applicability::MaybeIncorrect,
);
}
use super::{StringReader, UnmatchedBrace};
-use rustc_ast::token::{self, DelimToken, Token};
+use rustc_ast::token::{self, Delimiter, Token};
use rustc_ast::tokenstream::{
DelimSpan,
Spacing::{self, *},
string_reader: StringReader<'a>,
token: Token,
/// Stack of open delimiters and their spans. Used for error message.
- open_braces: Vec<(token::DelimToken, Span)>,
+ open_braces: Vec<(Delimiter, Span)>,
unmatched_braces: Vec<UnmatchedBrace>,
/// The type and spans for all braces
///
/// Used only for error recovery when arriving to EOF with mismatched braces.
- matching_delim_spans: Vec<(token::DelimToken, Span, Span)>,
+ matching_delim_spans: Vec<(Delimiter, Span, Span)>,
last_unclosed_found_span: Option<Span>,
/// Collect empty block spans that might have been auto-inserted by editors.
- last_delim_empty_block_spans: FxHashMap<token::DelimToken, Span>,
+ last_delim_empty_block_spans: FxHashMap<Delimiter, Span>,
/// Collect the spans of braces (Open, Close). Used only
/// for detecting if blocks are empty and only braces.
matching_block_spans: Vec<(Span, Span)>,
for &(_, sp) in &self.open_braces {
err.span_label(sp, "unclosed delimiter");
self.unmatched_braces.push(UnmatchedBrace {
- expected_delim: token::DelimToken::Brace,
+ expected_delim: Delimiter::Brace,
found_delim: None,
found_span: self.token.span,
unclosed_span: Some(sp),
}
//only add braces
- if let (DelimToken::Brace, DelimToken::Brace) = (open_brace, delim) {
+ if let (Delimiter::Brace, Delimiter::Brace) = (open_brace, delim) {
self.matching_block_spans.push((open_brace_span, close_brace_span));
}
// https://www.unicode.org/Public/security/10.0.0/confusables.txt
use super::StringReader;
-use crate::token;
+use crate::token::{self, Delimiter};
use rustc_errors::{Applicability, Diagnostic};
use rustc_span::{symbol::kw, BytePos, Pos, Span};
('!', "Exclamation Mark", Some(token::Not)),
('?', "Question Mark", Some(token::Question)),
('.', "Period", Some(token::Dot)),
- ('(', "Left Parenthesis", Some(token::OpenDelim(token::Paren))),
- (')', "Right Parenthesis", Some(token::CloseDelim(token::Paren))),
- ('[', "Left Square Bracket", Some(token::OpenDelim(token::Bracket))),
- (']', "Right Square Bracket", Some(token::CloseDelim(token::Bracket))),
- ('{', "Left Curly Brace", Some(token::OpenDelim(token::Brace))),
- ('}', "Right Curly Brace", Some(token::CloseDelim(token::Brace))),
+ ('(', "Left Parenthesis", Some(token::OpenDelim(Delimiter::Parenthesis))),
+ (')', "Right Parenthesis", Some(token::CloseDelim(Delimiter::Parenthesis))),
+ ('[', "Left Square Bracket", Some(token::OpenDelim(Delimiter::Bracket))),
+ (']', "Right Square Bracket", Some(token::CloseDelim(Delimiter::Bracket))),
+ ('{', "Left Curly Brace", Some(token::OpenDelim(Delimiter::Brace))),
+ ('}', "Right Curly Brace", Some(token::CloseDelim(Delimiter::Brace))),
('*', "Asterisk", Some(token::BinOp(token::Star))),
('/', "Slash", Some(token::BinOp(token::Slash))),
('\\', "Backslash", None),
ch: char,
err: &mut Diagnostic,
) -> Option<token::TokenKind> {
- let Some(&(_u_char, u_name, ascii_char)) = UNICODE_ARRAY.iter().find(|&&(c, _, _)| c == ch) else {
- return None;
- };
+ let &(_u_char, u_name, ascii_char) = UNICODE_ARRAY.iter().find(|&&(c, _, _)| c == ch)?;
let span = Span::with_root_ctxt(pos, pos + Pos::from_usize(ch.len_utf8()));
use super::{AttrWrapper, Capturing, FnParseMode, ForceCollect, Parser, PathStyle};
use rustc_ast as ast;
use rustc_ast::attr;
-use rustc_ast::token::{self, Nonterminal};
+use rustc_ast::token::{self, Delimiter, Nonterminal};
use rustc_ast_pretty::pprust;
use rustc_errors::{error_code, Diagnostic, PResult};
use rustc_span::{sym, BytePos, Span};
ast::AttrStyle::Outer
};
- this.expect(&token::OpenDelim(token::Bracket))?;
+ this.expect(&token::OpenDelim(Delimiter::Bracket))?;
let item = this.parse_attr_item(false)?;
- this.expect(&token::CloseDelim(token::Bracket))?;
+ this.expect(&token::CloseDelim(Delimiter::Bracket))?;
let attr_sp = lo.to(this.prev_token.span);
// Emit error if inner attribute is encountered and forbidden.
crate fn parse_meta_item_kind(&mut self) -> PResult<'a, ast::MetaItemKind> {
Ok(if self.eat(&token::Eq) {
ast::MetaItemKind::NameValue(self.parse_unsuffixed_lit()?)
- } else if self.check(&token::OpenDelim(token::Paren)) {
+ } else if self.check(&token::OpenDelim(Delimiter::Parenthesis)) {
// Matches `meta_seq = ( COMMASEP(meta_item_inner) )`.
let (list, _) = self.parse_paren_comma_seq(|p| p.parse_meta_item_inner())?;
ast::MetaItemKind::List(list)
use super::{Capturing, FlatToken, ForceCollect, Parser, ReplaceRange, TokenCursor, TrailingToken};
-use rustc_ast::token::{self, DelimToken, Token, TokenKind};
+use rustc_ast::token::{self, Delimiter, Token, TokenKind};
use rustc_ast::tokenstream::{AttrAnnotatedTokenStream, AttributesData, CreateTokenStream};
use rustc_ast::tokenstream::{AttrAnnotatedTokenTree, DelimSpan, LazyTokenStream, Spacing};
use rustc_ast::{self as ast};
use rustc_ast::{AstLike, AttrVec, Attribute};
use rustc_errors::PResult;
-use rustc_span::{sym, Span, DUMMY_SP};
+use rustc_span::{sym, Span};
use std::convert::TryInto;
use std::ops::Range;
impl CreateTokenStream for LazyTokenStreamImpl {
fn create_token_stream(&self) -> AttrAnnotatedTokenStream {
- // The token produced by the final call to `{,inlined_}next` or
- // `{,inlined_}next_desugared` was not actually consumed by the
- // callback. The combination of chaining the initial token and using
- // `take` produces the desired result - we produce an empty
- // `TokenStream` if no calls were made, and omit the final token
- // otherwise.
+ // The token produced by the final call to `{,inlined_}next` was not
+ // actually consumed by the callback. The combination of chaining the
+ // initial token and using `take` produces the desired result - we
+ // produce an empty `TokenStream` if no calls were made, and omit the
+ // final token otherwise.
let mut cursor_snapshot = self.cursor_snapshot.clone();
let tokens =
std::iter::once((FlatToken::Token(self.start_token.0.clone()), self.start_token.1))
.chain((0..self.num_calls).map(|_| {
- let token = if cursor_snapshot.desugar_doc_comments {
- cursor_snapshot.next_desugared()
- } else {
- cursor_snapshot.next()
- };
+ let token = cursor_snapshot.next(cursor_snapshot.desugar_doc_comments);
(FlatToken::Token(token.0), token.1)
}))
.take(self.num_calls);
/// Converts a flattened iterator of tokens (including open and close delimiter tokens)
/// into a `TokenStream`, creating a `TokenTree::Delimited` for each matching pair
/// of open and close delims.
-// FIXME(#67062): Currently, we don't parse `None`-delimited groups correctly,
-// which can cause us to end up with mismatched `None` delimiters in our
+// FIXME(#67062): Currently, we don't parse `Invisible`-delimited groups correctly,
+// which can cause us to end up with mismatched `Invisible` delimiters in our
// captured tokens. This function contains several hacks to work around this -
-// essentially, we throw away mismatched `None` delimiters when we encounter them.
-// Once we properly parse `None` delimiters, they can be captured just like any
+// essentially, we throw away mismatched `Invisible` delimiters when we encounter them.
+// Once we properly parse `Invisible` delimiters, they can be captured just like any
// other tokens, and these hacks can be removed.
fn make_token_stream(
mut iter: impl Iterator<Item = (FlatToken, Spacing)>,
) -> AttrAnnotatedTokenStream {
#[derive(Debug)]
struct FrameData {
- open: Span,
- open_delim: DelimToken,
+ // This is `None` for the first frame, `Some` for all others.
+ open_delim_sp: Option<(Delimiter, Span)>,
inner: Vec<(AttrAnnotatedTokenTree, Spacing)>,
}
- let mut stack =
- vec![FrameData { open: DUMMY_SP, open_delim: DelimToken::NoDelim, inner: vec![] }];
+ let mut stack = vec![FrameData { open_delim_sp: None, inner: vec![] }];
let mut token_and_spacing = iter.next();
while let Some((token, spacing)) = token_and_spacing {
match token {
FlatToken::Token(Token { kind: TokenKind::OpenDelim(delim), span }) => {
- stack.push(FrameData { open: span, open_delim: delim, inner: vec![] });
+ stack.push(FrameData { open_delim_sp: Some((delim, span)), inner: vec![] });
}
FlatToken::Token(Token { kind: TokenKind::CloseDelim(delim), span }) => {
- // HACK: If we encounter a mismatched `None` delimiter at the top
+ // HACK: If we encounter a mismatched `Invisible` delimiter at the top
// level, just ignore it.
- if matches!(delim, DelimToken::NoDelim)
+ if matches!(delim, Delimiter::Invisible)
&& (stack.len() == 1
- || !matches!(stack.last_mut().unwrap().open_delim, DelimToken::NoDelim))
+ || !matches!(
+ stack.last_mut().unwrap().open_delim_sp.unwrap().0,
+ Delimiter::Invisible
+ ))
{
token_and_spacing = iter.next();
continue;
.pop()
.unwrap_or_else(|| panic!("Token stack was empty for token: {:?}", token));
- // HACK: If our current frame has a mismatched opening `None` delimiter,
+ // HACK: If our current frame has a mismatched opening `Invisible` delimiter,
// merge our current frame with the one above it. That is, transform
// `[ { < first second } third ]` into `[ { first second } third ]`
- if !matches!(delim, DelimToken::NoDelim)
- && matches!(frame_data.open_delim, DelimToken::NoDelim)
+ if !matches!(delim, Delimiter::Invisible)
+ && matches!(frame_data.open_delim_sp.unwrap().0, Delimiter::Invisible)
{
stack.last_mut().unwrap().inner.extend(frame_data.inner);
// Process our closing delimiter again, this time at the previous
continue;
}
+ let (open_delim, open_sp) = frame_data.open_delim_sp.unwrap();
assert_eq!(
- frame_data.open_delim, delim,
+ open_delim, delim,
"Mismatched open/close delims: open={:?} close={:?}",
- frame_data.open, span
+ open_delim, span
);
- let dspan = DelimSpan::from_pair(frame_data.open, span);
+ let dspan = DelimSpan::from_pair(open_sp, span);
let stream = AttrAnnotatedTokenStream::new(frame_data.inner);
let delimited = AttrAnnotatedTokenTree::Delimited(dspan, delim, stream);
stack
}
token_and_spacing = iter.next();
}
- // HACK: If we don't have a closing `None` delimiter for our last
+ // HACK: If we don't have a closing `Invisible` delimiter for our last
// frame, merge the frame with the top-level frame. That is,
// turn `< first second` into `first second`
- if stack.len() == 2 && stack[1].open_delim == DelimToken::NoDelim {
+ if stack.len() == 2 && stack[1].open_delim_sp.unwrap().0 == Delimiter::Invisible {
let temp_buf = stack.pop().unwrap();
stack.last_mut().unwrap().inner.extend(temp_buf.inner);
}
use crate::lexer::UnmatchedBrace;
use rustc_ast as ast;
use rustc_ast::ptr::P;
-use rustc_ast::token::{self, Lit, LitKind, TokenKind};
+use rustc_ast::token::{self, Delimiter, Lit, LitKind, TokenKind};
use rustc_ast::util::parser::AssocOp;
use rustc_ast::{
AngleBracketedArg, AngleBracketedArgs, AnonConst, AttrVec, BinOpKind, BindingMode, Block,
use rustc_errors::{
Applicability, DiagnosticBuilder, DiagnosticMessage, Handler, MultiSpan, PResult,
};
+use rustc_macros::SessionDiagnostic;
use rustc_span::source_map::Spanned;
use rustc_span::symbol::{kw, Ident};
use rustc_span::{Span, SpanSnippetError, DUMMY_SP};
err.multipart_suggestions(msg, suggestions.map(|s| s.patches), applicability);
}
}
+
+#[derive(SessionDiagnostic)]
+#[error(slug = "parser-maybe-report-ambiguous-plus")]
+struct AmbiguousPlus {
+ pub sum_ty: String,
+ #[primary_span]
+ #[suggestion(code = "({sum_ty})")]
+ pub span: Span,
+}
+
// SnapshotParser is used to create a snapshot of the parser
// without causing duplicate errors being emitted when the `Parser`
// is dropped.
TokenKind::Comma,
TokenKind::Semi,
TokenKind::ModSep,
- TokenKind::OpenDelim(token::DelimToken::Brace),
- TokenKind::OpenDelim(token::DelimToken::Paren),
- TokenKind::CloseDelim(token::DelimToken::Brace),
- TokenKind::CloseDelim(token::DelimToken::Paren),
+ TokenKind::OpenDelim(Delimiter::Brace),
+ TokenKind::OpenDelim(Delimiter::Parenthesis),
+ TokenKind::CloseDelim(Delimiter::Brace),
+ TokenKind::CloseDelim(Delimiter::Parenthesis),
];
match self.token.ident() {
Some((ident, false))
} else if !sm.is_multiline(self.prev_token.span.until(self.token.span)) {
// The current token is in the same line as the prior token, not recoverable.
} else if [token::Comma, token::Colon].contains(&self.token.kind)
- && self.prev_token.kind == token::CloseDelim(token::Paren)
+ && self.prev_token.kind == token::CloseDelim(Delimiter::Parenthesis)
{
// Likely typo: The current token is on a new line and is expected to be
// `.`, `;`, `?`, or an operator after a close delimiter token.
// ^
// https://github.com/rust-lang/rust/issues/72253
} else if self.look_ahead(1, |t| {
- t == &token::CloseDelim(token::Brace)
+ t == &token::CloseDelim(Delimiter::Brace)
|| t.can_begin_expr() && t.kind != token::Colon
}) && [token::Comma, token::Colon].contains(&self.token.kind)
{
.emit();
return Ok(true);
} else if self.look_ahead(0, |t| {
- t == &token::CloseDelim(token::Brace)
- || (
- t.can_begin_expr() && t != &token::Semi && t != &token::Pound
- // Avoid triggering with too many trailing `#` in raw string.
- )
+ t == &token::CloseDelim(Delimiter::Brace)
+ || (t.can_begin_expr() && t != &token::Semi && t != &token::Pound)
+ // Avoid triggering with too many trailing `#` in raw string.
+ || (sm.is_multiline(
+ self.prev_token.span.shrink_to_hi().until(self.token.span.shrink_to_lo())
+ ) && t == &token::Pound)
}) {
// Missing semicolon typo. This is triggered if the next token could either start a
// new statement or is a block close. For example:
}
if self.check_too_many_raw_str_terminators(&mut err) {
- return Err(err);
+ if expected.contains(&TokenType::Token(token::Semi)) && self.eat(&token::Semi) {
+ err.emit();
+ return Ok(true);
+ } else {
+ return Err(err);
+ }
}
if self.prev_token.span == DUMMY_SP {
}
fn check_too_many_raw_str_terminators(&mut self, err: &mut Diagnostic) -> bool {
+ let sm = self.sess.source_map();
match (&self.prev_token.kind, &self.token.kind) {
(
TokenKind::Literal(Lit {
..
}),
TokenKind::Pound,
- ) => {
+ ) if !sm.is_multiline(
+ self.prev_token.span.shrink_to_hi().until(self.token.span.shrink_to_lo()),
+ ) =>
+ {
+ let n_hashes: u8 = *n_hashes;
err.set_primary_message("too many `#` when terminating raw string");
+ let str_span = self.prev_token.span;
+ let mut span = self.token.span;
+ let mut count = 0;
+ while self.token.kind == TokenKind::Pound
+ && !sm.is_multiline(span.shrink_to_hi().until(self.token.span.shrink_to_lo()))
+ {
+ span = span.with_hi(self.token.span.hi());
+ self.bump();
+ count += 1;
+ }
+ err.set_span(span);
err.span_suggestion(
- self.token.span,
- "remove the extra `#`",
+ span,
+ &format!("remove the extra `#`{}", pluralize!(count)),
String::new(),
Applicability::MachineApplicable,
);
- err.note(&format!("the raw string started with {n_hashes} `#`s"));
+ err.span_label(
+ str_span,
+ &format!("this raw string started with {n_hashes} `#`{}", pluralize!(n_hashes)),
+ );
true
}
_ => false,
(Err(snapshot_err), Err(err)) => {
// We don't know what went wrong, emit the normal error.
snapshot_err.cancel();
- self.consume_block(token::Brace, ConsumeClosingDelim::Yes);
+ self.consume_block(Delimiter::Brace, ConsumeClosingDelim::Yes);
Err(err)
}
(Ok(_), Ok(mut tail)) => {
trailing_span = trailing_span.to(self.token.span);
self.bump();
}
- if self.token.kind == token::OpenDelim(token::Paren) {
+ if self.token.kind == token::OpenDelim(Delimiter::Parenthesis) {
// Recover from bad turbofish: `foo.collect::Vec<_>()`.
let args = AngleBracketedArgs { args, span }.into();
segment.args = args;
[(token::Lt, 1), (token::Gt, -1), (token::BinOp(token::Shr), -2)];
self.consume_tts(1, &modifiers);
- if !&[token::OpenDelim(token::Paren), token::ModSep]
+ if !&[token::OpenDelim(Delimiter::Parenthesis), token::ModSep]
.contains(&self.token.kind)
{
// We don't have `foo< bar >(` or `foo< bar >::`, so we rewind the
Err(err)
}
}
- } else if token::OpenDelim(token::Paren) == self.token.kind {
+ } else if token::OpenDelim(Delimiter::Parenthesis) == self.token.kind {
// We have high certainty that this was a bad turbofish at this point.
// `foo< bar >(`
suggest(&mut err);
self.bump(); // `(`
// Consume the fn call arguments.
- let modifiers =
- [(token::OpenDelim(token::Paren), 1), (token::CloseDelim(token::Paren), -1)];
+ let modifiers = [
+ (token::OpenDelim(Delimiter::Parenthesis), 1),
+ (token::CloseDelim(Delimiter::Parenthesis), -1),
+ ];
self.consume_tts(1, &modifiers);
if self.token.kind == token::Eof {
ty: &Ty,
) {
if matches!(allow_plus, AllowPlus::No) && impl_dyn_multi {
- let sum_with_parens = format!("({})", pprust::ty_to_string(&ty));
- self.struct_span_err(ty.span, "ambiguous `+` in a type")
- .span_suggestion(
- ty.span,
- "use parentheses to disambiguate",
- sum_with_parens,
- Applicability::MachineApplicable,
- )
- .emit();
+ self.sess.emit_err(AmbiguousPlus { sum_ty: pprust::ty_to_string(&ty), span: ty.span });
}
}
fn recover_await_macro(&mut self) -> PResult<'a, (Span, P<Expr>, bool)> {
self.expect(&token::Not)?;
- self.expect(&token::OpenDelim(token::Paren))?;
+ self.expect(&token::OpenDelim(Delimiter::Parenthesis))?;
let expr = self.parse_expr()?;
- self.expect(&token::CloseDelim(token::Paren))?;
+ self.expect(&token::CloseDelim(Delimiter::Parenthesis))?;
Ok((self.prev_token.span, expr, false))
}
fn recover_await_prefix(&mut self, await_sp: Span) -> PResult<'a, (Span, P<Expr>, bool)> {
let is_question = self.eat(&token::Question); // Handle `await? <expr>`.
- let expr = if self.token == token::OpenDelim(token::Brace) {
+ let expr = if self.token == token::OpenDelim(Delimiter::Brace) {
// Handle `await { <expr> }`.
// This needs to be handled separately from the next arm to avoid
// interpreting `await { <expr> }?` as `<expr>?.await`.
/// If encountering `future.await()`, consumes and emits an error.
pub(super) fn recover_from_await_method_call(&mut self) {
- if self.token == token::OpenDelim(token::Paren)
- && self.look_ahead(1, |t| t == &token::CloseDelim(token::Paren))
+ if self.token == token::OpenDelim(Delimiter::Parenthesis)
+ && self.look_ahead(1, |t| t == &token::CloseDelim(Delimiter::Parenthesis))
{
// future.await()
let lo = self.token.span;
pub(super) fn try_macro_suggestion(&mut self) -> PResult<'a, P<Expr>> {
let is_try = self.token.is_keyword(kw::Try);
let is_questionmark = self.look_ahead(1, |t| t == &token::Not); //check for !
- let is_open = self.look_ahead(2, |t| t == &token::OpenDelim(token::Paren)); //check for (
+ let is_open = self.look_ahead(2, |t| t == &token::OpenDelim(Delimiter::Parenthesis)); //check for (
if is_try && is_questionmark && is_open {
let lo = self.token.span;
self.bump(); //remove !
let try_span = lo.to(self.token.span); //we take the try!( span
self.bump(); //remove (
- let is_empty = self.token == token::CloseDelim(token::Paren); //check if the block is empty
- self.consume_block(token::Paren, ConsumeClosingDelim::No); //eat the block
+ let is_empty = self.token == token::CloseDelim(Delimiter::Parenthesis); //check if the block is empty
+ self.consume_block(Delimiter::Parenthesis, ConsumeClosingDelim::No); //eat the block
let hi = self.token.span;
self.bump(); //remove )
let mut err = self.struct_span_err(lo.to(hi), "use of deprecated `try` macro");
begin_paren: Option<Span>,
) -> P<Pat> {
match (&self.token.kind, begin_paren) {
- (token::CloseDelim(token::Paren), Some(begin_par_sp)) => {
+ (token::CloseDelim(Delimiter::Parenthesis), Some(begin_par_sp)) => {
self.bump();
self.struct_span_err(
|| self.token.is_ident() &&
matches!(node, ast::ExprKind::Path(..) | ast::ExprKind::Field(..)) &&
!self.token.is_reserved_ident() && // v `foo:bar(baz)`
- self.look_ahead(1, |t| t == &token::OpenDelim(token::Paren))
- || self.look_ahead(1, |t| t == &token::OpenDelim(token::Brace)) // `foo:bar {`
+ self.look_ahead(1, |t| t == &token::OpenDelim(Delimiter::Parenthesis))
+ || self.look_ahead(1, |t| t == &token::OpenDelim(Delimiter::Brace)) // `foo:bar {`
|| self.look_ahead(1, |t| t == &token::Colon) && // `foo:bar::<baz`
self.look_ahead(2, |t| t == &token::Lt) &&
self.look_ahead(3, |t| t.is_ident())
pub(super) fn recover_seq_parse_error(
&mut self,
- delim: token::DelimToken,
+ delim: Delimiter,
lo: Span,
result: PResult<'a, P<Expr>>,
) -> P<Expr> {
loop {
debug!("recover_stmt_ loop {:?}", self.token);
match self.token.kind {
- token::OpenDelim(token::DelimToken::Brace) => {
+ token::OpenDelim(Delimiter::Brace) => {
brace_depth += 1;
self.bump();
if break_on_block == BlockMode::Break && brace_depth == 1 && bracket_depth == 0
in_block = true;
}
}
- token::OpenDelim(token::DelimToken::Bracket) => {
+ token::OpenDelim(Delimiter::Bracket) => {
bracket_depth += 1;
self.bump();
}
- token::CloseDelim(token::DelimToken::Brace) => {
+ token::CloseDelim(Delimiter::Brace) => {
if brace_depth == 0 {
debug!("recover_stmt_ return - close delim {:?}", self.token);
break;
break;
}
}
- token::CloseDelim(token::DelimToken::Bracket) => {
+ token::CloseDelim(Delimiter::Bracket) => {
bracket_depth -= 1;
if bracket_depth < 0 {
bracket_depth = 0;
.emit();
self.bump();
} else if self.token == token::Pound
- && self.look_ahead(1, |t| *t == token::OpenDelim(token::Bracket))
+ && self.look_ahead(1, |t| *t == token::OpenDelim(Delimiter::Bracket))
{
let lo = self.token.span;
// Skip every token until next possible arg.
- while self.token != token::CloseDelim(token::Bracket) {
+ while self.token != token::CloseDelim(Delimiter::Bracket) {
self.bump();
}
let sp = lo.to(self.token.span);
// If we find a pattern followed by an identifier, it could be an (incorrect)
// C-style parameter declaration.
if self.check_ident()
- && self.look_ahead(1, |t| *t == token::Comma || *t == token::CloseDelim(token::Paren))
+ && self.look_ahead(1, |t| {
+ *t == token::Comma || *t == token::CloseDelim(Delimiter::Parenthesis)
+ })
{
// `fn foo(String s) {}`
let ident = self.parse_ident().unwrap();
} else if require_name
&& (self.token == token::Comma
|| self.token == token::Lt
- || self.token == token::CloseDelim(token::Paren))
+ || self.token == token::CloseDelim(Delimiter::Parenthesis))
{
let rfc_note = "anonymous parameters are removed in the 2018 edition (see RFC 1685)";
Ok(param)
}
- pub(super) fn consume_block(
- &mut self,
- delim: token::DelimToken,
- consume_close: ConsumeClosingDelim,
- ) {
+ pub(super) fn consume_block(&mut self, delim: Delimiter, consume_close: ConsumeClosingDelim) {
let mut brace_depth = 0;
loop {
if self.eat(&token::OpenDelim(delim)) {
brace_depth -= 1;
continue;
}
- } else if self.token == token::Eof || self.eat(&token::CloseDelim(token::NoDelim)) {
+ } else if self.token == token::Eof || self.eat(&token::CloseDelim(Delimiter::Invisible))
+ {
return;
} else {
self.bump();
crate fn maybe_recover_unexpected_block_label(&mut self) -> bool {
let Some(label) = self.eat_label().filter(|_| {
- self.eat(&token::Colon) && self.token.kind == token::OpenDelim(token::Brace)
+ self.eat(&token::Colon) && self.token.kind == token::OpenDelim(Delimiter::Brace)
}) else {
return false;
};
/// Parse and throw away a parenthesized comma separated
/// sequence of patterns until `)` is reached.
fn skip_pat_list(&mut self) -> PResult<'a, ()> {
- while !self.check(&token::CloseDelim(token::Paren)) {
+ while !self.check(&token::CloseDelim(Delimiter::Parenthesis)) {
self.parse_pat_no_top_alt(None)?;
if !self.eat(&token::Comma) {
return Ok(());
};
use crate::maybe_recover_from_interpolated_ty_qpath;
-use ast::token::DelimToken;
use rustc_ast::ptr::P;
-use rustc_ast::token::{self, Token, TokenKind};
+use rustc_ast::token::{self, Delimiter, Token, TokenKind};
use rustc_ast::tokenstream::Spacing;
use rustc_ast::util::classify;
use rustc_ast::util::literal::LitError;
fn is_at_start_of_range_notation_rhs(&self) -> bool {
if self.token.can_begin_expr() {
// Parse `for i in 1.. { }` as infinite loop, not as `for i in (1..{})`.
- if self.token == token::OpenDelim(token::Brace) {
+ if self.token == token::OpenDelim(Delimiter::Brace) {
return !self.restrictions.contains(Restrictions::NO_STRUCT_LITERAL);
}
true
return Ok(e);
}
e = match self.token.kind {
- token::OpenDelim(token::Paren) => self.parse_fn_call_expr(lo, e),
- token::OpenDelim(token::Bracket) => self.parse_index_expr(lo, e)?,
+ token::OpenDelim(Delimiter::Parenthesis) => self.parse_fn_call_expr(lo, e),
+ token::OpenDelim(Delimiter::Bracket) => self.parse_index_expr(lo, e)?,
_ => return Ok(e),
}
}
/// Parse a function call expression, `expr(...)`.
fn parse_fn_call_expr(&mut self, lo: Span, fun: P<Expr>) -> P<Expr> {
- let snapshot = if self.token.kind == token::OpenDelim(token::Paren)
+ let snapshot = if self.token.kind == token::OpenDelim(Delimiter::Parenthesis)
&& self.look_ahead_type_ascription_as_field()
{
Some((self.create_snapshot_for_diagnostic(), fun.kind.clone()))
{
return expr;
}
- self.recover_seq_parse_error(token::Paren, lo, seq)
+ self.recover_seq_parse_error(Delimiter::Parenthesis, lo, seq)
}
/// If we encounter a parser state that looks like the user has written a `struct` literal with
(Err(err), Some((mut snapshot, ExprKind::Path(None, path)))) => {
let name = pprust::path_to_string(&path);
snapshot.bump(); // `(`
- match snapshot.parse_struct_fields(path, false, token::Paren) {
- Ok((fields, ..)) if snapshot.eat(&token::CloseDelim(token::Paren)) => {
+ match snapshot.parse_struct_fields(path, false, Delimiter::Parenthesis) {
+ Ok((fields, ..))
+ if snapshot.eat(&token::CloseDelim(Delimiter::Parenthesis)) =>
+ {
// We are certain we have `Enum::Foo(a: 3, b: 4)`, suggest
// `Enum::Foo { a: 3, b: 4 }` or `Enum::Foo(3, 4)`.
self.restore_snapshot(snapshot);
fn parse_index_expr(&mut self, lo: Span, base: P<Expr>) -> PResult<'a, P<Expr>> {
self.bump(); // `[`
let index = self.parse_expr()?;
- self.expect(&token::CloseDelim(token::Bracket))?;
+ self.expect(&token::CloseDelim(Delimiter::Bracket))?;
Ok(self.mk_expr(lo.to(self.prev_token.span), self.mk_index(base, index), AttrVec::new()))
}
let fn_span_lo = self.token.span;
let mut segment = self.parse_path_segment(PathStyle::Expr, None)?;
- self.check_trailing_angle_brackets(&segment, &[&token::OpenDelim(token::Paren)]);
+ self.check_trailing_angle_brackets(&segment, &[&token::OpenDelim(Delimiter::Parenthesis)]);
self.check_turbofish_missing_angle_brackets(&mut segment);
- if self.check(&token::OpenDelim(token::Paren)) {
+ if self.check(&token::OpenDelim(Delimiter::Parenthesis)) {
// Method call `expr.f()`
let mut args = self.parse_paren_expr_seq()?;
args.insert(0, self_arg);
// could be removed without changing functionality, but it's faster
// to have it here, especially for programs with large constants.
self.parse_lit_expr(attrs)
- } else if self.check(&token::OpenDelim(token::Paren)) {
+ } else if self.check(&token::OpenDelim(Delimiter::Parenthesis)) {
self.parse_tuple_parens_expr(attrs)
- } else if self.check(&token::OpenDelim(token::Brace)) {
+ } else if self.check(&token::OpenDelim(Delimiter::Brace)) {
self.parse_block_expr(None, lo, BlockCheckMode::Default, attrs)
} else if self.check(&token::BinOp(token::Or)) || self.check(&token::OrOr) {
self.parse_closure_expr(attrs).map_err(|mut err| {
}
err
})
- } else if self.check(&token::OpenDelim(token::Bracket)) {
- self.parse_array_or_repeat_expr(attrs, token::Bracket)
+ } else if self.check(&token::OpenDelim(Delimiter::Bracket)) {
+ self.parse_array_or_repeat_expr(attrs, Delimiter::Bracket)
} else if self.check_path() {
self.parse_path_start_expr(attrs)
} else if self.check_keyword(kw::Move) || self.check_keyword(kw::Static) {
self.parse_break_expr(attrs)
} else if self.eat_keyword(kw::Yield) {
self.parse_yield_expr(attrs)
+ } else if self.is_do_yeet() {
+ self.parse_yeet_expr(attrs)
} else if self.eat_keyword(kw::Let) {
self.parse_let_expr(attrs)
} else if self.eat_keyword(kw::Underscore) {
fn parse_tuple_parens_expr(&mut self, attrs: AttrVec) -> PResult<'a, P<Expr>> {
let lo = self.token.span;
- self.expect(&token::OpenDelim(token::Paren))?;
+ self.expect(&token::OpenDelim(Delimiter::Parenthesis))?;
let (es, trailing_comma) = match self.parse_seq_to_end(
- &token::CloseDelim(token::Paren),
+ &token::CloseDelim(Delimiter::Parenthesis),
SeqSep::trailing_allowed(token::Comma),
|p| p.parse_expr_catch_underscore(),
) {
Ok(x) => x,
- Err(err) => return Ok(self.recover_seq_parse_error(token::Paren, lo, Err(err))),
+ Err(err) => {
+ return Ok(self.recover_seq_parse_error(Delimiter::Parenthesis, lo, Err(err)));
+ }
};
let kind = if es.len() == 1 && !trailing_comma {
// `(e)` is parenthesized `e`.
fn parse_array_or_repeat_expr(
&mut self,
attrs: AttrVec,
- close_delim: token::DelimToken,
+ close_delim: Delimiter,
) -> PResult<'a, P<Expr>> {
let lo = self.token.span;
self.bump(); // `[` or other open delim
prior_type_ascription: self.last_type_ascription,
};
(self.prev_token.span, ExprKind::MacCall(mac))
- } else if self.check(&token::OpenDelim(token::Brace)) {
+ } else if self.check(&token::OpenDelim(Delimiter::Brace)) {
if let Some(expr) = self.maybe_parse_struct_expr(qself.as_ref(), &path, &attrs) {
if qself.is_some() {
self.sess.gated_spans.gate(sym::more_qualified_paths, path.span);
self.parse_for_expr(label, lo, attrs)
} else if self.eat_keyword(kw::Loop) {
self.parse_loop_expr(label, lo, attrs)
- } else if self.check(&token::OpenDelim(token::Brace)) || self.token.is_whole_block() {
+ } else if self.check(&token::OpenDelim(Delimiter::Brace)) || self.token.is_whole_block() {
self.parse_block_expr(label, lo, BlockCheckMode::Default, attrs)
} else if !ate_colon && (self.check(&TokenKind::Comma) || self.check(&TokenKind::Gt)) {
// We're probably inside of a `Path<'a>` that needs a turbofish
self.maybe_recover_from_bad_qpath(expr, true)
}
+ /// Parse `"do" "yeet" expr?`.
+ fn parse_yeet_expr(&mut self, attrs: AttrVec) -> PResult<'a, P<Expr>> {
+ let lo = self.token.span;
+
+ self.bump(); // `do`
+ self.bump(); // `yeet`
+
+ let kind = ExprKind::Yeet(self.parse_expr_opt()?);
+
+ let span = lo.to(self.prev_token.span);
+ self.sess.gated_spans.gate(sym::yeet_expr, span);
+ let expr = self.mk_expr(span, kind, attrs);
+ self.maybe_recover_from_bad_qpath(expr, true)
+ }
+
/// Parse `"break" (('label (:? expr)?) | expr?)` with `"break"` token already eaten.
/// If the label is followed immediately by a `:` token, the label and `:` are
/// parsed as part of the expression (i.e. a labeled loop). The language team has
)
.emit();
Some(lexpr)
- } else if self.token != token::OpenDelim(token::Brace)
+ } else if self.token != token::OpenDelim(Delimiter::Brace)
|| !self.restrictions.contains(Restrictions::NO_STRUCT_LITERAL)
{
let expr = self.parse_expr_opt()?;
.span_suggestion(
token.span,
"must have an integer part",
- pprust::token_to_string(token).into(),
+ pprust::token_to_string(token),
Applicability::MachineApplicable,
)
.emit();
attrs: AttrVec,
) -> Option<P<Expr>> {
let mut snapshot = self.create_snapshot_for_diagnostic();
- match snapshot.parse_array_or_repeat_expr(attrs, token::Brace) {
+ match snapshot.parse_array_or_repeat_expr(attrs, Delimiter::Brace) {
Ok(arr) => {
let hi = snapshot.prev_token.span;
self.struct_span_err(arr.span, "this is a block expression, not an array")
self.sess.gated_spans.gate(sym::async_closure, span);
}
- if self.token.kind == TokenKind::Semi && self.token_cursor.frame.delim == DelimToken::Paren
+ if self.token.kind == TokenKind::Semi
+ && matches!(self.token_cursor.frame.delim_sp, Some((Delimiter::Parenthesis, _)))
{
// It is likely that the closure body is a block but where the
// braces have been removed. We will recover and eat the next
}
} else {
let attrs = self.parse_outer_attributes()?.take_for_recovery(); // For recovery.
- let not_block = self.token != token::OpenDelim(token::Brace);
+ let not_block = self.token != token::OpenDelim(Delimiter::Brace);
let block = self.parse_block().map_err(|err| {
if not_block {
self.error_missing_if_then_block(lo, Some(err), missing_then_block_binop_span())
// This is used below for recovery in case of `for ( $stuff ) $block`
// in which case we will suggest `for $stuff $block`.
let begin_paren = match self.token.kind {
- token::OpenDelim(token::Paren) => Some(self.token.span),
+ token::OpenDelim(Delimiter::Parenthesis) => Some(self.token.span),
_ => None,
};
.span_suggestion_short(
span,
msg,
- sugg.into(),
+ sugg,
// Has been misleading, at least in the past (closed Issue #48492).
Applicability::MaybeIncorrect,
)
let match_span = self.prev_token.span;
let lo = self.prev_token.span;
let scrutinee = self.parse_expr_res(Restrictions::NO_STRUCT_LITERAL, None)?;
- if let Err(mut e) = self.expect(&token::OpenDelim(token::Brace)) {
+ if let Err(mut e) = self.expect(&token::OpenDelim(Delimiter::Brace)) {
if self.token == token::Semi {
e.span_suggestion_short(
match_span,
attrs.extend(self.parse_inner_attributes()?);
let mut arms: Vec<Arm> = Vec::new();
- while self.token != token::CloseDelim(token::Brace) {
+ while self.token != token::CloseDelim(Delimiter::Brace) {
match self.parse_arm() {
Ok(arm) => arms.push(arm),
Err(mut e) => {
e.emit();
self.recover_stmt();
let span = lo.to(self.token.span);
- if self.token == token::CloseDelim(token::Brace) {
+ if self.token == token::CloseDelim(Delimiter::Brace) {
self.bump();
}
return Ok(self.mk_expr(span, ExprKind::Match(scrutinee, arms), attrs));
// We might have either a `,` -> `;` typo, or a block without braces. We need
// a more subtle parsing strategy.
loop {
- if self.token.kind == token::CloseDelim(token::Brace) {
+ if self.token.kind == token::CloseDelim(Delimiter::Brace) {
// We have reached the closing brace of the `match` expression.
return Some(err(self, stmts));
}
})?;
let require_comma = classify::expr_requires_semi_to_be_stmt(&expr)
- && this.token != token::CloseDelim(token::Brace);
+ && this.token != token::CloseDelim(Delimiter::Brace);
let hi = this.prev_token.span;
TrailingToken::None,
));
}
- this.expect_one_of(&[token::Comma], &[token::CloseDelim(token::Brace)]).map_err(
- |mut err| {
+ this.expect_one_of(&[token::Comma], &[token::CloseDelim(Delimiter::Brace)])
+ .map_err(|mut err| {
match (sm.span_to_lines(expr.span), sm.span_to_lines(arm_start_span)) {
(Ok(ref expr_lines), Ok(ref arm_start_lines))
if arm_start_lines.lines[0].end_col
}
}
err
- },
- )?;
+ })?;
} else {
this.eat(&token::Comma);
}
fn is_do_catch_block(&self) -> bool {
self.token.is_keyword(kw::Do)
&& self.is_keyword_ahead(1, &[kw::Catch])
- && self.look_ahead(2, |t| *t == token::OpenDelim(token::Brace))
+ && self.look_ahead(2, |t| *t == token::OpenDelim(Delimiter::Brace))
&& !self.restrictions.contains(Restrictions::NO_STRUCT_LITERAL)
}
+ fn is_do_yeet(&self) -> bool {
+ self.token.is_keyword(kw::Do) && self.is_keyword_ahead(1, &[kw::Yeet])
+ }
+
fn is_try_block(&self) -> bool {
self.token.is_keyword(kw::Try)
- && self.look_ahead(1, |t| *t == token::OpenDelim(token::Brace))
+ && self.look_ahead(1, |t| *t == token::OpenDelim(Delimiter::Brace))
&& self.token.uninterpolated_span().rust_2018()
}
&& ((
// `async move {`
self.is_keyword_ahead(1, &[kw::Move])
- && self.look_ahead(2, |t| *t == token::OpenDelim(token::Brace))
+ && self.look_ahead(2, |t| *t == token::OpenDelim(Delimiter::Brace))
) || (
// `async {`
- self.look_ahead(1, |t| *t == token::OpenDelim(token::Brace))
+ self.look_ahead(1, |t| *t == token::OpenDelim(Delimiter::Brace))
))
}
) -> Option<PResult<'a, P<Expr>>> {
let struct_allowed = !self.restrictions.contains(Restrictions::NO_STRUCT_LITERAL);
if struct_allowed || self.is_certainly_not_a_block() {
- if let Err(err) = self.expect(&token::OpenDelim(token::Brace)) {
+ if let Err(err) = self.expect(&token::OpenDelim(Delimiter::Brace)) {
return Some(Err(err));
}
let expr = self.parse_struct_expr(qself.cloned(), path.clone(), attrs.clone(), true);
&mut self,
pth: ast::Path,
recover: bool,
- close_delim: token::DelimToken,
+ close_delim: Delimiter,
) -> PResult<'a, (Vec<ExprField>, ast::StructRest, bool)> {
let mut fields = Vec::new();
let mut base = ast::StructRest::None;
e.span_suggestion(
self.prev_token.span.shrink_to_hi(),
"try adding a comma",
- ",".into(),
+ ",",
Applicability::MachineApplicable,
);
}
) -> PResult<'a, P<Expr>> {
let lo = pth.span;
let (fields, base, recover_async) =
- self.parse_struct_fields(pth.clone(), recover, token::Brace)?;
+ self.parse_struct_fields(pth.clone(), recover, Delimiter::Brace)?;
let span = lo.to(self.token.span);
- self.expect(&token::CloseDelim(token::Brace))?;
+ self.expect(&token::CloseDelim(Delimiter::Brace))?;
let expr = if recover_async {
ExprKind::Err
} else {
let ident = self.parse_ident()?;
// Parse optional colon and param bounds.
+ let mut colon_span = None;
let bounds = if self.eat(&token::Colon) {
- self.parse_generic_bounds(Some(self.prev_token.span))?
+ colon_span = Some(self.prev_token.span);
+ self.parse_generic_bounds(colon_span)?
} else {
Vec::new()
};
bounds,
kind: GenericParamKind::Type { default },
is_placeholder: false,
+ colon_span,
})
}
bounds: Vec::new(),
kind: GenericParamKind::Const { ty, kw_span: const_span, default },
is_placeholder: false,
+ colon_span: None,
})
}
let param = if this.check_lifetime() {
let lifetime = this.expect_lifetime();
// Parse lifetime parameter.
- let bounds = if this.eat(&token::Colon) {
- this.parse_lt_param_bounds()
+ let (colon_span, bounds) = if this.eat(&token::Colon) {
+ (Some(this.prev_token.span), this.parse_lt_param_bounds())
} else {
- Vec::new()
+ (None, Vec::new())
};
Some(ast::GenericParam {
ident: lifetime.ident,
bounds,
kind: ast::GenericParamKind::Lifetime,
is_placeholder: false,
+ colon_span,
})
} else if this.check_keyword(kw::Const) {
// Parse const parameter.
use rustc_ast::ast::*;
use rustc_ast::ptr::P;
-use rustc_ast::token::{self, TokenKind};
+use rustc_ast::token::{self, Delimiter, TokenKind};
use rustc_ast::tokenstream::{DelimSpan, TokenStream, TokenTree};
use rustc_ast::{self as ast, AttrVec, Attribute, DUMMY_NODE_ID};
use rustc_ast::{Async, Const, Defaultness, IsAuto, Mutability, Unsafe, UseTree, UseTreeKind};
let mod_kind = if self.eat(&token::Semi) {
ModKind::Unloaded
} else {
- self.expect(&token::OpenDelim(token::Brace))?;
+ self.expect(&token::OpenDelim(Delimiter::Brace))?;
let (mut inner_attrs, items, inner_span) =
- self.parse_mod(&token::CloseDelim(token::Brace))?;
+ self.parse_mod(&token::CloseDelim(Delimiter::Brace))?;
attrs.append(&mut inner_attrs);
ModKind::Loaded(items, Inline::Yes, inner_span)
};
let sp = self.prev_token.span.between(self.token.span);
let full_sp = self.prev_token.span.to(self.token.span);
let ident_sp = self.token.span;
- if self.look_ahead(1, |t| *t == token::OpenDelim(token::Brace)) {
+ if self.look_ahead(1, |t| *t == token::OpenDelim(Delimiter::Brace)) {
// possible public struct definition where `struct` was forgotten
let ident = self.parse_ident().unwrap();
let msg = format!("add `struct` here to parse `{ident}` as a public struct");
err.span_suggestion_short(
sp,
&msg,
- " struct ".into(),
+ " struct ",
Applicability::MaybeIncorrect, // speculative
);
Err(err)
- } else if self.look_ahead(1, |t| *t == token::OpenDelim(token::Paren)) {
+ } else if self.look_ahead(1, |t| *t == token::OpenDelim(Delimiter::Parenthesis)) {
let ident = self.parse_ident().unwrap();
self.bump(); // `(`
let kw_name = self.recover_first_param();
- self.consume_block(token::Paren, ConsumeClosingDelim::Yes);
+ self.consume_block(Delimiter::Parenthesis, ConsumeClosingDelim::Yes);
let (kw, kw_name, ambiguous) = if self.check(&token::RArrow) {
- self.eat_to_tokens(&[&token::OpenDelim(token::Brace)]);
+ self.eat_to_tokens(&[&token::OpenDelim(Delimiter::Brace)]);
self.bump(); // `{`
("fn", kw_name, false)
- } else if self.check(&token::OpenDelim(token::Brace)) {
+ } else if self.check(&token::OpenDelim(Delimiter::Brace)) {
self.bump(); // `{`
("fn", kw_name, false)
} else if self.check(&token::Colon) {
let msg = format!("missing `{kw}` for {kw_name} definition");
let mut err = self.struct_span_err(sp, &msg);
if !ambiguous {
- self.consume_block(token::Brace, ConsumeClosingDelim::Yes);
+ self.consume_block(Delimiter::Brace, ConsumeClosingDelim::Yes);
let suggestion =
format!("add `{kw}` here to parse `{ident}` as a public {kw_name}");
err.span_suggestion_short(
let ident = self.parse_ident().unwrap();
self.eat_to_tokens(&[&token::Gt]);
self.bump(); // `>`
- let (kw, kw_name, ambiguous) = if self.eat(&token::OpenDelim(token::Paren)) {
+ let (kw, kw_name, ambiguous) = if self.eat(&token::OpenDelim(Delimiter::Parenthesis)) {
("fn", self.recover_first_param(), false)
- } else if self.check(&token::OpenDelim(token::Brace)) {
+ } else if self.check(&token::OpenDelim(Delimiter::Brace)) {
("struct", "struct", false)
} else {
("fn` or `struct", "function or struct", true)
.span_suggestion(
span,
"add a trait here",
- " Trait ".into(),
+ " Trait ",
Applicability::HasPlaceholders,
)
.span_suggestion(
span.to(self.token.span),
"for an inherent impl, drop this `for`",
- "".into(),
+ "",
Applicability::MaybeIncorrect,
)
.emit();
mut parse_item: impl FnMut(&mut Parser<'a>) -> PResult<'a, Option<Option<T>>>,
) -> PResult<'a, Vec<T>> {
let open_brace_span = self.token.span;
- self.expect(&token::OpenDelim(token::Brace))?;
+ self.expect(&token::OpenDelim(Delimiter::Brace))?;
attrs.append(&mut self.parse_inner_attributes()?);
let mut items = Vec::new();
- while !self.eat(&token::CloseDelim(token::Brace)) {
+ while !self.eat(&token::CloseDelim(Delimiter::Brace)) {
if self.recover_doc_comment_before_brace() {
continue;
}
Ok(None) => {
// We have to bail or we'll potentially never make progress.
let non_item_span = self.token.span;
- self.consume_block(token::Brace, ConsumeClosingDelim::Yes);
+ self.consume_block(Delimiter::Brace, ConsumeClosingDelim::Yes);
self.struct_span_err(non_item_span, "non-item in item list")
.span_label(open_brace_span, "item list starts here")
.span_label(non_item_span, "non-item starts here")
}
Ok(Some(item)) => items.extend(item),
Err(mut err) => {
- self.consume_block(token::Brace, ConsumeClosingDelim::Yes);
+ self.consume_block(Delimiter::Brace, ConsumeClosingDelim::Yes);
err.span_label(open_brace_span, "while parsing this item list starting here")
.span_label(self.prev_token.span, "the item list ends here")
.emit();
/// Recover on a doc comment before `}`.
fn recover_doc_comment_before_brace(&mut self) -> bool {
if let token::DocComment(..) = self.token.kind {
- if self.look_ahead(1, |tok| tok == &token::CloseDelim(token::Brace)) {
+ if self.look_ahead(1, |tok| tok == &token::CloseDelim(Delimiter::Brace)) {
struct_span_err!(
self.diagnostic(),
self.token.span,
let lo = self.token.span;
let mut prefix = ast::Path { segments: Vec::new(), span: lo.shrink_to_lo(), tokens: None };
- let kind = if self.check(&token::OpenDelim(token::Brace))
+ let kind = if self.check(&token::OpenDelim(Delimiter::Brace))
|| self.check(&token::BinOp(token::Star))
|| self.is_import_coupler()
{
/// USE_TREE_LIST = Ø | (USE_TREE `,`)* USE_TREE [`,`]
/// ```
fn parse_use_tree_list(&mut self) -> PResult<'a, Vec<(UseTree, ast::NodeId)>> {
- self.parse_delim_comma_seq(token::Brace, |p| Ok((p.parse_use_tree()?, DUMMY_NODE_ID)))
+ self.parse_delim_comma_seq(Delimiter::Brace, |p| Ok((p.parse_use_tree()?, DUMMY_NODE_ID)))
.map(|(r, _)| r)
}
&format!("`{}` must come before `{}`", invalid_qual, current_qual),
format!("{} {}", invalid_qual, current_qual),
Applicability::MachineApplicable,
- ).note("keyword order for functions declaration is `default`, `pub`, `const`, `async`, `unsafe`, `extern`");
+ ).note("keyword order for functions declaration is `pub`, `default`, `const`, `async`, `unsafe`, `extern`");
}
}
Err(err)
&& self.is_keyword_ahead(1, &[kw::Extern])
&& self.look_ahead(
2 + self.look_ahead(2, |t| t.can_begin_literal_maybe_minus() as usize),
- |t| t.kind == token::OpenDelim(token::Brace),
+ |t| t.kind == token::OpenDelim(Delimiter::Brace),
)
}
let mut generics = self.parse_generics()?;
generics.where_clause = self.parse_where_clause()?;
- let (variants, _) =
- self.parse_delim_comma_seq(token::Brace, |p| p.parse_enum_variant()).map_err(|e| {
+ let (variants, _) = self
+ .parse_delim_comma_seq(Delimiter::Brace, |p| p.parse_enum_variant())
+ .map_err(|e| {
self.recover_stmt();
e
})?;
}
let ident = this.parse_field_ident("enum", vlo)?;
- let struct_def = if this.check(&token::OpenDelim(token::Brace)) {
+ let struct_def = if this.check(&token::OpenDelim(Delimiter::Brace)) {
// Parse a struct variant.
let (fields, recovered) = this.parse_record_struct_body("struct", false)?;
VariantData::Struct(fields, recovered)
- } else if this.check(&token::OpenDelim(token::Paren)) {
+ } else if this.check(&token::OpenDelim(Delimiter::Parenthesis)) {
VariantData::Tuple(this.parse_tuple_struct_body()?, DUMMY_NODE_ID)
} else {
VariantData::Unit(DUMMY_NODE_ID)
} else if self.eat(&token::Semi) {
VariantData::Unit(DUMMY_NODE_ID)
// Record-style struct definition
- } else if self.token == token::OpenDelim(token::Brace) {
+ } else if self.token == token::OpenDelim(Delimiter::Brace) {
let (fields, recovered) =
self.parse_record_struct_body("struct", generics.where_clause.has_where_token)?;
VariantData::Struct(fields, recovered)
// Tuple-style struct definition with optional where-clause.
- } else if self.token == token::OpenDelim(token::Paren) {
+ } else if self.token == token::OpenDelim(Delimiter::Parenthesis) {
let body = VariantData::Tuple(self.parse_tuple_struct_body()?, DUMMY_NODE_ID);
generics.where_clause = self.parse_where_clause()?;
self.expect_semi()?;
let (fields, recovered) =
self.parse_record_struct_body("union", generics.where_clause.has_where_token)?;
VariantData::Struct(fields, recovered)
- } else if self.token == token::OpenDelim(token::Brace) {
+ } else if self.token == token::OpenDelim(Delimiter::Brace) {
let (fields, recovered) =
self.parse_record_struct_body("union", generics.where_clause.has_where_token)?;
VariantData::Struct(fields, recovered)
) -> PResult<'a, (Vec<FieldDef>, /* recovered */ bool)> {
let mut fields = Vec::new();
let mut recovered = false;
- if self.eat(&token::OpenDelim(token::Brace)) {
- while self.token != token::CloseDelim(token::Brace) {
+ if self.eat(&token::OpenDelim(Delimiter::Brace)) {
+ while self.token != token::CloseDelim(Delimiter::Brace) {
let field = self.parse_field_def(adt_ty).map_err(|e| {
- self.consume_block(token::Brace, ConsumeClosingDelim::No);
+ self.consume_block(Delimiter::Brace, ConsumeClosingDelim::No);
recovered = true;
e
});
}
}
}
- self.eat(&token::CloseDelim(token::Brace));
+ self.eat(&token::CloseDelim(Delimiter::Brace));
} else {
let token_str = super::token_descr(&self.token);
let msg = &format!(
token::Comma => {
self.bump();
}
- token::CloseDelim(token::Brace) => {}
+ token::CloseDelim(Delimiter::Brace) => {}
token::DocComment(..) => {
let previous_span = self.prev_token.span;
let mut err = self.span_err(self.token.span, Error::UselessDocComment);
if !seen_comma && comma_after_doc_seen {
seen_comma = true;
}
- if comma_after_doc_seen || self.token == token::CloseDelim(token::Brace) {
+ if comma_after_doc_seen || self.token == token::CloseDelim(Delimiter::Brace) {
err.emit();
} else {
if !seen_comma {
err.span_suggestion(
sp,
"missing comma here",
- ",".into(),
+ ",",
Applicability::MachineApplicable,
);
}
if let Some(last_segment) = segments.last() {
recovered = self.check_trailing_angle_brackets(
last_segment,
- &[&token::Comma, &token::CloseDelim(token::Brace)],
+ &[&token::Comma, &token::CloseDelim(Delimiter::Brace)],
);
if recovered {
// Handle a case like `Vec<u8>>,` where we can continue parsing fields
err.span_suggestion(
sp,
"try adding a comma",
- ",".into(),
+ ",",
Applicability::MachineApplicable,
);
err.emit();
/// ```
fn parse_item_decl_macro(&mut self, lo: Span) -> PResult<'a, ItemInfo> {
let ident = self.parse_ident()?;
- let body = if self.check(&token::OpenDelim(token::Brace)) {
+ let body = if self.check(&token::OpenDelim(Delimiter::Brace)) {
self.parse_mac_args()? // `MacBody`
- } else if self.check(&token::OpenDelim(token::Paren)) {
+ } else if self.check(&token::OpenDelim(Delimiter::Parenthesis)) {
let params = self.parse_token_tree(); // `MacParams`
let pspan = params.span();
- if !self.check(&token::OpenDelim(token::Brace)) {
+ if !self.check(&token::OpenDelim(Delimiter::Brace)) {
return self.unexpected();
}
let body = self.parse_token_tree(); // `MacBody`
self.expect_semi()?;
*sig_hi = self.prev_token.span;
(Vec::new(), None)
- } else if self.check(&token::OpenDelim(token::Brace)) || self.token.is_whole_block() {
+ } else if self.check(&token::OpenDelim(Delimiter::Brace)) || self.token.is_whole_block() {
self.parse_inner_attrs_and_block().map(|(attrs, body)| (attrs, Some(body)))?
} else if self.token.kind == token::Eq {
// Recover `fn foo() = $expr;`.
(Vec::new(), Some(self.mk_block_err(span)))
} else {
let expected = if req_body {
- &[token::OpenDelim(token::Brace)][..]
+ &[token::OpenDelim(Delimiter::Brace)][..]
} else {
- &[token::Semi, token::OpenDelim(token::Brace)]
+ &[token::Semi, token::OpenDelim(Delimiter::Brace)]
};
if let Err(mut err) = self.expected_one_of_not_found(&[], &expected) {
- if self.token.kind == token::CloseDelim(token::Brace) {
+ if self.token.kind == token::CloseDelim(Delimiter::Brace) {
// The enclosing `mod`, `trait` or `impl` is being closed, so keep the `fn` in
// the AST for typechecking.
err.span_label(ident.span, "while parsing this `fn`");
&format!("`{misplaced_qual}` must come before `{current_qual}`"),
format!("{misplaced_qual} {current_qual}"),
Applicability::MachineApplicable,
- ).note("keyword order for functions declaration is `default`, `pub`, `const`, `async`, `unsafe`, `extern`");
+ ).note("keyword order for functions declaration is `pub`, `default`, `const`, `async`, `unsafe`, `extern`");
}
}
// Recover incorrect visibility order such as `async pub`
e.emit();
let lo = p.prev_token.span;
// Skip every token until next possible arg or end.
- p.eat_to_tokens(&[&token::Comma, &token::CloseDelim(token::Paren)]);
+ p.eat_to_tokens(&[&token::Comma, &token::CloseDelim(Delimiter::Parenthesis)]);
// Create a placeholder argument for proper arg count (issue #34264).
Ok(dummy_arg(Ident::new(kw::Empty, lo.to(p.prev_token.span))))
});
let mut ty = this.parse_ty_for_param();
if ty.is_ok()
&& this.token != token::Comma
- && this.token != token::CloseDelim(token::Paren)
+ && this.token != token::CloseDelim(Delimiter::Parenthesis)
{
// This wasn't actually a type, but a pattern looking like a type,
// so we are going to rollback and re-parse for recovery.
pub use path::PathStyle;
use rustc_ast::ptr::P;
-use rustc_ast::token::{self, DelimToken, Nonterminal, Token, TokenKind};
+use rustc_ast::token::{self, Delimiter, Nonterminal, Token, TokenKind};
use rustc_ast::tokenstream::AttributesData;
use rustc_ast::tokenstream::{self, DelimSpan, Spacing};
use rustc_ast::tokenstream::{TokenStream, TokenTree};
pub capture_cfg: bool,
restrictions: Restrictions,
expected_tokens: Vec<TokenType>,
- // Important: This must only be advanced from `next_tok`
- // to ensure that `token_cursor.num_next_calls` is updated properly
+ // Important: This must only be advanced from `bump` to ensure that
+ // `token_cursor.num_next_calls` is updated properly.
token_cursor: TokenCursor,
desugar_doc_comments: bool,
/// This field is used to keep track of how many left angle brackets we have seen. This is
pub current_closure: Option<ClosureSpans>,
}
+// This type is used a lot, e.g. it's cloned when matching many declarative macro rules. Make sure
+// it doesn't unintentionally get bigger.
+#[cfg(all(target_arch = "x86_64", target_pointer_width = "64"))]
+rustc_data_structures::static_assert_size!(Parser<'_>, 328);
+
/// Stores span information about a closure.
#[derive(Clone)]
pub struct ClosureSpans {
#[derive(Clone)]
struct TokenCursor {
+ // The current (innermost) frame. `frame` and `stack` could be combined,
+ // but it's faster to have them separately to access `frame` directly
+ // rather than via something like `stack.last().unwrap()` or
+ // `stack[stack.len() - 1]`.
frame: TokenCursorFrame,
+ // Additional frames that enclose `frame`.
stack: Vec<TokenCursorFrame>,
desugar_doc_comments: bool,
- // Counts the number of calls to `{,inlined_}next` or
- // `{,inlined_}next_desugared`, depending on whether
- // `desugar_doc_comments` is set.
+ // Counts the number of calls to `{,inlined_}next`.
num_next_calls: usize,
// During parsing, we may sometimes need to 'unglue' a
// glued token into two component tokens
#[derive(Clone)]
struct TokenCursorFrame {
- delim: token::DelimToken,
- span: DelimSpan,
- open_delim: bool,
+ delim_sp: Option<(Delimiter, DelimSpan)>,
tree_cursor: tokenstream::Cursor,
- close_delim: bool,
}
impl TokenCursorFrame {
- fn new(span: DelimSpan, delim: DelimToken, tts: TokenStream) -> Self {
- TokenCursorFrame {
- delim,
- span,
- open_delim: false,
- tree_cursor: tts.into_trees(),
- close_delim: false,
- }
+ fn new(delim_sp: Option<(Delimiter, DelimSpan)>, tts: TokenStream) -> Self {
+ TokenCursorFrame { delim_sp, tree_cursor: tts.into_trees() }
}
}
impl TokenCursor {
- fn next(&mut self) -> (Token, Spacing) {
- self.inlined_next()
+ fn next(&mut self, desugar_doc_comments: bool) -> (Token, Spacing) {
+ self.inlined_next(desugar_doc_comments)
}
/// This always-inlined version should only be used on hot code paths.
#[inline(always)]
- fn inlined_next(&mut self) -> (Token, Spacing) {
+ fn inlined_next(&mut self, desugar_doc_comments: bool) -> (Token, Spacing) {
loop {
- let (tree, spacing) = if !self.frame.open_delim {
- self.frame.open_delim = true;
- TokenTree::open_tt(self.frame.span, self.frame.delim).into()
- } else if let Some(tree) = self.frame.tree_cursor.next_with_spacing() {
- tree
- } else if !self.frame.close_delim {
- self.frame.close_delim = true;
- TokenTree::close_tt(self.frame.span, self.frame.delim).into()
+ // FIXME: we currently don't return `Delimiter` open/close delims. To fix #67062 we will
+ // need to, whereupon the `delim != Delimiter::Invisible` conditions below can be
+ // removed.
+ if let Some((tree, spacing)) = self.frame.tree_cursor.next_with_spacing_ref() {
+ match tree {
+ &TokenTree::Token(ref token) => match (desugar_doc_comments, token) {
+ (true, &Token { kind: token::DocComment(_, attr_style, data), span }) => {
+ return self.desugar(attr_style, data, span);
+ }
+ _ => return (token.clone(), *spacing),
+ },
+ &TokenTree::Delimited(sp, delim, ref tts) => {
+ // Set `open_delim` to true here because we deal with it immediately.
+ let frame = TokenCursorFrame::new(Some((delim, sp)), tts.clone());
+ self.stack.push(mem::replace(&mut self.frame, frame));
+ if delim != Delimiter::Invisible {
+ return (Token::new(token::OpenDelim(delim), sp.open), Spacing::Alone);
+ }
+ // No open delimeter to return; continue on to the next iteration.
+ }
+ };
} else if let Some(frame) = self.stack.pop() {
+ if let Some((delim, span)) = self.frame.delim_sp && delim != Delimiter::Invisible {
+ self.frame = frame;
+ return (Token::new(token::CloseDelim(delim), span.close), Spacing::Alone);
+ }
self.frame = frame;
- continue;
+ // No close delimiter to return; continue on to the next iteration.
} else {
- (TokenTree::Token(Token::new(token::Eof, DUMMY_SP)), Spacing::Alone)
- };
-
- match tree {
- TokenTree::Token(token) => {
- return (token, spacing);
- }
- TokenTree::Delimited(sp, delim, tts) => {
- let frame = TokenCursorFrame::new(sp, delim, tts);
- self.stack.push(mem::replace(&mut self.frame, frame));
- }
+ return (Token::new(token::Eof, DUMMY_SP), Spacing::Alone);
}
}
}
- fn next_desugared(&mut self) -> (Token, Spacing) {
- self.inlined_next_desugared()
- }
-
- /// This always-inlined version should only be used on hot code paths.
- #[inline(always)]
- fn inlined_next_desugared(&mut self) -> (Token, Spacing) {
- let (data, attr_style, sp) = match self.inlined_next() {
- (Token { kind: token::DocComment(_, attr_style, data), span }, _) => {
- (data, attr_style, span)
- }
- tok => return tok,
- };
-
+ fn desugar(&mut self, attr_style: AttrStyle, data: Symbol, span: Span) -> (Token, Spacing) {
// Searches for the occurrences of `"#*` and returns the minimum number of `#`s
// required to wrap the text.
let mut num_of_hashes = 0;
num_of_hashes = cmp::max(num_of_hashes, count);
}
- let delim_span = DelimSpan::from_single(sp);
+ let delim_span = DelimSpan::from_single(span);
let body = TokenTree::Delimited(
delim_span,
- token::Bracket,
+ Delimiter::Bracket,
[
- TokenTree::token(token::Ident(sym::doc, false), sp),
- TokenTree::token(token::Eq, sp),
- TokenTree::token(TokenKind::lit(token::StrRaw(num_of_hashes), data, None), sp),
+ TokenTree::token(token::Ident(sym::doc, false), span),
+ TokenTree::token(token::Eq, span),
+ TokenTree::token(TokenKind::lit(token::StrRaw(num_of_hashes), data, None), span),
]
.iter()
.cloned()
self.stack.push(mem::replace(
&mut self.frame,
TokenCursorFrame::new(
- delim_span,
- token::NoDelim,
+ None,
if attr_style == AttrStyle::Inner {
- [TokenTree::token(token::Pound, sp), TokenTree::token(token::Not, sp), body]
+ [TokenTree::token(token::Pound, span), TokenTree::token(token::Not, span), body]
.iter()
.cloned()
.collect::<TokenStream>()
} else {
- [TokenTree::token(token::Pound, sp), body]
+ [TokenTree::token(token::Pound, span), body]
.iter()
.cloned()
.collect::<TokenStream>()
),
));
- self.next()
+ self.next(/* desugar_doc_comments */ false)
}
}
desugar_doc_comments: bool,
subparser_name: Option<&'static str>,
) -> Self {
- let mut start_frame = TokenCursorFrame::new(DelimSpan::dummy(), token::NoDelim, tokens);
- start_frame.open_delim = true;
- start_frame.close_delim = true;
-
let mut parser = Parser {
sess,
token: Token::dummy(),
restrictions: Restrictions::empty(),
expected_tokens: Vec::new(),
token_cursor: TokenCursor {
- frame: start_frame,
+ frame: TokenCursorFrame::new(None, tokens),
stack: Vec::new(),
num_next_calls: 0,
desugar_doc_comments,
parser
}
- #[inline]
- fn next_tok(&mut self, fallback_span: Span) -> (Token, Spacing) {
- loop {
- let (mut next, spacing) = if self.desugar_doc_comments {
- self.token_cursor.inlined_next_desugared()
- } else {
- self.token_cursor.inlined_next()
- };
- self.token_cursor.num_next_calls += 1;
- // We've retrieved an token from the underlying
- // cursor, so we no longer need to worry about
- // an unglued token. See `break_and_eat` for more details
- self.token_cursor.break_last_token = false;
- if next.span.is_dummy() {
- // Tweak the location for better diagnostics, but keep syntactic context intact.
- next.span = fallback_span.with_ctxt(next.span.ctxt());
- }
- if matches!(
- next.kind,
- token::OpenDelim(token::NoDelim) | token::CloseDelim(token::NoDelim)
- ) {
- continue;
- }
- return (next, spacing);
- }
- }
-
pub fn unexpected<T>(&mut self) -> PResult<'a, T> {
match self.expect_one_of(&[], &[]) {
Err(e) => Err(e),
self.is_keyword_ahead(dist, &[kw::Const])
&& self.look_ahead(dist + 1, |t| match t.kind {
token::Interpolated(ref nt) => matches!(**nt, token::NtBlock(..)),
- token::OpenDelim(DelimToken::Brace) => true,
+ token::OpenDelim(Delimiter::Brace) => true,
_ => false,
})
}
//
// If we consume any additional tokens, then this token
// is not needed (we'll capture the entire 'glued' token),
- // and `next_tok` will set this field to `None`
+ // and `bump` will set this field to `None`
self.token_cursor.break_last_token = true;
// Use the spacing of the glued token as the spacing
// of the unglued second token.
.span_suggestion_verbose(
self.prev_token.span.shrink_to_hi().until(self.token.span),
&msg,
- " @ ".to_string(),
+ " @ ",
Applicability::MaybeIncorrect,
)
.emit();
.span_suggestion_short(
sp,
&format!("missing `{}`", token_str),
- token_str.into(),
+ token_str,
Applicability::MaybeIncorrect,
)
.emit();
fn parse_delim_comma_seq<T>(
&mut self,
- delim: DelimToken,
+ delim: Delimiter,
f: impl FnMut(&mut Parser<'a>) -> PResult<'a, T>,
) -> PResult<'a, (Vec<T>, bool)> {
self.parse_unspanned_seq(
&mut self,
f: impl FnMut(&mut Parser<'a>) -> PResult<'a, T>,
) -> PResult<'a, (Vec<T>, bool)> {
- self.parse_delim_comma_seq(token::Paren, f)
+ self.parse_delim_comma_seq(Delimiter::Parenthesis, f)
}
/// Advance the parser by one token using provided token as the next one.
/// This always-inlined version should only be used on hot code paths.
#[inline(always)]
fn inlined_bump_with(&mut self, (next_token, next_spacing): (Token, Spacing)) {
- // Bumping after EOF is a bad sign, usually an infinite loop.
- if self.prev_token.kind == TokenKind::Eof {
- let msg = "attempted to bump the parser past EOF (may be stuck in a loop)";
- self.span_bug(self.token.span, msg);
- }
-
// Update the current and previous tokens.
self.prev_token = mem::replace(&mut self.token, next_token);
self.token_spacing = next_spacing;
/// Advance the parser by one token.
pub fn bump(&mut self) {
- let next_token = self.next_tok(self.token.span);
- self.inlined_bump_with(next_token);
+ // Note: destructuring here would give nicer code, but it was found in #96210 to be slower
+ // than `.0`/`.1` access.
+ let mut next = self.token_cursor.inlined_next(self.desugar_doc_comments);
+ self.token_cursor.num_next_calls += 1;
+ // We've retrieved an token from the underlying
+ // cursor, so we no longer need to worry about
+ // an unglued token. See `break_and_eat` for more details
+ self.token_cursor.break_last_token = false;
+ if next.0.span.is_dummy() {
+ // Tweak the location for better diagnostics, but keep syntactic context intact.
+ let fallback_span = self.token.span;
+ next.0.span = fallback_span.with_ctxt(next.0.span.ctxt());
+ }
+ debug_assert!(!matches!(
+ next.0.kind,
+ token::OpenDelim(Delimiter::Invisible) | token::CloseDelim(Delimiter::Invisible)
+ ));
+ self.inlined_bump_with(next)
}
/// Look-ahead `dist` tokens of `self.token` and get access to that token there.
}
let frame = &self.token_cursor.frame;
- if frame.delim != DelimToken::NoDelim {
+ if let Some((delim, span)) = frame.delim_sp && delim != Delimiter::Invisible {
let all_normal = (0..dist).all(|i| {
let token = frame.tree_cursor.look_ahead(i);
- !matches!(token, Some(TokenTree::Delimited(_, DelimToken::NoDelim, _)))
+ !matches!(token, Some(TokenTree::Delimited(_, Delimiter::Invisible, _)))
});
if all_normal {
return match frame.tree_cursor.look_ahead(dist - 1) {
looker(&Token::new(token::OpenDelim(*delim), dspan.open))
}
},
- None => looker(&Token::new(token::CloseDelim(frame.delim), frame.span.close)),
+ None => looker(&Token::new(token::CloseDelim(delim), span.close)),
};
}
}
let mut i = 0;
let mut token = Token::dummy();
while i < dist {
- token = cursor.next().0;
+ token = cursor.next(/* desugar_doc_comments */ false).0;
if matches!(
token.kind,
- token::OpenDelim(token::NoDelim) | token::CloseDelim(token::NoDelim)
+ token::OpenDelim(Delimiter::Invisible) | token::CloseDelim(Delimiter::Invisible)
) {
continue;
}
/// Parses constness: `const` or nothing.
fn parse_constness(&mut self) -> Const {
// Avoid const blocks to be parsed as const items
- if self.look_ahead(1, |t| t != &token::OpenDelim(DelimToken::Brace))
+ if self.look_ahead(1, |t| t != &token::OpenDelim(Delimiter::Brace))
&& self.eat_keyword(kw::Const)
{
Const::Yes(self.prev_token.uninterpolated_span())
fn parse_mac_args_common(&mut self, delimited_only: bool) -> PResult<'a, MacArgs> {
Ok(
- if self.check(&token::OpenDelim(DelimToken::Paren))
- || self.check(&token::OpenDelim(DelimToken::Bracket))
- || self.check(&token::OpenDelim(DelimToken::Brace))
+ if self.check(&token::OpenDelim(Delimiter::Parenthesis))
+ || self.check(&token::OpenDelim(Delimiter::Bracket))
+ || self.check(&token::OpenDelim(Delimiter::Brace))
{
match self.parse_token_tree() {
TokenTree::Delimited(dspan, delim, tokens) =>
pub(crate) fn parse_token_tree(&mut self) -> TokenTree {
match self.token.kind {
token::OpenDelim(..) => {
- let depth = self.token_cursor.stack.len();
-
- // We keep advancing the token cursor until we hit
- // the matching `CloseDelim` token.
- while !(depth == self.token_cursor.stack.len()
- && matches!(self.token.kind, token::CloseDelim(_)))
- {
+ // Grab the tokens from this frame.
+ let frame = &self.token_cursor.frame;
+ let stream = frame.tree_cursor.stream.clone();
+ let (delim, span) = frame.delim_sp.unwrap();
+
+ // Advance the token cursor through the entire delimited
+ // sequence. After getting the `OpenDelim` we are *within* the
+ // delimited sequence, i.e. at depth `d`. After getting the
+ // matching `CloseDelim` we are *after* the delimited sequence,
+ // i.e. at depth `d - 1`.
+ let target_depth = self.token_cursor.stack.len() - 1;
+ loop {
// Advance one token at a time, so `TokenCursor::next()`
// can capture these tokens if necessary.
self.bump();
+ if self.token_cursor.stack.len() == target_depth {
+ debug_assert!(matches!(self.token.kind, token::CloseDelim(_)));
+ break;
+ }
}
- // We are still inside the frame corresponding
- // to the delimited stream we captured, so grab
- // the tokens from this frame.
- let frame = &self.token_cursor.frame;
- let stream = frame.tree_cursor.stream.clone();
- let span = frame.span;
- let delim = frame.delim;
+
// Consume close delimiter
self.bump();
TokenTree::Delimited(span, delim, stream)
}
let lo = self.prev_token.span;
- if self.check(&token::OpenDelim(token::Paren)) {
+ if self.check(&token::OpenDelim(Delimiter::Parenthesis)) {
// We don't `self.bump()` the `(` yet because this might be a struct definition where
// `()` or a tuple might be allowed. For example, `struct Struct(pub (), pub (usize));`.
// Because of this, we only `bump` the `(` if we're assured it is appropriate to do so
// Parse `pub(crate)`.
self.bump(); // `(`
self.bump(); // `crate`
- self.expect(&token::CloseDelim(token::Paren))?; // `)`
+ self.expect(&token::CloseDelim(Delimiter::Parenthesis))?; // `)`
let vis = VisibilityKind::Crate(CrateSugar::PubCrate);
return Ok(Visibility {
span: lo.to(self.prev_token.span),
self.bump(); // `(`
self.bump(); // `in`
let path = self.parse_path(PathStyle::Mod)?; // `path`
- self.expect(&token::CloseDelim(token::Paren))?; // `)`
+ self.expect(&token::CloseDelim(Delimiter::Parenthesis))?; // `)`
let vis = VisibilityKind::Restricted { path: P(path), id: ast::DUMMY_NODE_ID };
return Ok(Visibility {
span: lo.to(self.prev_token.span),
kind: vis,
tokens: None,
});
- } else if self.look_ahead(2, |t| t == &token::CloseDelim(token::Paren))
+ } else if self.look_ahead(2, |t| t == &token::CloseDelim(Delimiter::Parenthesis))
&& self.is_keyword_ahead(1, &[kw::Super, kw::SelfLower])
{
// Parse `pub(self)` or `pub(super)`.
self.bump(); // `(`
let path = self.parse_path(PathStyle::Mod)?; // `super`/`self`
- self.expect(&token::CloseDelim(token::Paren))?; // `)`
+ self.expect(&token::CloseDelim(Delimiter::Parenthesis))?; // `)`
let vis = VisibilityKind::Restricted { path: P(path), id: ast::DUMMY_NODE_ID };
return Ok(Visibility {
span: lo.to(self.prev_token.span),
fn recover_incorrect_vis_restriction(&mut self) -> PResult<'a, ()> {
self.bump(); // `(`
let path = self.parse_path(PathStyle::Mod)?;
- self.expect(&token::CloseDelim(token::Paren))?; // `)`
+ self.expect(&token::CloseDelim(Delimiter::Parenthesis))?; // `)`
let msg = "incorrect visibility restriction";
let suggestion = r##"some possible visibility restrictions are:
fn is_import_coupler(&mut self) -> bool {
self.check(&token::ModSep)
&& self.look_ahead(1, |t| {
- *t == token::OpenDelim(token::Brace) || *t == token::BinOp(token::Star)
+ *t == token::OpenDelim(Delimiter::Brace) || *t == token::BinOp(token::Star)
})
}
use rustc_ast::ptr::P;
-use rustc_ast::token::{self, NonterminalKind, Token};
+use rustc_ast::token::{self, Delimiter, NonterminalKind, Token};
use rustc_ast::AstLike;
use rustc_ast_pretty::pprust;
use rustc_errors::PResult;
impl<'a> Parser<'a> {
/// Checks whether a non-terminal may begin with a particular token.
///
- /// Returning `false` is a *stability guarantee* that such a matcher will *never* begin with that
- /// token. Be conservative (return true) if not sure.
+ /// Returning `false` is a *stability guarantee* that such a matcher will *never* begin with
+ /// that token. Be conservative (return true) if not sure. Inlined because it has a single call
+ /// site.
+ #[inline]
pub fn nonterminal_may_begin_with(kind: NonterminalKind, token: &Token) -> bool {
/// Checks whether the non-terminal may contain a single (non-keyword) identifier.
fn may_be_ident(nt: &token::Nonterminal) -> bool {
_ => token.can_begin_type(),
},
NonterminalKind::Block => match token.kind {
- token::OpenDelim(token::Brace) => true,
+ token::OpenDelim(Delimiter::Brace) => true,
token::Interpolated(ref nt) => !matches!(
**nt,
token::NtItem(_)
NonterminalKind::PatParam { .. } | NonterminalKind::PatWithOr { .. } => {
match token.kind {
token::Ident(..) | // box, ref, mut, and other identifiers (can stricten)
- token::OpenDelim(token::Paren) | // tuple pattern
- token::OpenDelim(token::Bracket) | // slice pattern
+ token::OpenDelim(Delimiter::Parenthesis) | // tuple pattern
+ token::OpenDelim(Delimiter::Bracket) | // slice pattern
token::BinOp(token::And) | // reference
token::BinOp(token::Minus) | // negative literal
token::AndAnd | // double reference
}
}
- /// Parse a non-terminal (e.g. MBE `:pat` or `:ident`).
+ /// Parse a non-terminal (e.g. MBE `:pat` or `:ident`). Inlined because there is only one call
+ /// site.
+ #[inline]
pub fn parse_nonterminal(&mut self, kind: NonterminalKind) -> PResult<'a, NtOrTt> {
// Any `Nonterminal` which stores its tokens (currently `NtItem` and `NtExpr`)
// needs to have them force-captured here.
use crate::{maybe_recover_from_interpolated_ty_qpath, maybe_whole};
use rustc_ast::mut_visit::{noop_visit_pat, MutVisitor};
use rustc_ast::ptr::P;
-use rustc_ast::token;
+use rustc_ast::token::{self, Delimiter};
use rustc_ast::{
self as ast, AttrVec, Attribute, BindingMode, Expr, ExprKind, MacCall, Mutability, Pat,
PatField, PatKind, Path, QSelf, RangeEnd, RangeSyntax,
| token::Semi // e.g. `let a |;`.
| token::Colon // e.g. `let a | :`.
| token::Comma // e.g. `let (a |,)`.
- | token::CloseDelim(token::Bracket) // e.g. `let [a | ]`.
- | token::CloseDelim(token::Paren) // e.g. `let (a | )`.
- | token::CloseDelim(token::Brace) // e.g. `let A { f: a | }`.
+ | token::CloseDelim(Delimiter::Bracket) // e.g. `let [a | ]`.
+ | token::CloseDelim(Delimiter::Parenthesis) // e.g. `let (a | )`.
+ | token::CloseDelim(Delimiter::Brace) // e.g. `let A { f: a | }`.
)
});
match (is_end_ahead, &self.token.kind) {
let pat = if self.check(&token::BinOp(token::And)) || self.token.kind == token::AndAnd {
self.parse_pat_deref(expected)?
- } else if self.check(&token::OpenDelim(token::Paren)) {
+ } else if self.check(&token::OpenDelim(Delimiter::Parenthesis)) {
self.parse_pat_tuple_or_parens()?
- } else if self.check(&token::OpenDelim(token::Bracket)) {
+ } else if self.check(&token::OpenDelim(Delimiter::Bracket)) {
// Parse `[pat, pat,...]` as a slice pattern.
- let (pats, _) = self.parse_delim_comma_seq(token::Bracket, |p| {
+ let (pats, _) = self.parse_delim_comma_seq(Delimiter::Bracket, |p| {
p.parse_pat_allow_top_alt(
None,
RecoverComma::No,
} else if let Some(form) = self.parse_range_end() {
let begin = self.mk_expr(span, ExprKind::Path(qself, path), AttrVec::new());
self.parse_pat_range_begin_with(begin, form)?
- } else if self.check(&token::OpenDelim(token::Brace)) {
+ } else if self.check(&token::OpenDelim(Delimiter::Brace)) {
self.parse_pat_struct(qself, path)?
- } else if self.check(&token::OpenDelim(token::Paren)) {
+ } else if self.check(&token::OpenDelim(Delimiter::Parenthesis)) {
self.parse_pat_tuple_struct(qself, path)?
} else {
PatKind::Path(qself, path)
.span_suggestion(
mutref_span,
"try switching the order",
- "ref mut".into(),
+ "ref mut",
Applicability::MachineApplicable,
)
.emit();
// Avoid `in`. Due to recovery in the list parser this messes with `for ( $pat in $expr )`.
&& !self.token.is_keyword(kw::In)
// Try to do something more complex?
- && self.look_ahead(1, |t| !matches!(t.kind, token::OpenDelim(token::Paren) // A tuple struct pattern.
- | token::OpenDelim(token::Brace) // A struct pattern.
+ && self.look_ahead(1, |t| !matches!(t.kind, token::OpenDelim(Delimiter::Parenthesis) // A tuple struct pattern.
+ | token::OpenDelim(Delimiter::Brace) // A struct pattern.
| token::DotDotDot | token::DotDotEq | token::DotDot // A range pattern.
| token::ModSep // A tuple / struct variant pattern.
| token::Not)) // A macro expanding to a pattern.
// This shortly leads to a parse error. Note that if there is no explicit
// binding mode then we do not end up here, because the lookahead
// will direct us over to `parse_enum_variant()`.
- if self.token == token::OpenDelim(token::Paren) {
+ if self.token == token::OpenDelim(Delimiter::Parenthesis) {
return Err(self
.struct_span_err(self.prev_token.span, "expected identifier, found enum pattern"));
}
let mut delayed_err: Option<DiagnosticBuilder<'a, ErrorGuaranteed>> = None;
let mut etc_span = None;
- while self.token != token::CloseDelim(token::Brace) {
+ while self.token != token::CloseDelim(Delimiter::Brace) {
let attrs = match self.parse_outer_attributes() {
Ok(attrs) => attrs,
Err(err) => {
self.recover_one_fewer_dotdot();
self.bump(); // `..` || `...`
- if self.token == token::CloseDelim(token::Brace) {
+ if self.token == token::CloseDelim(Delimiter::Brace) {
etc_span = Some(etc_sp);
break;
}
}
etc_span = Some(etc_sp.until(self.token.span));
- if self.token == token::CloseDelim(token::Brace) {
+ if self.token == token::CloseDelim(Delimiter::Brace) {
// If the struct looks otherwise well formed, recover and continue.
if let Some(sp) = comma_sp {
err.span_suggestion_short(
use super::{Parser, Restrictions, TokenType};
use crate::maybe_whole;
use rustc_ast::ptr::P;
-use rustc_ast::token::{self, Token};
+use rustc_ast::token::{self, Delimiter, Token};
use rustc_ast::{
self as ast, AngleBracketedArg, AngleBracketedArgs, AnonConst, AssocConstraint,
AssocConstraintKind, BlockCheckMode, GenericArg, GenericArgs, Generics, ParenthesizedArgs,
token.kind,
token::Lt
| token::BinOp(token::Shl)
- | token::OpenDelim(token::Paren)
+ | token::OpenDelim(Delimiter::Parenthesis)
| token::LArrow
)
};
let check_args_start = |this: &mut Self| {
this.expected_tokens.extend_from_slice(&[
TokenType::Token(token::Lt),
- TokenType::Token(token::OpenDelim(token::Paren)),
+ TokenType::Token(token::OpenDelim(Delimiter::Parenthesis)),
]);
is_args_start(&this.token)
};
/// the caller.
pub(super) fn parse_const_arg(&mut self) -> PResult<'a, AnonConst> {
// Parse const argument.
- let value = if let token::OpenDelim(token::Brace) = self.token.kind {
+ let value = if let token::OpenDelim(Delimiter::Brace) = self.token.kind {
self.parse_block_expr(
None,
self.token.span,
GenericArg::Const(self.parse_const_arg()?)
} else if self.check_type() {
// Parse type argument.
- let is_const_fn = self.look_ahead(1, |t| t.kind == token::OpenDelim(token::Paren));
+ let is_const_fn =
+ self.look_ahead(1, |t| t.kind == token::OpenDelim(Delimiter::Parenthesis));
let mut snapshot = self.create_snapshot_for_diagnostic();
match self.parse_ty() {
Ok(ty) => GenericArg::Type(ty),
use rustc_ast as ast;
use rustc_ast::ptr::P;
-use rustc_ast::token::{self, TokenKind};
+use rustc_ast::token::{self, Delimiter, TokenKind};
use rustc_ast::util::classify;
use rustc_ast::{
AstLike, AttrStyle, AttrVec, Attribute, LocalKind, MacCall, MacCallStmt, MacStmtStyle,
// Do not attempt to parse an expression if we're done here.
self.error_outer_attrs(&attrs.take_for_recovery());
self.mk_stmt(lo, StmtKind::Empty)
- } else if self.token != token::CloseDelim(token::Brace) {
+ } else if self.token != token::CloseDelim(Delimiter::Brace) {
// Remainder are line-expr stmts.
let e = if force_collect == ForceCollect::Yes {
self.collect_tokens_no_attrs(|this| {
}
}
- let expr = if this.eat(&token::OpenDelim(token::Brace)) {
+ let expr = if this.eat(&token::OpenDelim(Delimiter::Brace)) {
this.parse_struct_expr(None, path, AttrVec::new(), true)?
} else {
let hi = this.prev_token.span;
let delim = args.delim();
let hi = self.prev_token.span;
- let style =
- if delim == token::Brace { MacStmtStyle::Braces } else { MacStmtStyle::NoBraces };
+ let style = match delim {
+ Some(Delimiter::Brace) => MacStmtStyle::Braces,
+ Some(_) => MacStmtStyle::NoBraces,
+ None => unreachable!(),
+ };
let mac = MacCall { path, args, prior_type_ascription: self.last_type_ascription };
- let kind =
- if (delim == token::Brace && self.token != token::Dot && self.token != token::Question)
- || self.token == token::Semi
- || self.token == token::Eof
- {
- StmtKind::MacCall(P(MacCallStmt { mac, style, attrs, tokens: None }))
- } else {
- // Since none of the above applied, this is an expression statement macro.
- let e = self.mk_expr(lo.to(hi), ExprKind::MacCall(mac), AttrVec::new());
- let e = self.maybe_recover_from_bad_qpath(e, true)?;
- let e = self.parse_dot_or_call_expr_with(e, lo, attrs.into())?;
- let e = self.parse_assoc_expr_with(0, LhsExpr::AlreadyParsed(e))?;
- StmtKind::Expr(e)
- };
+ let kind = if (style == MacStmtStyle::Braces
+ && self.token != token::Dot
+ && self.token != token::Question)
+ || self.token == token::Semi
+ || self.token == token::Eof
+ {
+ StmtKind::MacCall(P(MacCallStmt { mac, style, attrs, tokens: None }))
+ } else {
+ // Since none of the above applied, this is an expression statement macro.
+ let e = self.mk_expr(lo.to(hi), ExprKind::MacCall(mac), AttrVec::new());
+ let e = self.maybe_recover_from_bad_qpath(e, true)?;
+ let e = self.parse_dot_or_call_expr_with(e, lo, attrs.into())?;
+ let e = self.parse_assoc_expr_with(0, LhsExpr::AlreadyParsed(e))?;
+ StmtKind::Expr(e)
+ };
Ok(self.mk_stmt(lo.to(hi), kind))
}
// If the next token is an open brace (e.g., `if a b {`), the place-
// inside-a-block suggestion would be more likely wrong than right.
Ok(Some(_))
- if self.look_ahead(1, |t| t == &token::OpenDelim(token::Brace))
+ if self.look_ahead(1, |t| t == &token::OpenDelim(Delimiter::Brace))
|| do_not_suggest_help => {}
// Do not suggest `if foo println!("") {;}` (as would be seen in test for #46836).
Ok(Some(Stmt { kind: StmtKind::Empty, .. })) => {}
maybe_whole!(self, NtBlock, |x| (Vec::new(), x));
self.maybe_recover_unexpected_block_label();
- if !self.eat(&token::OpenDelim(token::Brace)) {
+ if !self.eat(&token::OpenDelim(Delimiter::Brace)) {
return self.error_block_no_opening_brace();
}
recover: AttemptLocalParseRecovery,
) -> PResult<'a, P<Block>> {
let mut stmts = vec![];
- while !self.eat(&token::CloseDelim(token::Brace)) {
+ while !self.eat(&token::CloseDelim(Delimiter::Brace)) {
if self.token == token::Eof {
break;
}
{
// Just check for errors and recover; do not eat semicolon yet.
if let Err(mut e) =
- self.expect_one_of(&[], &[token::Semi, token::CloseDelim(token::Brace)])
+ self.expect_one_of(&[], &[token::Semi, token::CloseDelim(Delimiter::Brace)])
{
if let TokenKind::DocComment(..) = self.token.kind {
if let Ok(snippet) = self.span_to_snippet(self.token.span) {
use crate::{maybe_recover_from_interpolated_ty_qpath, maybe_whole};
use rustc_ast::ptr::P;
-use rustc_ast::token::{self, Token, TokenKind};
+use rustc_ast::token::{self, Delimiter, Token, TokenKind};
use rustc_ast::{
self as ast, BareFnTy, FnRetTy, GenericBound, GenericBounds, GenericParam, Generics, Lifetime,
MacCall, MutTy, Mutability, PolyTraitRef, TraitBoundModifier, TraitObjectSyntax, Ty, TyKind,
let lo = self.token.span;
let mut impl_dyn_multi = false;
- let kind = if self.check(&token::OpenDelim(token::Paren)) {
+ let kind = if self.check(&token::OpenDelim(Delimiter::Parenthesis)) {
self.parse_ty_tuple_or_parens(lo, allow_plus)?
} else if self.eat(&token::Not) {
// Never type `!`
TyKind::Never
} else if self.eat(&token::BinOp(token::Star)) {
self.parse_ty_ptr()?
- } else if self.eat(&token::OpenDelim(token::Bracket)) {
+ } else if self.eat(&token::OpenDelim(Delimiter::Bracket)) {
self.parse_array_or_slice_ty()?
} else if self.check(&token::BinOp(token::And)) || self.check(&token::AndAnd) {
// Reference
let elt_ty = match self.parse_ty() {
Ok(ty) => ty,
Err(mut err)
- if self.look_ahead(1, |t| t.kind == token::CloseDelim(token::Bracket))
+ if self.look_ahead(1, |t| t.kind == token::CloseDelim(Delimiter::Bracket))
| self.look_ahead(1, |t| t.kind == token::Semi) =>
{
// Recover from `[LIT; EXPR]` and `[LIT]`
let ty = if self.eat(&token::Semi) {
let mut length = self.parse_anon_const_expr()?;
- if let Err(e) = self.expect(&token::CloseDelim(token::Bracket)) {
+ if let Err(e) = self.expect(&token::CloseDelim(Delimiter::Bracket)) {
// Try to recover from `X<Y, ...>` when `X::<Y, ...>` works
self.check_mistyped_turbofish_with_multiple_type_params(e, &mut length.value)?;
- self.expect(&token::CloseDelim(token::Bracket))?;
+ self.expect(&token::CloseDelim(Delimiter::Bracket))?;
}
TyKind::Array(elt_ty, length)
} else {
- self.expect(&token::CloseDelim(token::Bracket))?;
+ self.expect(&token::CloseDelim(Delimiter::Bracket))?;
TyKind::Slice(elt_ty)
};
// Parses the `typeof(EXPR)`.
// To avoid ambiguity, the type is surrounded by parentheses.
fn parse_typeof_ty(&mut self) -> PResult<'a, TyKind> {
- self.expect(&token::OpenDelim(token::Paren))?;
+ self.expect(&token::OpenDelim(Delimiter::Parenthesis))?;
let expr = self.parse_anon_const_expr()?;
- self.expect(&token::CloseDelim(token::Paren))?;
+ self.expect(&token::CloseDelim(Delimiter::Parenthesis))?;
Ok(TyKind::Typeof(expr))
}
|| self.check(&token::Question)
|| self.check(&token::Tilde)
|| self.check_keyword(kw::For)
- || self.check(&token::OpenDelim(token::Paren))
+ || self.check(&token::OpenDelim(Delimiter::Parenthesis))
}
fn error_negative_bounds(
fn parse_generic_bound(&mut self) -> PResult<'a, Result<GenericBound, Span>> {
let anchor_lo = self.prev_token.span;
let lo = self.token.span;
- let has_parens = self.eat(&token::OpenDelim(token::Paren));
+ let has_parens = self.eat(&token::OpenDelim(Delimiter::Parenthesis));
let inner_lo = self.token.span;
let is_negative = self.eat(&token::Not);
/// Recover on `('lifetime)` with `(` already eaten.
fn recover_paren_lifetime(&mut self, lo: Span, inner_lo: Span) -> PResult<'a, ()> {
let inner_span = inner_lo.to(self.prev_token.span);
- self.expect(&token::CloseDelim(token::Paren))?;
+ self.expect(&token::CloseDelim(Delimiter::Parenthesis))?;
let mut err = self.struct_span_err(
lo.to(self.prev_token.span),
"parenthesized lifetime bounds are not supported",
// suggestion is given.
let bounds = vec![];
self.parse_remaining_bounds(bounds, true)?;
- self.expect(&token::CloseDelim(token::Paren))?;
+ self.expect(&token::CloseDelim(Delimiter::Parenthesis))?;
let sp = vec![lo, self.prev_token.span];
let sugg: Vec<_> = sp.iter().map(|sp| (*sp, String::new())).collect();
self.struct_span_err(sp, "incorrect braces around trait bounds")
)
.emit();
} else {
- self.expect(&token::CloseDelim(token::Paren))?;
+ self.expect(&token::CloseDelim(Delimiter::Parenthesis))?;
}
}
live_symbols: FxHashSet<LocalDefId>,
repr_has_repr_c: bool,
in_pat: bool,
- inherited_pub_visibility: bool,
- pub_visibility: bool,
ignore_variant_stack: Vec<DefId>,
// maps from tuple struct constructors to tuple struct items
struct_constructors: FxHashMap<LocalDefId, LocalDefId>,
_ if self.in_pat => {}
Res::PrimTy(..) | Res::SelfCtor(..) | Res::Local(..) => {}
Res::Def(DefKind::Ctor(CtorOf::Variant, ..), ctor_def_id) => {
- let variant_id = self.tcx.parent(ctor_def_id).unwrap();
- let enum_id = self.tcx.parent(variant_id).unwrap();
+ let variant_id = self.tcx.parent(ctor_def_id);
+ let enum_id = self.tcx.parent(variant_id);
self.check_def_id(enum_id);
if !self.ignore_variant_stack.contains(&ctor_def_id) {
self.check_def_id(variant_id);
}
}
Res::Def(DefKind::Variant, variant_id) => {
- let enum_id = self.tcx.parent(variant_id).unwrap();
+ let enum_id = self.tcx.parent(variant_id);
self.check_def_id(enum_id);
if !self.ignore_variant_stack.contains(&variant_id) {
self.check_def_id(variant_id);
}
let had_repr_c = self.repr_has_repr_c;
- let had_inherited_pub_visibility = self.inherited_pub_visibility;
- let had_pub_visibility = self.pub_visibility;
self.repr_has_repr_c = false;
- self.inherited_pub_visibility = false;
- self.pub_visibility = false;
match node {
- Node::Item(item) => {
- self.pub_visibility = item.vis.node.is_pub();
+ Node::Item(item) => match item.kind {
+ hir::ItemKind::Struct(..) | hir::ItemKind::Union(..) => {
+ let def = self.tcx.adt_def(item.def_id);
+ self.repr_has_repr_c = def.repr().c();
- match item.kind {
- hir::ItemKind::Struct(..) | hir::ItemKind::Union(..) => {
- let def = self.tcx.adt_def(item.def_id);
- self.repr_has_repr_c = def.repr().c();
-
- intravisit::walk_item(self, &item);
- }
- hir::ItemKind::Enum(..) => {
- self.inherited_pub_visibility = self.pub_visibility;
-
- intravisit::walk_item(self, &item);
- }
- hir::ItemKind::ForeignMod { .. } => {}
- _ => {
- intravisit::walk_item(self, &item);
- }
+ intravisit::walk_item(self, &item);
}
- }
+ hir::ItemKind::Enum(..) => {
+ intravisit::walk_item(self, &item);
+ }
+ hir::ItemKind::ForeignMod { .. } => {}
+ _ => {
+ intravisit::walk_item(self, &item);
+ }
+ },
Node::TraitItem(trait_item) => {
intravisit::walk_trait_item(self, trait_item);
}
}
_ => {}
}
- self.pub_visibility = had_pub_visibility;
- self.inherited_pub_visibility = had_inherited_pub_visibility;
self.repr_has_repr_c = had_repr_c;
}
_: hir::HirId,
_: rustc_span::Span,
) {
+ let tcx = self.tcx;
let has_repr_c = self.repr_has_repr_c;
- let inherited_pub_visibility = self.inherited_pub_visibility;
- let pub_visibility = self.pub_visibility;
- let live_fields = def.fields().iter().filter(|f| {
- has_repr_c || (pub_visibility && (inherited_pub_visibility || f.vis.node.is_pub()))
+ let live_fields = def.fields().iter().filter_map(|f| {
+ let def_id = tcx.hir().local_def_id(f.hir_id);
+ if has_repr_c {
+ return Some(def_id);
+ }
+ if !tcx.visibility(f.hir_id.owner).is_public() {
+ return None;
+ }
+ if tcx.visibility(def_id).is_public() { Some(def_id) } else { None }
});
- let hir = self.tcx.hir();
- self.live_symbols.extend(live_fields.map(|f| hir.local_def_id(f.hir_id)));
+ self.live_symbols.extend(live_fields);
intravisit::walk_struct_def(self, def);
}
if of_trait.is_some() {
self.worklist.push(item.def_id);
}
- for impl_item_ref in items {
+ for impl_item_ref in *items {
let impl_item = self.tcx.hir().impl_item(impl_item_ref.id);
if of_trait.is_some()
|| has_allow_dead_code_or_lang_attr(self.tcx, impl_item.hir_id())
live_symbols: Default::default(),
repr_has_repr_c: false,
in_pat: false,
- inherited_pub_visibility: false,
- pub_visibility: false,
ignore_variant_stack: vec![],
struct_constructors,
ignored_derived_traits: FxHashMap::default(),
impl<'tcx> ItemLikeVisitor<'tcx> for EntryContext<'tcx> {
fn visit_item(&mut self, item: &'tcx Item<'tcx>) {
- let at_root = self.tcx.local_parent(item.def_id) == Some(CRATE_DEF_ID);
+ let at_root = self.tcx.opt_local_parent(item.def_id) == Some(CRATE_DEF_ID);
find_item(item, self, at_root);
}
// completely accurate (some things might be counted twice, others missed).
use rustc_ast::visit as ast_visit;
+use rustc_ast::visit::BoundKind;
use rustc_ast::{self as ast, AttrId, NodeId};
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_hir as hir;
ast_visit::walk_assoc_item(self, item, ctxt);
}
- fn visit_param_bound(&mut self, bounds: &'v ast::GenericBound) {
+ fn visit_param_bound(&mut self, bounds: &'v ast::GenericBound, _ctxt: BoundKind) {
self.record("GenericBound", Id::None, bounds);
ast_visit::walk_param_bound(self, bounds)
}
let def_id = local_def_id.to_def_id();
// Don't run unused pass for #[derive()]
- if let Some(parent) = self.tcx.parent(def_id)
- && let DefKind::Impl = self.tcx.def_kind(parent.expect_local())
- && self.tcx.has_attr(parent, sym::automatically_derived)
+ let parent = self.tcx.local_parent(local_def_id);
+ if let DefKind::Impl = self.tcx.def_kind(parent)
+ && self.tcx.has_attr(parent.to_def_id(), sym::automatically_derived)
{
return;
}
self.visit_nested_body(body);
}
hir::ImplItemKind::Fn(_, body) => {
- let impl_def_id =
- self.tcx.parent(search_item.to_def_id()).unwrap().expect_local();
+ let impl_def_id = self.tcx.local_parent(search_item);
if method_might_be_inlined(self.tcx, impl_item, impl_def_id) {
self.visit_nested_body(body)
}
let codegen_attrs = self.tcx.codegen_fn_attrs(def_id);
if codegen_attrs.contains_extern_indicator()
|| codegen_attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL)
+ // FIXME(nbdd0121): `#[used]` are marked as reachable here so it's picked up by
+ // `linked_symbols` in cg_ssa. They won't be exported in binary or cdylib due to their
+ // `SymbolExportLevel::Rust` export level but may end up being exported in dylibs.
+ || codegen_attrs.flags.contains(CodegenFnAttrFlags::USED)
+ || codegen_attrs.flags.contains(CodegenFnAttrFlags::USED_LINKER)
{
self.worklist.push(def_id);
}
}
}
- for impl_item_ref in items {
+ for impl_item_ref in *items {
let impl_item = self.tcx.associated_item(impl_item_ref.id.def_id);
if let Some(def_id) = impl_item.trait_item_def_id {
use rustc_middle::ty::fold::TypeVisitor;
use rustc_middle::ty::query::Providers;
use rustc_middle::ty::subst::InternalSubsts;
-use rustc_middle::ty::{self, Const, GenericParamDefKind, TraitRef, Ty, TyCtxt, TypeFoldable};
+use rustc_middle::ty::{self, Const, DefIdTree, GenericParamDefKind};
+use rustc_middle::ty::{TraitRef, Ty, TyCtxt, TypeFoldable};
use rustc_session::lint;
use rustc_span::hygiene::Transparency;
use rustc_span::symbol::{kw, Ident};
if vis1.is_at_least(vis2, tcx) { vis2 } else { vis1 }
}
-////////////////////////////////////////////////////////////////////////////////
-/// Visitor used to determine if pub(restricted) is used anywhere in the crate.
-///
-/// This is done so that `private_in_public` warnings can be turned into hard errors
-/// in crates that have been updated to use pub(restricted).
-////////////////////////////////////////////////////////////////////////////////
-struct PubRestrictedVisitor<'tcx> {
- tcx: TyCtxt<'tcx>,
- has_pub_restricted: bool,
-}
-
-impl<'tcx> Visitor<'tcx> for PubRestrictedVisitor<'tcx> {
- type NestedFilter = nested_filter::All;
-
- fn nested_visit_map(&mut self) -> Self::Map {
- self.tcx.hir()
- }
- fn visit_vis(&mut self, vis: &'tcx hir::Visibility<'tcx>) {
- self.has_pub_restricted = self.has_pub_restricted || vis.node.is_pub_restricted();
- }
-}
-
////////////////////////////////////////////////////////////////////////////////
/// Visitor used to determine impl visibility and reachability.
////////////////////////////////////////////////////////////////////////////////
return;
}
- let item_def_id = local_def_id.to_def_id();
- let macro_module_def_id =
- ty::DefIdTree::parent(self.tcx, item_def_id).unwrap().expect_local();
+ let macro_module_def_id = self.tcx.local_parent(local_def_id);
if self.tcx.hir().opt_def_kind(macro_module_def_id) != Some(DefKind::Mod) {
// The macro's parent doesn't correspond to a `mod`, return early (#63164, #65252).
return;
if changed_reachability || module_def_id == CRATE_DEF_ID {
break;
}
- module_def_id =
- ty::DefIdTree::parent(self.tcx, module_def_id.to_def_id()).unwrap().expect_local();
+ module_def_id = self.tcx.local_parent(module_def_id);
}
}
self.update_with_hir_id(ctor_hir_id, item_level);
}
for field in def.fields() {
- if field.vis.node.is_pub() {
+ let def_id = self.tcx.hir().local_def_id(field.hir_id);
+ let vis = self.tcx.visibility(def_id);
+ if vis.is_public() {
self.update_with_hir_id(field.hir_id, item_level);
}
}
// .. and it corresponds to a private type in the AST (this returns
// `None` for type parameters).
match self.tcx.hir().find(self.tcx.hir().local_def_id_to_hir_id(did)) {
- Some(Node::Item(item)) => !item.vis.node.is_pub(),
+ Some(Node::Item(_)) => !self.tcx.visibility(did).is_public(),
Some(_) | None => false,
}
} else {
}
}
- fn item_is_public(&self, def_id: LocalDefId, vis: &hir::Visibility<'_>) -> bool {
- self.access_levels.is_reachable(def_id) || vis.node.is_pub()
+ fn item_is_public(&self, def_id: LocalDefId) -> bool {
+ self.access_levels.is_reachable(def_id) || self.tcx.visibility(def_id).is_public()
}
}
let impl_item = self.tcx.hir().impl_item(impl_item_ref.id);
match impl_item.kind {
hir::ImplItemKind::Const(..) | hir::ImplItemKind::Fn(..)
- if self
- .item_is_public(impl_item.def_id, &impl_item.vis) =>
+ if self.item_is_public(impl_item.def_id) =>
{
intravisit::walk_impl_item(self, impl_item)
}
hir::ItemKind::TyAlias(..) => return,
// Not at all public, so we don't care.
- _ if !self.item_is_public(item.def_id, &item.vis) => {
+ _ if !self.item_is_public(item.def_id) => {
return;
}
}
fn visit_generics(&mut self, generics: &'tcx hir::Generics<'tcx>) {
- for param in generics.params {
- for bound in param.bounds {
- self.check_generic_bound(bound);
- }
- }
- for predicate in generics.where_clause.predicates {
+ for predicate in generics.predicates {
match predicate {
hir::WherePredicate::BoundPredicate(bound_pred) => {
for bound in bound_pred.bounds.iter() {
}
fn visit_field_def(&mut self, s: &'tcx hir::FieldDef<'tcx>) {
- if s.vis.node.is_pub() || self.in_variant {
+ let def_id = self.tcx.hir().local_def_id(s.hir_id);
+ let vis = self.tcx.visibility(def_id);
+ if vis.is_public() || self.in_variant {
intravisit::walk_field_def(self, s);
}
}
item_def_id: LocalDefId,
/// The visitor checks that each component type is at least this visible.
required_visibility: ty::Visibility,
- has_pub_restricted: bool,
has_old_errors: bool,
in_assoc_ty: bool,
}
};
let make_msg = || format!("{} {} `{}` in public interface", vis_descr, kind, descr);
let span = self.tcx.def_span(self.item_def_id.to_def_id());
- if self.has_pub_restricted || self.has_old_errors || self.in_assoc_ty {
+ if self.has_old_errors
+ || self.in_assoc_ty
+ || self.tcx.resolutions(()).has_pub_restricted
+ {
let mut err = if kind == "trait" {
struct_span_err!(self.tcx.sess, span, E0445, "{}", make_msg())
} else {
struct PrivateItemsInPublicInterfacesVisitor<'tcx> {
tcx: TyCtxt<'tcx>,
- has_pub_restricted: bool,
old_error_set_ancestry: LocalDefIdSet,
}
tcx: self.tcx,
item_def_id: def_id,
required_visibility,
- has_pub_restricted: self.has_pub_restricted,
has_old_errors: self.old_error_set_ancestry.contains(&def_id),
in_assoc_ty: false,
}
match tcx.hir().get(hir_id) {
// Unique types created for closures participate in type privacy checking.
// They have visibilities inherited from the module they are defined in.
- Node::Expr(hir::Expr { kind: hir::ExprKind::Closure(..), .. }) => {
- ty::Visibility::Restricted(tcx.parent_module(hir_id).to_def_id())
- }
- // - AST lowering may clone `use` items and the clones don't
+ Node::Expr(hir::Expr { kind: hir::ExprKind::Closure(..), .. })
+ // - AST lowering creates dummy `use` items which don't
// get their entries in the resolver's visibility table.
// - AST lowering also creates opaque type items with inherited visibilities.
// Visibility on them should have no effect, but to avoid the visibility
// query failing on some items, we provide it for opaque types as well.
- Node::Item(hir::Item {
- vis,
- kind: hir::ItemKind::Use(..) | hir::ItemKind::OpaqueTy(..),
+ | Node::Item(hir::Item {
+ kind: hir::ItemKind::Use(_, hir::UseKind::ListStem) | hir::ItemKind::OpaqueTy(..),
..
- }) => ty::Visibility::from_hir(vis, hir_id, tcx),
+ }) => ty::Visibility::Restricted(tcx.parent_module(hir_id).to_def_id()),
// Visibilities of trait impl items are inherited from their traits
// and are not filled in resolve.
Node::ImplItem(impl_item) => {
};
tcx.hir().walk_toplevel_module(&mut visitor);
- let has_pub_restricted = {
- let mut pub_restricted_visitor = PubRestrictedVisitor { tcx, has_pub_restricted: false };
- tcx.hir().walk_toplevel_module(&mut pub_restricted_visitor);
- pub_restricted_visitor.has_pub_restricted
- };
-
let mut old_error_set_ancestry = HirIdSet::default();
for mut id in visitor.old_error_set.iter().copied() {
loop {
// Check for private types and traits in public interfaces.
let mut visitor = PrivateItemsInPublicInterfacesVisitor {
tcx,
- has_pub_restricted,
// Only definition IDs are ever searched in `old_error_set_ancestry`,
// so we can filter away all non-definition IDs at this point.
old_error_set_ancestry: old_error_set_ancestry
self.0.default_span(tcx)
}
}
+
+impl<'tcx> Key for (Ty<'tcx>, ty::ValTree<'tcx>) {
+ #[inline(always)]
+ fn query_crate_is_local(&self) -> bool {
+ true
+ }
+
+ fn default_span(&self, _: TyCtxt<'_>) -> Span {
+ DUMMY_SP
+ }
+}
})
}
ast::VisibilityKind::Restricted { ref path, id, .. } => {
+ // Make `PRIVATE_IN_PUBLIC` lint a hard error.
+ self.r.has_pub_restricted = true;
// For visibilities we are not ready to provide correct implementation of "uniform
// paths" right now, so on 2018 edition we only allow module-relative paths for now.
// On 2015 edition visibilities are resolved as crate-relative by default,
&segments,
Some(TypeNS),
parent_scope,
- if finalize { Finalize::SimplePath(id, path.span) } else { Finalize::No },
+ finalize.then(|| Finalize::new(id, path.span)),
None,
) {
PathResult::Module(ModuleOrUniformRoot::Module(module)) => {
let mut source = module_path.pop().unwrap();
let mut type_ns_only = false;
+ self.r.visibilities.insert(self.r.local_def_id(id), vis);
+ if id1 != ast::DUMMY_NODE_ID {
+ self.r.visibilities.insert(self.r.local_def_id(id1), vis);
+ }
+ if id2 != ast::DUMMY_NODE_ID {
+ self.r.visibilities.insert(self.r.local_def_id(id2), vis);
+ }
+
if nested {
// Correctly handle `self`
if source.ident.name == kw::SelfLower {
is_prelude: self.r.session.contains_name(&item.attrs, sym::prelude_import),
max_vis: Cell::new(ty::Visibility::Invisible),
};
+ self.r.visibilities.insert(self.r.local_def_id(id), vis);
self.add_import(prefix, kind, use_tree.span, id, item, root_span, item.id, vis);
}
ast::UseTreeKind::Nested(ref items) => {
.span_suggestion(
item.span,
"rename the `self` crate to be able to import it",
- "extern crate self as name;".into(),
+ "extern crate self as name;",
Applicability::HasPlaceholders,
)
.emit();
self.insert_unused_macro(ident, def_id, item.id);
}
self.r.visibilities.insert(def_id, vis);
- self.r.arenas.alloc_macro_rules_scope(MacroRulesScope::Binding(
+ let scope = self.r.arenas.alloc_macro_rules_scope(MacroRulesScope::Binding(
self.r.arenas.alloc_macro_rules_binding(MacroRulesBinding {
parent_macro_rules_scope: parent_scope.macro_rules,
binding,
ident,
}),
- ))
+ ));
+ self.r.macro_rules_scopes.insert(def_id, scope);
+ scope
} else {
let module = parent_scope.module;
let vis = match item.kind {
&& self
.r
.trait_impl_items
- .contains(&ty::DefIdTree::parent(&*self.r, def_id).unwrap().expect_local()))
+ .contains(&ty::DefIdTree::local_parent(&*self.r, local_def_id)))
{
// Trait impl item visibility is inherited from its trait when not specified
// explicitly. In that case we cannot determine it here in early resolve,
let (span, found_use) = if let Some(def_id) = def_id.as_local() {
UsePlacementFinder::check(krate, self.def_id_to_node_id[def_id])
} else {
- (None, false)
+ (None, FoundUse::No)
};
if !candidates.is_empty() {
show_candidates(
&mut err,
span,
&candidates,
- instead,
+ if instead { Instead::Yes } else { Instead::No },
found_use,
+ IsPattern::No,
);
} else if let Some((span, msg, sugg, appl)) = suggestion {
err.span_suggestion(span, msg, sugg, appl);
crate fn lint_if_path_starts_with_module(
&mut self,
- finalize: Finalize,
+ finalize: Option<Finalize>,
path: &[Segment],
second_binding: Option<&NameBinding<'_>>,
) {
- let (diag_id, diag_span) = match finalize {
- Finalize::No => return,
- Finalize::SimplePath(id, path_span) => (id, path_span),
- Finalize::UsePath { root_id, root_span, .. } => (root_id, root_span),
- Finalize::QPathTrait { qpath_id, qpath_span, .. } => (qpath_id, qpath_span),
+ let Some(Finalize { node_id, root_span, .. }) = finalize else {
+ return;
};
let first_name = match path.get(0) {
}
}
- let diag = BuiltinLintDiagnostics::AbsPathWithModule(diag_span);
+ let diag = BuiltinLintDiagnostics::AbsPathWithModule(root_span);
self.lint_buffer.buffer_lint_with_diagnostic(
ABSOLUTE_PATHS_NOT_STARTING_WITH_CRATE,
- diag_id,
- diag_span,
+ node_id,
+ root_span,
"absolute paths must start with `self`, `super`, \
`crate`, or an external crate name in the 2018 edition",
diag,
///
/// This takes the error provided, combines it with the span and any additional spans inside the
/// error and emits it.
- crate fn report_error(&self, span: Span, resolution_error: ResolutionError<'_>) {
+ crate fn report_error(&mut self, span: Span, resolution_error: ResolutionError<'a>) {
self.into_struct_error(span, resolution_error).emit();
}
crate fn into_struct_error(
- &self,
+ &mut self,
span: Span,
- resolution_error: ResolutionError<'_>,
+ resolution_error: ResolutionError<'a>,
) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
match resolution_error {
ResolutionError::GenericParamsFromOuterFunction(outer_res, has_generic_params) => {
}
err
}
- ResolutionError::VariableNotBoundInPattern(binding_error) => {
+ ResolutionError::VariableNotBoundInPattern(binding_error, parent_scope) => {
let BindingError { name, target, origin, could_be_path } = binding_error;
let target_sp = target.iter().copied().collect::<Vec<_>>();
for sp in origin_sp {
err.span_label(sp, "variable not in all patterns");
}
- if *could_be_path {
- let help_msg = format!(
- "if you meant to match on a variant or a `const` item, consider \
- making the path in the pattern qualified: `?::{}`",
- name,
+ if could_be_path {
+ let import_suggestions = self.lookup_import_candidates(
+ Ident::with_dummy_span(name),
+ Namespace::ValueNS,
+ &parent_scope,
+ &|res: Res| match res {
+ Res::Def(
+ DefKind::Ctor(CtorOf::Variant, CtorKind::Const)
+ | DefKind::Ctor(CtorOf::Struct, CtorKind::Const)
+ | DefKind::Const
+ | DefKind::AssocConst,
+ _,
+ ) => true,
+ _ => false,
+ },
+ );
+
+ if import_suggestions.is_empty() {
+ let help_msg = format!(
+ "if you meant to match on a variant or a `const` item, consider \
+ making the path in the pattern qualified: `path::to::ModOrType::{}`",
+ name,
+ );
+ err.span_help(span, &help_msg);
+ }
+ show_candidates(
+ &self.definitions,
+ self.session,
+ &mut err,
+ Some(span),
+ &import_suggestions,
+ Instead::No,
+ FoundUse::Yes,
+ IsPattern::Yes,
);
- err.span_help(span, &help_msg);
}
err
}
}
ResolutionError::InvalidAsmSym => {
let mut err = self.session.struct_span_err(span, "invalid `sym` operand");
- err.span_label(span, &format!("is a local variable"));
+ err.span_label(span, "is a local variable");
err.help("`sym` operands must refer to either a function or a static");
err
}
}
crate fn report_vis_error(
- &self,
+ &mut self,
vis_resolution_error: VisResolutionError<'_>,
) -> ErrorGuaranteed {
match vis_resolution_error {
segms.push(ast::PathSegment::from_ident(ident));
let path = Path { span: name_binding.span, segments: segms, tokens: None };
let did = match res {
- Res::Def(DefKind::Ctor(..), did) => this.parent(did),
+ Res::Def(DefKind::Ctor(..), did) => this.opt_parent(did),
_ => res.opt_def_id(),
};
err,
None,
&import_suggestions,
- false,
- true,
+ Instead::No,
+ FoundUse::Yes,
+ IsPattern::No,
);
if macro_kind == MacroKind::Derive && (ident.name == sym::Send || ident.name == sym::Sync) {
&parent_scope,
None,
false,
- false,
None,
) {
let desc = match binding.res() {
_,
) = binding.kind
{
- let def_id = self.parent(ctor_def_id).expect("no parent for a constructor");
+ let def_id = self.parent(ctor_def_id);
let fields = self.field_names.get(&def_id)?;
return fields.iter().map(|name| name.span).reduce(Span::to); // None for `struct Foo()`
}
opt_ns: Option<Namespace>, // `None` indicates a module path in import
parent_scope: &ParentScope<'a>,
ribs: Option<&PerNS<Vec<Rib<'a>>>>,
- unusable_binding: Option<&'a NameBinding<'a>>,
+ ignore_binding: Option<&'a NameBinding<'a>>,
module: Option<ModuleOrUniformRoot<'a>>,
i: usize,
ident: Ident,
ns_to_try,
parent_scope,
None,
- false,
- unusable_binding,
+ ignore_binding,
).ok()
} else if let Some(ribs) = ribs
&& let Some(TypeNS | ValueNS) = opt_ns
ident,
ns_to_try,
parent_scope,
- Finalize::No,
+ None,
&ribs[ns_to_try],
- unusable_binding,
+ ignore_binding,
) {
// we found a locally-imported or available item/module
Some(LexicalScopeBinding::Item(binding)) => Some(binding),
parent_scope,
None,
false,
- false,
- unusable_binding,
+ ignore_binding,
).ok()
};
if let Some(binding) = binding {
ident,
ValueNS,
parent_scope,
- Finalize::No,
+ None,
&ribs[ValueNS],
- unusable_binding,
+ ignore_binding,
)
} else {
None
(next_left_bracket == after_second_colon, from_second_colon)
}
+/// A suggestion has already been emitted, change the wording slightly to clarify that both are
+/// independent options.
+enum Instead {
+ Yes,
+ No,
+}
+
+/// Whether an existing place with an `use` item was found.
+enum FoundUse {
+ Yes,
+ No,
+}
+
+/// Whether a binding is part of a pattern or an expression. Used for diagnostics.
+enum IsPattern {
+ /// The binding is part of a pattern
+ Yes,
+ /// The binding is part of an expression
+ No,
+}
+
/// When an entity with a given name is not available in scope, we search for
/// entities with that name in all crates. This method allows outputting the
/// results of this search in a programmer-friendly way
// This is `None` if all placement locations are inside expansions
use_placement_span: Option<Span>,
candidates: &[ImportSuggestion],
- instead: bool,
- found_use: bool,
+ instead: Instead,
+ found_use: FoundUse,
+ is_pattern: IsPattern,
) {
if candidates.is_empty() {
return;
}
if !accessible_path_strings.is_empty() {
- let (determiner, kind) = if accessible_path_strings.len() == 1 {
- ("this", accessible_path_strings[0].1)
+ let (determiner, kind, name) = if accessible_path_strings.len() == 1 {
+ ("this", accessible_path_strings[0].1, format!(" `{}`", accessible_path_strings[0].0))
} else {
- ("one of these", "items")
+ ("one of these", "items", String::new())
};
- let instead = if instead { " instead" } else { "" };
- let mut msg = format!("consider importing {} {}{}", determiner, kind, instead);
+ let instead = if let Instead::Yes = instead { " instead" } else { "" };
+ let mut msg = if let IsPattern::Yes = is_pattern {
+ format!(
+ "if you meant to match on {}{}{}, use the full path in the pattern",
+ kind, instead, name
+ )
+ } else {
+ format!("consider importing {} {}{}", determiner, kind, instead)
+ };
for note in accessible_path_strings.iter().flat_map(|cand| cand.3.as_ref()) {
err.note(note);
}
- if let Some(span) = use_placement_span {
+ if let (IsPattern::Yes, Some(span)) = (is_pattern, use_placement_span) {
+ err.span_suggestions(
+ span,
+ &msg,
+ accessible_path_strings.into_iter().map(|a| a.0),
+ Applicability::MaybeIncorrect,
+ );
+ } else if let Some(span) = use_placement_span {
for candidate in &mut accessible_path_strings {
// produce an additional newline to separate the new use statement
// from the directly following item.
- let additional_newline = if found_use { "" } else { "\n" };
+ let additional_newline = if let FoundUse::Yes = found_use { "" } else { "\n" };
candidate.0 = format!("use {};\n{}", &candidate.0, additional_newline);
}
span,
&msg,
accessible_path_strings.into_iter().map(|a| a.0),
- Applicability::Unspecified,
+ Applicability::MaybeIncorrect,
);
} else {
msg.push(':');
} else {
assert!(!inaccessible_path_strings.is_empty());
+ let prefix =
+ if let IsPattern::Yes = is_pattern { "you might have meant to match on " } else { "" };
if inaccessible_path_strings.len() == 1 {
let (name, descr, def_id, note) = &inaccessible_path_strings[0];
- let msg = format!("{} `{}` exists but is inaccessible", descr, name);
+ let msg = format!(
+ "{}{} `{}`{} exists but is inaccessible",
+ prefix,
+ descr,
+ name,
+ if let IsPattern::Yes = is_pattern { ", which" } else { "" }
+ );
if let Some(local_def_id) = def_id.and_then(|did| did.as_local()) {
let span = definitions.def_span(local_def_id);
"item".to_string()
};
- let mut msg = format!("these {}s exist but are inaccessible", descr);
+ let mut msg = format!("{}these {}s exist but are inaccessible", prefix, descr);
let mut has_colon = false;
let mut spans = Vec::new();
}
impl UsePlacementFinder {
- fn check(krate: &Crate, target_module: NodeId) -> (Option<Span>, bool) {
+ fn check(krate: &Crate, target_module: NodeId) -> (Option<Span>, FoundUse) {
let mut finder =
UsePlacementFinder { target_module, first_legal_span: None, first_use_span: None };
finder.visit_crate(krate);
if let Some(use_span) = finder.first_use_span {
- (Some(use_span), true)
+ (Some(use_span), FoundUse::Yes)
} else {
- (finder.first_legal_span, false)
+ (finder.first_legal_span, FoundUse::No)
}
}
}
mut ident: Ident,
ns: Namespace,
parent_scope: &ParentScope<'a>,
- finalize_full: Finalize,
+ finalize: Option<Finalize>,
ribs: &[Rib<'a>],
- unusable_binding: Option<&'a NameBinding<'a>>,
+ ignore_binding: Option<&'a NameBinding<'a>>,
) -> Option<LexicalScopeBinding<'a>> {
assert!(ns == TypeNS || ns == ValueNS);
let orig_ident = ident;
let normalized_ident = Ident { span: normalized_span, ..ident };
// Walk backwards up the ribs in scope.
- let finalize = finalize_full.path_span();
let mut module = self.graph_root;
for i in (0..ribs.len()).rev() {
debug!("walk rib\n{:?}", ribs[i].bindings);
i,
rib_ident,
*res,
- finalize,
+ finalize.map(|finalize| finalize.path_span),
*original_rib_ident_def,
ribs,
)));
ns,
parent_scope,
finalize,
- false,
- unusable_binding,
+ ignore_binding,
);
if let Ok(binding) = item {
// The ident resolves to an item.
}
self.early_resolve_ident_in_lexical_scope(
orig_ident,
- ScopeSet::Late(ns, module, finalize_full.node_id()),
+ ScopeSet::Late(ns, module, finalize.map(|finalize| finalize.node_id)),
parent_scope,
finalize,
finalize.is_some(),
- false,
- unusable_binding,
+ ignore_binding,
)
.ok()
.map(LexicalScopeBinding::Item)
orig_ident: Ident,
scope_set: ScopeSet<'a>,
parent_scope: &ParentScope<'a>,
- finalize: Option<Span>,
+ finalize: Option<Finalize>,
force: bool,
- last_import_segment: bool,
- unusable_binding: Option<&'a NameBinding<'a>>,
+ ignore_binding: Option<&'a NameBinding<'a>>,
) -> Result<&'a NameBinding<'a>, Determinacy> {
bitflags::bitflags! {
struct Flags: u8 {
ns,
parent_scope,
finalize,
- last_import_segment,
- unusable_binding,
+ ignore_binding,
);
match binding {
Ok(binding) => Ok((binding, Flags::MODULE | Flags::MISC_SUGGEST_CRATE)),
adjusted_parent_scope,
!matches!(scope_set, ScopeSet::Late(..)),
finalize,
- last_import_segment,
- unusable_binding,
+ ignore_binding,
);
match binding {
Ok(binding) => {
ns,
parent_scope,
None,
- last_import_segment,
- unusable_binding,
+ ignore_binding,
) {
if use_prelude || this.is_builtin_macro(binding.res()) {
result = Ok((binding, Flags::MISC_FROM_PRELUDE));
ns: Namespace,
parent_scope: &ParentScope<'a>,
) -> Result<&'a NameBinding<'a>, Determinacy> {
- self.resolve_ident_in_module_ext(module, ident, ns, parent_scope, None, false, None)
+ self.resolve_ident_in_module_ext(module, ident, ns, parent_scope, None, None)
.map_err(|(determinacy, _)| determinacy)
}
ident: Ident,
ns: Namespace,
parent_scope: &ParentScope<'a>,
- finalize: Option<Span>,
- // We are resolving a last import segment during import validation.
- last_import_segment: bool,
- // This binding should be ignored during in-module resolution, so that we don't get
- // "self-confirming" import resolutions during import validation.
- unusable_binding: Option<&'a NameBinding<'a>>,
+ finalize: Option<Finalize>,
+ ignore_binding: Option<&'a NameBinding<'a>>,
) -> Result<&'a NameBinding<'a>, Determinacy> {
- self.resolve_ident_in_module_ext(
- module,
- ident,
- ns,
- parent_scope,
- finalize,
- last_import_segment,
- unusable_binding,
- )
- .map_err(|(determinacy, _)| determinacy)
+ self.resolve_ident_in_module_ext(module, ident, ns, parent_scope, finalize, ignore_binding)
+ .map_err(|(determinacy, _)| determinacy)
}
#[tracing::instrument(level = "debug", skip(self))]
mut ident: Ident,
ns: Namespace,
parent_scope: &ParentScope<'a>,
- finalize: Option<Span>,
- last_import_segment: bool,
- unusable_binding: Option<&'a NameBinding<'a>>,
+ finalize: Option<Finalize>,
+ ignore_binding: Option<&'a NameBinding<'a>>,
) -> Result<&'a NameBinding<'a>, (Determinacy, Weak)> {
let tmp_parent_scope;
let mut adjusted_parent_scope = parent_scope;
adjusted_parent_scope,
false,
finalize,
- last_import_segment,
- unusable_binding,
+ ignore_binding,
)
}
ident: Ident,
ns: Namespace,
parent_scope: &ParentScope<'a>,
- finalize: Option<Span>,
- last_import_segment: bool,
- unusable_binding: Option<&'a NameBinding<'a>>,
+ finalize: Option<Finalize>,
+ ignore_binding: Option<&'a NameBinding<'a>>,
) -> Result<&'a NameBinding<'a>, Determinacy> {
self.resolve_ident_in_module_unadjusted_ext(
module,
parent_scope,
false,
finalize,
- last_import_segment,
- unusable_binding,
+ ignore_binding,
)
.map_err(|(determinacy, _)| determinacy)
}
ns: Namespace,
parent_scope: &ParentScope<'a>,
restricted_shadowing: bool,
- finalize: Option<Span>,
- last_import_segment: bool,
- unusable_binding: Option<&'a NameBinding<'a>>,
+ finalize: Option<Finalize>,
+ // This binding should be ignored during in-module resolution, so that we don't get
+ // "self-confirming" import resolutions during import validation and checking.
+ ignore_binding: Option<&'a NameBinding<'a>>,
) -> Result<&'a NameBinding<'a>, (Determinacy, Weak)> {
let module = match module {
ModuleOrUniformRoot::Module(module) => module,
parent_scope,
finalize,
finalize.is_some(),
- last_import_segment,
- unusable_binding,
+ ignore_binding,
);
return binding.map_err(|determinacy| (determinacy, Weak::No));
}
parent_scope,
finalize,
finalize.is_some(),
- last_import_segment,
- unusable_binding,
+ ignore_binding,
);
return binding.map_err(|determinacy| (determinacy, Weak::No));
}
let resolution =
self.resolution(module, key).try_borrow_mut().map_err(|_| (Determined, Weak::No))?; // This happens when there is a cycle of imports.
- if let Some(path_span) = finalize {
+ if let Some(Finalize { path_span, report_private, .. }) = finalize {
// If the primary binding is unusable, search further and return the shadowed glob
// binding if it exists. What we really want here is having two separate scopes in
// a module - one for non-globs and one for globs, but until that's done use this
// hack to avoid inconsistent resolution ICEs during import validation.
let binding = [resolution.binding, resolution.shadowed_glob]
.into_iter()
- .filter_map(|binding| match (binding, unusable_binding) {
- (Some(binding), Some(unusable_binding))
- if ptr::eq(binding, unusable_binding) =>
- {
- None
- }
+ .filter_map(|binding| match (binding, ignore_binding) {
+ (Some(binding), Some(ignored)) if ptr::eq(binding, ignored) => None,
_ => binding,
})
.next();
};
if !self.is_accessible_from(binding.vis, parent_scope.module) {
- if last_import_segment {
- return Err((Determined, Weak::No));
- } else {
+ if report_private {
self.privacy_errors.push(PrivacyError {
ident,
binding,
dedup_span: path_span,
});
+ } else {
+ return Err((Determined, Weak::No));
}
}
}
let check_usable = |this: &mut Self, binding: &'a NameBinding<'a>| {
- if let Some(unusable_binding) = unusable_binding {
- if ptr::eq(binding, unusable_binding) {
- return Err((Determined, Weak::No));
- }
+ if let Some(ignored) = ignore_binding && ptr::eq(binding, ignored) {
+ return Err((Determined, Weak::No));
}
let usable = this.is_accessible_from(binding.vis, parent_scope.module);
if usable { Ok(binding) } else { Err((Determined, Weak::No)) }
ns,
&single_import.parent_scope,
None,
- last_import_segment,
- unusable_binding,
+ ignore_binding,
) {
Err(Determined) => continue,
Ok(binding)
ns,
adjusted_parent_scope,
None,
- last_import_segment,
- unusable_binding,
+ ignore_binding,
);
match result {
ConstantItemRibKind(trivial, _) => {
let features = self.session.features_untracked();
// HACK(min_const_generics): We currently only allow `N` or `{ N }`.
- if !(trivial || features.generic_const_exprs) {
+ if !(trivial == HasGenericParams::Yes || features.generic_const_exprs) {
// HACK(min_const_generics): If we encounter `Self` in an anonymous constant
// we can't easily tell if it's generic at this stage, so we instead remember
// this and then enforce the self type to be concrete later on.
ConstantItemRibKind(trivial, _) => {
let features = self.session.features_untracked();
// HACK(min_const_generics): We currently only allow `N` or `{ N }`.
- if !(trivial || features.generic_const_exprs) {
+ if !(trivial == HasGenericParams::Yes || features.generic_const_exprs) {
if let Some(span) = finalize {
self.report_error(
span,
opt_ns: Option<Namespace>, // `None` indicates a module path in import
parent_scope: &ParentScope<'a>,
) -> PathResult<'a> {
- self.resolve_path_with_ribs(path, opt_ns, parent_scope, Finalize::No, None, None)
+ self.resolve_path_with_ribs(path, opt_ns, parent_scope, None, None, None)
}
#[tracing::instrument(level = "debug", skip(self))]
path: &[Segment],
opt_ns: Option<Namespace>, // `None` indicates a module path in import
parent_scope: &ParentScope<'a>,
- finalize: Finalize,
- unusable_binding: Option<&'a NameBinding<'a>>,
+ finalize: Option<Finalize>,
+ ignore_binding: Option<&'a NameBinding<'a>>,
) -> PathResult<'a> {
- self.resolve_path_with_ribs(path, opt_ns, parent_scope, finalize, None, unusable_binding)
+ self.resolve_path_with_ribs(path, opt_ns, parent_scope, finalize, None, ignore_binding)
}
crate fn resolve_path_with_ribs(
path: &[Segment],
opt_ns: Option<Namespace>, // `None` indicates a module path in import
parent_scope: &ParentScope<'a>,
- finalize_full: Finalize,
+ finalize: Option<Finalize>,
ribs: Option<&PerNS<Vec<Rib<'a>>>>,
- unusable_binding: Option<&'a NameBinding<'a>>,
+ ignore_binding: Option<&'a NameBinding<'a>>,
) -> PathResult<'a> {
- debug!("resolve_path(path={:?}, opt_ns={:?}, finalize={:?})", path, opt_ns, finalize_full);
+ debug!("resolve_path(path={:?}, opt_ns={:?}, finalize={:?})", path, opt_ns, finalize);
- let finalize = finalize_full.path_span();
let mut module = None;
let mut allow_super = true;
let mut second_binding = None;
ns,
parent_scope,
finalize,
- false,
- unusable_binding,
+ ignore_binding,
)
} else if let Some(ribs) = ribs
&& let Some(TypeNS | ValueNS) = opt_ns
ident,
ns,
parent_scope,
- finalize_full,
+ finalize,
&ribs[ns],
- unusable_binding,
+ ignore_binding,
) {
// we found a locally-imported or available item/module
Some(LexicalScopeBinding::Item(binding)) => Ok(binding),
parent_scope,
finalize,
finalize.is_some(),
- false,
- unusable_binding,
+ ignore_binding,
)
};
FindBindingResult::Binding(binding)
} else if res == Res::Err {
return PathResult::NonModule(PartialRes::new(Res::Err));
} else if opt_ns.is_some() && (is_last || maybe_assoc) {
- self.lint_if_path_starts_with_module(finalize_full, path, second_binding);
+ self.lint_if_path_starts_with_module(finalize, path, second_binding);
return PathResult::NonModule(PartialRes::with_unresolved_segments(
res,
path.len() - i - 1,
opt_ns,
parent_scope,
ribs,
- unusable_binding,
+ ignore_binding,
module,
i,
ident,
}
}
- self.lint_if_path_starts_with_module(finalize_full, path, second_binding);
+ self.lint_if_path_starts_with_module(finalize, path, second_binding);
PathResult::Module(match module {
Some(module) => module,
use rustc_data_structures::intern::Interned;
use rustc_errors::{pluralize, struct_span_err, Applicability, MultiSpan};
use rustc_hir::def::{self, PartialRes};
-use rustc_hir::def_id::DefId;
use rustc_middle::metadata::ModChild;
use rustc_middle::span_bug;
use rustc_middle::ty;
pub r: &'a mut Resolver<'b>,
}
-impl<'a, 'b> ty::DefIdTree for &'a ImportResolver<'a, 'b> {
- fn parent(self, id: DefId) -> Option<DefId> {
- self.r.parent(id)
- }
-}
-
impl<'a, 'b> ImportResolver<'a, 'b> {
// Import resolution
//
ns,
&import.parent_scope,
None,
- false,
None,
);
import.vis.set(orig_vis);
/// consolidate multiple unresolved import errors into a single diagnostic.
fn finalize_import(&mut self, import: &'b Import<'b>) -> Option<UnresolvedImportError> {
let orig_vis = import.vis.replace(ty::Visibility::Invisible);
- let unusable_binding = match &import.kind {
+ let ignore_binding = match &import.kind {
ImportKind::Single { target_bindings, .. } => target_bindings[TypeNS].get(),
_ => None,
};
let prev_ambiguity_errors_len = self.r.ambiguity_errors.len();
- let finalize = Finalize::UsePath {
- root_id: import.root_id,
- root_span: import.root_span,
- path_span: import.span,
- };
+ let finalize = Finalize::with_root_span(import.root_id, import.span, import.root_span);
let path_res = self.r.resolve_path(
&import.module_path,
None,
&import.parent_scope,
- finalize,
- unusable_binding,
+ Some(finalize),
+ ignore_binding,
);
let no_ambiguity = self.r.ambiguity_errors.len() == prev_ambiguity_errors_len;
import.vis.set(orig_vis);
// 2 segments, so the `resolve_path` above won't trigger it.
let mut full_path = import.module_path.clone();
full_path.push(Segment::from_ident(Ident::empty()));
- self.r.lint_if_path_starts_with_module(finalize, &full_path, None);
+ self.r.lint_if_path_starts_with_module(Some(finalize), &full_path, None);
}
if let ModuleOrUniformRoot::Module(module) = module {
}
if !is_prelude &&
max_vis.get() != ty::Visibility::Invisible && // Allow empty globs.
- !max_vis.get().is_at_least(import.vis.get(), &*self)
+ !max_vis.get().is_at_least(import.vis.get(), &*self.r)
{
let msg = "glob import doesn't reexport anything because no candidate is public enough";
self.r.lint_buffer.buffer_lint(UNUSED_IMPORTS, import.id, import.span, msg);
ident,
ns,
&import.parent_scope,
- Some(import.span),
- true,
+ Some(Finalize { report_private: false, ..finalize }),
target_bindings[ns].get(),
);
import.vis.set(orig_vis);
ident,
ns,
&import.parent_scope,
- Some(import.span),
- false,
+ Some(finalize),
None,
);
if binding.is_ok() {
full_path.push(Segment::from_ident(ident));
self.r.per_ns(|this, ns| {
if let Ok(binding) = source_bindings[ns].get() {
- this.lint_if_path_starts_with_module(finalize, &full_path, Some(binding));
+ this.lint_if_path_starts_with_module(Some(finalize), &full_path, Some(binding));
}
});
}
&import.parent_scope,
None,
false,
- false,
target_bindings[ns].get(),
) {
Ok(other_binding) => {
use crate::{ResolutionError, Resolver, Segment, UseError};
use rustc_ast::ptr::P;
-use rustc_ast::visit::{self, AssocCtxt, FnCtxt, FnKind, Visitor};
+use rustc_ast::visit::{self, AssocCtxt, BoundKind, FnCtxt, FnKind, Visitor};
use rustc_ast::*;
-use rustc_ast_lowering::ResolverAstLowering;
-use rustc_data_structures::fx::{FxHashMap, FxHashSet};
+use rustc_ast_lowering::{LifetimeRes, ResolverAstLowering};
+use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap};
use rustc_errors::DiagnosticId;
use rustc_hir::def::Namespace::{self, *};
use rustc_hir::def::{self, CtorKind, DefKind, PartialRes, PerNS};
use rustc_hir::def_id::{DefId, CRATE_DEF_ID};
+use rustc_hir::definitions::DefPathData;
use rustc_hir::{PrimTy, TraitCandidate};
+use rustc_index::vec::Idx;
use rustc_middle::ty::DefIdTree;
use rustc_middle::{bug, span_bug};
use rustc_session::lint;
No,
}
+impl HasGenericParams {
+ fn force_yes_if(self, b: bool) -> Self {
+ if b { Self::Yes } else { self }
+ }
+}
+
#[derive(Copy, Clone, Debug, Eq, PartialEq)]
crate enum ConstantItemKind {
Const,
/// We're in a constant item. Can't refer to dynamic stuff.
///
- /// The `bool` indicates if this constant may reference generic parameters
- /// and is used to only allow generic parameters to be used in trivial constant expressions.
- ConstantItemRibKind(bool, Option<(Ident, ConstantItemKind)>),
+ /// The item may reference generic parameters in trivial constant expressions.
+ /// All other constants aren't allowed to use generic params at all.
+ ConstantItemRibKind(HasGenericParams, Option<(Ident, ConstantItemKind)>),
/// We passed through a module.
ModuleRibKind(Module<'a>),
Item,
/// This rib declares generic parameters.
- Generics { span: Span, kind: LifetimeBinderKind },
+ Generics { parent: NodeId, span: Span, kind: LifetimeBinderKind },
+
+ /// FIXME(const_generics): This patches over an ICE caused by non-'static lifetimes in const
+ /// generics. We are disallowing this until we can decide on how we want to handle non-'static
+ /// lifetimes in const generics. See issue #74052 for discussion.
+ ConstGeneric,
+
+ /// Non-static lifetimes are prohibited in anonymous constants under `min_const_generics`.
+ /// This function will emit an error if `generic_const_exprs` is not enabled, the body identified by
+ /// `body_id` is an anonymous constant and `lifetime_ref` is non-static.
+ AnonConst,
/// For **Modern** cases, create a new anonymous region parameter
/// and reference that.
/// `resolve_lifetime` code.
///
/// For **Deprecated** cases, report an error.
- AnonymousCreateParameter,
+ AnonymousCreateParameter(NodeId),
/// Give a hard error when either `&` or `'_` is written. Used to
/// rule out things like `where T: Foo<'_>`. Does not imply an
AnonymousReportError,
/// Pass responsibility to `resolve_lifetime` code for all cases.
- AnonymousPassThrough,
+ AnonymousPassThrough(NodeId),
}
#[derive(Copy, Clone, Debug)]
#[derive(Debug)]
struct LifetimeRib {
kind: LifetimeRibKind,
- bindings: IdentMap<()>,
+ // We need to preserve insertion order for async fns.
+ bindings: FxIndexMap<Ident, (NodeId, LifetimeRes)>,
}
impl LifetimeRib {
}
fn visit_anon_const(&mut self, constant: &'ast AnonConst) {
// We deal with repeat expressions explicitly in `resolve_expr`.
- self.resolve_anon_const(constant, IsRepeatExpr::No);
+ self.with_lifetime_rib(LifetimeRibKind::AnonConst, |this| {
+ this.resolve_anon_const(constant, IsRepeatExpr::No);
+ })
}
fn visit_expr(&mut self, expr: &'ast Expr) {
self.resolve_expr(expr, None);
.resolve_ident_in_lexical_scope(
self_ty,
TypeNS,
- Finalize::SimplePath(ty.id, ty.span),
+ Some(Finalize::new(ty.id, ty.span)),
None,
)
.map_or(Res::Err, |d| d.res());
self.with_generic_param_rib(
&bare_fn.generic_params,
NormalRibKind,
- LifetimeRibKind::Generics { kind: LifetimeBinderKind::BareFnType, span },
+ LifetimeRibKind::Generics {
+ parent: ty.id,
+ kind: LifetimeBinderKind::BareFnType,
+ span,
+ },
|this| {
- this.with_lifetime_rib(LifetimeRibKind::AnonymousPassThrough, |this| {
- this.visit_generic_param_vec(&bare_fn.generic_params, false);
- visit::walk_fn_decl(this, &bare_fn.decl);
- });
+ this.with_lifetime_rib(
+ LifetimeRibKind::AnonymousPassThrough(ty.id),
+ |this| {
+ this.visit_generic_param_vec(&bare_fn.generic_params, false);
+ visit::walk_fn_decl(this, &bare_fn.decl);
+ },
+ );
},
);
self.diagnostic_metadata.current_trait_object = prev;
self.with_generic_param_rib(
&tref.bound_generic_params,
NormalRibKind,
- LifetimeRibKind::Generics { kind: LifetimeBinderKind::PolyTrait, span },
+ LifetimeRibKind::Generics {
+ parent: tref.trait_ref.ref_id,
+ kind: LifetimeBinderKind::PolyTrait,
+ span,
+ },
|this| {
this.visit_generic_param_vec(&tref.bound_generic_params, false);
this.smart_resolve_path(
&generics.params,
ItemRibKind(HasGenericParams::Yes),
LifetimeRibKind::Generics {
+ parent: foreign_item.id,
kind: LifetimeBinderKind::Item,
span: generics.span,
},
&generics.params,
ItemRibKind(HasGenericParams::Yes),
LifetimeRibKind::Generics {
+ parent: foreign_item.id,
kind: LifetimeBinderKind::Function,
span: generics.span,
},
}
}
}
- fn visit_fn(&mut self, fn_kind: FnKind<'ast>, sp: Span, _: NodeId) {
+ fn visit_fn(&mut self, fn_kind: FnKind<'ast>, sp: Span, fn_id: NodeId) {
let rib_kind = match fn_kind {
// Bail if the function is foreign, and thus cannot validly have
// a body, or if there's no body for some other reason.
FnKind::Fn(FnCtxt::Foreign, _, sig, _, generics, _)
| FnKind::Fn(_, _, sig, _, generics, None) => {
- self.with_lifetime_rib(LifetimeRibKind::AnonymousPassThrough, |this| {
+ self.with_lifetime_rib(LifetimeRibKind::AnonymousPassThrough(fn_id), |this| {
// We don't need to deal with patterns in parameters, because
// they are not possible for foreign or bodiless functions.
this.visit_fn_header(&sig.header);
this.visit_generics(generics);
}
- if async_node_id.is_some() {
+ if let Some(async_node_id) = async_node_id {
// In `async fn`, argument-position elided lifetimes
// must be transformed into fresh generic parameters so that
// they can be applied to the opaque `impl Trait` return type.
- this.with_lifetime_rib(LifetimeRibKind::AnonymousCreateParameter, |this| {
- // Add each argument to the rib.
- this.resolve_params(&declaration.inputs)
- });
+ this.with_lifetime_rib(
+ LifetimeRibKind::AnonymousCreateParameter(fn_id),
+ |this| {
+ // Add each argument to the rib.
+ this.resolve_params(&declaration.inputs)
+ },
+ );
- this.with_lifetime_rib(LifetimeRibKind::AnonymousPassThrough, |this| {
- visit::walk_fn_ret_ty(this, &declaration.output)
- });
+ // Construct the list of in-scope lifetime parameters for async lowering.
+ // We include all lifetime parameters, either named or "Fresh".
+ // The order of those parameters does not matter, as long as it is
+ // deterministic.
+ let mut extra_lifetime_params =
+ this.r.extra_lifetime_params_map.get(&fn_id).cloned().unwrap_or_default();
+ for rib in this.lifetime_ribs.iter().rev() {
+ extra_lifetime_params.extend(
+ rib.bindings
+ .iter()
+ .map(|(&ident, &(node_id, res))| (ident, node_id, res)),
+ );
+ match rib.kind {
+ LifetimeRibKind::Item => break,
+ LifetimeRibKind::AnonymousCreateParameter(id) => {
+ if let Some(earlier_fresh) =
+ this.r.extra_lifetime_params_map.get(&id)
+ {
+ extra_lifetime_params.extend(earlier_fresh);
+ }
+ }
+ _ => {}
+ }
+ }
+ this.r.extra_lifetime_params_map.insert(async_node_id, extra_lifetime_params);
+
+ this.with_lifetime_rib(
+ LifetimeRibKind::AnonymousPassThrough(async_node_id),
+ |this| visit::walk_fn_ret_ty(this, &declaration.output),
+ );
} else {
- this.with_lifetime_rib(LifetimeRibKind::AnonymousPassThrough, |this| {
+ this.with_lifetime_rib(LifetimeRibKind::AnonymousPassThrough(fn_id), |this| {
// Add each argument to the rib.
this.resolve_params(&declaration.inputs);
// Be sure not to set this until the function signature has been resolved.
let previous_state = replace(&mut this.in_func_body, true);
// Resolve the function body, potentially inside the body of an async closure
- this.with_lifetime_rib(
- LifetimeRibKind::AnonymousPassThrough,
- |this| match fn_kind {
+ this.with_lifetime_rib(LifetimeRibKind::AnonymousPassThrough(fn_id), |this| {
+ match fn_kind {
FnKind::Fn(.., body) => walk_list!(this, visit_block, body),
FnKind::Closure(_, body) => this.visit_expr(body),
- },
- );
+ }
+ });
debug!("(resolving function) leaving function");
this.in_func_body = previous_state;
// Note that we might not be inside of an repeat expression here,
// but considering that `IsRepeatExpr` is only relevant for
// non-trivial constants this is doesn't matter.
- self.with_constant_rib(IsRepeatExpr::No, true, None, |this| {
- this.smart_resolve_path(
- ty.id,
- qself.as_ref(),
- path,
- PathSource::Expr(None),
- );
-
- if let Some(ref qself) = *qself {
- this.visit_ty(&qself.ty);
- }
- this.visit_path(path, ty.id);
- });
+ self.with_constant_rib(
+ IsRepeatExpr::No,
+ HasGenericParams::Yes,
+ None,
+ |this| {
+ this.smart_resolve_path(
+ ty.id,
+ qself.as_ref(),
+ path,
+ PathSource::Expr(None),
+ );
+
+ if let Some(ref qself) = *qself {
+ this.visit_ty(&qself.ty);
+ }
+ this.visit_path(path, ty.id);
+ },
+ );
self.diagnostic_metadata.currently_processing_generics = prev;
return;
if let Some(ref args) = path_segment.args {
match &**args {
GenericArgs::AngleBracketed(..) => visit::walk_generic_args(self, path_span, args),
- GenericArgs::Parenthesized(..) => self
- .with_lifetime_rib(LifetimeRibKind::AnonymousPassThrough, |this| {
- visit::walk_generic_args(this, path_span, args)
- }),
+ GenericArgs::Parenthesized(..) => self.with_lifetime_rib(
+ LifetimeRibKind::AnonymousPassThrough(path_segment.id),
+ |this| visit::walk_generic_args(this, path_span, args),
+ ),
}
}
}
this.with_generic_param_rib(
&bound_generic_params,
NormalRibKind,
- LifetimeRibKind::Generics { kind: LifetimeBinderKind::WhereBound, span },
+ LifetimeRibKind::Generics {
+ parent: bounded_ty.id,
+ kind: LifetimeBinderKind::WhereBound,
+ span,
+ },
|this| {
this.visit_generic_param_vec(&bound_generic_params, false);
this.visit_ty(bounded_ty);
for bound in bounds {
- this.visit_param_bound(bound)
+ this.visit_param_bound(bound, BoundKind::Bound)
}
},
);
ident,
ns,
&self.parent_scope,
- Finalize::No,
+ None,
&self.ribs[ns],
None,
)
&mut self,
ident: Ident,
ns: Namespace,
- finalize: Finalize,
- unusable_binding: Option<&'a NameBinding<'a>>,
+ finalize: Option<Finalize>,
+ ignore_binding: Option<&'a NameBinding<'a>>,
) -> Option<LexicalScopeBinding<'a>> {
self.r.resolve_ident_in_lexical_scope(
ident,
&self.parent_scope,
finalize,
&self.ribs[ns],
- unusable_binding,
+ ignore_binding,
)
}
&mut self,
path: &[Segment],
opt_ns: Option<Namespace>, // `None` indicates a module path in import
- finalize: Finalize,
+ finalize: Option<Finalize>,
) -> PathResult<'a> {
self.r.resolve_path_with_ribs(
path,
match param.kind {
GenericParamKind::Lifetime => {
for bound in ¶m.bounds {
- this.visit_param_bound(bound);
+ this.visit_param_bound(bound, BoundKind::Bound);
}
}
GenericParamKind::Type { ref default } => {
for bound in ¶m.bounds {
- this.visit_param_bound(bound);
+ this.visit_param_bound(bound, BoundKind::Bound);
}
if let Some(ref ty) = default {
this.ribs[TypeNS].push(Rib::new(ConstParamTyRibKind));
this.ribs[ValueNS].push(Rib::new(ConstParamTyRibKind));
- this.visit_ty(ty);
+ this.with_lifetime_rib(LifetimeRibKind::ConstGeneric, |this| {
+ this.visit_ty(ty)
+ });
this.ribs[TypeNS].pop().unwrap();
this.ribs[ValueNS].pop().unwrap();
if let Some(ref expr) = default {
this.ribs[TypeNS].push(forward_ty_ban_rib);
this.ribs[ValueNS].push(forward_const_ban_rib);
- this.visit_anon_const(expr);
+ this.with_lifetime_rib(LifetimeRibKind::ConstGeneric, |this| {
+ this.resolve_anon_const(expr, IsRepeatExpr::No)
+ });
forward_const_ban_rib = this.ribs[ValueNS].pop().unwrap();
forward_ty_ban_rib = this.ribs[TypeNS].pop().unwrap();
}
let ident = lifetime.ident;
if ident.name == kw::StaticLifetime {
+ self.record_lifetime_res(lifetime.id, LifetimeRes::Static);
return;
}
for i in &mut indices {
let rib = &self.lifetime_ribs[i];
let normalized_ident = ident.normalize_to_macros_2_0();
- if let Some(_) = rib.bindings.get_key_value(&normalized_ident) {
+ if let Some(&(_, region)) = rib.bindings.get(&normalized_ident) {
+ self.record_lifetime_res(lifetime.id, region);
return;
}
- if let LifetimeRibKind::Item = rib.kind {
- break;
+ match rib.kind {
+ LifetimeRibKind::Item => break,
+ LifetimeRibKind::ConstGeneric => {
+ self.emit_non_static_lt_in_const_generic_error(lifetime);
+ self.r.lifetimes_res_map.insert(lifetime.id, LifetimeRes::Error);
+ return;
+ }
+ LifetimeRibKind::AnonConst => {
+ self.maybe_emit_forbidden_non_static_lifetime_error(lifetime);
+ self.r.lifetimes_res_map.insert(lifetime.id, LifetimeRes::Error);
+ return;
+ }
+ _ => {}
}
}
}
self.emit_undeclared_lifetime_error(lifetime, outer_res);
+ self.record_lifetime_res(lifetime.id, LifetimeRes::Error);
}
#[tracing::instrument(level = "debug", skip(self))]
for i in (0..self.lifetime_ribs.len()).rev() {
let rib = &mut self.lifetime_ribs[i];
match rib.kind {
+ LifetimeRibKind::AnonymousCreateParameter(item_node_id) => {
+ self.create_fresh_lifetime(lifetime.id, lifetime.ident, item_node_id);
+ return;
+ }
LifetimeRibKind::AnonymousReportError => {
let (msg, note) = if elided {
(
.span_label(lifetime.ident.span, note)
.emit();
+ self.record_lifetime_res(lifetime.id, LifetimeRes::Error);
return;
}
- LifetimeRibKind::AnonymousCreateParameter
- | LifetimeRibKind::AnonymousPassThrough
- | LifetimeRibKind::Item => return,
+ LifetimeRibKind::AnonymousPassThrough(node_id) => {
+ self.record_lifetime_res(
+ lifetime.id,
+ LifetimeRes::Anonymous { binder: node_id, elided },
+ );
+ return;
+ }
+ LifetimeRibKind::Item => break,
_ => {}
}
}
+ // This resolution is wrong, it passes the work to HIR lifetime resolution.
+ // We cannot use `LifetimeRes::Error` because we do not emit a diagnostic.
+ self.record_lifetime_res(
+ lifetime.id,
+ LifetimeRes::Anonymous { binder: DUMMY_NODE_ID, elided },
+ );
}
#[tracing::instrument(level = "debug", skip(self))]
fn resolve_elided_lifetime(&mut self, anchor_id: NodeId, span: Span) {
let id = self.r.next_node_id();
+ self.record_lifetime_res(
+ anchor_id,
+ LifetimeRes::ElidedAnchor { start: id, end: NodeId::from_u32(id.as_u32() + 1) },
+ );
+
let lt = Lifetime { id, ident: Ident::new(kw::UnderscoreLifetime, span) };
self.resolve_anonymous_lifetime(<, true);
}
+ #[tracing::instrument(level = "debug", skip(self))]
+ fn create_fresh_lifetime(&mut self, id: NodeId, ident: Ident, item_node_id: NodeId) {
+ debug_assert_eq!(ident.name, kw::UnderscoreLifetime);
+ debug!(?ident.span);
+ let item_def_id = self.r.local_def_id(item_node_id);
+ let def_node_id = self.r.next_node_id();
+ let def_id = self.r.create_def(
+ item_def_id,
+ def_node_id,
+ DefPathData::LifetimeNs(kw::UnderscoreLifetime),
+ self.parent_scope.expansion.to_expn_id(),
+ ident.span,
+ );
+ debug!(?def_id);
+
+ let region = LifetimeRes::Fresh { param: def_id, binder: item_node_id };
+ self.record_lifetime_res(id, region);
+ self.r.extra_lifetime_params_map.entry(item_node_id).or_insert_with(Vec::new).push((
+ ident,
+ def_node_id,
+ region,
+ ));
+ }
+
#[tracing::instrument(level = "debug", skip(self))]
fn resolve_elided_lifetimes_in_path(
&mut self,
partial_res: PartialRes,
path: &[Segment],
source: PathSource<'_>,
- finalize: Finalize,
+ path_span: Span,
) {
- let Some(path_span) = finalize.path_span() else {
- return;
- };
let proj_start = path.len() - partial_res.unresolved_segments();
for (i, segment) in path.iter().enumerate() {
if segment.has_lifetime_args {
// Figure out if this is a type/trait segment,
// which may need lifetime elision performed.
let type_def_id = match partial_res.base_res() {
- Res::Def(DefKind::AssocTy, def_id) if i + 2 == proj_start => {
- self.r.parent(def_id).unwrap()
- }
- Res::Def(DefKind::Variant, def_id) if i + 1 == proj_start => {
- self.r.parent(def_id).unwrap()
- }
+ Res::Def(DefKind::AssocTy, def_id) if i + 2 == proj_start => self.r.parent(def_id),
+ Res::Def(DefKind::Variant, def_id) if i + 1 == proj_start => self.r.parent(def_id),
Res::Def(DefKind::Struct, def_id)
| Res::Def(DefKind::Union, def_id)
| Res::Def(DefKind::Enum, def_id)
| PathSource::Struct
| PathSource::TupleStruct(..) => false,
};
- let mut error = false;
+ let mut res = LifetimeRes::Error;
for rib in self.lifetime_ribs.iter().rev() {
match rib.kind {
// In create-parameter mode we error here because we don't want to support
//
// impl Foo for std::cell::Ref<u32> // note lack of '_
// async fn foo(_: std::cell::Ref<u32>) { ... }
- LifetimeRibKind::AnonymousCreateParameter => {
- error = true;
+ LifetimeRibKind::AnonymousCreateParameter(_) => {
break;
}
// `PassThrough` is the normal case.
// `PathSegment`, for which there is no associated `'_` or `&T` with no explicit
// lifetime. Instead, we simply create an implicit lifetime, which will be checked
// later, at which point a suitable error will be emitted.
- LifetimeRibKind::AnonymousPassThrough
- | LifetimeRibKind::AnonymousReportError
- | LifetimeRibKind::Item => break,
+ LifetimeRibKind::AnonymousPassThrough(binder) => {
+ res = LifetimeRes::Anonymous { binder, elided: true };
+ break;
+ }
+ LifetimeRibKind::AnonymousReportError | LifetimeRibKind::Item => {
+ // FIXME(cjgillot) This resolution is wrong, but this does not matter
+ // since these cases are erroneous anyway. Lifetime resolution should
+ // emit a "missing lifetime specifier" diagnostic.
+ res = LifetimeRes::Anonymous { binder: DUMMY_NODE_ID, elided: true };
+ break;
+ }
_ => {}
}
}
+ let node_ids = self.r.next_node_ids(expected_lifetimes);
+ self.record_lifetime_res(
+ segment_id,
+ LifetimeRes::ElidedAnchor { start: node_ids.start, end: node_ids.end },
+ );
+ for i in 0..expected_lifetimes {
+ let id = node_ids.start.plus(i);
+ self.record_lifetime_res(id, res);
+ }
+
if !missing {
continue;
}
// originating from macros, since the segment's span might be from a macro arg.
segment.ident.span.find_ancestor_inside(path_span).unwrap_or(path_span)
};
- if error {
+ if let LifetimeRes::Error = res {
let sess = self.r.session;
let mut err = rustc_errors::struct_span_err!(
sess,
}
}
+ #[tracing::instrument(level = "debug", skip(self))]
+ fn record_lifetime_res(&mut self, id: NodeId, res: LifetimeRes) {
+ if let Some(prev_res) = self.r.lifetimes_res_map.insert(id, res) {
+ panic!(
+ "lifetime {:?} resolved multiple times ({:?} before, {:?} now)",
+ id, prev_res, res
+ )
+ }
+ }
+
/// Searches the current set of local scopes for labels. Returns the `NodeId` of the resolved
/// label and reports an error if the label is not found or is unreachable.
- fn resolve_label(&self, mut label: Ident) -> Option<NodeId> {
+ fn resolve_label(&mut self, mut label: Ident) -> Option<NodeId> {
let mut suggestion = None;
// Preserve the original span so that errors contain "in this macro invocation"
let ident = label.normalize_to_macro_rules();
if let Some((ident, id)) = rib.bindings.get_key_value(&ident) {
+ let definition_span = ident.span;
return if self.is_label_valid_from_rib(i) {
Some(*id)
} else {
original_span,
ResolutionError::UnreachableLabel {
name: label.name,
- definition_span: ident.span,
+ definition_span,
suggestion,
},
);
this.with_generic_param_rib(
&generics.params,
ItemRibKind(HasGenericParams::Yes),
- LifetimeRibKind::Generics { kind: LifetimeBinderKind::Item, span: generics.span },
+ LifetimeRibKind::Generics {
+ parent: item.id,
+ kind: LifetimeBinderKind::Item,
+ span: generics.span,
+ },
|this| {
let item_def_id = this.r.local_def_id(item.id).to_def_id();
this.with_self_rib(
report_error(self, ns);
}
Some(LexicalScopeBinding::Item(binding)) => {
- if let Some(LexicalScopeBinding::Res(..)) = self
- .resolve_ident_in_lexical_scope(ident, ns, Finalize::No, Some(binding))
+ if let Some(LexicalScopeBinding::Res(..)) =
+ self.resolve_ident_in_lexical_scope(ident, ns, None, Some(binding))
{
report_error(self, ns);
}
&generics.params,
ItemRibKind(HasGenericParams::Yes),
LifetimeRibKind::Generics {
+ parent: item.id,
kind: LifetimeBinderKind::Item,
span: generics.span,
},
&generics.params,
ItemRibKind(HasGenericParams::Yes),
LifetimeRibKind::Generics {
+ parent: item.id,
kind: LifetimeBinderKind::Function,
span: generics.span,
},
&generics.params,
ItemRibKind(HasGenericParams::Yes),
LifetimeRibKind::Generics {
+ parent: item.id,
kind: LifetimeBinderKind::Item,
span: generics.span,
},
Res::SelfTy { trait_: Some(local_def_id), alias_to: None },
|this| {
this.visit_generics(generics);
- walk_list!(this, visit_param_bound, bounds);
+ walk_list!(this, visit_param_bound, bounds, BoundKind::SuperTraits);
let walk_assoc_item =
|this: &mut Self,
this.with_generic_param_rib(
&generics.params,
AssocItemRibKind,
- LifetimeRibKind::Generics { span: generics.span, kind },
+ LifetimeRibKind::Generics {
+ parent: item.id,
+ span: generics.span,
+ kind,
+ },
|this| {
visit::walk_assoc_item(this, item, AssocCtxt::Trait)
},
// not used as part of the type system, this is far less surprising.
this.with_constant_rib(
IsRepeatExpr::No,
- true,
+ HasGenericParams::Yes,
None,
|this| this.visit_expr(expr),
);
&generics.params,
ItemRibKind(HasGenericParams::Yes),
LifetimeRibKind::Generics {
+ parent: item.id,
kind: LifetimeBinderKind::Item,
span: generics.span,
},
Res::SelfTy { trait_: Some(local_def_id), alias_to: None },
|this| {
this.visit_generics(generics);
- walk_list!(this, visit_param_bound, bounds);
+ walk_list!(this, visit_param_bound, bounds, BoundKind::Bound);
},
);
},
// so it doesn't matter whether this is a trivial constant.
this.with_constant_rib(
IsRepeatExpr::No,
- true,
+ HasGenericParams::Yes,
Some((item.ident, constant_item_kind)),
|this| this.visit_expr(expr),
);
"invalid lifetime parameter name: `{}`",
param.ident,
)
- .span_label(param.ident.span, format!("'static is a reserved lifetime name"))
+ .span_label(param.ident.span, "'static is a reserved lifetime name")
.emit();
continue;
}
GenericParamKind::Type { .. } => (&mut function_type_rib, DefKind::TyParam),
GenericParamKind::Const { .. } => (&mut function_value_rib, DefKind::ConstParam),
GenericParamKind::Lifetime => {
- function_lifetime_rib.bindings.insert(ident, ());
+ let LifetimeRibKind::Generics { parent, .. } = lifetime_kind else { panic!() };
+ let res = LifetimeRes::Param { param: def_id, binder: parent };
+ self.record_lifetime_res(param.id, res);
+ function_lifetime_rib.bindings.insert(ident, (param.id, res));
continue;
}
};
// Note that we intentionally still forbid `[0; N + 1]` during
// name resolution so that we don't extend the future
// compat lint to new cases.
+ #[instrument(level = "debug", skip(self, f))]
fn with_constant_rib(
&mut self,
is_repeat: IsRepeatExpr,
- is_trivial: bool,
+ may_use_generics: HasGenericParams,
item: Option<(Ident, ConstantItemKind)>,
f: impl FnOnce(&mut Self),
) {
- debug!("with_constant_rib: is_repeat={:?} is_trivial={}", is_repeat, is_trivial);
- self.with_rib(ValueNS, ConstantItemRibKind(is_trivial, item), |this| {
+ self.with_rib(ValueNS, ConstantItemRibKind(may_use_generics, item), |this| {
this.with_rib(
TypeNS,
- ConstantItemRibKind(is_repeat == IsRepeatExpr::Yes || is_trivial, item),
+ ConstantItemRibKind(
+ may_use_generics.force_yes_if(is_repeat == IsRepeatExpr::Yes),
+ item,
+ ),
|this| {
- this.with_label_rib(ConstantItemRibKind(is_trivial, item), f);
+ this.with_label_rib(ConstantItemRibKind(may_use_generics, item), f);
},
)
});
None,
&path,
PathSource::Trait(AliasPossibility::No),
- Finalize::SimplePath(trait_ref.ref_id, trait_ref.path.span),
+ Finalize::new(trait_ref.ref_id, trait_ref.path.span),
);
if let Some(def_id) = res.base_res().opt_def_id() {
new_id = Some(def_id);
) {
debug!("resolve_implementation");
// If applicable, create a rib for the type parameters.
- self.with_generic_param_rib(&generics.params, ItemRibKind(HasGenericParams::Yes), LifetimeRibKind::Generics { span: generics.span, kind: LifetimeBinderKind::ImplBlock }, |this| {
+ self.with_generic_param_rib(&generics.params, ItemRibKind(HasGenericParams::Yes), LifetimeRibKind::Generics { span: generics.span, parent: item_id, kind: LifetimeBinderKind::ImplBlock }, |this| {
// Dummy self type for better errors if `Self` is used in the trait path.
this.with_self_rib(Res::SelfTy { trait_: None, alias_to: None }, |this| {
- this.with_lifetime_rib(LifetimeRibKind::AnonymousCreateParameter, |this| {
+ this.with_lifetime_rib(LifetimeRibKind::AnonymousCreateParameter(item_id), |this| {
// Resolve the trait reference, if necessary.
this.with_optional_trait_ref(opt_trait_reference.as_ref(), |this, trait_id| {
let item_def_id = this.r.local_def_id(item_id);
this.visit_generics(generics);
// Resolve the items within the impl.
- this.with_lifetime_rib(LifetimeRibKind::AnonymousPassThrough,
+ this.with_lifetime_rib(LifetimeRibKind::AnonymousPassThrough(item_id),
|this| {
this.with_current_self_type(self_type, |this| {
this.with_self_rib_ns(ValueNS, Res::SelfCtor(item_def_id), |this| {
// not used as part of the type system, this is far less surprising.
this.with_constant_rib(
IsRepeatExpr::No,
- true,
+ HasGenericParams::Yes,
None,
|this| {
visit::walk_assoc_item(
this.with_generic_param_rib(
&generics.params,
AssocItemRibKind,
- LifetimeRibKind::Generics { span: generics.span, kind: LifetimeBinderKind::Function },
+ LifetimeRibKind::Generics { parent: item.id, span: generics.span, kind: LifetimeBinderKind::Function },
|this| {
// If this is a trait impl, ensure the method
// exists in trait
this.with_generic_param_rib(
&generics.params,
AssocItemRibKind,
- LifetimeRibKind::Generics { span: generics.span, kind: LifetimeBinderKind::Item },
+ LifetimeRibKind::Generics { parent: item.id, span: generics.span, kind: LifetimeBinderKind::Item },
|this| {
// If this is a trait impl, ensure the type
// exists in trait
span: Span,
err: F,
) where
- F: FnOnce(Ident, &str, Option<Symbol>) -> ResolutionError<'_>,
+ F: FnOnce(Ident, String, Option<Symbol>) -> ResolutionError<'a>,
{
// If there is a TraitRef in scope for an impl, then the method must be in the trait.
let Some((module, _)) = &self.current_trait_ref else { return; };
// We could not find the method: report an error.
let candidate = self.find_similarly_named_assoc_item(ident.name, kind);
let path = &self.current_trait_ref.as_ref().unwrap().1.path;
- self.report_error(span, err(ident, &path_names_to_string(path), candidate));
+ let path_names = path_names_to_string(path);
+ self.report_error(span, err(ident, path_names, candidate));
return;
};
AssocItemKind::TyAlias(..) => (rustc_errors::error_code!(E0325), "type"),
AssocItemKind::MacCall(..) => span_bug!(span, "unexpanded macro"),
};
+ let trait_path = path_names_to_string(path);
self.report_error(
span,
ResolutionError::TraitImplMismatch {
name: ident.name,
kind,
code,
- trait_path: path_names_to_string(path),
+ trait_path,
trait_item_span: binding.span,
},
);
}
// 3) Report all missing variables we found.
- let mut missing_vars = missing_vars.iter_mut().collect::<Vec<_>>();
- missing_vars.sort_by_key(|(sym, _err)| sym.as_str());
+ let mut missing_vars = missing_vars.into_iter().collect::<Vec<_>>();
+ missing_vars.sort_by_key(|&(sym, ref _err)| sym);
- for (name, mut v) in missing_vars {
- if inconsistent_vars.contains_key(name) {
+ for (name, mut v) in missing_vars.into_iter() {
+ if inconsistent_vars.contains_key(&name) {
v.could_be_path = false;
}
self.report_error(
*v.origin.iter().next().unwrap(),
- ResolutionError::VariableNotBoundInPattern(v),
+ ResolutionError::VariableNotBoundInPattern(v, self.parent_scope),
);
}
qself,
&Segment::from_path(path),
source,
- Finalize::SimplePath(id, path.span),
+ Finalize::new(id, path.span),
);
}
);
let ns = source.namespace();
- let (id, path_span) =
- finalize.node_id_and_path_span().expect("unexpected speculative resolution");
+ let Finalize { node_id, path_span, .. } = finalize;
let report_errors = |this: &mut Self, res: Option<Res>| {
if this.should_report_errs() {
let (err, candidates) =
if ns == ValueNS {
let item_name = path.last().unwrap().ident;
let traits = self.traits_in_scope(item_name, ns);
- self.r.trait_map.insert(id, traits);
+ self.r.trait_map.insert(node_id, traits);
}
if PrimTy::from_name(path[0].ident.name).is_some() {
std_path.push(Segment::from_ident(Ident::with_dummy_span(sym::std)));
std_path.extend(path);
if let PathResult::Module(_) | PathResult::NonModule(_) =
- self.resolve_path(&std_path, Some(ns), Finalize::No)
+ self.resolve_path(&std_path, Some(ns), None)
{
// Check if we wrote `str::from_utf8` instead of `std::str::from_utf8`
let item_span =
if !matches!(source, PathSource::TraitItem(..)) {
// Avoid recording definition of `A::B` in `<T as A>::B::C`.
- self.r.record_partial_res(id, partial_res);
+ self.r.record_partial_res(node_id, partial_res);
+ self.resolve_elided_lifetimes_in_path(node_id, partial_res, path, source, path_span);
}
- self.resolve_elided_lifetimes_in_path(id, partial_res, path, source, finalize);
partial_res
}
/// A wrapper around [`Resolver::report_error`].
///
/// This doesn't emit errors for function bodies if this is rustdoc.
- fn report_error(&self, span: Span, resolution_error: ResolutionError<'_>) {
+ fn report_error(&mut self, span: Span, resolution_error: ResolutionError<'a>) {
if self.should_report_errs() {
self.r.report_error(span, resolution_error);
}
// the trait (the slice upto and including
// `qself.position`). And then we recursively resolve that,
// but with `qself` set to `None`.
- //
- // However, setting `qself` to none (but not changing the
- // span) loses the information about where this path
- // *actually* appears, so for the purposes of the crate
- // lint we pass along information that this is the trait
- // name from a fully qualified path, and this also
- // contains the full span (the `Finalize::QPathTrait`).
let ns = if qself.position + 1 == path.len() { ns } else { TypeNS };
let partial_res = self.smart_resolve_path_fragment(
None,
&path[..=qself.position],
PathSource::TraitItem(ns),
- finalize.node_id_and_path_span().map_or(Finalize::No, |(qpath_id, path_span)| {
- Finalize::QPathTrait { qpath_id, qpath_span: qself.path_span, path_span }
- }),
+ Finalize::with_root_span(finalize.node_id, finalize.path_span, qself.path_span),
);
// The remaining segments (the `C` in our example) will
)));
}
- let result = match self.resolve_path(&path, Some(ns), finalize) {
+ let result = match self.resolve_path(&path, Some(ns), Some(finalize)) {
PathResult::NonModule(path_res) => path_res,
PathResult::Module(ModuleOrUniformRoot::Module(module)) if !module.is_normal() => {
PartialRes::new(module.res().unwrap())
&& result.base_res() != Res::Err
&& path[0].ident.name != kw::PathRoot
&& path[0].ident.name != kw::DollarCrate
- && let Some((id, path_span)) = finalize.node_id_and_path_span()
{
let unqualified_result = {
- match self.resolve_path(&[*path.last().unwrap()], Some(ns), Finalize::No) {
+ match self.resolve_path(&[*path.last().unwrap()], Some(ns), None) {
PathResult::NonModule(path_res) => path_res.base_res(),
PathResult::Module(ModuleOrUniformRoot::Module(module)) => {
module.res().unwrap()
};
if result.base_res() == unqualified_result {
let lint = lint::builtin::UNUSED_QUALIFICATIONS;
- self.r.lint_buffer.buffer_lint(lint, id, path_span, "unnecessary qualification")
+ self.r.lint_buffer.buffer_lint(
+ lint,
+ finalize.node_id,
+ finalize.path_span,
+ "unnecessary qualification",
+ )
}
}
debug!("resolve_anon_const {:?} is_repeat: {:?}", constant, is_repeat);
self.with_constant_rib(
is_repeat,
- constant.value.is_potential_trivial_const_param(),
- None,
- |this| {
- visit::walk_anon_const(this, constant);
+ if constant.value.is_potential_trivial_const_param() {
+ HasGenericParams::Yes
+ } else {
+ HasGenericParams::No
},
+ None,
+ |this| visit::walk_anon_const(this, constant),
);
}
if const_args.contains(&idx) {
self.with_constant_rib(
IsRepeatExpr::No,
- argument.is_potential_trivial_const_param(),
+ if argument.is_potential_trivial_const_param() {
+ HasGenericParams::Yes
+ } else {
+ HasGenericParams::No
+ },
None,
|this| {
this.resolve_expr(argument, None);
}
ExprKind::Repeat(ref elem, ref ct) => {
self.visit_expr(elem);
- self.resolve_anon_const(ct, IsRepeatExpr::Yes);
+ self.with_lifetime_rib(LifetimeRibKind::AnonConst, |this| {
+ this.resolve_anon_const(ct, IsRepeatExpr::Yes)
+ });
+ }
+ ExprKind::ConstBlock(ref ct) => {
+ self.resolve_anon_const(ct, IsRepeatExpr::No);
}
ExprKind::Index(ref elem, ref idx) => {
self.resolve_expr(elem, Some(expr));
use crate::late::{AliasPossibility, LateResolutionVisitor, RibKind};
use crate::late::{LifetimeBinderKind, LifetimeRibKind};
use crate::path_names_to_string;
-use crate::{Finalize, Module, ModuleKind, ModuleOrUniformRoot};
+use crate::{Module, ModuleKind, ModuleOrUniformRoot};
use crate::{PathResult, PathSource, Segment};
use rustc_ast::visit::FnKind;
}
}
-impl<'tcx> Into<MissingLifetimeSpot<'tcx>> for &'tcx hir::Generics<'tcx> {
+impl<'tcx> Into<MissingLifetimeSpot<'tcx>> for &&'tcx hir::Generics<'tcx> {
fn into(self) -> MissingLifetimeSpot<'tcx> {
MissingLifetimeSpot::Generics(self)
}
(String::new(), "the crate root".to_string())
} else {
let mod_path = &path[..path.len() - 1];
- let mod_prefix = match self.resolve_path(mod_path, Some(TypeNS), Finalize::No) {
+ let mod_prefix = match self.resolve_path(mod_path, Some(TypeNS), None) {
PathResult::Module(ModuleOrUniformRoot::Module(module)) => module.res(),
_ => None,
}
if let crate::PathSource::TraitItem(_) = source {
let mod_path = &path[..path.len() - 1];
if let PathResult::Module(ModuleOrUniformRoot::Module(module)) =
- self.resolve_path(mod_path, None, Finalize::No)
+ self.resolve_path(mod_path, None, None)
{
let resolutions = self.r.resolutions(module).borrow();
let targets: Vec<_> =
ident: Symbol,
kind: &AssocItemKind,
) -> Option<Symbol> {
- let Some((module, _)) = &self.current_trait_ref else {
- return None;
- };
+ let (module, _) = self.current_trait_ref.as_ref()?;
if ident == kw::Underscore {
// We do nothing for `_`.
return None;
// Search in module.
let mod_path = &path[..path.len() - 1];
if let PathResult::Module(ModuleOrUniformRoot::Module(module)) =
- self.resolve_path(mod_path, Some(TypeNS), Finalize::No)
+ self.resolve_path(mod_path, Some(TypeNS), None)
{
self.r.add_module_candidates(module, &mut names, &filter_fn);
}
for rib in self.lifetime_ribs.iter().rev() {
match rib.kind {
- LifetimeRibKind::Generics { span, kind } => {
+ LifetimeRibKind::Generics { parent: _, span, kind } => {
if !span.can_be_used_for_suggestions() && suggest_note {
suggest_note = false; // Avoid displaying the same help multiple times.
err.span_label(
err.emit();
}
+
+ crate fn emit_non_static_lt_in_const_generic_error(&self, lifetime_ref: &ast::Lifetime) {
+ struct_span_err!(
+ self.r.session,
+ lifetime_ref.ident.span,
+ E0771,
+ "use of non-static lifetime `{}` in const generic",
+ lifetime_ref.ident
+ )
+ .note(
+ "for more information, see issue #74052 \
+ <https://github.com/rust-lang/rust/issues/74052>",
+ )
+ .emit();
+ }
+
+ /// Non-static lifetimes are prohibited in anonymous constants under `min_const_generics`.
+ /// This function will emit an error if `generic_const_exprs` is not enabled, the body identified by
+ /// `body_id` is an anonymous constant and `lifetime_ref` is non-static.
+ crate fn maybe_emit_forbidden_non_static_lifetime_error(&self, lifetime_ref: &ast::Lifetime) {
+ let feature_active = self.r.session.features_untracked().generic_const_exprs;
+ if !feature_active {
+ feature_err(
+ &self.r.session.parse_sess,
+ sym::generic_const_exprs,
+ lifetime_ref.ident.span,
+ "a non-static lifetime is not allowed in a `const`",
+ )
+ .emit();
+ }
+ }
}
impl<'tcx> LifetimeContext<'_, 'tcx> {
}
}
- // FIXME(const_generics): This patches over an ICE caused by non-'static lifetimes in const
- // generics. We are disallowing this until we can decide on how we want to handle non-'static
- // lifetimes in const generics. See issue #74052 for discussion.
- crate fn emit_non_static_lt_in_const_generic_error(&self, lifetime_ref: &hir::Lifetime) {
- let mut err = struct_span_err!(
- self.tcx.sess,
- lifetime_ref.span,
- E0771,
- "use of non-static lifetime `{}` in const generic",
- lifetime_ref
- );
- err.note(
- "for more information, see issue #74052 \
- <https://github.com/rust-lang/rust/issues/74052>",
- );
- err.emit();
- }
-
crate fn is_trait_ref_fn_scope(&mut self, trait_ref: &'tcx hir::PolyTraitRef<'tcx>) -> bool {
if let def::Res::Def(_, did) = trait_ref.trait_ref.path.res {
if [
_ => unreachable!(),
}
}
-
- /// Non-static lifetimes are prohibited in anonymous constants under `min_const_generics`.
- /// This function will emit an error if `generic_const_exprs` is not enabled, the body identified by
- /// `body_id` is an anonymous constant and `lifetime_ref` is non-static.
- crate fn maybe_emit_forbidden_non_static_lifetime_error(
- &self,
- body_id: hir::BodyId,
- lifetime_ref: &'tcx hir::Lifetime,
- ) {
- let is_anon_const = matches!(
- self.tcx.def_kind(self.tcx.hir().body_owner_def_id(body_id)),
- hir::def::DefKind::AnonConst
- );
- let is_allowed_lifetime = matches!(
- lifetime_ref.name,
- hir::LifetimeName::Implicit | hir::LifetimeName::Static | hir::LifetimeName::Underscore
- );
-
- if !self.tcx.lazy_normalization() && is_anon_const && !is_allowed_lifetime {
- feature_err(
- &self.tcx.sess.parse_sess,
- sym::generic_const_exprs,
- lifetime_ref.span,
- "a non-static lifetime is not allowed in a `const`",
- )
- .emit();
- }
- }
}
map: &'a mut NamedRegionMap,
scope: ScopeRef<'a>,
- is_in_const_generic: bool,
-
/// Indicates that we only care about the definition of a trait. This should
/// be false if the `Item` we are resolving lifetimes for is not a trait or
/// we eventually need lifetimes resolve for trait items.
tcx,
map: &mut named_region_map,
scope: ROOT_SCOPE,
- is_in_const_generic: false,
trait_definition_only,
labels_in_fn: vec![],
xcrate_object_lifetime_defaults: Default::default(),
) -> Option<(LocalDefId, &'tcx FxHashSet<LocalDefId>)> {
match tcx.def_kind(def_id) {
DefKind::AnonConst | DefKind::InlineConst => {
- let mut def_id = tcx
- .parent(def_id.to_def_id())
- .unwrap_or_else(|| bug!("anon const or closure without a parent"));
+ let mut def_id = tcx.local_parent(def_id);
// We search for the next outer anon const or fn here
// while skipping closures.
//
// Note that for `AnonConst` we still just recurse until we
// find a function body, but who cares :shrug:
- while tcx.is_closure(def_id) {
- def_id = tcx
- .parent(def_id)
- .unwrap_or_else(|| bug!("anon const or closure without a parent"));
+ while tcx.is_closure(def_id.to_def_id()) {
+ def_id = tcx.local_parent(def_id);
}
- tcx.is_late_bound_map(def_id.expect_local())
+ tcx.is_late_bound_map(def_id)
}
_ => resolve_lifetimes_for(tcx, def_id).late_bound.get(&def_id).map(|lt| (def_id, lt)),
}
hir_id: hir::HirId,
) {
let name = match fk {
- intravisit::FnKind::ItemFn(id, _, _, _) => id.name,
- intravisit::FnKind::Method(id, _, _) => id.name,
+ intravisit::FnKind::ItemFn(id, _, _) => id.name,
+ intravisit::FnKind::Method(id, _) => id.name,
intravisit::FnKind::Closure => sym::closure,
};
let name = name.as_str();
self.insert_lifetime(lifetime_ref, Region::Static);
return;
}
- if self.is_in_const_generic && lifetime_ref.name != LifetimeName::Error {
- self.emit_non_static_lt_in_const_generic_error(lifetime_ref);
- return;
- }
self.resolve_lifetime_ref(lifetime_ref);
}
match param.kind {
GenericParamKind::Lifetime { .. } => {}
GenericParamKind::Type { ref default, .. } => {
- walk_list!(this, visit_param_bound, param.bounds);
if let Some(ref ty) = default {
this.visit_ty(&ty);
}
}
GenericParamKind::Const { ref ty, default } => {
- let was_in_const_generic = this.is_in_const_generic;
- this.is_in_const_generic = true;
- walk_list!(this, visit_param_bound, param.bounds);
this.visit_ty(&ty);
if let Some(default) = default {
this.visit_body(this.tcx.hir().body(default.body));
}
- this.is_in_const_generic = was_in_const_generic;
}
}
}
- for predicate in generics.where_clause.predicates {
+ for predicate in generics.predicates {
match predicate {
&hir::WherePredicate::BoundPredicate(hir::WhereBoundPredicate {
ref bounded_ty,
}) => {
this.visit_lifetime(lifetime);
walk_list!(this, visit_param_bound, bounds);
+
+ if lifetime.name != hir::LifetimeName::Static {
+ for bound in bounds {
+ let hir::GenericBound::Outlives(ref lt) = bound else {
+ continue;
+ };
+ if lt.name != hir::LifetimeName::Static {
+ continue;
+ }
+ this.insert_lifetime(lt, Region::Static);
+ this.tcx
+ .sess
+ .struct_span_warn(
+ lifetime.span,
+ &format!(
+ "unnecessary lifetime parameter `{}`",
+ lifetime.name.ident(),
+ ),
+ )
+ .help(&format!(
+ "you can use the `'static` lifetime directly, in place of `{}`",
+ lifetime.name.ident(),
+ ))
+ .emit();
+ }
+ }
}
&hir::WherePredicate::EqPredicate(hir::WhereEqPredicate {
ref lhs_ty,
GenericParamKind::Type { .. } => {
let mut set = Set1::Empty;
- add_bounds(&mut set, ¶m.bounds);
-
let param_def_id = tcx.hir().local_def_id(param.hir_id);
- for predicate in generics.where_clause.predicates {
+ for predicate in generics.predicates {
// Look for `type: ...` where clauses.
let hir::WherePredicate::BoundPredicate(ref data) = *predicate else { continue };
tcx: *tcx,
map,
scope: &wrap_scope,
- is_in_const_generic: self.is_in_const_generic,
trait_definition_only: self.trait_definition_only,
labels_in_fn,
xcrate_object_lifetime_defaults,
let remove_decl = self
.tcx
.parent(def_id)
- .and_then(|parent_def_id| parent_def_id.as_local())
+ .as_local()
.and_then(|parent_def_id| self.tcx.hir().get_generics(parent_def_id))
.and_then(|generics| self.lifetime_deletion_span(name, generics));
continue;
}
- if let Some(parent_def_id) = self.tcx.parent(def_id) {
- if let Some(def_id) = parent_def_id.as_local() {
- // lifetimes in `derive` expansions don't count (Issue #53738)
- if self
- .tcx
- .get_attrs(def_id.to_def_id())
- .iter()
- .any(|attr| attr.has_name(sym::automatically_derived))
- {
- continue;
- }
+ let parent_def_id = self.tcx.parent(def_id);
+ if let Some(def_id) = parent_def_id.as_local() {
+ // lifetimes in `derive` expansions don't count (Issue #53738)
+ if self
+ .tcx
+ .get_attrs(def_id.to_def_id())
+ .iter()
+ .any(|attr| attr.has_name(sym::automatically_derived))
+ {
+ continue;
+ }
- // opaque types generated when desugaring an async function can have a single
- // use lifetime even if it is explicitly denied (Issue #77175)
- if let hir::Node::Item(hir::Item {
- kind: hir::ItemKind::OpaqueTy(ref opaque),
- ..
- }) = self.tcx.hir().get_by_def_id(def_id)
- {
- if !matches!(opaque.origin, hir::OpaqueTyOrigin::AsyncFn(..)) {
+ // opaque types generated when desugaring an async function can have a single
+ // use lifetime even if it is explicitly denied (Issue #77175)
+ if let hir::Node::Item(hir::Item {
+ kind: hir::ItemKind::OpaqueTy(ref opaque),
+ ..
+ }) = self.tcx.hir().get_by_def_id(def_id)
+ {
+ if !matches!(opaque.origin, hir::OpaqueTyOrigin::AsyncFn(..)) {
+ continue 'lifetimes;
+ }
+ // We want to do this only if the lifetime identifier is already defined
+ // in the async function that generated this. Otherwise it could be
+ // an opaque type defined by the developer and we still want this
+ // lint to fail compilation
+ for p in opaque.generics.params {
+ if defined_by.contains_key(&p.name) {
continue 'lifetimes;
}
- // We want to do this only if the lifetime identifier is already defined
- // in the async function that generated this. Otherwise it could be
- // an opaque type defined by the developer and we still want this
- // lint to fail compilation
- for p in opaque.generics.params {
- if defined_by.contains_key(&p.name) {
- continue 'lifetimes;
- }
- }
}
}
}
|lint| {
let mut err = lint
.build(&format!("lifetime parameter `{}` never used", name));
- if let Some(parent_def_id) = self.tcx.parent(def_id) {
- if let Some(generics) =
- self.tcx.hir().get_generics(parent_def_id.expect_local())
- {
- let unused_lt_span =
- self.lifetime_deletion_span(name, generics);
- if let Some(span) = unused_lt_span {
- err.span_suggestion(
- span,
- "elide the unused lifetime",
- String::new(),
- Applicability::MachineApplicable,
- );
- }
+ let parent_def_id = self.tcx.parent(def_id);
+ if let Some(generics) =
+ self.tcx.hir().get_generics(parent_def_id.expect_local())
+ {
+ let unused_lt_span =
+ self.lifetime_deletion_span(name, generics);
+ if let Some(span) = unused_lt_span {
+ err.span_suggestion(
+ span,
+ "elide the unused lifetime",
+ String::new(),
+ Applicability::MachineApplicable,
+ );
}
}
err.emit();
let result = loop {
match *scope {
Scope::Body { id, s } => {
- // Non-static lifetimes are prohibited in anonymous constants without
- // `generic_const_exprs`.
- self.maybe_emit_forbidden_non_static_lifetime_error(id, lifetime_ref);
-
outermost_body = Some(id);
scope = s;
}
// It is a soft error to shadow a lifetime within a parent scope.
self.check_lifetime_param_for_shadowing(old_scope, &lifetime_i);
-
- for bound in lifetime_i.bounds {
- match bound {
- hir::GenericBound::Outlives(ref lt) => match lt.name {
- hir::LifetimeName::Underscore => {
- self.tcx.sess.delay_span_bug(
- lt.span,
- "use of `'_` in illegal place, but not caught by lowering",
- );
- }
- hir::LifetimeName::Static => {
- self.insert_lifetime(lt, Region::Static);
- self.tcx
- .sess
- .struct_span_warn(
- lifetime_i.span.to(lt.span),
- &format!(
- "unnecessary lifetime parameter `{}`",
- lifetime_i.name.ident(),
- ),
- )
- .help(&format!(
- "you can use the `'static` lifetime directly, in place of `{}`",
- lifetime_i.name.ident(),
- ))
- .emit();
- }
- hir::LifetimeName::Param(_) | hir::LifetimeName::Implicit => {
- self.resolve_lifetime_ref(lt);
- }
- hir::LifetimeName::ImplicitObjectLifetimeDefault => {
- self.tcx.sess.delay_span_bug(
- lt.span,
- "lowering generated `ImplicitObjectLifetimeDefault` \
- outside of an object type",
- );
- }
- hir::LifetimeName::Error => {
- // No need to do anything, error already reported.
- }
- },
- _ => bug!(),
- }
- }
}
}
// ignore binders here and scrape up all names we see.
let mut appears_in_where_clause = AllCollector::default();
appears_in_where_clause.visit_generics(generics);
-
- for param in generics.params {
- if let hir::GenericParamKind::Lifetime { .. } = param.kind {
- if !param.bounds.is_empty() {
- // `'a: 'b` means both `'a` and `'b` are referenced
- appears_in_where_clause
- .regions
- .insert(hir::LifetimeName::Param(param.name.normalize_to_macros_2_0()));
- }
- }
- }
-
debug!(?appears_in_where_clause.regions);
// Late bound regions are those that:
use rustc_ast::node_id::NodeMap;
use rustc_ast::{self as ast, NodeId, CRATE_NODE_ID};
use rustc_ast::{AngleBracketedArg, Crate, Expr, ExprKind, GenericArg, GenericArgs, LitKind, Path};
-use rustc_ast_lowering::ResolverAstLowering;
+use rustc_ast_lowering::{LifetimeRes, ResolverAstLowering};
use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap};
use rustc_data_structures::intern::Interned;
use rustc_data_structures::sync::Lrc;
/// but not for late resolution yet.
#[derive(Clone, Copy, Debug)]
pub struct ParentScope<'a> {
- module: Module<'a>,
+ pub module: Module<'a>,
expansion: LocalExpnId,
- macro_rules: MacroRulesScopeRef<'a>,
+ pub macro_rules: MacroRulesScopeRef<'a>,
derives: &'a [ast::Path],
}
/// parameter list.
NameAlreadyUsedInParameterList(Symbol, Span),
/// Error E0407: method is not a member of trait.
- MethodNotMemberOfTrait(Ident, &'a str, Option<Symbol>),
+ MethodNotMemberOfTrait(Ident, String, Option<Symbol>),
/// Error E0437: type is not a member of trait.
- TypeNotMemberOfTrait(Ident, &'a str, Option<Symbol>),
+ TypeNotMemberOfTrait(Ident, String, Option<Symbol>),
/// Error E0438: const is not a member of trait.
- ConstNotMemberOfTrait(Ident, &'a str, Option<Symbol>),
+ ConstNotMemberOfTrait(Ident, String, Option<Symbol>),
/// Error E0408: variable `{}` is not bound in all patterns.
- VariableNotBoundInPattern(&'a BindingError),
+ VariableNotBoundInPattern(BindingError, ParentScope<'a>),
/// Error E0409: variable `{}` is bound in inconsistent ways within the same match arm.
VariableBoundWithDifferentMode(Symbol, Span),
/// Error E0415: identifier is bound more than once in this parameter list.
import_res_map: NodeMap<PerNS<Option<Res>>>,
/// Resolutions for labels (node IDs of their corresponding blocks or loops).
label_res_map: NodeMap<NodeId>,
+ /// Resolutions for lifetimes.
+ lifetimes_res_map: NodeMap<LifetimeRes>,
+ /// Lifetime parameters that lowering will have to introduce.
+ extra_lifetime_params_map: NodeMap<Vec<(Ident, NodeId, LifetimeRes)>>,
/// `CrateNum` resolutions of `extern crate` items.
extern_crate_map: FxHashMap<LocalDefId, CrateNum>,
glob_map: FxHashMap<LocalDefId, FxHashSet<Symbol>>,
/// Visibilities in "lowered" form, for all entities that have them.
visibilities: FxHashMap<LocalDefId, ty::Visibility>,
+ has_pub_restricted: bool,
used_imports: FxHashSet<NodeId>,
maybe_unused_trait_imports: FxHashSet<LocalDefId>,
maybe_unused_extern_crates: Vec<(LocalDefId, Span)>,
/// `macro_rules` scopes *produced* by expanding the macro invocations,
/// include all the `macro_rules` items and other invocations generated by them.
output_macro_rules_scopes: FxHashMap<LocalExpnId, MacroRulesScopeRef<'a>>,
+ /// `macro_rules` scopes produced by `macro_rules` item definitions.
+ macro_rules_scopes: FxHashMap<LocalDefId, MacroRulesScopeRef<'a>>,
/// Helper attributes that are in scope for the given expansion.
helper_attrs: FxHashMap<LocalExpnId, Vec<Ident>>,
/// Ready or in-progress results of resolving paths inside the `#[derive(...)]` attribute
}
impl<'a, 'b> DefIdTree for &'a Resolver<'b> {
- fn parent(self, id: DefId) -> Option<DefId> {
+ #[inline]
+ fn opt_parent(self, id: DefId) -> Option<DefId> {
match id.as_local() {
Some(id) => self.definitions.def_key(id).parent,
None => self.cstore().def_key(id).parent,
self.label_res_map.get(&id).cloned()
}
+ fn get_lifetime_res(&self, id: NodeId) -> Option<LifetimeRes> {
+ self.lifetimes_res_map.get(&id).copied()
+ }
+
+ fn take_extra_lifetime_params(&mut self, id: NodeId) -> Vec<(Ident, NodeId, LifetimeRes)> {
+ self.extra_lifetime_params_map.remove(&id).unwrap_or_default()
+ }
+
fn create_stable_hashing_context(&self) -> StableHashingContext<'_> {
StableHashingContext::new(self.session, &self.definitions, self.crate_loader.cstore())
}
partial_res_map: Default::default(),
import_res_map: Default::default(),
label_res_map: Default::default(),
+ lifetimes_res_map: Default::default(),
+ extra_lifetime_params_map: Default::default(),
extern_crate_map: Default::default(),
reexport_map: FxHashMap::default(),
trait_map: NodeMap::default(),
glob_map: Default::default(),
visibilities,
+ has_pub_restricted: false,
used_imports: FxHashSet::default(),
maybe_unused_trait_imports: Default::default(),
maybe_unused_extern_crates: Vec::new(),
non_macro_attr: Lrc::new(SyntaxExtension::non_macro_attr(session.edition())),
invocation_parent_scopes: Default::default(),
output_macro_rules_scopes: Default::default(),
+ macro_rules_scopes: Default::default(),
helper_attrs: Default::default(),
derive_data: Default::default(),
local_macro_def_scopes: FxHashMap::default(),
let proc_macros = self.proc_macros.iter().map(|id| self.local_def_id(*id)).collect();
let definitions = self.definitions;
let visibilities = self.visibilities;
+ let has_pub_restricted = self.has_pub_restricted;
let extern_crate_map = self.extern_crate_map;
let reexport_map = self.reexport_map;
let maybe_unused_trait_imports = self.maybe_unused_trait_imports;
definitions,
cstore: Box::new(self.crate_loader.into_cstore()),
visibilities,
+ has_pub_restricted,
access_levels,
extern_crate_map,
reexport_map,
access_levels: self.access_levels.clone(),
cstore: Box::new(self.cstore().clone()),
visibilities: self.visibilities.clone(),
+ has_pub_restricted: self.has_pub_restricted,
extern_crate_map: self.extern_crate_map.clone(),
reexport_map: self.reexport_map.clone(),
glob_map: self.glob_map.clone(),
&mut self,
path_str: &str,
ns: Namespace,
- mut module_id: DefId,
+ mut parent_scope: ParentScope<'a>,
) -> Option<Res> {
let mut segments =
Vec::from_iter(path_str.split("::").map(Ident::from_str).map(Segment::from_ident));
if let Some(segment) = segments.first_mut() {
if segment.ident.name == kw::Crate {
// FIXME: `resolve_path` always resolves `crate` to the current crate root, but
- // rustdoc wants it to resolve to the `module_id`'s crate root. This trick of
+ // rustdoc wants it to resolve to the `parent_scope`'s crate root. This trick of
// replacing `crate` with `self` and changing the current module should achieve
// the same effect.
segment.ident.name = kw::SelfLower;
- module_id = module_id.krate.as_def_id();
+ parent_scope.module =
+ self.expect_module(parent_scope.module.def_id().krate.as_def_id());
} else if segment.ident.name == kw::Empty {
segment.ident.name = kw::PathRoot;
}
}
- let module = self.expect_module(module_id);
- match self.maybe_resolve_path(&segments, Some(ns), &ParentScope::module(module, self)) {
+ match self.maybe_resolve_path(&segments, Some(ns), &parent_scope) {
PathResult::Module(ModuleOrUniformRoot::Module(module)) => Some(module.res().unwrap()),
PathResult::NonModule(path_res) if path_res.unresolved_segments() == 0 => {
Some(path_res.base_res())
}
}
- // For rustdoc.
- pub fn graph_root(&self) -> Module<'a> {
- self.graph_root
- }
-
// For rustdoc.
pub fn take_all_macro_rules(&mut self) -> FxHashMap<Symbol, Res> {
mem::take(&mut self.all_macro_rules)
}
}
+ /// For rustdoc.
+ pub fn macro_rules_scope(&self, def_id: LocalDefId) -> MacroRulesScopeRef<'a> {
+ *self.macro_rules_scopes.get(&def_id).expect("not a `macro_rules` item")
+ }
+
/// Retrieves the span of the given `DefId` if `DefId` is in the local crate.
#[inline]
pub fn opt_span(&self, def_id: DefId) -> Option<Span> {
}
#[derive(Copy, Clone, Debug)]
-enum Finalize {
- /// Do not issue the lint.
- No,
-
- /// This lint applies to some arbitrary path; e.g., `impl ::foo::Bar`.
- /// In this case, we can take the span of that path.
- SimplePath(NodeId, Span),
-
- /// This lint comes from a `use` statement. In this case, what we
- /// care about really is the *root* `use` statement; e.g., if we
- /// have nested things like `use a::{b, c}`, we care about the
- /// `use a` part.
- UsePath { root_id: NodeId, root_span: Span, path_span: Span },
-
- /// This is the "trait item" from a fully qualified path. For example,
- /// we might be resolving `X::Y::Z` from a path like `<T as X::Y>::Z`.
- /// The `path_span` is the span of the to the trait itself (`X::Y`).
- QPathTrait { qpath_id: NodeId, qpath_span: Span, path_span: Span },
+struct Finalize {
+ /// Node ID for linting.
+ node_id: NodeId,
+ /// Span of the whole path or some its characteristic fragment.
+ /// E.g. span of `b` in `foo::{a, b, c}`, or full span for regular paths.
+ path_span: Span,
+ /// Span of the path start, suitable for prepending something to to it.
+ /// E.g. span of `foo` in `foo::{a, b, c}`, or full span for regular paths.
+ root_span: Span,
+ /// Whether to report privacy errors or silently return "no resolution" for them,
+ /// similarly to speculative resolution.
+ report_private: bool,
}
impl Finalize {
- fn node_id_and_path_span(&self) -> Option<(NodeId, Span)> {
- match *self {
- Finalize::No => None,
- Finalize::SimplePath(id, path_span)
- | Finalize::UsePath { root_id: id, path_span, .. }
- | Finalize::QPathTrait { qpath_id: id, path_span, .. } => Some((id, path_span)),
- }
- }
-
- fn node_id(&self) -> Option<NodeId> {
- self.node_id_and_path_span().map(|(id, _)| id)
+ fn new(node_id: NodeId, path_span: Span) -> Finalize {
+ Finalize::with_root_span(node_id, path_span, path_span)
}
- fn path_span(&self) -> Option<Span> {
- self.node_id_and_path_span().map(|(_, path_span)| path_span)
+ fn with_root_span(node_id: NodeId, path_span: Span, root_span: Span) -> Finalize {
+ Finalize { node_id, path_span, root_span, report_private: true }
}
}
parent_scope,
None,
force,
- false,
None,
);
if let Err(Determinacy::Undetermined) = binding {
&path,
Some(MacroNS),
&parent_scope,
- Finalize::SimplePath(ast::CRATE_NODE_ID, path_span),
+ Some(Finalize::new(ast::CRATE_NODE_ID, path_span)),
None,
) {
PathResult::NonModule(path_res) if path_res.unresolved_segments() == 0 => {
ident,
ScopeSet::Macro(kind),
&parent_scope,
- Some(ident.span),
+ Some(Finalize::new(ast::CRATE_NODE_ID, ident.span)),
true,
- false,
None,
) {
Ok(binding) => {
ident,
ScopeSet::Macro(MacroKind::Attr),
&parent_scope,
- Some(ident.span),
+ Some(Finalize::new(ast::CRATE_NODE_ID, ident.span)),
true,
- false,
None,
);
}
use rustc_middle::span_bug;
use rustc_middle::ty::{self, DefIdTree, TyCtxt};
use rustc_session::config::Input;
-use rustc_span::source_map::respan;
use rustc_span::symbol::Ident;
use rustc_span::*;
}
macro_rules! access_from {
- ($save_ctxt:expr, $item:expr, $id:expr) => {
+ ($save_ctxt:expr, $id:expr) => {
Access {
- public: $item.vis.node.is_pub(),
+ public: $save_ctxt.tcx.visibility($id).is_public(),
reachable: $save_ctxt.access_levels.is_reachable($id),
}
};
}
-macro_rules! access_from_vis {
- ($save_ctxt:expr, $vis:expr, $id:expr) => {
- Access { public: $vis.node.is_pub(), reachable: $save_ctxt.access_levels.is_reachable($id) }
- };
-}
-
pub struct DumpVisitor<'tcx> {
pub save_ctxt: SaveContext<'tcx>,
tcx: TyCtxt<'tcx>,
def_id: LocalDefId,
ident: Ident,
generics: &'tcx hir::Generics<'tcx>,
- vis: &hir::Visibility<'tcx>,
span: Span,
) {
debug!("process_method: {:?}:{}", def_id, ident);
v.process_generic_params(&generics, &method_data.qualname, hir_id);
method_data.value =
- fn_to_string(sig.decl, sig.header, Some(ident.name), generics, vis, &[], None);
+ fn_to_string(sig.decl, sig.header, Some(ident.name), generics, &[], None);
method_data.sig = sig::method_signature(hir_id, ident, generics, sig, &v.save_ctxt);
- v.dumper.dump_def(&access_from_vis!(v.save_ctxt, vis, def_id), method_data);
+ v.dumper.dump_def(&access_from!(v.save_ctxt, def_id), method_data);
}
// walk arg and return types
let field_data = self.save_ctxt.get_field_data(field, parent_id);
if let Some(field_data) = field_data {
self.dumper.dump_def(
- &access_from!(self.save_ctxt, field, self.tcx.hir().local_def_id(field.hir_id)),
+ &access_from!(self.save_ctxt, self.tcx.hir().local_def_id(field.hir_id)),
field_data,
);
}
v.process_formals(body.params, &fn_data.qualname);
v.process_generic_params(ty_params, &fn_data.qualname, item.hir_id());
- v.dumper.dump_def(&access_from!(v.save_ctxt, item, item.def_id), fn_data);
+ v.dumper.dump_def(&access_from!(v.save_ctxt, item.def_id), fn_data);
}
for arg in decl.inputs {
self.nest_typeck_results(item.def_id, |v| {
if let Some(var_data) = v.save_ctxt.get_item_data(item) {
down_cast_data!(var_data, DefData, item.span);
- v.dumper.dump_def(&access_from!(v.save_ctxt, item, item.def_id), var_data);
+ v.dumper.dump_def(&access_from!(v.save_ctxt, item.def_id), var_data);
}
v.visit_ty(&typ);
v.visit_expr(expr);
typ: &'tcx hir::Ty<'tcx>,
expr: Option<&'tcx hir::Expr<'tcx>>,
parent_id: DefId,
- vis: &hir::Visibility<'tcx>,
attrs: &'tcx [ast::Attribute],
) {
let qualname = format!("::{}", self.tcx.def_path_str(def_id.to_def_id()));
let span = self.span_from_span(ident.span);
self.dumper.dump_def(
- &access_from_vis!(self.save_ctxt, vis, def_id),
+ &access_from!(self.save_ctxt, def_id),
Def {
kind: DefKind::Const,
id: id_from_hir_id(hir_id, &self.save_ctxt),
let fields_str = fields
.iter()
.filter_map(|f| {
- if include_priv_fields || f.vis.node.is_pub() {
+ if include_priv_fields {
+ return Some(f.ident.to_string());
+ }
+ let def_id = self.save_ctxt.tcx.hir().local_def_id(f.hir_id);
+ if self.save_ctxt.tcx.visibility(def_id).is_public() {
Some(f.ident.to_string())
} else {
None
let span = self.span_from_span(item.ident.span);
let attrs = self.tcx.hir().attrs(item.hir_id());
self.dumper.dump_def(
- &access_from!(self.save_ctxt, item, item.def_id),
+ &access_from!(self.save_ctxt, item.def_id),
Def {
kind,
id: id_from_def_id(item.def_id.to_def_id()),
};
down_cast_data!(enum_data, DefData, item.span);
- let access = access_from!(self.save_ctxt, item, item.def_id);
+ let access = access_from!(self.save_ctxt, item.def_id);
for variant in enum_definition.variants {
let name = variant.ident.name.to_string();
methods.iter().map(|i| id_from_def_id(i.id.def_id.to_def_id())).collect();
let attrs = self.tcx.hir().attrs(item.hir_id());
self.dumper.dump_def(
- &access_from!(self.save_ctxt, item, item.def_id),
+ &access_from!(self.save_ctxt, item.def_id),
Def {
kind: DefKind::Trait,
id,
fn process_mod(&mut self, item: &'tcx hir::Item<'tcx>) {
if let Some(mod_data) = self.save_ctxt.get_item_data(item) {
down_cast_data!(mod_data, DefData, item.span);
- self.dumper.dump_def(&access_from!(self.save_ctxt, item, item.def_id), mod_data);
+ self.dumper.dump_def(&access_from!(self.save_ctxt, item.def_id), mod_data);
}
}
fn process_trait_item(&mut self, trait_item: &'tcx hir::TraitItem<'tcx>, trait_id: DefId) {
self.process_macro_use(trait_item.span);
- let vis_span = trait_item.span.shrink_to_lo();
match trait_item.kind {
hir::TraitItemKind::Const(ref ty, body) => {
let body = body.map(|b| &self.tcx.hir().body(b).value);
- let respan = respan(vis_span, hir::VisibilityKind::Public);
let attrs = self.tcx.hir().attrs(trait_item.hir_id());
self.process_assoc_const(
trait_item.def_id,
&ty,
body,
trait_id,
- &respan,
attrs,
);
}
hir::TraitItemKind::Fn(ref sig, ref trait_fn) => {
let body =
if let hir::TraitFn::Provided(body) = trait_fn { Some(*body) } else { None };
- let respan = respan(vis_span, hir::VisibilityKind::Public);
self.process_method(
sig,
body,
trait_item.def_id,
trait_item.ident,
&trait_item.generics,
- &respan,
trait_item.span,
);
}
&ty,
Some(&body.value),
impl_id,
- &impl_item.vis,
attrs,
);
}
impl_item.def_id,
impl_item.ident,
&impl_item.generics,
- &impl_item.vis,
impl_item.span,
);
}
hir::ItemKind::Use(path, hir::UseKind::Single) => {
let sub_span = path.segments.last().unwrap().ident.span;
if !self.span.filter_generated(sub_span) {
- let access = access_from!(self.save_ctxt, item, item.def_id);
+ let access = access_from!(self.save_ctxt, item.def_id);
let ref_id = self.lookup_def_id(item.hir_id()).map(id_from_def_id);
let span = self.span_from_span(sub_span);
- let parent =
- self.save_ctxt.tcx.parent(item.def_id.to_def_id()).map(id_from_def_id);
+ let parent = self.save_ctxt.tcx.local_parent(item.def_id);
self.dumper.import(
&access,
Import {
alias_span: None,
name: item.ident.to_string(),
value: String::new(),
- parent,
+ parent: Some(id_from_def_id(parent.to_def_id())),
},
);
self.write_sub_paths_truncated(&path);
// we don't want to track anyway, since it's probably macro-internal `use`
if let Some(sub_span) = self.span.sub_span_of_star(item.span) {
if !self.span.filter_generated(item.span) {
- let access = access_from!(self.save_ctxt, item, item.def_id);
+ let access = access_from!(self.save_ctxt, item.def_id);
let span = self.span_from_span(sub_span);
- let parent =
- self.save_ctxt.tcx.parent(item.def_id.to_def_id()).map(id_from_def_id);
+ let parent = self.save_ctxt.tcx.local_parent(item.def_id);
self.dumper.import(
&access,
Import {
alias_span: None,
name: "*".to_owned(),
value: names.join(", "),
- parent,
+ parent: Some(id_from_def_id(parent.to_def_id())),
},
);
self.write_sub_paths(&path);
let name_span = item.ident.span;
if !self.span.filter_generated(name_span) {
let span = self.span_from_span(name_span);
- let parent =
- self.save_ctxt.tcx.parent(item.def_id.to_def_id()).map(id_from_def_id);
+ let parent = self.save_ctxt.tcx.local_parent(item.def_id);
self.dumper.import(
&Access { public: false, reachable: false },
Import {
alias_span: None,
name: item.ident.to_string(),
value: String::new(),
- parent,
+ parent: Some(id_from_def_id(parent.to_def_id())),
},
);
}
let attrs = self.tcx.hir().attrs(item.hir_id());
self.dumper.dump_def(
- &access_from!(self.save_ctxt, item, item.def_id),
+ &access_from!(self.save_ctxt, item.def_id),
Def {
kind: DefKind::Type,
id,
match param.kind {
hir::GenericParamKind::Lifetime { .. } => {}
hir::GenericParamKind::Type { ref default, .. } => {
- self.process_bounds(param.bounds);
if let Some(ref ty) = default {
self.visit_ty(ty);
}
}
hir::GenericParamKind::Const { ref ty, ref default } => {
- self.process_bounds(param.bounds);
self.visit_ty(ty);
if let Some(default) = default {
self.visit_anon_const(default);
}
}
}
- for pred in generics.where_clause.predicates {
+ for pred in generics.predicates {
if let hir::WherePredicate::BoundPredicate(ref wbp) = *pred {
self.process_bounds(wbp.bounds);
self.visit_ty(wbp.bounded_ty);
}
fn visit_foreign_item(&mut self, item: &'tcx hir::ForeignItem<'tcx>) {
- let access = access_from!(self.save_ctxt, item, item.def_id);
+ let access = access_from!(self.save_ctxt, item.def_id);
match item.kind {
hir::ForeignItemKind::Fn(decl, _, ref generics) => {
use rustc_session::config::{CrateType, Input, OutputType};
use rustc_session::cstore::ExternCrate;
use rustc_session::output::{filename_for_metadata, out_filename};
-use rustc_span::source_map::Spanned;
use rustc_span::symbol::Ident;
use rustc_span::*;
},
Some(item.ident.name),
generics,
- &item.vis,
arg_names,
None,
),
sig.header,
Some(item.ident.name),
generics,
- &item.vis,
&[],
None,
),
let qualname = format!("::{}", self.tcx.def_path_str(def_id));
filter!(self.span_utils, item.ident.span);
let value =
- enum_def_to_string(def, generics, item.ident.name, item.span, &item.vis);
+ enum_def_to_string(def, generics, item.ident.name, item.span);
Some(Data::DefData(Def {
kind: DefKind::Enum,
id: id_from_def_id(def_id),
Node::TraitRef(tr) => tr.path.res,
Node::Item(&hir::Item { kind: hir::ItemKind::Use(path, _), .. }) => path.res,
- Node::Visibility(&Spanned {
- node: hir::VisibilityKind::Restricted { ref path, .. },
- ..
- }) => path.res,
-
Node::PathSegment(seg) => match seg.res {
Some(res) if res != Res::Err => res,
_ => {
// This is a reference to a tuple struct or an enum variant where the def_id points
// to an invisible constructor function. That is not a very useful
// def, so adjust to point to the tuple struct or enum variant itself.
- let parent_def_id = self.tcx.parent(def_id).unwrap();
+ let parent_def_id = self.tcx.parent(def_id);
Some(Ref { kind: RefKind::Type, span, ref_id: id_from_def_id(parent_def_id) })
}
Res::Def(HirDefKind::Static(_) | HirDefKind::Const | HirDefKind::AssocConst, _) => {
param_text.push_str(&id_to_string(&scx.tcx.hir(), default.hir_id));
}
}
- if !param.bounds.is_empty() {
- param_text.push_str(": ");
- match param.kind {
- hir::GenericParamKind::Lifetime { .. } => {
- let bounds = param
- .bounds
- .iter()
- .map(|bound| match bound {
- hir::GenericBound::Outlives(lt) => lt.name.ident().to_string(),
- _ => panic!(),
- })
- .collect::<Vec<_>>()
- .join(" + ");
- param_text.push_str(&bounds);
- // FIXME add lifetime bounds refs.
- }
- hir::GenericParamKind::Type { .. } => {
- param_text.push_str(&bounds_to_string(param.bounds));
- // FIXME descend properly into bounds.
- }
- hir::GenericParamKind::Const { .. } => {
- // Const generics cannot contain bounds.
- }
- }
- }
text.push_str(¶m_text);
text.push(',');
}
/// Therefore, the recursion depth is the binary logarithm of the number of
/// tokens to count, and the expanded tree is likewise very small.
macro_rules! count {
- () => (0usize);
($one:tt) => (1usize);
($($pairs:tt $_p:tt)*) => (count!($($pairs)*) << 1usize);
($odd:tt $($rest:tt)*) => (count!($($rest)*) | 1usize);
/// This can be disabled with the `noprelude` option like
/// `--extern noprelude:name`.
pub add_prelude: bool,
+ /// The extern entry shouldn't be considered for unused dependency warnings.
+ ///
+ /// `--extern nounused:std=/path/to/lib/libstd.rlib`. This is used to
+ /// suppress `unused-crate-dependencies` warnings.
+ pub nounused_dep: bool,
}
#[derive(Clone, Debug)]
impl ExternEntry {
fn new(location: ExternLocation) -> ExternEntry {
- ExternEntry { location, is_private_dep: false, add_prelude: false }
+ ExternEntry { location, is_private_dep: false, add_prelude: false, nounused_dep: false }
}
pub fn files(&self) -> Option<impl Iterator<Item = &CanonicalizedPath>> {
real_rust_source_base_dir: None,
edition: DEFAULT_EDITION,
json_artifact_notifications: false,
- json_unused_externs: false,
+ json_unused_externs: JsonUnusedExterns::No,
json_future_incompat: false,
pretty: None,
working_dir: RealFileName::LocalPath(std::env::current_dir().unwrap()),
sym::target_has_atomic_load_store,
sym::target_has_atomic,
sym::target_has_atomic_equal_alignment,
+ sym::target_feature,
sym::panic,
sym::sanitize,
sym::debug_assertions,
.into_iter()
.map(|sanitizer| Symbol::intern(sanitizer.as_str().unwrap()));
+ // Unknown possible values:
+ // - `feature`
+ // - `target_feature`
+
// No-values
for name in [
sym::doc,
pub struct JsonConfig {
pub json_rendered: HumanReadableErrorType,
pub json_artifact_notifications: bool,
- pub json_unused_externs: bool,
+ pub json_unused_externs: JsonUnusedExterns,
pub json_future_incompat: bool,
}
+/// Report unused externs in event stream
+#[derive(Copy, Clone)]
+pub enum JsonUnusedExterns {
+ /// Do not
+ No,
+ /// Report, but do not exit with failure status for deny/forbid
+ Silent,
+ /// Report, and also exit with failure status for deny/forbid
+ Loud,
+}
+
+impl JsonUnusedExterns {
+ pub fn is_enabled(&self) -> bool {
+ match self {
+ JsonUnusedExterns::No => false,
+ JsonUnusedExterns::Loud | JsonUnusedExterns::Silent => true,
+ }
+ }
+
+ pub fn is_loud(&self) -> bool {
+ match self {
+ JsonUnusedExterns::No | JsonUnusedExterns::Silent => false,
+ JsonUnusedExterns::Loud => true,
+ }
+ }
+}
+
/// Parse the `--json` flag.
///
/// The first value returned is how to render JSON diagnostics, and the second
HumanReadableErrorType::Default;
let mut json_color = ColorConfig::Never;
let mut json_artifact_notifications = false;
- let mut json_unused_externs = false;
+ let mut json_unused_externs = JsonUnusedExterns::No;
let mut json_future_incompat = false;
for option in matches.opt_strs("json") {
// For now conservatively forbid `--color` with `--json` since `--json`
"diagnostic-short" => json_rendered = HumanReadableErrorType::Short,
"diagnostic-rendered-ansi" => json_color = ColorConfig::Always,
"artifacts" => json_artifact_notifications = true,
- "unused-externs" => json_unused_externs = true,
+ "unused-externs" => json_unused_externs = JsonUnusedExterns::Loud,
+ "unused-externs-silent" => json_unused_externs = JsonUnusedExterns::Silent,
"future-incompat" => json_future_incompat = true,
s => early_error(
ErrorOutputType::default(),
let mut is_private_dep = false;
let mut add_prelude = true;
+ let mut nounused_dep = false;
if let Some(opts) = options {
if !is_unstable_enabled {
early_error(
);
}
}
+ "nounused" => nounused_dep = true,
_ => early_error(error_format, &format!("unknown --extern option `{opt}`")),
}
}
// Crates start out being not private, and go to being private `priv`
// is specified.
entry.is_private_dep |= is_private_dep;
+ // likewise `nounused`
+ entry.nounused_dep |= nounused_dep;
// If any flag is missing `noprelude`, then add to the prelude.
entry.add_prelude |= add_prelude;
}
check_debug_option_stability(&debugging_opts, error_format, json_rendered);
- if !debugging_opts.unstable_options && json_unused_externs {
+ if !debugging_opts.unstable_options && json_unused_externs.is_enabled() {
early_error(
error_format,
"the `-Z unstable-options` flag must also be passed to enable \
json_artifact_notifications: bool [TRACKED],
/// `true` if we're emitting a JSON blob containing the unused externs
- json_unused_externs: bool [UNTRACKED],
+ json_unused_externs: JsonUnusedExterns [UNTRACKED],
/// `true` if we're emitting a JSON job containing a future-incompat report for lints
json_future_incompat: bool [TRACKED],
pub const parse_opt_langid: &str = "a language identifier";
pub const parse_opt_pathbuf: &str = "a path";
pub const parse_list: &str = "a space-separated list of strings";
+ pub const parse_list_with_polarity: &str =
+ "a comma-separated list of strings, with elements beginning with + or -";
pub const parse_opt_comma_list: &str = "a comma-separated list of strings";
pub const parse_number: &str = "a number";
pub const parse_opt_number: &str = parse_number;
}
}
+ crate fn parse_list_with_polarity(slot: &mut Vec<(String, bool)>, v: Option<&str>) -> bool {
+ match v {
+ Some(s) => {
+ for s in s.split(",") {
+ let Some(pass_name) = s.strip_prefix(&['+', '-'][..]) else { return false };
+ slot.push((pass_name.to_string(), &s[..1] == "+"));
+ }
+ true
+ }
+ None => false,
+ }
+ }
+
crate fn parse_location_detail(ld: &mut LocationDetail, v: Option<&str>) -> bool {
if let Some(v) = v {
ld.line = false;
mir_emit_retag: bool = (false, parse_bool, [TRACKED],
"emit Retagging MIR statements, interpreted e.g., by miri; implies -Zmir-opt-level=0 \
(default: no)"),
+ mir_enable_passes: Vec<(String, bool)> = (Vec::new(), parse_list_with_polarity, [TRACKED],
+ "use like `-Zmir-enable-passes=+DestProp,-InstCombine`. Forces the specified passes to be \
+ enabled, overriding all other checks. Passes that are not specified are enabled or \
+ disabled by other flags as usual."),
mir_opt_level: Option<usize> = (None, parse_opt_number, [TRACKED],
"MIR optimization level (0-4; default: 1 in non optimized builds and 2 in optimized builds)"),
move_size_limit: Option<usize> = (None, parse_opt_number, [TRACKED],
use crate::config::CheckCfg;
use crate::lint::{BufferedEarlyLint, BuiltinLintDiagnostics, Lint, LintId};
+use crate::SessionDiagnostic;
use rustc_ast::node_id::NodeId;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_data_structures::sync::{Lock, Lrc};
use rustc_errors::{emitter::SilentEmitter, ColorConfig, Handler};
use rustc_errors::{
error_code, fallback_fluent_bundle, Applicability, Diagnostic, DiagnosticBuilder,
- ErrorGuaranteed, MultiSpan,
+ DiagnosticMessage, ErrorGuaranteed, MultiSpan,
};
use rustc_feature::{find_feature_issue, GateIssue, UnstableFeatures};
use rustc_span::edition::Edition;
pub fn proc_macro_quoted_spans(&self) -> Vec<Span> {
self.proc_macro_quoted_spans.lock().clone()
}
+
+ pub fn emit_err<'a>(&'a self, err: impl SessionDiagnostic<'a>) -> ErrorGuaranteed {
+ err.into_diagnostic(self).emit()
+ }
+
+ pub fn emit_warning<'a>(&'a self, warning: impl SessionDiagnostic<'a, ()>) {
+ warning.into_diagnostic(self).emit()
+ }
+
+ pub fn struct_err(
+ &self,
+ msg: impl Into<DiagnosticMessage>,
+ ) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
+ self.span_diagnostic.struct_err(msg)
+ }
+
+ pub fn struct_warn(&self, msg: impl Into<DiagnosticMessage>) -> DiagnosticBuilder<'_, ()> {
+ self.span_diagnostic.struct_warn(msg)
+ }
}
pub trait SessionDiagnostic<'a, T: EmissionGuarantee = ErrorGuaranteed> {
/// Write out as a diagnostic out of `sess`.
#[must_use]
- fn into_diagnostic(self, sess: &'a Session) -> DiagnosticBuilder<'a, T>;
+ fn into_diagnostic(self, sess: &'a ParseSess) -> DiagnosticBuilder<'a, T>;
}
impl Session {
&self,
msg: impl Into<DiagnosticMessage>,
) -> DiagnosticBuilder<'_, ErrorGuaranteed> {
- self.diagnostic().struct_err(msg)
+ self.parse_sess.struct_err(msg)
}
pub fn struct_err_with_code(
&self,
self.diagnostic().err(msg)
}
pub fn emit_err<'a>(&'a self, err: impl SessionDiagnostic<'a>) -> ErrorGuaranteed {
- err.into_diagnostic(self).emit()
+ self.parse_sess.emit_err(err)
}
pub fn emit_warning<'a>(&'a self, warning: impl SessionDiagnostic<'a, ()>) {
- warning.into_diagnostic(self).emit()
+ self.parse_sess.emit_warning(warning)
}
#[inline]
pub fn err_count(&self) -> usize {
use crate::parse::ParseSess;
use crate::session::Session;
-use rustc_ast::token::{self, DelimToken, Nonterminal, Token};
+use rustc_ast::token::{self, Delimiter, Nonterminal, Token};
use rustc_ast::tokenstream::CanSynthesizeMissingTokens;
use rustc_ast::tokenstream::{DelimSpan, TokenStream, TokenTree};
use rustc_data_structures::profiling::VerboseTimingGuard;
let tts = (self.nt_to_tokenstream)(&nt, self.parse_sess, self.synthesize_tokens);
TokenTree::Delimited(
DelimSpan::from_single(token.span),
- DelimToken::NoDelim,
+ Delimiter::Invisible,
self.process_token_stream(tts),
)
.into()
CondTemporary,
QuestionMark,
TryBlock,
+ YeetExpr,
/// Desugaring of an `impl Trait` in return type position
/// to an `type Foo = impl Trait;` and replacing the
/// `impl Trait` with `Foo`.
DesugaringKind::Await => "`await` expression",
DesugaringKind::QuestionMark => "operator `?`",
DesugaringKind::TryBlock => "`try` block",
+ DesugaringKind::YeetExpr => "`do yeet` expression",
DesugaringKind::OpaqueTy => "`impl Trait`",
DesugaringKind::ForLoop => "`for` loop",
DesugaringKind::LetElse => "`let...else`",
mod analyze_source_file;
pub mod fatal_error;
+pub mod profiling;
+
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
use rustc_data_structures::sync::{Lock, Lrc};
--- /dev/null
+use std::borrow::Borrow;
+
+use rustc_data_structures::profiling::EventArgRecorder;
+
+/// Extension trait for self-profiling purposes: allows to record spans within a generic activity's
+/// event arguments.
+pub trait SpannedEventArgRecorder {
+ /// Records the following event arguments within the current generic activity being profiled:
+ /// - the provided `event_arg`
+ /// - a string representation of the provided `span`
+ ///
+ /// Note: when self-profiling with costly event arguments, at least one argument
+ /// needs to be recorded. A panic will be triggered if that doesn't happen.
+ fn record_arg_with_span<A>(&mut self, event_arg: A, span: crate::Span)
+ where
+ A: Borrow<str> + Into<String>;
+}
+
+impl SpannedEventArgRecorder for EventArgRecorder<'_> {
+ fn record_arg_with_span<A>(&mut self, event_arg: A, span: crate::Span)
+ where
+ A: Borrow<str> + Into<String>,
+ {
+ self.record_arg(event_arg);
+
+ let span_arg = crate::with_session_globals(|session_globals| {
+ if let Some(source_map) = &*session_globals.source_map.borrow() {
+ source_map.span_to_embeddable_string(span)
+ } else {
+ format!("{:?}", span)
+ }
+ });
+ self.record_arg(span_arg);
+ }
+}
// take precedence.
for &(ref from, ref to) in self.mapping.iter().rev() {
if let Ok(rest) = path.strip_prefix(from) {
- return (to.join(rest), true);
+ let remapped = if rest.as_os_str().is_empty() {
+ // This is subtle, joining an empty path onto e.g. `foo/bar` will
+ // result in `foo/bar/`, that is, there'll be an additional directory
+ // separator at the end. This can lead to duplicated directory separators
+ // in remapped paths down the line.
+ // So, if we have an exact match, we just return that without a call
+ // to `Path::join()`.
+ to.clone()
+ } else {
+ to.join(rest)
+ };
+
+ return (remapped, true);
}
}
}
}
}
+
+fn map_path_prefix(mapping: &FilePathMapping, path: &str) -> String {
+ // It's important that we convert to a string here because that's what
+ // later stages do too (e.g. in the backend), and comparing `Path` values
+ // won't catch some differences at the string level, e.g. "abc" and "abc/"
+ // compare as equal.
+ mapping.map_prefix(path.into()).0.to_string_lossy().to_string()
+}
+
+#[cfg(unix)]
+#[test]
+fn path_prefix_remapping() {
+ // Relative to relative
+ {
+ let mapping = &FilePathMapping::new(vec![("abc/def".into(), "foo".into())]);
+
+ assert_eq!(map_path_prefix(mapping, "abc/def/src/main.rs"), "foo/src/main.rs");
+ assert_eq!(map_path_prefix(mapping, "abc/def"), "foo");
+ }
+
+ // Relative to absolute
+ {
+ let mapping = &FilePathMapping::new(vec![("abc/def".into(), "/foo".into())]);
+
+ assert_eq!(map_path_prefix(mapping, "abc/def/src/main.rs"), "/foo/src/main.rs");
+ assert_eq!(map_path_prefix(mapping, "abc/def"), "/foo");
+ }
+
+ // Absolute to relative
+ {
+ let mapping = &FilePathMapping::new(vec![("/abc/def".into(), "foo".into())]);
+
+ assert_eq!(map_path_prefix(mapping, "/abc/def/src/main.rs"), "foo/src/main.rs");
+ assert_eq!(map_path_prefix(mapping, "/abc/def"), "foo");
+ }
+
+ // Absolute to absolute
+ {
+ let mapping = &FilePathMapping::new(vec![("/abc/def".into(), "/foo".into())]);
+
+ assert_eq!(map_path_prefix(mapping, "/abc/def/src/main.rs"), "/foo/src/main.rs");
+ assert_eq!(map_path_prefix(mapping, "/abc/def"), "/foo");
+ }
+}
+
+#[cfg(windows)]
+#[test]
+fn path_prefix_remapping_from_relative2() {
+ // Relative to relative
+ {
+ let mapping = &FilePathMapping::new(vec![("abc\\def".into(), "foo".into())]);
+
+ assert_eq!(map_path_prefix(mapping, "abc\\def\\src\\main.rs"), "foo\\src\\main.rs");
+ assert_eq!(map_path_prefix(mapping, "abc\\def"), "foo");
+ }
+
+ // Relative to absolute
+ {
+ let mapping = &FilePathMapping::new(vec![("abc\\def".into(), "X:\\foo".into())]);
+
+ assert_eq!(map_path_prefix(mapping, "abc\\def\\src\\main.rs"), "X:\\foo\\src\\main.rs");
+ assert_eq!(map_path_prefix(mapping, "abc\\def"), "X:\\foo");
+ }
+
+ // Absolute to relative
+ {
+ let mapping = &FilePathMapping::new(vec![("X:\\abc\\def".into(), "foo".into())]);
+
+ assert_eq!(map_path_prefix(mapping, "X:\\abc\\def\\src\\main.rs"), "foo\\src\\main.rs");
+ assert_eq!(map_path_prefix(mapping, "X:\\abc\\def"), "foo");
+ }
+
+ // Absolute to absolute
+ {
+ let mapping = &FilePathMapping::new(vec![("X:\\abc\\def".into(), "X:\\foo".into())]);
+
+ assert_eq!(map_path_prefix(mapping, "X:\\abc\\def\\src\\main.rs"), "X:\\foo\\src\\main.rs");
+ assert_eq!(map_path_prefix(mapping, "X:\\abc\\def"), "X:\\foo");
+ }
+}
MacroRules: "macro_rules",
Raw: "raw",
Union: "union",
+ Yeet: "yeet",
}
// Pre-interned symbols that can be referred to with `rustc_span::sym::*`.
from_residual,
from_size_align_unchecked,
from_usize,
+ from_yeet,
fsub_fast,
fundamental,
future,
x87_reg,
xer,
xmm_reg,
+ yeet_desugar_details,
+ yeet_expr,
ymm_reg,
zmm_reg,
}
"sparc" => sparc::compute_abi_info(cx, self),
"sparc64" => sparc64::compute_abi_info(cx, self),
"nvptx" => nvptx::compute_abi_info(self),
- "nvptx64" => nvptx64::compute_abi_info(self),
+ "nvptx64" => {
+ if cx.target_spec().adjust_abi(abi) == spec::abi::Abi::PtxKernel {
+ nvptx64::compute_ptx_kernel_abi_info(cx, self)
+ } else {
+ nvptx64::compute_abi_info(self)
+ }
+ }
"hexagon" => hexagon::compute_abi_info(self),
"riscv32" | "riscv64" => riscv::compute_abi_info(cx, self),
"wasm32" | "wasm64" => {
-// Reference: PTX Writer's Guide to Interoperability
-// https://docs.nvidia.com/cuda/ptx-writers-guide-to-interoperability
-
-use crate::abi::call::{ArgAbi, FnAbi};
+use crate::abi::call::{ArgAbi, FnAbi, PassMode, Reg, Size, Uniform};
+use crate::abi::{HasDataLayout, TyAbiInterface};
fn classify_ret<Ty>(ret: &mut ArgAbi<'_, Ty>) {
if ret.layout.is_aggregate() && ret.layout.size.bits() > 64 {
ret.make_indirect();
- } else {
- ret.extend_integer_width_to(64);
}
}
fn classify_arg<Ty>(arg: &mut ArgAbi<'_, Ty>) {
if arg.layout.is_aggregate() && arg.layout.size.bits() > 64 {
arg.make_indirect();
- } else {
- arg.extend_integer_width_to(64);
+ }
+}
+
+fn classify_arg_kernel<'a, Ty, C>(_cx: &C, arg: &mut ArgAbi<'a, Ty>)
+where
+ Ty: TyAbiInterface<'a, C> + Copy,
+ C: HasDataLayout,
+{
+ if matches!(arg.mode, PassMode::Pair(..)) && (arg.layout.is_adt() || arg.layout.is_tuple()) {
+ let align_bytes = arg.layout.align.abi.bytes();
+
+ let unit = match align_bytes {
+ 1 => Reg::i8(),
+ 2 => Reg::i16(),
+ 4 => Reg::i32(),
+ 8 => Reg::i64(),
+ 16 => Reg::i128(),
+ _ => unreachable!("Align is given as power of 2 no larger than 16 bytes"),
+ };
+ arg.cast_to(Uniform { unit, total: Size::from_bytes(2 * align_bytes) });
}
}
classify_arg(arg);
}
}
+
+pub fn compute_ptx_kernel_abi_info<'a, Ty, C>(cx: &C, fn_abi: &mut FnAbi<'a, Ty>)
+where
+ Ty: TyAbiInterface<'a, C> + Copy,
+ C: HasDataLayout,
+{
+ if !fn_abi.ret.layout.is_unit() && !fn_abi.ret.layout.is_never() {
+ panic!("Kernels should not return anything other than () or !");
+ }
+
+ for arg in &mut fn_abi.args {
+ if arg.is_ignore() {
+ continue;
+ }
+ classify_arg_kernel(cx, arg);
+ }
+}
cx: &C,
offset: Size,
) -> Option<PointeeInfo>;
+ fn is_adt(this: TyAndLayout<'a, Self>) -> bool;
+ fn is_never(this: TyAndLayout<'a, Self>) -> bool;
+ fn is_tuple(this: TyAndLayout<'a, Self>) -> bool;
+ fn is_unit(this: TyAndLayout<'a, Self>) -> bool;
}
impl<'a, Ty> TyAndLayout<'a, Ty> {
_ => false,
}
}
+
+ pub fn is_adt<C>(self) -> bool
+ where
+ Ty: TyAbiInterface<'a, C>,
+ {
+ Ty::is_adt(self)
+ }
+
+ pub fn is_never<C>(self) -> bool
+ where
+ Ty: TyAbiInterface<'a, C>,
+ {
+ Ty::is_never(self)
+ }
+
+ pub fn is_tuple<C>(self) -> bool
+ where
+ Ty: TyAbiInterface<'a, C>,
+ {
+ Ty::is_tuple(self)
+ }
+
+ pub fn is_unit<C>(self) -> bool
+ where
+ Ty: TyAbiInterface<'a, C>,
+ {
+ Ty::is_unit(self)
+ }
}
impl<'a, Ty> TyAndLayout<'a, Ty> {
}
}
-// The reserved registers are somewhat taken from <https://git.io/JUR1k#L150>.
+// The reserved registers are somewhat taken from
+// <https://github.com/llvm/llvm-project/blob/deb8f8bcf31540c657716ea5242183b0792702a1/llvm/lib/Target/Mips/MipsRegisterInfo.cpp#L150>.
def_regs! {
Mips MipsInlineAsmReg MipsInlineAsmRegClass {
r2: reg = ["$2"],
let name = (stringify!($attr)).replace("_", "-");
d.insert(name, self.$attr.to_json());
}};
- ($attr:ident, $key_name:expr) => {{
- let name = $key_name;
- d.insert(name.into(), self.$attr.to_json());
- }};
}
macro_rules! target_option_val {
Target {
llvm_target: "wasm64-unknown-unknown".into(),
pointer_width: 64,
- data_layout: "e-m:e-p:64:64-i64:64-n32:64-S128-ni:1:10:20".into(),
+ data_layout: "e-m:e-p:64:64-p10:8:8-p20:8:8-i64:64-n32:64-S128-ni:1:10:20".into(),
arch: "wasm64".into(),
options,
}
} else if cat_a == cat_b {
match (a.kind(), b.kind()) {
(ty::Adt(def_a, _), ty::Adt(def_b, _)) => def_a == def_b,
+ (ty::Foreign(def_a), ty::Foreign(def_b)) => def_a == def_b,
// Matching on references results in a lot of unhelpful
// suggestions, so let's just not do that for now.
//
};
let sized_trait = self.tcx.lang_items().sized_trait();
debug!("maybe_suggest_unsized_generics: generics.params={:?}", generics.params);
- debug!("maybe_suggest_unsized_generics: generics.where_clause={:?}", generics.where_clause);
- let param = generics.params.iter().filter(|param| param.span == span).find(|param| {
- // Check that none of the explicit trait bounds is `Sized`. Assume that an explicit
- // `Sized` bound is there intentionally and we don't need to suggest relaxing it.
- param
- .bounds
- .iter()
- .all(|bound| bound.trait_ref().and_then(|tr| tr.trait_def_id()) != sized_trait)
- });
- let Some(param) = param else {
+ debug!("maybe_suggest_unsized_generics: generics.predicates={:?}", generics.predicates);
+ let Some(param) = generics.params.iter().find(|param| param.span == span) else {
return;
};
- let param_def_id = self.tcx.hir().local_def_id(param.hir_id).to_def_id();
- let preds = generics.where_clause.predicates.iter();
- let explicitly_sized = preds
- .filter_map(|pred| match pred {
- hir::WherePredicate::BoundPredicate(bp) => Some(bp),
- _ => None,
- })
- .filter(|bp| bp.is_param_bound(param_def_id))
+ let param_def_id = self.tcx.hir().local_def_id(param.hir_id);
+ // Check that none of the explicit trait bounds is `Sized`. Assume that an explicit
+ // `Sized` bound is there intentionally and we don't need to suggest relaxing it.
+ let explicitly_sized = generics
+ .bounds_for_param(param_def_id)
.flat_map(|bp| bp.bounds)
.any(|bound| bound.trait_ref().and_then(|tr| tr.trait_def_id()) == sized_trait);
if explicitly_sized {
_ => {}
};
// Didn't add an indirection suggestion, so add a general suggestion to relax `Sized`.
- let (span, separator) = match param.bounds {
- [] => (span.shrink_to_hi(), ":"),
- [.., bound] => (bound.span().shrink_to_hi(), " +"),
+ let (span, separator) = if let Some(s) = generics.bounds_span_for_suggestions(param_def_id)
+ {
+ (s, " +")
+ } else {
+ (span.shrink_to_hi(), ":")
};
err.span_suggestion_verbose(
span,
flags.push((sym::_Self, Some(shortname.to_owned())));
}
+ // Slices give us `[]`, `[{ty}]`
+ if let ty::Slice(aty) = self_ty.kind() {
+ flags.push((sym::_Self, Some("[]".to_string())));
+ if let Some(def) = aty.ty_adt_def() {
+ // We also want to be able to select the slice's type's original
+ // signature with no type arguments resolved
+ let type_string = self.tcx.type_of(def.did()).to_string();
+ flags.push((sym::_Self, Some(format!("[{type_string}]"))));
+ }
+ if aty.is_integral() {
+ flags.push((sym::_Self, Some("[{integral}]".to_string())));
+ }
+ }
+
+ // Arrays give us `[]`, `[{ty}; _]` and `[{ty}; N]`
if let ty::Array(aty, len) = self_ty.kind() {
- flags.push((sym::_Self, Some("[]".to_owned())));
- flags.push((sym::_Self, Some(format!("[{}]", aty))));
+ flags.push((sym::_Self, Some("[]".to_string())));
+ let len = len.val().try_to_value().and_then(|v| v.try_to_machine_usize(self.tcx));
+ flags.push((sym::_Self, Some(format!("[{}; _]", aty))));
+ if let Some(n) = len {
+ flags.push((sym::_Self, Some(format!("[{}; {}]", aty, n))));
+ }
if let Some(def) = aty.ty_adt_def() {
// We also want to be able to select the array's type's original
// signature with no type arguments resolved
let type_string = self.tcx.type_of(def.did()).to_string();
- flags.push((sym::_Self, Some(format!("[{}]", type_string))));
-
- let len =
- len.val().try_to_value().and_then(|v| v.try_to_machine_usize(self.tcx));
- let string = match len {
- Some(n) => format!("[{}; {}]", type_string, n),
- None => format!("[{}; _]", type_string),
- };
- flags.push((sym::_Self, Some(string)));
+ flags.push((sym::_Self, Some(format!("[{type_string}; _]"))));
+ if let Some(n) = len {
+ flags.push((sym::_Self, Some(format!("[{type_string}; {n}]"))));
+ }
+ }
+ if aty.is_integral() {
+ flags.push((sym::_Self, Some("[{integral}; _]".to_string())));
+ if let Some(n) = len {
+ flags.push((sym::_Self, Some(format!("[{{integral}}; {n}]"))));
+ }
}
}
if let ty::Dynamic(traits, _) = self_ty.kind() {
fn predicate_constraint(generics: &hir::Generics<'_>, pred: String) -> (Span, String) {
(
- generics.where_clause.tail_span_for_suggestion(),
- format!(
- "{} {}",
- if !generics.where_clause.predicates.is_empty() { "," } else { " where" },
- pred,
- ),
+ generics.tail_span_for_predicate_suggestion(),
+ format!("{} {}", if generics.has_where_clause { "," } else { " where" }, pred,),
)
}
// - ^^^^^^^^^ GenericBounds
// |
// &Ident
- let span = generics.where_clause.span_for_predicates_or_empty_place();
+ let span = generics.span_for_predicates_or_empty_place();
if span.from_expansion() || span.desugaring_kind().is_some() {
return;
}
let pred = trait_pred.to_predicate(tcx).to_string();
let pred = pred.replace(&impl_trait_str, &type_param_name);
let mut sugg = vec![
- // Find the last of the generic parameters contained within the span of
- // the generics
- match generics
- .params
- .iter()
- .map(|p| p.bounds_span_for_suggestions().unwrap_or(p.span.shrink_to_hi()))
- .filter(|&span| generics.span.contains(span) && span.can_be_used_for_suggestions())
- .max_by_key(|span| span.hi())
- {
- // `fn foo(t: impl Trait)`
- // ^ suggest `<T: Trait>` here
- None => (generics.span, format!("<{}>", type_param)),
- // `fn foo<A>(t: impl Trait)`
- // ^^^ suggest `<A, T: Trait>` here
- Some(span) => (span, format!(", {}", type_param)),
+ if let Some(span) = generics.span_for_param_suggestion() {
+ (span, format!(", {}", type_param))
+ } else {
+ (generics.span, format!("<{}>", type_param))
},
// `fn foo(t: impl Trait)`
// ^ suggest `where <T as Trait>::A: Bound`
GeneratorKind::Async(AsyncGeneratorKind::Fn) => self
.tcx
.parent(generator_did)
- .and_then(|parent_did| parent_did.as_local())
+ .as_local()
.map(|parent_did| hir.local_def_id_to_hir_id(parent_did))
.and_then(|parent_hir_id| hir.opt_name(parent_hir_id))
.map(|name| {
}
ObligationCauseCode::RepeatElementCopy { is_const_fn } => {
err.note(
- "the `Copy` trait is required because the repeated element will be copied",
+ "the `Copy` trait is required because this value will be copied for each element of the array",
);
if is_const_fn {
..
}) => Some(
generics
- .where_clause
.predicates
.iter()
.filter_map(|pred| {
// We'll attempt to provide a structured suggestion for `Self: Sized`.
let sugg =
tcx.hir().get_if_local(method.def_id).as_ref().and_then(|node| node.generics()).map(
- |generics| match generics.where_clause.predicates {
- [] => (" where Self: Sized", generics.where_clause.span),
+ |generics| match generics.predicates {
+ [] => (" where Self: Sized", generics.where_clause_span),
[.., pred] => (", Self: Sized", pred.span().shrink_to_hi()),
},
);
// Any type with multiple potential metadata types is therefore not eligible.
let self_ty = selcx.infcx().shallow_resolve(obligation.predicate.self_ty());
- let tail = selcx.tcx().struct_tail_with_normalize(self_ty, |ty| {
- // We throw away any obligations we get from this, since we normalize
- // and confirm these obligations once again during confirmation
- normalize_with_depth(
- selcx,
- obligation.param_env,
- obligation.cause.clone(),
- obligation.recursion_depth + 1,
- ty,
- )
- .value
- });
+ let tail = selcx.tcx().struct_tail_with_normalize(
+ self_ty,
+ |ty| {
+ // We throw away any obligations we get from this, since we normalize
+ // and confirm these obligations once again during confirmation
+ normalize_with_depth(
+ selcx,
+ obligation.param_env,
+ obligation.cause.clone(),
+ obligation.recursion_depth + 1,
+ ty,
+ )
+ .value
+ },
+ || {},
+ );
match tail.kind() {
ty::Bool
let param_type = tcx.infer_ctxt().enter(|infcx| {
infcx.resolve_numeric_literals_with_default(tcx.type_of(param.def_id))
});
- if param_type.is_suggestable() {
+ if param_type.is_suggestable(tcx) {
err.span_suggestion(
tcx.def_span(src_def_id),
"consider changing this type parameter to be a `const` generic",
let self_ty_def_id = tcx.hir().local_def_id(self_ty).to_def_id();
for clause in where_clause {
if let hir::WherePredicate::BoundPredicate(pred) = clause {
- match pred.bounded_ty.kind {
- hir::TyKind::Path(hir::QPath::Resolved(_, path)) => match path.res {
- Res::Def(DefKind::TyParam, def_id) if def_id == self_ty_def_id => {}
- _ => continue,
- },
- _ => continue,
+ if pred.is_param_bound(self_ty_def_id) {
+ search_bounds(pred.bounds);
}
- search_bounds(pred.bounds);
}
}
}
) -> Ty<'tcx> {
let tcx = self.tcx();
- let trait_def_id = tcx.parent(item_def_id).unwrap();
+ let trait_def_id = tcx.parent(item_def_id);
debug!("qpath_to_ty: trait_def_id={:?}", trait_def_id);
// `DefKind::Ctor` -> `DefKind::Variant`
if let DefKind::Ctor(..) = kind {
- def_id = tcx.parent(def_id).unwrap()
+ def_id = tcx.parent(def_id);
}
// `DefKind::Variant` -> `DefKind::Enum`
- let enum_def_id = tcx.parent(def_id).unwrap();
+ let enum_def_id = tcx.parent(def_id);
(enum_def_id, last - 1)
} else {
// FIXME: lint here recommending `Enum::<...>::Variant` form
bf.unsafety,
bf.abi,
bf.decl,
- &hir::Generics::empty(),
None,
Some(ast_ty),
))
span,
ty,
opt_sugg: Some((span, Applicability::MachineApplicable))
- .filter(|_| ty.is_suggestable()),
+ .filter(|_| ty.is_suggestable(tcx)),
});
ty
unsafety: hir::Unsafety,
abi: abi::Abi,
decl: &hir::FnDecl<'_>,
- generics: &hir::Generics<'_>,
- ident_span: Option<Span>,
+ generics: Option<&hir::Generics<'_>>,
hir_ty: Option<&hir::Ty<'_>>,
) -> ty::PolyFnSig<'tcx> {
debug!("ty_of_fn");
// We proactively collect all the inferred type params to emit a single error per fn def.
let mut visitor = HirPlaceholderCollector::default();
- for ty in decl.inputs {
- visitor.visit_ty(ty);
+ let mut infer_replacements = vec![];
+
+ if let Some(generics) = generics {
+ walk_generics(&mut visitor, generics);
}
- walk_generics(&mut visitor, generics);
- let input_tys = decl.inputs.iter().map(|a| self.ty_of_arg(a, None));
+ let input_tys: Vec<_> = decl
+ .inputs
+ .iter()
+ .enumerate()
+ .map(|(i, a)| {
+ if let hir::TyKind::Infer = a.kind && !self.allow_ty_infer() {
+ if let Some(suggested_ty) =
+ self.suggest_trait_fn_ty_for_impl_fn_infer(hir_id, Some(i))
+ {
+ infer_replacements.push((a.span, suggested_ty.to_string()));
+ return suggested_ty;
+ }
+ }
+
+ // Only visit the type looking for `_` if we didn't fix the type above
+ visitor.visit_ty(a);
+ self.ty_of_arg(a, None)
+ })
+ .collect();
+
let output_ty = match decl.output {
hir::FnRetTy::Return(output) => {
- visitor.visit_ty(output);
- self.ast_ty_to_ty(output)
+ if let hir::TyKind::Infer = output.kind
+ && !self.allow_ty_infer()
+ && let Some(suggested_ty) =
+ self.suggest_trait_fn_ty_for_impl_fn_infer(hir_id, None)
+ {
+ infer_replacements.push((output.span, suggested_ty.to_string()));
+ suggested_ty
+ } else {
+ visitor.visit_ty(output);
+ self.ast_ty_to_ty(output)
+ }
}
hir::FnRetTy::DefaultReturn(..) => tcx.mk_unit(),
};
debug!("ty_of_fn: output_ty={:?}", output_ty);
- let fn_ty = tcx.mk_fn_sig(input_tys, output_ty, decl.c_variadic, unsafety, abi);
+ let fn_ty = tcx.mk_fn_sig(input_tys.into_iter(), output_ty, decl.c_variadic, unsafety, abi);
let bare_fn_ty = ty::Binder::bind_with_vars(fn_ty, bound_vars);
- if !self.allow_ty_infer() {
+ if !self.allow_ty_infer() && !(visitor.0.is_empty() && infer_replacements.is_empty()) {
// We always collect the spans for placeholder types when evaluating `fn`s, but we
// only want to emit an error complaining about them if infer types (`_`) are not
// allowed. `allow_ty_infer` gates this behavior. We check for the presence of
// `ident_span` to not emit an error twice when we have `fn foo(_: fn() -> _)`.
- crate::collect::placeholder_type_error(
+ let mut diag = crate::collect::placeholder_type_error_diag(
tcx,
- ident_span.map(|sp| sp.shrink_to_hi()),
- generics.params,
+ generics,
visitor.0,
+ infer_replacements.iter().map(|(s, _)| *s).collect(),
true,
hir_ty,
"function",
);
+
+ if !infer_replacements.is_empty() {
+ diag.multipart_suggestion(&format!(
+ "try replacing `_` with the type{} in the corresponding trait method signature",
+ rustc_errors::pluralize!(infer_replacements.len()),
+ ), infer_replacements, Applicability::MachineApplicable);
+ }
+
+ diag.emit();
}
// Find any late-bound regions declared in return type that do
bare_fn_ty
}
+ /// Given a fn_hir_id for a impl function, suggest the type that is found on the
+ /// corresponding function in the trait that the impl implements, if it exists.
+ /// If arg_idx is Some, then it corresponds to an input type index, otherwise it
+ /// corresponds to the return type.
+ fn suggest_trait_fn_ty_for_impl_fn_infer(
+ &self,
+ fn_hir_id: hir::HirId,
+ arg_idx: Option<usize>,
+ ) -> Option<Ty<'tcx>> {
+ let tcx = self.tcx();
+ let hir = tcx.hir();
+
+ let hir::Node::ImplItem(hir::ImplItem { kind: hir::ImplItemKind::Fn(..), ident, .. }) =
+ hir.get(fn_hir_id) else { return None };
+ let hir::Node::Item(hir::Item { kind: hir::ItemKind::Impl(i), .. }) =
+ hir.get(hir.get_parent_node(fn_hir_id)) else { bug!("ImplItem should have Impl parent") };
+
+ let trait_ref =
+ self.instantiate_mono_trait_ref(i.of_trait.as_ref()?, self.ast_ty_to_ty(i.self_ty));
+
+ let assoc = tcx.associated_items(trait_ref.def_id).find_by_name_and_kind(
+ tcx,
+ *ident,
+ ty::AssocKind::Fn,
+ trait_ref.def_id,
+ )?;
+
+ let fn_sig = tcx.fn_sig(assoc.def_id).subst(
+ tcx,
+ trait_ref.substs.extend_to(tcx, assoc.def_id, |param, _| tcx.mk_param_from_def(param)),
+ );
+
+ let ty = if let Some(arg_idx) = arg_idx { fn_sig.input(arg_idx) } else { fn_sig.output() };
+
+ Some(tcx.liberate_late_bound_regions(fn_hir_id.expect_owner().to_def_id(), ty))
+ }
+
fn validate_late_bound_regions(
&self,
constrained_regions: FxHashSet<ty::BoundRegionKind>,
DUMMY_SP,
param_env,
));
+ // HACK(oli-obk): we rewrite the declared return type, too, so that we don't end up inferring all
+ // unconstrained RPIT to have `()` as their hidden type. This would happen because further down we
+ // compare the ret_coercion with declared_ret_ty, and anything uninferred would be inferred to the
+ // opaque type itself. That again would cause writeback to assume we have a recursive call site
+ // and do the sadly stabilized fallback to `()`.
+ let declared_ret_ty = ret_ty;
fcx.ret_coercion = Some(RefCell::new(CoerceMany::new(ret_ty)));
fcx.ret_type_span = Some(decl.output.span());
iter::zip(impl_m_type_params, trait_m_type_params)
{
if impl_synthetic != trait_synthetic {
- let impl_hir_id = tcx.hir().local_def_id_to_hir_id(impl_def_id.expect_local());
+ let impl_def_id = impl_def_id.expect_local();
+ let impl_hir_id = tcx.hir().local_def_id_to_hir_id(impl_def_id);
let impl_span = tcx.hir().span(impl_hir_id);
let trait_span = tcx.def_span(trait_def_id);
let mut err = struct_span_err!(
hir::ImplItemKind::Fn(ref sig, _) => sig.decl.inputs,
_ => unreachable!(),
};
- struct Visitor(Option<Span>, hir::def_id::DefId);
+ struct Visitor(Option<Span>, hir::def_id::LocalDefId);
impl<'v> intravisit::Visitor<'v> for Visitor {
fn visit_ty(&mut self, ty: &'v hir::Ty<'v>) {
intravisit::walk_ty(self, ty);
if let hir::TyKind::Path(hir::QPath::Resolved(None, ref path)) =
ty.kind
&& let Res::Def(DefKind::TyParam, def_id) = path.res
- && def_id == self.1
+ && def_id == self.1.to_def_id()
{
self.0 = Some(ty.span);
}
}
let span = visitor.0?;
- let bounds =
- impl_m.generics.params.iter().find_map(|param| match param.kind {
- GenericParamKind::Lifetime { .. } => None,
- GenericParamKind::Type { .. } | GenericParamKind::Const { .. } => {
- if param.hir_id == impl_hir_id {
- Some(¶m.bounds)
- } else {
- None
- }
- }
- })?;
+ let bounds = impl_m.generics.bounds_for_param(impl_def_id).next()?.bounds;
let bounds = bounds.first()?.span().to(bounds.last()?.span());
let bounds = tcx.sess.source_map().span_to_snippet(bounds).ok()?;
};
use crate::type_error_struct;
+use super::suggest_call_constructor;
use crate::errors::{AddressOfTemporaryTaken, ReturnStmtOutsideOfFnBody, StructExprNonExhaustive};
use rustc_ast as ast;
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::stack::ensure_sufficient_stack;
use rustc_errors::Diagnostic;
+use rustc_errors::EmissionGuarantee;
use rustc_errors::ErrorGuaranteed;
use rustc_errors::{pluralize, struct_span_err, Applicability, DiagnosticBuilder, DiagnosticId};
use rustc_hir as hir;
// While we don't allow *arbitrary* coercions here, we *do* allow
// coercions from ! to `expected`.
if ty.is_never() {
- assert!(
- !self.typeck_results.borrow().adjustments().contains_key(expr.hir_id),
- "expression with never type wound up being adjusted"
- );
+ if let Some(adjustments) = self.typeck_results.borrow().adjustments().get(expr.hir_id) {
+ self.tcx().sess.delay_span_bug(
+ expr.span,
+ "expression with never type wound up being adjusted",
+ );
+ return if let [Adjustment { kind: Adjust::NeverToAny, target }] = &adjustments[..] {
+ target.to_owned()
+ } else {
+ self.tcx().ty_error()
+ };
+ }
+
let adj_ty = self.next_ty_var(TypeVariableOrigin {
kind: TypeVariableOriginKind::AdjustmentType,
span: expr.span,
return tcx.ty_error();
}
+ self.check_repeat_element_needs_copy_bound(element, count, element_ty);
+
tcx.mk_ty(ty::Array(t, count))
}
+ fn check_repeat_element_needs_copy_bound(
+ &self,
+ element: &hir::Expr<'_>,
+ count: ty::Const<'tcx>,
+ element_ty: Ty<'tcx>,
+ ) {
+ let tcx = self.tcx;
+ // Actual constants as the repeat element get inserted repeatedly instead of getting copied via Copy.
+ match &element.kind {
+ hir::ExprKind::ConstBlock(..) => return,
+ hir::ExprKind::Path(qpath) => {
+ let res = self.typeck_results.borrow().qpath_res(qpath, element.hir_id);
+ if let Res::Def(DefKind::Const | DefKind::AssocConst | DefKind::AnonConst, _) = res
+ {
+ return;
+ }
+ }
+ _ => {}
+ }
+ // If someone calls a const fn, they can extract that call out into a separate constant (or a const
+ // block in the future), so we check that to tell them that in the diagnostic. Does not affect typeck.
+ let is_const_fn = match element.kind {
+ hir::ExprKind::Call(func, _args) => match *self.node_ty(func.hir_id).kind() {
+ ty::FnDef(def_id, _) => tcx.is_const_fn(def_id),
+ _ => false,
+ },
+ _ => false,
+ };
+
+ // If the length is 0, we don't create any elements, so we don't copy any. If the length is 1, we
+ // don't copy that one element, we move it. Only check for Copy if the length is larger.
+ if count.try_eval_usize(tcx, self.param_env).map_or(true, |len| len > 1) {
+ let lang_item = self.tcx.require_lang_item(LangItem::Copy, None);
+ let code = traits::ObligationCauseCode::RepeatElementCopy { is_const_fn };
+ self.require_type_meets(element_ty, element.span, code, lang_item);
+ }
+ }
+
fn check_expr_tuple(
&self,
elts: &'tcx [hir::Expr<'tcx>],
self.tcx().ty_error()
}
+ fn check_call_constructor<G: EmissionGuarantee>(
+ &self,
+ err: &mut DiagnosticBuilder<'_, G>,
+ base: &'tcx hir::Expr<'tcx>,
+ def_id: DefId,
+ ) {
+ let local_id = def_id.expect_local();
+ let hir_id = self.tcx.hir().local_def_id_to_hir_id(local_id);
+ let node = self.tcx.hir().get(hir_id);
+
+ if let Some(fields) = node.tuple_fields() {
+ let kind = match self.tcx.opt_def_kind(local_id) {
+ Some(DefKind::Ctor(of, _)) => of,
+ _ => return,
+ };
+
+ suggest_call_constructor(base.span, kind, fields.len(), err);
+ }
+ }
+
fn suggest_await_on_field_access(
&self,
err: &mut Diagnostic,
ty::Opaque(_, _) => {
self.suggest_await_on_field_access(&mut err, field, base, expr_t.peel_refs());
}
+ ty::FnDef(def_id, _) => {
+ self.check_call_constructor(&mut err, base, def_id);
+ }
_ => {}
}
// try to add a suggestion in case the field is a nested field of a field of the Adt
if let Some((fields, substs)) = self.get_field_candidates(span, expr_t) {
for candidate_field in fields.iter() {
- if let Some(field_path) = self.check_for_nested_field(
+ if let Some(mut field_path) = self.check_for_nested_field_satisfying(
span,
- field,
+ &|candidate_field, _| candidate_field.ident(self.tcx()) == field,
candidate_field,
substs,
vec![],
self.tcx.parent_module(id).to_def_id(),
) {
+ // field_path includes `field` that we're looking for, so pop it.
+ field_path.pop();
+
let field_path_str = field_path
.iter()
.map(|id| id.name.to_ident_string())
err
}
- fn get_field_candidates(
+ crate fn get_field_candidates(
&self,
span: Span,
base_t: Ty<'tcx>,
/// This method is called after we have encountered a missing field error to recursively
/// search for the field
- fn check_for_nested_field(
+ crate fn check_for_nested_field_satisfying(
&self,
span: Span,
- target_field: Ident,
+ matches: &impl Fn(&ty::FieldDef, Ty<'tcx>) -> bool,
candidate_field: &ty::FieldDef,
subst: SubstsRef<'tcx>,
mut field_path: Vec<Ident>,
id: DefId,
) -> Option<Vec<Ident>> {
debug!(
- "check_for_nested_field(span: {:?}, candidate_field: {:?}, field_path: {:?}",
+ "check_for_nested_field_satisfying(span: {:?}, candidate_field: {:?}, field_path: {:?}",
span, candidate_field, field_path
);
- if candidate_field.ident(self.tcx) == target_field {
- Some(field_path)
- } else if field_path.len() > 3 {
+ if field_path.len() > 3 {
// For compile-time reasons and to avoid infinite recursion we only check for fields
// up to a depth of three
None
} else {
// recursively search fields of `candidate_field` if it's a ty::Adt
-
field_path.push(candidate_field.ident(self.tcx).normalize_to_macros_2_0());
let field_ty = candidate_field.ty(self.tcx, subst);
if let Some((nested_fields, subst)) = self.get_field_candidates(span, field_ty) {
for field in nested_fields.iter() {
- let accessible = field.vis.is_accessible_from(id, self.tcx);
- if accessible {
- let ident = field.ident(self.tcx).normalize_to_macros_2_0();
- if ident == target_field {
+ if field.vis.is_accessible_from(id, self.tcx) {
+ if matches(candidate_field, field_ty) {
return Some(field_path);
- }
- let field_path = field_path.clone();
- if let Some(path) = self.check_for_nested_field(
+ } else if let Some(field_path) = self.check_for_nested_field_satisfying(
span,
- target_field,
+ matches,
field,
subst,
- field_path,
+ field_path.clone(),
id,
) {
- return Some(path);
+ return Some(field_path);
}
}
}
formal_args: &[Ty<'tcx>],
) -> Option<Vec<Ty<'tcx>>> {
let formal_ret = self.resolve_vars_with_obligations(formal_ret);
- let Some(ret_ty) = expected_ret.only_has_type(self) else { return None };
+ let ret_ty = expected_ret.only_has_type(self)?;
// HACK(oli-obk): This is a hack to keep RPIT and TAIT in sync wrt their behaviour.
// Without it, the inference
let def_kind = self.tcx.def_kind(def_id);
let item_ty = if let DefKind::Variant = def_kind {
- self.tcx.type_of(self.tcx.parent(def_id).expect("variant w/out parent"))
+ self.tcx.type_of(self.tcx.parent(def_id))
} else {
self.tcx.type_of(def_id)
};
errors.drain_filter(|error| {
let Error::Invalid(input_idx, Compatibility::Incompatible(error)) = error else { return false };
let expected_ty = expected_input_tys[*input_idx];
- let provided_ty = final_arg_types[*input_idx].map(|ty| ty.0).unwrap();
+ let Some(Some((provided_ty, _))) = final_arg_types.get(*input_idx) else { return false };
let cause = &self.misc(provided_args[*input_idx].span);
- let trace = TypeTrace::types(cause, true, expected_ty, provided_ty);
+ let trace = TypeTrace::types(cause, true, expected_ty, *provided_ty);
if let Some(e) = error {
if !matches!(trace.cause.as_failure_code(e), FailureCode::Error0308(_)) {
self.report_and_explain_type_error(trace, e).emit();
Error::Invalid(input_idx, compatibility) => {
let expected_ty = expected_input_tys[input_idx];
if let Compatibility::Incompatible(error) = &compatibility {
- let provided_ty = final_arg_types[input_idx].map(|ty| ty.0).unwrap();
- let cause = &self.misc(provided_args[input_idx].span);
+ let provided_ty = final_arg_types
+ .get(input_idx)
+ .and_then(|x| x.as_ref())
+ .map(|ty| ty.0)
+ .unwrap_or(tcx.ty_error());
+ let cause = &self.misc(
+ provided_args.get(input_idx).map(|i| i.span).unwrap_or(call_span),
+ );
let trace = TypeTrace::types(cause, true, expected_ty, provided_ty);
if let Some(e) = error {
self.note_type_err(
}
}
- self.emit_coerce_suggestions(
- &mut err,
- &provided_args[input_idx],
- final_arg_types[input_idx].map(|ty| ty.0).unwrap(),
- final_arg_types[input_idx].map(|ty| ty.1).unwrap(),
- None,
- None,
- );
+ if let Some(expr) = provided_args.get(input_idx) {
+ self.emit_coerce_suggestions(
+ &mut err,
+ &expr,
+ final_arg_types[input_idx].map(|ty| ty.0).unwrap(),
+ final_arg_types[input_idx].map(|ty| ty.1).unwrap(),
+ None,
+ None,
+ );
+ }
}
Error::Extra(arg_idx) => {
let arg_type = if let Some((_, ty)) = final_arg_types[arg_idx] {
SuggestionText::Remove(plural) => {
Some(format!("remove the extra argument{}", if plural { "s" } else { "" }))
}
- SuggestionText::Swap => Some(format!("swap these arguments")),
- SuggestionText::Reorder => Some(format!("reorder these arguments")),
- SuggestionText::DidYouMean => Some(format!("did you mean")),
+ SuggestionText::Swap => Some("swap these arguments".to_string()),
+ SuggestionText::Reorder => Some("reorder these arguments".to_string()),
+ SuggestionText::DidYouMean => Some("did you mean".to_string()),
};
if let Some(suggestion_text) = suggestion_text {
let source_map = self.sess().source_map();
);
for (idx, arg) in matched_inputs.iter().enumerate() {
let suggestion_text = if let Some(arg) = arg {
- let arg_span = provided_args[*arg].span;
+ let arg_span = provided_args[*arg].span.source_callsite();
let arg_text = source_map.span_to_snippet(arg_span).unwrap();
arg_text
} else {
use super::FnCtxt;
use crate::astconv::AstConv;
+use crate::errors::{AddReturnTypeSuggestion, ExpectedReturnTypeLabel};
use rustc_ast::util::parser::ExprPrecedence;
use rustc_errors::{Applicability, Diagnostic, MultiSpan};
self.resolve_numeric_literals_with_default(self.resolve_vars_if_possible(found));
// Only suggest changing the return type for methods that
// haven't set a return type at all (and aren't `fn main()` or an impl).
- match (&fn_decl.output, found.is_suggestable(), can_suggest, expected.is_unit()) {
+ match (&fn_decl.output, found.is_suggestable(self.tcx), can_suggest, expected.is_unit()) {
(&hir::FnRetTy::DefaultReturn(span), true, true, true) => {
- err.span_suggestion(
- span,
- "try adding a return type",
- format!("-> {} ", found),
- Applicability::MachineApplicable,
- );
+ err.subdiagnostic(AddReturnTypeSuggestion::Add { span, found });
true
}
(&hir::FnRetTy::DefaultReturn(span), false, true, true) => {
// FIXME: if `found` could be `impl Iterator` or `impl Fn*`, we should suggest
// that.
- err.span_suggestion(
- span,
- "a return type might be missing here",
- "-> _ ".to_string(),
- Applicability::HasPlaceholders,
- );
+ err.subdiagnostic(AddReturnTypeSuggestion::MissingHere { span });
true
}
(&hir::FnRetTy::DefaultReturn(span), _, false, true) => {
// `fn main()` must return `()`, do not suggest changing return type
- err.span_label(span, "expected `()` because of default return type");
+ err.subdiagnostic(ExpectedReturnTypeLabel::Unit { span });
true
}
// expectation was caused by something else, not the default return
// Only point to return type if the expected type is the return type, as if they
// are not, the expectation must have been caused by something else.
debug!("suggest_missing_return_type: return type {:?} node {:?}", ty, ty.kind);
- let sp = ty.span;
+ let span = ty.span;
let ty = <dyn AstConv<'_>>::ast_ty_to_ty(self, ty);
debug!("suggest_missing_return_type: return type {:?}", ty);
debug!("suggest_missing_return_type: expected type {:?}", ty);
let bound_vars = self.tcx.late_bound_vars(fn_id);
let ty = Binder::bind_with_vars(ty, bound_vars);
- let ty = self.normalize_associated_types_in(sp, ty);
+ let ty = self.normalize_associated_types_in(span, ty);
let ty = self.tcx.erase_late_bound_regions(ty);
if self.can_coerce(expected, ty) {
- err.span_label(sp, format!("expected `{}` because of return type", expected));
+ err.subdiagnostic(ExpectedReturnTypeLabel::Other { span, expected });
self.try_suggest_return_impl_trait(err, expected, ty, fn_id);
return true;
}
kind:
hir::ItemKind::Fn(
hir::FnSig { decl: hir::FnDecl { inputs: fn_parameters, output: fn_return, .. }, .. },
- hir::Generics { params, where_clause, .. },
+ hir::Generics { params, predicates, .. },
_body_id,
),
..
})) = fn_node else { return };
- let Some(expected_generic_param) = params.get(expected_ty_as_param.index as usize) else { return };
+ if params.get(expected_ty_as_param.index as usize).is_none() {
+ return;
+ };
// get all where BoundPredicates here, because they are used in to cases below
- let where_predicates = where_clause
- .predicates
+ let where_predicates = predicates
.iter()
.filter_map(|p| match p {
WherePredicate::BoundPredicate(hir::WhereBoundPredicate {
where_predicates.iter().flatten().flat_map(|bounds| bounds.iter());
// extract all bounds from the source code using their spans
- let all_matching_bounds_strs = expected_generic_param
- .bounds
- .iter()
- .chain(predicates_from_where)
+ let all_matching_bounds_strs = predicates_from_where
.filter_map(|bound| match bound {
GenericBound::Trait(_, _) => {
self.tcx.sess.source_map().span_to_snippet(bound.span()).ok()
| hir::Node::Ctor(..)
| hir::Node::Lifetime(..)
| hir::Node::GenericParam(..)
- | hir::Node::Visibility(..)
| hir::Node::Crate(..)
| hir::Node::Infer(..) => bug!("Unsupported branch target: {:?}", node),
}
MultiSpan,
};
use rustc_hir as hir;
+use rustc_hir::def::DefKind;
use rustc_hir::def_id::DefId;
use rustc_hir::lang_items::LangItem;
use rustc_hir::{ExprKind, Node, QPath};
use std::cmp::Ordering;
use std::iter;
-use super::probe::Mode;
-use super::{CandidateSource, MethodError, NoMatchData};
+use super::probe::{Mode, ProbeScope};
+use super::{super::suggest_call_constructor, CandidateSource, MethodError, NoMatchData};
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
fn is_fn_ty(&self, ty: Ty<'tcx>, span: Span) -> bool {
(None, true) => "variant",
}
};
- // FIXME(eddyb) this indentation is probably unnecessary.
- let mut err = {
- // Suggest clamping down the type if the method that is being attempted to
- // be used exists at all, and the type is an ambiguous numeric type
- // ({integer}/{float}).
- let mut candidates = all_traits(self.tcx)
- .into_iter()
- .filter_map(|info| self.associated_value(info.def_id, item_name));
- // There are methods that are defined on the primitive types and won't be
- // found when exploring `all_traits`, but we also need them to be accurate on
- // our suggestions (#47759).
- let found_assoc = |ty: Ty<'tcx>| {
- simplify_type(tcx, ty, TreatParams::AsPlaceholders)
- .and_then(|simp| {
- tcx.incoherent_impls(simp)
- .iter()
- .find_map(|&id| self.associated_value(id, item_name))
- })
- .is_some()
- };
- let found_candidate = candidates.next().is_some()
- || found_assoc(tcx.types.i8)
- || found_assoc(tcx.types.i16)
- || found_assoc(tcx.types.i32)
- || found_assoc(tcx.types.i64)
- || found_assoc(tcx.types.i128)
- || found_assoc(tcx.types.u8)
- || found_assoc(tcx.types.u16)
- || found_assoc(tcx.types.u32)
- || found_assoc(tcx.types.u64)
- || found_assoc(tcx.types.u128)
- || found_assoc(tcx.types.f32)
- || found_assoc(tcx.types.f32);
- if let (true, false, SelfSource::MethodCall(expr), true) = (
- actual.is_numeric(),
- actual.has_concrete_skeleton(),
- source,
- found_candidate,
- ) {
- let mut err = struct_span_err!(
- tcx.sess,
- span,
- E0689,
- "can't call {} `{}` on ambiguous numeric type `{}`",
- item_kind,
- item_name,
- ty_str
- );
- let concrete_type = if actual.is_integral() { "i32" } else { "f32" };
- match expr.kind {
- ExprKind::Lit(ref lit) => {
- // numeric literal
- let snippet = tcx
- .sess
- .source_map()
- .span_to_snippet(lit.span)
- .unwrap_or_else(|_| "<numeric literal>".to_owned());
-
- // If this is a floating point literal that ends with '.',
- // get rid of it to stop this from becoming a member access.
- let snippet = snippet.strip_suffix('.').unwrap_or(&snippet);
- err.span_suggestion(
- lit.span,
- &format!(
- "you must specify a concrete type for this numeric value, \
- like `{}`",
- concrete_type
- ),
- format!("{snippet}_{concrete_type}"),
- Applicability::MaybeIncorrect,
- );
- }
- ExprKind::Path(QPath::Resolved(_, path)) => {
- // local binding
- if let hir::def::Res::Local(hir_id) = path.res {
- let span = tcx.hir().span(hir_id);
- let snippet = tcx.sess.source_map().span_to_snippet(span);
- let filename = tcx.sess.source_map().span_to_filename(span);
-
- let parent_node =
- self.tcx.hir().get(self.tcx.hir().get_parent_node(hir_id));
- let msg = format!(
- "you must specify a type for this binding, like `{}`",
- concrete_type,
- );
-
- match (filename, parent_node, snippet) {
- (
- FileName::Real(_),
- Node::Local(hir::Local {
- source: hir::LocalSource::Normal,
- ty,
- ..
- }),
- Ok(ref snippet),
- ) => {
- err.span_suggestion(
- // account for `let x: _ = 42;`
- // ^^^^
- span.to(ty
- .as_ref()
- .map(|ty| ty.span)
- .unwrap_or(span)),
- &msg,
- format!("{}: {}", snippet, concrete_type),
- Applicability::MaybeIncorrect,
- );
- }
- _ => {
- err.span_label(span, msg);
- }
- }
- }
+ if self.suggest_constraining_numerical_ty(
+ tcx, actual, source, span, item_kind, item_name, &ty_str,
+ ) {
+ return None;
+ }
+
+ span = item_name.span;
+
+ // Don't show generic arguments when the method can't be found in any implementation (#81576).
+ let mut ty_str_reported = ty_str.clone();
+ if let ty::Adt(_, generics) = actual.kind() {
+ if generics.len() > 0 {
+ let mut autoderef = self.autoderef(span, actual);
+ let candidate_found = autoderef.any(|(ty, _)| {
+ if let ty::Adt(adt_deref, _) = ty.kind() {
+ self.tcx
+ .inherent_impls(adt_deref.did())
+ .iter()
+ .filter_map(|def_id| self.associated_value(*def_id, item_name))
+ .count()
+ >= 1
+ } else {
+ false
}
- _ => {}
- }
- err.emit();
- return None;
- } else {
- span = item_name.span;
-
- // Don't show generic arguments when the method can't be found in any implementation (#81576).
- let mut ty_str_reported = ty_str.clone();
- if let ty::Adt(_, generics) = actual.kind() {
- if generics.len() > 0 {
- let mut autoderef = self.autoderef(span, actual);
- let candidate_found = autoderef.any(|(ty, _)| {
- if let ty::Adt(adt_deref, _) = ty.kind() {
- self.tcx
- .inherent_impls(adt_deref.did())
- .iter()
- .filter_map(|def_id| {
- self.associated_value(*def_id, item_name)
- })
- .count()
- >= 1
- } else {
- false
- }
- });
- let has_deref = autoderef.step_count() > 0;
- if !candidate_found
- && !has_deref
- && unsatisfied_predicates.is_empty()
- {
- if let Some((path_string, _)) = ty_str.split_once('<') {
- ty_str_reported = path_string.to_string();
- }
- }
+ });
+ let has_deref = autoderef.step_count() > 0;
+ if !candidate_found && !has_deref && unsatisfied_predicates.is_empty() {
+ if let Some((path_string, _)) = ty_str.split_once('<') {
+ ty_str_reported = path_string.to_string();
}
}
+ }
+ }
- let mut err = struct_span_err!(
- tcx.sess,
- span,
- E0599,
- "no {} named `{}` found for {} `{}` in the current scope",
- item_kind,
- item_name,
- actual.prefix_string(self.tcx),
- ty_str_reported,
- );
- if let Mode::MethodCall = mode && let SelfSource::MethodCall(cal) = source {
- self.suggest_await_before_method(
- &mut err, item_name, actual, cal, span,
- );
- }
- if let Some(span) =
- tcx.resolutions(()).confused_type_with_std_module.get(&span)
- {
- if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(*span) {
- err.span_suggestion(
- *span,
- "you are looking for the module in `std`, \
+ let mut err = struct_span_err!(
+ tcx.sess,
+ span,
+ E0599,
+ "no {} named `{}` found for {} `{}` in the current scope",
+ item_kind,
+ item_name,
+ actual.prefix_string(self.tcx),
+ ty_str_reported,
+ );
+ if actual.references_error() {
+ err.downgrade_to_delayed_bug();
+ }
+
+ if let Mode::MethodCall = mode && let SelfSource::MethodCall(cal) = source {
+ self.suggest_await_before_method(
+ &mut err, item_name, actual, cal, span,
+ );
+ }
+ if let Some(span) = tcx.resolutions(()).confused_type_with_std_module.get(&span) {
+ if let Ok(snippet) = tcx.sess.source_map().span_to_snippet(*span) {
+ err.span_suggestion(
+ *span,
+ "you are looking for the module in `std`, \
not the primitive type",
- format!("std::{}", snippet),
- Applicability::MachineApplicable,
- );
- }
- }
- if let ty::RawPtr(_) = &actual.kind() {
- err.note(
- "try using `<*const T>::as_ref()` to get a reference to the \
+ format!("std::{}", snippet),
+ Applicability::MachineApplicable,
+ );
+ }
+ }
+ if let ty::RawPtr(_) = &actual.kind() {
+ err.note(
+ "try using `<*const T>::as_ref()` to get a reference to the \
type behind the pointer: https://doc.rust-lang.org/std/\
primitive.pointer.html#method.as_ref",
- );
- err.note(
- "using `<*const T>::as_ref()` on a pointer \
+ );
+ err.note(
+ "using `<*const T>::as_ref()` on a pointer \
which is unaligned or points to invalid \
or uninitialized memory is undefined behavior",
- );
- }
- err
- }
- };
-
- if actual.references_error() {
- err.downgrade_to_delayed_bug();
+ );
}
if let Some(def) = actual.ty_adt_def() {
}
if self.is_fn_ty(rcvr_ty, span) {
- fn report_function<T: std::fmt::Display>(err: &mut Diagnostic, name: T) {
- err.note(
- &format!("`{}` is a function, perhaps you wish to call it", name,),
- );
- }
-
if let SelfSource::MethodCall(expr) = source {
- if let Ok(expr_string) = tcx.sess.source_map().span_to_snippet(expr.span) {
- report_function(&mut err, expr_string);
- } else if let ExprKind::Path(QPath::Resolved(_, path)) = expr.kind {
- if let Some(segment) = path.segments.last() {
- report_function(&mut err, segment.ident);
+ let suggest = if let ty::FnDef(def_id, _) = rcvr_ty.kind() {
+ let local_id = def_id.expect_local();
+ let hir_id = tcx.hir().local_def_id_to_hir_id(local_id);
+ let node = tcx.hir().get(hir_id);
+ let fields = node.tuple_fields();
+
+ if let Some(fields) = fields
+ && let Some(DefKind::Ctor(of, _)) = self.tcx.opt_def_kind(local_id) {
+ Some((fields, of))
+ } else {
+ None
}
+ } else {
+ None
+ };
+
+ // If the function is a tuple constructor, we recommend that they call it
+ if let Some((fields, kind)) = suggest {
+ suggest_call_constructor(expr.span, kind, fields.len(), &mut err);
+ } else {
+ // General case
+ err.span_label(
+ expr.span,
+ "this is a function, perhaps you wish to call it",
+ );
}
}
}
};
if let Some(hir::Node::Item(hir::Item { kind, .. })) = node {
if let Some(g) = kind.generics() {
- let key = match g.where_clause.predicates {
+ let key = match g.predicates {
[.., pred] => (pred.span().shrink_to_hi(), false),
- [] => (
- g.where_clause.span_for_predicates_or_empty_place(),
- true,
- ),
+ [] => (g.span_for_predicates_or_empty_place(), true),
};
type_params
.entry(key)
}
}
- let mut label_span_not_found = || {
+ let label_span_not_found = |err: &mut DiagnosticBuilder<'_, _>| {
if unsatisfied_predicates.is_empty() {
err.span_label(span, format!("{item_kind} not found in `{ty_str}`"));
let is_string_or_ref_str = match actual.kind() {
// If the method name is the name of a field with a function or closure type,
// give a helping note that it has to be called as `(x.f)(...)`.
if let SelfSource::MethodCall(expr) = source {
- let field_receiver =
- self.autoderef(span, rcvr_ty).find_map(|(ty, _)| match ty.kind() {
- ty::Adt(def, substs) if !def.is_enum() => {
- let variant = &def.non_enum_variant();
- self.tcx.find_field_index(item_name, variant).map(|index| {
- let field = &variant.fields[index];
- let field_ty = field.ty(tcx, substs);
- (field, field_ty)
- })
- }
- _ => None,
- });
+ if !self.suggest_field_call(span, rcvr_ty, expr, item_name, &mut err)
+ && lev_candidate.is_none()
+ && !custom_span_label
+ {
+ label_span_not_found(&mut err);
+ }
+ } else if !custom_span_label {
+ label_span_not_found(&mut err);
+ }
- if let Some((field, field_ty)) = field_receiver {
- let scope = self.tcx.parent_module(self.body_id).to_def_id();
- let is_accessible = field.vis.is_accessible_from(scope, self.tcx);
+ if let SelfSource::MethodCall(expr) = source
+ && let Some((fields, substs)) = self.get_field_candidates(span, actual)
+ {
+ let call_expr =
+ self.tcx.hir().expect_expr(self.tcx.hir().get_parent_node(expr.hir_id));
+ for candidate_field in fields.iter() {
+ if let Some(field_path) = self.check_for_nested_field_satisfying(
+ span,
+ &|_, field_ty| {
+ self.lookup_probe(
+ span,
+ item_name,
+ field_ty,
+ call_expr,
+ ProbeScope::AllTraits,
+ )
+ .is_ok()
+ },
+ candidate_field,
+ substs,
+ vec![],
+ self.tcx.parent_module(expr.hir_id).to_def_id(),
+ ) {
+ let field_path_str = field_path
+ .iter()
+ .map(|id| id.name.to_ident_string())
+ .collect::<Vec<String>>()
+ .join(".");
+ debug!("field_path_str: {:?}", field_path_str);
- if is_accessible {
- if self.is_fn_ty(field_ty, span) {
- let expr_span = expr.span.to(item_name.span);
- err.multipart_suggestion(
- &format!(
- "to call the function stored in `{}`, \
- surround the field access with parentheses",
- item_name,
- ),
- vec![
- (expr_span.shrink_to_lo(), '('.to_string()),
- (expr_span.shrink_to_hi(), ')'.to_string()),
- ],
- Applicability::MachineApplicable,
- );
- } else {
- let call_expr = self
- .tcx
- .hir()
- .expect_expr(self.tcx.hir().get_parent_node(expr.hir_id));
-
- if let Some(span) = call_expr.span.trim_start(item_name.span) {
- err.span_suggestion(
- span,
- "remove the arguments",
- String::new(),
- Applicability::MaybeIncorrect,
- );
- }
- }
+ err.span_suggestion_verbose(
+ item_name.span.shrink_to_lo(),
+ "one of the expressions' fields has a method of the same name",
+ format!("{field_path_str}."),
+ Applicability::MaybeIncorrect,
+ );
}
-
- let field_kind = if is_accessible { "field" } else { "private field" };
- err.span_label(item_name.span, format!("{}, not a method", field_kind));
- } else if lev_candidate.is_none() && !custom_span_label {
- label_span_not_found();
}
- } else if !custom_span_label {
- label_span_not_found();
}
bound_spans.sort();
None
}
+ fn suggest_field_call(
+ &self,
+ span: Span,
+ rcvr_ty: Ty<'tcx>,
+ expr: &hir::Expr<'_>,
+ item_name: Ident,
+ err: &mut DiagnosticBuilder<'tcx, ErrorGuaranteed>,
+ ) -> bool {
+ let tcx = self.tcx;
+ let field_receiver = self.autoderef(span, rcvr_ty).find_map(|(ty, _)| match ty.kind() {
+ ty::Adt(def, substs) if !def.is_enum() => {
+ let variant = &def.non_enum_variant();
+ tcx.find_field_index(item_name, variant).map(|index| {
+ let field = &variant.fields[index];
+ let field_ty = field.ty(tcx, substs);
+ (field, field_ty)
+ })
+ }
+ _ => None,
+ });
+ if let Some((field, field_ty)) = field_receiver {
+ let scope = tcx.parent_module(self.body_id).to_def_id();
+ let is_accessible = field.vis.is_accessible_from(scope, tcx);
+
+ if is_accessible {
+ if self.is_fn_ty(field_ty, span) {
+ let expr_span = expr.span.to(item_name.span);
+ err.multipart_suggestion(
+ &format!(
+ "to call the function stored in `{}`, \
+ surround the field access with parentheses",
+ item_name,
+ ),
+ vec![
+ (expr_span.shrink_to_lo(), '('.to_string()),
+ (expr_span.shrink_to_hi(), ')'.to_string()),
+ ],
+ Applicability::MachineApplicable,
+ );
+ } else {
+ let call_expr = tcx.hir().expect_expr(tcx.hir().get_parent_node(expr.hir_id));
+
+ if let Some(span) = call_expr.span.trim_start(item_name.span) {
+ err.span_suggestion(
+ span,
+ "remove the arguments",
+ String::new(),
+ Applicability::MaybeIncorrect,
+ );
+ }
+ }
+ }
+
+ let field_kind = if is_accessible { "field" } else { "private field" };
+ err.span_label(item_name.span, format!("{}, not a method", field_kind));
+ return true;
+ }
+ false
+ }
+
+ fn suggest_constraining_numerical_ty(
+ &self,
+ tcx: TyCtxt<'tcx>,
+ actual: Ty<'tcx>,
+ source: SelfSource<'_>,
+ span: Span,
+ item_kind: &str,
+ item_name: Ident,
+ ty_str: &str,
+ ) -> bool {
+ let found_candidate = all_traits(self.tcx)
+ .into_iter()
+ .any(|info| self.associated_value(info.def_id, item_name).is_some());
+ let found_assoc = |ty: Ty<'tcx>| {
+ simplify_type(tcx, ty, TreatParams::AsPlaceholders)
+ .and_then(|simp| {
+ tcx.incoherent_impls(simp)
+ .iter()
+ .find_map(|&id| self.associated_value(id, item_name))
+ })
+ .is_some()
+ };
+ let found_candidate = found_candidate
+ || found_assoc(tcx.types.i8)
+ || found_assoc(tcx.types.i16)
+ || found_assoc(tcx.types.i32)
+ || found_assoc(tcx.types.i64)
+ || found_assoc(tcx.types.i128)
+ || found_assoc(tcx.types.u8)
+ || found_assoc(tcx.types.u16)
+ || found_assoc(tcx.types.u32)
+ || found_assoc(tcx.types.u64)
+ || found_assoc(tcx.types.u128)
+ || found_assoc(tcx.types.f32)
+ || found_assoc(tcx.types.f32);
+ if found_candidate
+ && actual.is_numeric()
+ && !actual.has_concrete_skeleton()
+ && let SelfSource::MethodCall(expr) = source
+ {
+ let mut err = struct_span_err!(
+ tcx.sess,
+ span,
+ E0689,
+ "can't call {} `{}` on ambiguous numeric type `{}`",
+ item_kind,
+ item_name,
+ ty_str
+ );
+ let concrete_type = if actual.is_integral() { "i32" } else { "f32" };
+ match expr.kind {
+ ExprKind::Lit(ref lit) => {
+ // numeric literal
+ let snippet = tcx
+ .sess
+ .source_map()
+ .span_to_snippet(lit.span)
+ .unwrap_or_else(|_| "<numeric literal>".to_owned());
+
+ // If this is a floating point literal that ends with '.',
+ // get rid of it to stop this from becoming a member access.
+ let snippet = snippet.strip_suffix('.').unwrap_or(&snippet);
+
+ err.span_suggestion(
+ lit.span,
+ &format!(
+ "you must specify a concrete type for this numeric value, \
+ like `{}`",
+ concrete_type
+ ),
+ format!("{snippet}_{concrete_type}"),
+ Applicability::MaybeIncorrect,
+ );
+ }
+ ExprKind::Path(QPath::Resolved(_, path)) => {
+ // local binding
+ if let hir::def::Res::Local(hir_id) = path.res {
+ let span = tcx.hir().span(hir_id);
+ let snippet = tcx.sess.source_map().span_to_snippet(span);
+ let filename = tcx.sess.source_map().span_to_filename(span);
+
+ let parent_node =
+ self.tcx.hir().get(self.tcx.hir().get_parent_node(hir_id));
+ let msg = format!(
+ "you must specify a type for this binding, like `{}`",
+ concrete_type,
+ );
+
+ match (filename, parent_node, snippet) {
+ (
+ FileName::Real(_),
+ Node::Local(hir::Local {
+ source: hir::LocalSource::Normal,
+ ty,
+ ..
+ }),
+ Ok(ref snippet),
+ ) => {
+ err.span_suggestion(
+ // account for `let x: _ = 42;`
+ // ^^^^
+ span.to(ty.as_ref().map(|ty| ty.span).unwrap_or(span)),
+ &msg,
+ format!("{}: {}", snippet, concrete_type),
+ Applicability::MaybeIncorrect,
+ );
+ }
+ _ => {
+ err.span_label(span, msg);
+ }
+ }
+ }
+ }
+ _ => {}
+ }
+ err.emit();
+ return true;
+ }
+ false
+ }
+
crate fn note_unmet_impls_on_type(
&self,
err: &mut Diagnostic,
let (candidates, globs): (Vec<_>, Vec<_>) = candidates.into_iter().partition(|trait_did| {
if let Some(parent_did) = parent_map.get(trait_did) {
// If the item is re-exported as `_`, we should suggest a glob-import instead.
- if Some(*parent_did) != self.tcx.parent(*trait_did)
+ if *parent_did != self.tcx.parent(*trait_did)
&& self
.tcx
.module_children(*parent_did)
// instead we suggest `T: Foo + Bar` in that case.
match hir.get(id) {
Node::GenericParam(param) => {
- let mut impl_trait = false;
- let has_bounds =
- if let hir::GenericParamKind::Type { synthetic: true, .. } =
- ¶m.kind
- {
- // We've found `fn foo(x: impl Trait)` instead of
- // `fn foo<T>(x: T)`. We want to suggest the correct
- // `fn foo(x: impl Trait + TraitBound)` instead of
- // `fn foo<T: TraitBound>(x: T)`. (#63706)
- impl_trait = true;
- param.bounds.get(1)
- } else {
- param.bounds.get(0)
- };
- let sp = hir.span(id);
- let sp = if let Some(first_bound) = has_bounds {
- sp.until(first_bound.span())
- } else if let Some(colon_sp) =
- // If the generic param is declared with a colon but without bounds:
- // fn foo<T:>(t: T) { ... }
- param.colon_span_for_suggestions(
- self.inh.tcx.sess.source_map(),
- )
+ enum Introducer {
+ Plus,
+ Colon,
+ Nothing,
+ }
+ let ast_generics = hir.get_generics(id.owner).unwrap();
+ let (sp, mut introducer) = if let Some(span) =
+ ast_generics.bounds_span_for_suggestions(def_id)
{
- sp.to(colon_sp)
+ (span, Introducer::Plus)
+ } else if let Some(colon_span) = param.colon_span {
+ (colon_span.shrink_to_hi(), Introducer::Nothing)
} else {
- sp
+ (param.span.shrink_to_hi(), Introducer::Colon)
};
- let trait_def_ids: FxHashSet<DefId> = param
- .bounds
- .iter()
+ if matches!(
+ param.kind,
+ hir::GenericParamKind::Type { synthetic: true, .. },
+ ) {
+ introducer = Introducer::Plus
+ }
+ let trait_def_ids: FxHashSet<DefId> = ast_generics
+ .bounds_for_param(def_id)
+ .flat_map(|bp| bp.bounds.iter())
.filter_map(|bound| bound.trait_ref()?.trait_def_id())
.collect();
if !candidates.iter().any(|t| trait_def_ids.contains(&t.def_id)) {
)),
candidates.iter().map(|t| {
format!(
- "{}{} {}{}",
- param.name.ident(),
- if impl_trait { " +" } else { ":" },
+ "{} {}",
+ match introducer {
+ Introducer::Plus => " +",
+ Introducer::Colon => ":",
+ Introducer::Nothing => "",
+ },
self.tcx.def_path_str(t.def_id),
- if has_bounds.is_some() { " + " } else { "" },
)
}),
Applicability::MaybeIncorrect,
pub use diverges::Diverges;
pub use expectation::Expectation;
pub use fn_ctxt::*;
+use hir::def::CtorOf;
pub use inherited::{Inherited, InheritedBuilder};
use crate::astconv::AstConv;
use crate::check::gather_locals::GatherLocalsVisitor;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
-use rustc_errors::{pluralize, struct_span_err, Applicability, MultiSpan};
+use rustc_errors::{
+ pluralize, struct_span_err, Applicability, DiagnosticBuilder, EmissionGuarantee, MultiSpan,
+};
use rustc_hir as hir;
use rustc_hir::def::Res;
use rustc_hir::def_id::{DefId, LocalDefId};
let (fcx, wf_tys) = if let Some(hir::FnSig { header, decl, .. }) = fn_sig {
let fn_sig = if crate::collect::get_infer_ret_ty(&decl.output).is_some() {
let fcx = FnCtxt::new(&inh, param_env, body.value.hir_id);
- <dyn AstConv<'_>>::ty_of_fn(
- &fcx,
- id,
- header.unsafety,
- header.abi,
- decl,
- &hir::Generics::empty(),
- None,
- None,
- )
+ <dyn AstConv<'_>>::ty_of_fn(&fcx, id, header.unsafety, header.abi, decl, None, None)
} else {
tcx.fn_sig(def_id)
};
generics.count() == expected + if generics.has_self { 1 } else { 0 }
})
}
+
+/// Suggests calling the constructor of a tuple struct or enum variant
+///
+/// * `snippet` - The snippet of code that references the constructor
+/// * `span` - The span of the snippet
+/// * `params` - The number of parameters the constructor accepts
+/// * `err` - A mutable diagnostic builder to add the suggestion to
+fn suggest_call_constructor<G: EmissionGuarantee>(
+ span: Span,
+ kind: CtorOf,
+ params: usize,
+ err: &mut DiagnosticBuilder<'_, G>,
+) {
+ // Note: tuple-structs don't have named fields, so just use placeholders
+ let args = vec!["_"; params].join(", ");
+ let applicable = if params > 0 {
+ Applicability::HasPlaceholders
+ } else {
+ // When n = 0, it's an empty-tuple struct/enum variant
+ // so we trivially know how to construct it
+ Applicability::MachineApplicable
+ };
+ let kind = match kind {
+ CtorOf::Struct => "a struct",
+ CtorOf::Variant => "an enum variant",
+ };
+ err.span_label(span, &format!("this is the constructor of {kind}"));
+ err.multipart_suggestion(
+ "call the constructor",
+ vec![(span.shrink_to_lo(), "(".to_string()), (span.shrink_to_hi(), format!(")({args})"))],
+ applicable,
+ );
+}
};
use rustc_middle::ty::fold::TypeFolder;
use rustc_middle::ty::TyKind::{Adt, Array, Char, FnDef, Never, Ref, Str, Tuple, Uint};
-use rustc_middle::ty::{
- self, suggest_constraining_type_param, Ty, TyCtxt, TypeFoldable, TypeVisitor,
-};
+use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeVisitor};
use rustc_span::source_map::Spanned;
use rustc_span::symbol::{sym, Ident};
use rustc_span::Span;
use rustc_trait_selection::infer::InferCtxtExt;
+use rustc_trait_selection::traits::error_reporting::suggestions::InferCtxtExt as _;
use rustc_trait_selection::traits::{FulfillmentError, TraitEngine, TraitEngineExt};
use std::ops::ControlFlow;
Err(_) if lhs_ty.references_error() || rhs_ty.references_error() => self.tcx.ty_error(),
Err(errors) => {
let source_map = self.tcx.sess.source_map();
- let (mut err, missing_trait, use_output) = match is_assign {
+ let (mut err, missing_trait, _use_output) = match is_assign {
IsAssign::Yes => {
let mut err = struct_span_err!(
self.tcx.sess,
// concatenation (e.g., "Hello " + "World!"). This means
// we don't want the note in the else clause to be emitted
} else if let [ty] = &visitor.0[..] {
- if let ty::Param(p) = *ty.kind() {
- // Check if the method would be found if the type param wasn't
- // involved. If so, it means that adding a trait bound to the param is
- // enough. Otherwise we do not give the suggestion.
- let mut eraser = TypeParamEraser(self, expr.span);
- let needs_bound = self
- .lookup_op_method(
- eraser.fold_ty(lhs_ty),
- Some(eraser.fold_ty(rhs_ty)),
- Some(rhs_expr),
- Op::Binary(op, is_assign),
- )
- .is_ok();
- if needs_bound {
- suggest_constraining_param(
- self.tcx,
- self.body_id,
+ // Look for a TraitPredicate in the Fulfillment errors,
+ // and use it to generate a suggestion.
+ //
+ // Note that lookup_op_method must be called again but
+ // with a specific rhs_ty instead of a placeholder so
+ // the resulting predicate generates a more specific
+ // suggestion for the user.
+ let errors = self
+ .lookup_op_method(
+ lhs_ty,
+ Some(rhs_ty),
+ Some(rhs_expr),
+ Op::Binary(op, is_assign),
+ )
+ .unwrap_err();
+ let predicates = errors
+ .into_iter()
+ .filter_map(|error| error.obligation.predicate.to_opt_poly_trait_pred())
+ .collect::<Vec<_>>();
+ if !predicates.is_empty() {
+ for pred in predicates {
+ self.infcx.suggest_restricting_param_bound(
&mut err,
- *ty,
- rhs_ty,
- missing_trait,
- p,
- use_output,
+ pred,
+ self.body_id,
);
- } else if *ty != lhs_ty {
- // When we know that a missing bound is responsible, we don't show
- // this note as it is redundant.
- err.note(&format!(
- "the trait `{missing_trait}` is not implemented for `{lhs_ty}`"
- ));
}
- } else {
- bug!("type param visitor stored a non type param: {:?}", ty.kind());
+ } else if *ty != lhs_ty {
+ // When we know that a missing bound is responsible, we don't show
+ // this note as it is redundant.
+ err.note(&format!(
+ "the trait `{missing_trait}` is not implemented for `{lhs_ty}`"
+ ));
}
}
}
ex.span,
format!("cannot apply unary operator `{}`", op.as_str()),
);
- let missing_trait = match op {
- hir::UnOp::Deref => unreachable!("check unary op `-` or `!` only"),
- hir::UnOp::Not => "std::ops::Not",
- hir::UnOp::Neg => "std::ops::Neg",
- };
+
let mut visitor = TypeParamVisitor(vec![]);
visitor.visit_ty(operand_ty);
- if let [ty] = &visitor.0[..] && let ty::Param(p) = *operand_ty.kind() {
- suggest_constraining_param(
- self.tcx,
- self.body_id,
- &mut err,
- *ty,
- operand_ty,
- missing_trait,
- p,
- true,
- );
+ if let [_] = &visitor.0[..] && let ty::Param(_) = *operand_ty.kind() {
+ let predicates = errors
+ .iter()
+ .filter_map(|error| {
+ error.obligation.predicate.clone().to_opt_poly_trait_pred()
+ });
+ for pred in predicates {
+ self.infcx.suggest_restricting_param_bound(
+ &mut err,
+ pred,
+ self.body_id,
+ );
+ }
}
let sp = self.tcx.sess.source_map().start_point(ex.span);
}
}
-fn suggest_constraining_param(
- tcx: TyCtxt<'_>,
- body_id: hir::HirId,
- mut err: &mut Diagnostic,
- lhs_ty: Ty<'_>,
- rhs_ty: Ty<'_>,
- missing_trait: &str,
- p: ty::ParamTy,
- set_output: bool,
-) {
- let hir = tcx.hir();
- let msg = &format!("`{lhs_ty}` might need a bound for `{missing_trait}`");
- // Try to find the def-id and details for the parameter p. We have only the index,
- // so we have to find the enclosing function's def-id, then look through its declared
- // generic parameters to get the declaration.
- let def_id = hir.body_owner_def_id(hir::BodyId { hir_id: body_id });
- let generics = tcx.generics_of(def_id);
- let param_def_id = generics.type_param(&p, tcx).def_id;
- if let Some(generics) = param_def_id
- .as_local()
- .map(|id| hir.local_def_id_to_hir_id(id))
- .and_then(|id| hir.find_by_def_id(hir.get_parent_item(id)))
- .as_ref()
- .and_then(|node| node.generics())
- {
- let output = if set_output { format!("<Output = {rhs_ty}>") } else { String::new() };
- suggest_constraining_type_param(
- tcx,
- generics,
- &mut err,
- &lhs_ty.to_string(),
- &format!("{missing_trait}{output}"),
- None,
- );
- } else {
- let span = tcx.def_span(param_def_id);
- err.span_label(span, msg);
- }
-}
-
struct TypeParamVisitor<'tcx>(Vec<Ty<'tcx>>);
impl<'tcx> TypeVisitor<'tcx> for TypeParamVisitor<'tcx> {
});
let pre = if in_match { "in the same arm, " } else { "" };
err.note(&format!("{}a binding must have the same type in all alternatives", pre));
+ // FIXME: check if `var_ty` and `ty` can be made the same type by adding or removing
+ // `ref` or `&` to the pattern.
err.emit();
}
}
let suggestion = format!(
"{} {}",
- if !gat_item_hir.generics.where_clause.predicates.is_empty() {
- ","
- } else {
- " where"
- },
+ if !gat_item_hir.generics.predicates.is_empty() { "," } else { " where" },
unsatisfied_bounds.join(", "),
);
err.span_suggestion(
- gat_item_hir.generics.where_clause.tail_span_for_suggestion(),
+ gat_item_hir.generics.tail_span_for_predicate_suggestion(),
&format!("add the required where clause{plural}"),
suggestion,
Applicability::MachineApplicable,
let explicitly_bounded_params = Lazy::new(|| {
let icx = crate::collect::ItemCtxt::new(tcx, item.def_id.to_def_id());
hir_generics
- .where_clause
.predicates
.iter()
.filter_map(|predicate| match predicate {
match param.name {
hir::ParamName::Error => {}
_ => {
- let has_explicit_bounds =
- !param.bounds.is_empty() || explicitly_bounded_params.contains(¶meter);
+ let has_explicit_bounds = explicitly_bounded_params.contains(¶meter);
report_bivariance(tcx, param, has_explicit_bounds);
}
}
// only use the span of the predicate clause (#90869)
- if let Some(hir::Generics { where_clause, .. }) =
+ if let Some(hir::Generics { predicates, .. }) =
hir_node.and_then(|node| node.generics())
{
let obligation_span = obligation.cause.span(fcx.tcx);
- span = where_clause
- .predicates
+ span = predicates
.iter()
// There seems to be no better way to find out which predicate we are in
.find(|pred| pred.span().contains(obligation_span))
for id in tcx.hir().items() {
if matches!(tcx.hir().def_kind(id.def_id), DefKind::Use) {
+ if tcx.visibility(id.def_id).is_public() {
+ continue;
+ }
let item = tcx.hir().item(id);
- if item.vis.node.is_pub() || item.span.is_dummy() {
+ if item.span.is_dummy() {
continue;
}
if let hir::ItemKind::Use(path, _) = item.kind {
Some(orig_name) => format!("use {} as {};", orig_name, item.ident.name),
None => format!("use {};", item.ident.name),
};
- let vis = tcx.sess.source_map().span_to_snippet(item.vis.span).unwrap_or_default();
+ let vis = tcx.sess.source_map().span_to_snippet(item.vis_span).unwrap_or_default();
let add_vis = |to| if vis.is_empty() { to } else { format!("{} {}", vis, to) };
lint.build("`extern crate` is not idiomatic in the new edition")
.span_suggestion_short(
tcx,
sp,
tr.path.span,
+ trait_ref.self_ty(),
impl_.self_ty.span,
&impl_.generics,
err,
tcx: TyCtxt<'tcx>,
sp: Span,
trait_span: Span,
+ self_ty: Ty<'tcx>,
self_ty_span: Span,
generics: &hir::Generics<'tcx>,
err: traits::OrphanCheckErr<'tcx>,
) -> Result<!, ErrorGuaranteed> {
Err(match err {
traits::OrphanCheckErr::NonLocalInputType(tys) => {
+ let msg = match self_ty.kind() {
+ ty::Adt(..) => "can be implemented for types defined outside of the crate",
+ _ if self_ty.is_primitive() => "can be implemented for primitive types",
+ _ => "can be implemented for arbitrary types",
+ };
let mut err = struct_span_err!(
tcx.sess,
sp,
E0117,
- "only traits defined in the current crate can be implemented for \
- arbitrary types"
+ "only traits defined in the current crate {msg}"
);
err.span_label(sp, "impl doesn't use only types from inside the current crate");
for (ty, is_target_ty) in &tys {
-// ignore-tidy-filelength
//! "Collection" is the process of determining the type and other external
//! details of each item in Rust. Collection is specifically concerned
//! with *inter-procedural* things -- for example, for a function
use rustc_middle::ty::util::Discr;
use rustc_middle::ty::util::IntTypeExt;
use rustc_middle::ty::{self, AdtKind, Const, DefIdTree, Ty, TyCtxt};
-use rustc_middle::ty::{ReprOptions, ToPredicate, TypeFoldable};
+use rustc_middle::ty::{ReprOptions, ToPredicate};
use rustc_session::lint;
use rustc_session::parse::feature_err;
use rustc_span::symbol::{kw, sym, Ident, Symbol};
/// all already existing generic type parameters to avoid suggesting a name that is already in use.
crate fn placeholder_type_error<'tcx>(
tcx: TyCtxt<'tcx>,
- span: Option<Span>,
- generics: &[hir::GenericParam<'_>],
+ generics: Option<&hir::Generics<'_>>,
placeholder_types: Vec<Span>,
suggest: bool,
hir_ty: Option<&hir::Ty<'_>>,
return;
}
- let type_name = generics.next_type_param_name(None);
+ placeholder_type_error_diag(tcx, generics, placeholder_types, vec![], suggest, hir_ty, kind)
+ .emit();
+}
+
+crate fn placeholder_type_error_diag<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ generics: Option<&hir::Generics<'_>>,
+ placeholder_types: Vec<Span>,
+ additional_spans: Vec<Span>,
+ suggest: bool,
+ hir_ty: Option<&hir::Ty<'_>>,
+ kind: &'static str,
+) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
+ if placeholder_types.is_empty() {
+ return bad_placeholder(tcx, additional_spans, kind);
+ }
+
+ let params = generics.map(|g| g.params).unwrap_or_default();
+ let type_name = params.next_type_param_name(None);
let mut sugg: Vec<_> =
placeholder_types.iter().map(|sp| (*sp, (*type_name).to_string())).collect();
- if generics.is_empty() {
- if let Some(span) = span {
- sugg.push((span, format!("<{}>", type_name)));
+ if let Some(generics) = generics {
+ if let Some(arg) = params.iter().find(|arg| {
+ matches!(arg.name, hir::ParamName::Plain(Ident { name: kw::Underscore, .. }))
+ }) {
+ // Account for `_` already present in cases like `struct S<_>(_);` and suggest
+ // `struct S<T>(T);` instead of `struct S<_, T>(T);`.
+ sugg.push((arg.span, (*type_name).to_string()));
+ } else if let Some(span) = generics.span_for_param_suggestion() {
+ // Account for bounds, we want `fn foo<T: E, K>(_: K)` not `fn foo<T, K: E>(_: K)`.
+ sugg.push((span, format!(", {}", type_name)));
+ } else {
+ sugg.push((generics.span, format!("<{}>", type_name)));
}
- } else if let Some(arg) = generics
- .iter()
- .find(|arg| matches!(arg.name, hir::ParamName::Plain(Ident { name: kw::Underscore, .. })))
- {
- // Account for `_` already present in cases like `struct S<_>(_);` and suggest
- // `struct S<T>(T);` instead of `struct S<_, T>(T);`.
- sugg.push((arg.span, (*type_name).to_string()));
- } else {
- let last = generics.iter().last().unwrap();
- // Account for bounds, we want `fn foo<T: E, K>(_: K)` not `fn foo<T, K: E>(_: K)`.
- let span = last.bounds_span_for_suggestions().unwrap_or(last.span.shrink_to_hi());
- sugg.push((span, format!(", {}", type_name)));
}
- let mut err = bad_placeholder(tcx, placeholder_types, kind);
+ let mut err =
+ bad_placeholder(tcx, placeholder_types.into_iter().chain(additional_spans).collect(), kind);
// Suggest, but only if it is not a function in const or static
if suggest {
);
}
}
- err.emit();
+
+ err
}
fn reject_placeholder_type_signatures_in_item<'tcx>(
let mut visitor = HirPlaceholderCollector::default();
visitor.visit_item(item);
- placeholder_type_error(
- tcx,
- Some(generics.span),
- generics.params,
- visitor.0,
- suggest,
- None,
- item.kind.descr(),
- );
+ placeholder_type_error(tcx, Some(generics), visitor.0, suggest, None, item.kind.descr());
}
impl<'tcx> Visitor<'tcx> for CollectItemTypesVisitor<'tcx> {
only_self_bounds: OnlySelfBounds,
assoc_name: Option<Ident>,
) -> Vec<(ty::Predicate<'tcx>, Span)> {
- let from_ty_params = ast_generics
- .params
- .iter()
- .filter_map(|param| match param.kind {
- GenericParamKind::Type { .. } | GenericParamKind::Const { .. }
- if param.hir_id == param_id =>
- {
- Some(¶m.bounds)
- }
- _ => None,
- })
- .flat_map(|bounds| bounds.iter())
- .filter(|b| match assoc_name {
- Some(assoc_name) => self.bound_defines_assoc_item(b, assoc_name),
- None => true,
- })
- .flat_map(|b| predicates_from_bound(self, ty, b, ty::List::empty()));
-
let param_def_id = self.tcx.hir().local_def_id(param_id).to_def_id();
- let from_where_clauses = ast_generics
- .where_clause
+ ast_generics
.predicates
.iter()
.filter_map(|wp| match *wp {
})
.filter_map(move |b| bt.map(|bt| (bt, b, bvars)))
})
- .flat_map(|(bt, b, bvars)| predicates_from_bound(self, bt, b, bvars));
-
- from_ty_params.chain(from_where_clauses).collect()
+ .flat_map(|(bt, b, bvars)| predicates_from_bound(self, bt, b, bvars))
+ .collect()
}
fn bound_defines_assoc_item(&self, b: &hir::GenericBound<'_>, assoc_name: Ident) -> bool {
placeholder_type_error(
tcx,
None,
- &[],
visitor.0,
false,
None,
if let hir::TyKind::TraitObject(..) = ty.kind {
let mut visitor = HirPlaceholderCollector::default();
visitor.visit_item(it);
- placeholder_type_error(
- tcx,
- None,
- &[],
- visitor.0,
- false,
- None,
- it.kind.descr(),
- );
+ placeholder_type_error(tcx, None, visitor.0, false, None, it.kind.descr());
}
}
_ => (),
// Account for `const C: _;`.
let mut visitor = HirPlaceholderCollector::default();
visitor.visit_trait_item(trait_item);
- placeholder_type_error(tcx, None, &[], visitor.0, false, None, "constant");
+ placeholder_type_error(tcx, None, visitor.0, false, None, "constant");
}
hir::TraitItemKind::Type(_, Some(_)) => {
// Account for `type T = _;`.
let mut visitor = HirPlaceholderCollector::default();
visitor.visit_trait_item(trait_item);
- placeholder_type_error(tcx, None, &[], visitor.0, false, None, "associated type");
+ placeholder_type_error(tcx, None, visitor.0, false, None, "associated type");
}
hir::TraitItemKind::Type(_, None) => {
let mut visitor = HirPlaceholderCollector::default();
visitor.visit_trait_item(trait_item);
- placeholder_type_error(tcx, None, &[], visitor.0, false, None, "associated type");
+ placeholder_type_error(tcx, None, visitor.0, false, None, "associated type");
}
};
let mut visitor = HirPlaceholderCollector::default();
visitor.visit_impl_item(impl_item);
- placeholder_type_error(tcx, None, &[], visitor.0, false, None, "associated type");
+ placeholder_type_error(tcx, None, visitor.0, false, None, "associated type");
}
hir::ImplItemKind::Const(..) => {}
}
match tcx.hir().get(hir_id) {
TraitItem(hir::TraitItem {
kind: TraitItemKind::Fn(sig, TraitFn::Provided(_)),
- ident,
generics,
..
})
- | ImplItem(hir::ImplItem { kind: ImplItemKind::Fn(sig, _), ident, generics, .. })
- | Item(hir::Item { kind: ItemKind::Fn(sig, generics, _), ident, .. }) => {
- match get_infer_ret_ty(&sig.decl.output) {
- Some(ty) => {
- let fn_sig = tcx.typeck(def_id).liberated_fn_sigs()[hir_id];
- // Typeck doesn't expect erased regions to be returned from `type_of`.
- let fn_sig = tcx.fold_regions(fn_sig, &mut false, |r, _| match *r {
- ty::ReErased => tcx.lifetimes.re_static,
- _ => r,
- });
- let fn_sig = ty::Binder::dummy(fn_sig);
-
- let mut visitor = HirPlaceholderCollector::default();
- visitor.visit_ty(ty);
- let mut diag = bad_placeholder(tcx, visitor.0, "return type");
- let ret_ty = fn_sig.skip_binder().output();
- if !ret_ty.references_error() {
- if !ret_ty.is_closure() {
- let ret_ty_str = match ret_ty.kind() {
- // Suggest a function pointer return type instead of a unique function definition
- // (e.g. `fn() -> i32` instead of `fn() -> i32 { f }`, the latter of which is invalid
- // syntax)
- ty::FnDef(..) => ret_ty.fn_sig(tcx).to_string(),
- _ => ret_ty.to_string(),
- };
- diag.span_suggestion(
- ty.span,
- "replace with the correct return type",
- ret_ty_str,
- Applicability::MaybeIncorrect,
- );
- } else {
- // We're dealing with a closure, so we should suggest using `impl Fn` or trait bounds
- // to prevent the user from getting a papercut while trying to use the unique closure
- // syntax (e.g. `[closure@src/lib.rs:2:5: 2:9]`).
- diag.help("consider using an `Fn`, `FnMut`, or `FnOnce` trait bound");
- diag.note("for more information on `Fn` traits and closure types, see https://doc.rust-lang.org/book/ch13-01-closures.html");
- }
- }
- diag.emit();
+ | Item(hir::Item { kind: ItemKind::Fn(sig, generics, _), .. }) => {
+ infer_return_ty_for_fn_sig(tcx, sig, generics, def_id, &icx)
+ }
- fn_sig
- }
- None => <dyn AstConv<'_>>::ty_of_fn(
+ ImplItem(hir::ImplItem { kind: ImplItemKind::Fn(sig, _), generics, .. }) => {
+ // Do not try to inference the return type for a impl method coming from a trait
+ if let Item(hir::Item { kind: ItemKind::Impl(i), .. }) =
+ tcx.hir().get(tcx.hir().get_parent_node(hir_id))
+ && i.of_trait.is_some()
+ {
+ <dyn AstConv<'_>>::ty_of_fn(
&icx,
hir_id,
sig.header.unsafety,
sig.header.abi,
sig.decl,
- generics,
- Some(ident.span),
+ Some(generics),
None,
- ),
+ )
+ } else {
+ infer_return_ty_for_fn_sig(tcx, sig, generics, def_id, &icx)
}
}
TraitItem(hir::TraitItem {
kind: TraitItemKind::Fn(FnSig { header, decl, span: _ }, _),
- ident,
generics,
..
}) => <dyn AstConv<'_>>::ty_of_fn(
header.unsafety,
header.abi,
decl,
- generics,
- Some(ident.span),
+ Some(generics),
None,
),
- ForeignItem(&hir::ForeignItem {
- kind: ForeignItemKind::Fn(fn_decl, _, _), ident, ..
- }) => {
+ ForeignItem(&hir::ForeignItem { kind: ForeignItemKind::Fn(fn_decl, _, _), .. }) => {
let abi = tcx.hir().get_foreign_abi(hir_id);
- compute_sig_of_foreign_fn_decl(tcx, def_id.to_def_id(), fn_decl, abi, ident)
+ compute_sig_of_foreign_fn_decl(tcx, def_id.to_def_id(), fn_decl, abi)
}
Ctor(data) | Variant(hir::Variant { data, .. }) if data.ctor_hir_id().is_some() => {
}
}
+fn infer_return_ty_for_fn_sig<'tcx>(
+ tcx: TyCtxt<'tcx>,
+ sig: &hir::FnSig<'_>,
+ generics: &hir::Generics<'_>,
+ def_id: LocalDefId,
+ icx: &ItemCtxt<'tcx>,
+) -> ty::PolyFnSig<'tcx> {
+ let hir_id = tcx.hir().local_def_id_to_hir_id(def_id);
+
+ match get_infer_ret_ty(&sig.decl.output) {
+ Some(ty) => {
+ let fn_sig = tcx.typeck(def_id).liberated_fn_sigs()[hir_id];
+ // Typeck doesn't expect erased regions to be returned from `type_of`.
+ let fn_sig = tcx.fold_regions(fn_sig, &mut false, |r, _| match *r {
+ ty::ReErased => tcx.lifetimes.re_static,
+ _ => r,
+ });
+ let fn_sig = ty::Binder::dummy(fn_sig);
+
+ let mut visitor = HirPlaceholderCollector::default();
+ visitor.visit_ty(ty);
+ let mut diag = bad_placeholder(tcx, visitor.0, "return type");
+ let ret_ty = fn_sig.skip_binder().output();
+ if ret_ty.is_suggestable(tcx) {
+ diag.span_suggestion(
+ ty.span,
+ "replace with the correct return type",
+ ret_ty.to_string(),
+ Applicability::MachineApplicable,
+ );
+ } else if matches!(ret_ty.kind(), ty::FnDef(..)) {
+ let fn_sig = ret_ty.fn_sig(tcx);
+ if fn_sig.skip_binder().inputs_and_output.iter().all(|t| t.is_suggestable(tcx)) {
+ diag.span_suggestion(
+ ty.span,
+ "replace with the correct return type",
+ fn_sig.to_string(),
+ Applicability::MachineApplicable,
+ );
+ }
+ } else if ret_ty.is_closure() {
+ // We're dealing with a closure, so we should suggest using `impl Fn` or trait bounds
+ // to prevent the user from getting a papercut while trying to use the unique closure
+ // syntax (e.g. `[closure@src/lib.rs:2:5: 2:9]`).
+ diag.help("consider using an `Fn`, `FnMut`, or `FnOnce` trait bound");
+ diag.note("for more information on `Fn` traits and closure types, see https://doc.rust-lang.org/book/ch13-01-closures.html");
+ }
+ diag.emit();
+
+ fn_sig
+ }
+ None => <dyn AstConv<'_>>::ty_of_fn(
+ icx,
+ hir_id,
+ sig.header.unsafety,
+ sig.header.abi,
+ sig.decl,
+ Some(generics),
+ None,
+ ),
+ }
+}
+
fn impl_trait_ref(tcx: TyCtxt<'_>, def_id: DefId) -> Option<ty::TraitRef<'_>> {
let icx = ItemCtxt::new(tcx, def_id);
match tcx.hir().expect_item(def_id.expect_local()).kind {
let icx = ItemCtxt::new(tcx, def_id);
- const NO_GENERICS: &hir::Generics<'_> = &hir::Generics::empty();
+ const NO_GENERICS: &hir::Generics<'_> = hir::Generics::empty();
// We use an `IndexSet` to preserves order of insertion.
// Preserving the order of insertion is important here so as not to break UI tests.
let mut predicates: FxIndexSet<(ty::Predicate<'_>, Span)> = FxIndexSet::default();
let ast_generics = match node {
- Node::TraitItem(item) => &item.generics,
+ Node::TraitItem(item) => item.generics,
- Node::ImplItem(item) => &item.generics,
+ Node::ImplItem(item) => item.generics,
Node::Item(item) => {
match item.kind {
| ItemKind::TyAlias(_, ref generics)
| ItemKind::Enum(_, ref generics)
| ItemKind::Struct(_, ref generics)
- | ItemKind::Union(_, ref generics) => generics,
+ | ItemKind::Union(_, ref generics) => *generics,
ItemKind::Trait(_, _, ref generics, ..) => {
is_trait = Some(ty::TraitRef::identity(tcx, def_id));
- generics
+ *generics
}
ItemKind::TraitAlias(ref generics, _) => {
is_trait = Some(ty::TraitRef::identity(tcx, def_id));
- generics
+ *generics
}
ItemKind::OpaqueTy(OpaqueTy {
origin: hir::OpaqueTyOrigin::AsyncFn(..) | hir::OpaqueTyOrigin::FnReturn(..),
Node::ForeignItem(item) => match item.kind {
ForeignItemKind::Static(..) => NO_GENERICS,
- ForeignItemKind::Fn(_, _, ref generics) => generics,
+ ForeignItemKind::Fn(_, _, ref generics) => *generics,
ForeignItemKind::Type => NO_GENERICS,
},
// Collect the region predicates that were declared inline as
// well. In the case of parameters declared on a fn or method, we
// have to be careful to only iterate over early-bound regions.
- let mut index = parent_count + has_own_self as u32;
- for param in early_bound_lifetimes_from_generics(tcx, hir_id.owner, ast_generics) {
- let region = tcx.mk_region(ty::ReEarlyBound(ty::EarlyBoundRegion {
- def_id: tcx.hir().local_def_id(param.hir_id).to_def_id(),
- index,
- name: param.name.ident().name,
- }));
- index += 1;
-
- match param.kind {
- GenericParamKind::Lifetime { .. } => {
- param.bounds.iter().for_each(|bound| match bound {
- hir::GenericBound::Outlives(lt) => {
- let bound = <dyn AstConv<'_>>::ast_region_to_region(&icx, lt, None);
- let outlives = ty::Binder::dummy(ty::OutlivesPredicate(region, bound));
- predicates.insert((outlives.to_predicate(tcx), lt.span));
- }
- _ => bug!(),
- });
- }
- _ => bug!(),
- }
- }
+ let mut index = parent_count
+ + has_own_self as u32
+ + early_bound_lifetimes_from_generics(tcx, hir_id.owner, ast_generics).count() as u32;
// Collect the predicates that were written inline by the user on each
// type parameter (e.g., `<T: Foo>`).
let param_ty = ty::ParamTy::new(index, name).to_ty(tcx);
index += 1;
- let mut bounds = <dyn AstConv<'_>>::compute_bounds(&icx, param_ty, param.bounds);
+ let mut bounds = Bounds::default();
// Params are implicitly sized unless a `?Sized` bound is found
<dyn AstConv<'_>>::add_implicitly_sized(
&icx,
&mut bounds,
- param.bounds,
- Some((param.hir_id, ast_generics.where_clause.predicates)),
+ &[],
+ Some((param.hir_id, ast_generics.predicates)),
param.span,
);
predicates.extend(bounds.predicates(tcx, param_ty));
}
GenericParamKind::Const { .. } => {
// Bounds on const parameters are currently not possible.
- debug_assert!(param.bounds.is_empty());
index += 1;
}
}
}
// Add in the bounds that appear in the where-clause.
- let where_clause = &ast_generics.where_clause;
- for predicate in where_clause.predicates {
+ for predicate in ast_generics.predicates {
match predicate {
hir::WherePredicate::BoundPredicate(bound_pred) => {
let ty = icx.to_ty(bound_pred.bounded_ty);
def_id: DefId,
decl: &'tcx hir::FnDecl<'tcx>,
abi: abi::Abi,
- ident: Ident,
) -> ty::PolyFnSig<'tcx> {
let unsafety = if abi == abi::Abi::RustIntrinsic {
intrinsic_operation_unsafety(tcx.item_name(def_id))
unsafety,
abi,
decl,
- &hir::Generics::empty(),
- Some(ident.span),
+ None,
None,
);
// #73631: closures inherit `#[target_feature]` annotations
if tcx.features().target_feature_11 && tcx.is_closure(id) {
- let owner_id = tcx.parent(id).expect("closure should have a parent");
+ let owner_id = tcx.parent(id);
codegen_fn_attrs
.target_features
.extend(tcx.codegen_fn_attrs(owner_id).target_features.iter().copied())
icx.to_ty(ty)
}
}
- ItemKind::TyAlias(self_ty, _)
- | ItemKind::Impl(hir::Impl { self_ty, .. }) => icx.to_ty(self_ty),
+ ItemKind::TyAlias(self_ty, _) => icx.to_ty(self_ty),
+ ItemKind::Impl(hir::Impl { self_ty, .. }) => icx.to_ty(*self_ty),
ItemKind::Fn(..) => {
let substs = InternalSubsts::identity_for_item(tcx, def_id.to_def_id());
tcx.mk_fn_def(def_id.to_def_id(), substs)
Some(hidden) => hidden.ty,
None => {
let span = tcx.def_span(def_id);
- let name = tcx.item_name(tcx.parent(def_id.to_def_id()).unwrap());
+ let name = tcx.item_name(tcx.local_parent(def_id).to_def_id());
let label = format!(
"`{}` must be used in combination with a concrete type within the same module",
name
//! Errors emitted by typeck.
use rustc_errors::Applicability;
-use rustc_macros::SessionDiagnostic;
+use rustc_macros::{SessionDiagnostic, SessionSubdiagnostic};
use rustc_middle::ty::Ty;
use rustc_span::{symbol::Ident, Span, Symbol};
#[primary_span]
#[label]
pub span: Span,
- #[suggestion_verbose(message = "suggestion", code = "{ty}")]
+ #[suggestion_verbose(code = "{ty}")]
pub opt_sugg: Option<(Span, Applicability)>,
}
#[label]
pub span: Span,
}
+
+#[derive(SessionSubdiagnostic)]
+pub enum AddReturnTypeSuggestion<'tcx> {
+ #[suggestion(
+ slug = "typeck-add-return-type-add",
+ code = "-> {found} ",
+ applicability = "machine-applicable"
+ )]
+ Add {
+ #[primary_span]
+ span: Span,
+ found: Ty<'tcx>,
+ },
+ #[suggestion(
+ slug = "typeck-add-return-type-missing-here",
+ code = "-> _ ",
+ applicability = "has-placeholders"
+ )]
+ MissingHere {
+ #[primary_span]
+ span: Span,
+ },
+}
+
+#[derive(SessionSubdiagnostic)]
+pub enum ExpectedReturnTypeLabel<'tcx> {
+ #[label(slug = "typeck-expected-default-return-type")]
+ Unit {
+ #[primary_span]
+ span: Span,
+ },
+ #[label(slug = "typeck-expected-return-type")]
+ Other {
+ #[primary_span]
+ span: Span,
+ expected: Ty<'tcx>,
+ },
+}
let hir_id = tcx.hir().local_def_id_to_hir_id(def_id.expect_local());
match tcx.hir().find(hir_id) {
Some(Node::Item(hir::Item { kind: hir::ItemKind::Fn(_, ref generics, _), .. })) => {
- generics.where_clause.span()
+ generics.where_clause_span()
}
_ => {
span_bug!(tcx.def_span(def_id), "main has a non-function type");
.emit();
error = true;
}
- if let Some(sp) = generics.where_clause.span() {
+ if let Some(sp) = generics.where_clause_span() {
struct_span_err!(
tcx.sess,
sp,
black_box(deq);
})
}
+
+#[bench]
+fn bench_extend_bytes(b: &mut Bencher) {
+ let mut ring: VecDeque<u8> = VecDeque::with_capacity(1000);
+ let input: &[u8] = &[128; 512];
+
+ b.iter(|| {
+ ring.clear();
+ ring.extend(black_box(input));
+ });
+}
+
+#[bench]
+fn bench_extend_vec(b: &mut Bencher) {
+ let mut ring: VecDeque<u8> = VecDeque::with_capacity(1000);
+ let input = vec![128; 512];
+
+ b.iter(|| {
+ ring.clear();
+
+ let input = input.clone();
+ ring.extend(black_box(input));
+ });
+}
///
/// # Examples
///
- /// Calling `into_owned` on a `Cow::Borrowed` clones the underlying data
- /// and becomes a `Cow::Owned`:
+ /// Calling `into_owned` on a `Cow::Borrowed` returns a clone of the borrowed data:
///
/// ```
/// use std::borrow::Cow;
/// );
/// ```
///
- /// Calling `into_owned` on a `Cow::Owned` is a no-op:
+ /// Calling `into_owned` on a `Cow::Owned` returns the owned data. The data is moved out of the
+ /// `Cow` without being cloned.
///
/// ```
/// use std::borrow::Cow;
use super::SpecExtend;
+#[cfg(test)]
+mod tests;
+
/// A priority queue implemented with a binary heap.
///
/// This will be a max-heap.
--- /dev/null
+use super::*;
+use crate::boxed::Box;
+use std::iter::TrustedLen;
+use std::panic::{catch_unwind, AssertUnwindSafe};
+use std::sync::atomic::{AtomicU32, Ordering};
+
+#[test]
+fn test_iterator() {
+ let data = vec![5, 9, 3];
+ let iterout = [9, 5, 3];
+ let heap = BinaryHeap::from(data);
+ let mut i = 0;
+ for el in &heap {
+ assert_eq!(*el, iterout[i]);
+ i += 1;
+ }
+}
+
+#[test]
+fn test_iter_rev_cloned_collect() {
+ let data = vec![5, 9, 3];
+ let iterout = vec![3, 5, 9];
+ let pq = BinaryHeap::from(data);
+
+ let v: Vec<_> = pq.iter().rev().cloned().collect();
+ assert_eq!(v, iterout);
+}
+
+#[test]
+fn test_into_iter_collect() {
+ let data = vec![5, 9, 3];
+ let iterout = vec![9, 5, 3];
+ let pq = BinaryHeap::from(data);
+
+ let v: Vec<_> = pq.into_iter().collect();
+ assert_eq!(v, iterout);
+}
+
+#[test]
+fn test_into_iter_size_hint() {
+ let data = vec![5, 9];
+ let pq = BinaryHeap::from(data);
+
+ let mut it = pq.into_iter();
+
+ assert_eq!(it.size_hint(), (2, Some(2)));
+ assert_eq!(it.next(), Some(9));
+
+ assert_eq!(it.size_hint(), (1, Some(1)));
+ assert_eq!(it.next(), Some(5));
+
+ assert_eq!(it.size_hint(), (0, Some(0)));
+ assert_eq!(it.next(), None);
+}
+
+#[test]
+fn test_into_iter_rev_collect() {
+ let data = vec![5, 9, 3];
+ let iterout = vec![3, 5, 9];
+ let pq = BinaryHeap::from(data);
+
+ let v: Vec<_> = pq.into_iter().rev().collect();
+ assert_eq!(v, iterout);
+}
+
+#[test]
+fn test_into_iter_sorted_collect() {
+ let heap = BinaryHeap::from(vec![2, 4, 6, 2, 1, 8, 10, 3, 5, 7, 0, 9, 1]);
+ let it = heap.into_iter_sorted();
+ let sorted = it.collect::<Vec<_>>();
+ assert_eq!(sorted, vec![10, 9, 8, 7, 6, 5, 4, 3, 2, 2, 1, 1, 0]);
+}
+
+#[test]
+fn test_drain_sorted_collect() {
+ let mut heap = BinaryHeap::from(vec![2, 4, 6, 2, 1, 8, 10, 3, 5, 7, 0, 9, 1]);
+ let it = heap.drain_sorted();
+ let sorted = it.collect::<Vec<_>>();
+ assert_eq!(sorted, vec![10, 9, 8, 7, 6, 5, 4, 3, 2, 2, 1, 1, 0]);
+}
+
+fn check_exact_size_iterator<I: ExactSizeIterator>(len: usize, it: I) {
+ let mut it = it;
+
+ for i in 0..it.len() {
+ let (lower, upper) = it.size_hint();
+ assert_eq!(Some(lower), upper);
+ assert_eq!(lower, len - i);
+ assert_eq!(it.len(), len - i);
+ it.next();
+ }
+ assert_eq!(it.len(), 0);
+ assert!(it.is_empty());
+}
+
+#[test]
+fn test_exact_size_iterator() {
+ let heap = BinaryHeap::from(vec![2, 4, 6, 2, 1, 8, 10, 3, 5, 7, 0, 9, 1]);
+ check_exact_size_iterator(heap.len(), heap.iter());
+ check_exact_size_iterator(heap.len(), heap.clone().into_iter());
+ check_exact_size_iterator(heap.len(), heap.clone().into_iter_sorted());
+ check_exact_size_iterator(heap.len(), heap.clone().drain());
+ check_exact_size_iterator(heap.len(), heap.clone().drain_sorted());
+}
+
+fn check_trusted_len<I: TrustedLen>(len: usize, it: I) {
+ let mut it = it;
+ for i in 0..len {
+ let (lower, upper) = it.size_hint();
+ if upper.is_some() {
+ assert_eq!(Some(lower), upper);
+ assert_eq!(lower, len - i);
+ }
+ it.next();
+ }
+}
+
+#[test]
+fn test_trusted_len() {
+ let heap = BinaryHeap::from(vec![2, 4, 6, 2, 1, 8, 10, 3, 5, 7, 0, 9, 1]);
+ check_trusted_len(heap.len(), heap.clone().into_iter_sorted());
+ check_trusted_len(heap.len(), heap.clone().drain_sorted());
+}
+
+#[test]
+fn test_peek_and_pop() {
+ let data = vec![2, 4, 6, 2, 1, 8, 10, 3, 5, 7, 0, 9, 1];
+ let mut sorted = data.clone();
+ sorted.sort();
+ let mut heap = BinaryHeap::from(data);
+ while !heap.is_empty() {
+ assert_eq!(heap.peek().unwrap(), sorted.last().unwrap());
+ assert_eq!(heap.pop().unwrap(), sorted.pop().unwrap());
+ }
+}
+
+#[test]
+fn test_peek_mut() {
+ let data = vec![2, 4, 6, 2, 1, 8, 10, 3, 5, 7, 0, 9, 1];
+ let mut heap = BinaryHeap::from(data);
+ assert_eq!(heap.peek(), Some(&10));
+ {
+ let mut top = heap.peek_mut().unwrap();
+ *top -= 2;
+ }
+ assert_eq!(heap.peek(), Some(&9));
+}
+
+#[test]
+fn test_peek_mut_pop() {
+ let data = vec![2, 4, 6, 2, 1, 8, 10, 3, 5, 7, 0, 9, 1];
+ let mut heap = BinaryHeap::from(data);
+ assert_eq!(heap.peek(), Some(&10));
+ {
+ let mut top = heap.peek_mut().unwrap();
+ *top -= 2;
+ assert_eq!(PeekMut::pop(top), 8);
+ }
+ assert_eq!(heap.peek(), Some(&9));
+}
+
+#[test]
+fn test_push() {
+ let mut heap = BinaryHeap::from(vec![2, 4, 9]);
+ assert_eq!(heap.len(), 3);
+ assert!(*heap.peek().unwrap() == 9);
+ heap.push(11);
+ assert_eq!(heap.len(), 4);
+ assert!(*heap.peek().unwrap() == 11);
+ heap.push(5);
+ assert_eq!(heap.len(), 5);
+ assert!(*heap.peek().unwrap() == 11);
+ heap.push(27);
+ assert_eq!(heap.len(), 6);
+ assert!(*heap.peek().unwrap() == 27);
+ heap.push(3);
+ assert_eq!(heap.len(), 7);
+ assert!(*heap.peek().unwrap() == 27);
+ heap.push(103);
+ assert_eq!(heap.len(), 8);
+ assert!(*heap.peek().unwrap() == 103);
+}
+
+#[test]
+fn test_push_unique() {
+ let mut heap = BinaryHeap::<Box<_>>::from(vec![box 2, box 4, box 9]);
+ assert_eq!(heap.len(), 3);
+ assert!(**heap.peek().unwrap() == 9);
+ heap.push(box 11);
+ assert_eq!(heap.len(), 4);
+ assert!(**heap.peek().unwrap() == 11);
+ heap.push(box 5);
+ assert_eq!(heap.len(), 5);
+ assert!(**heap.peek().unwrap() == 11);
+ heap.push(box 27);
+ assert_eq!(heap.len(), 6);
+ assert!(**heap.peek().unwrap() == 27);
+ heap.push(box 3);
+ assert_eq!(heap.len(), 7);
+ assert!(**heap.peek().unwrap() == 27);
+ heap.push(box 103);
+ assert_eq!(heap.len(), 8);
+ assert!(**heap.peek().unwrap() == 103);
+}
+
+fn check_to_vec(mut data: Vec<i32>) {
+ let heap = BinaryHeap::from(data.clone());
+ let mut v = heap.clone().into_vec();
+ v.sort();
+ data.sort();
+
+ assert_eq!(v, data);
+ assert_eq!(heap.into_sorted_vec(), data);
+}
+
+#[test]
+fn test_to_vec() {
+ check_to_vec(vec![]);
+ check_to_vec(vec![5]);
+ check_to_vec(vec![3, 2]);
+ check_to_vec(vec![2, 3]);
+ check_to_vec(vec![5, 1, 2]);
+ check_to_vec(vec![1, 100, 2, 3]);
+ check_to_vec(vec![1, 3, 5, 7, 9, 2, 4, 6, 8, 0]);
+ check_to_vec(vec![2, 4, 6, 2, 1, 8, 10, 3, 5, 7, 0, 9, 1]);
+ check_to_vec(vec![9, 11, 9, 9, 9, 9, 11, 2, 3, 4, 11, 9, 0, 0, 0, 0]);
+ check_to_vec(vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
+ check_to_vec(vec![10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]);
+ check_to_vec(vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 1, 2]);
+ check_to_vec(vec![5, 4, 3, 2, 1, 5, 4, 3, 2, 1, 5, 4, 3, 2, 1]);
+}
+
+#[test]
+fn test_in_place_iterator_specialization() {
+ let src: Vec<usize> = vec![1, 2, 3];
+ let src_ptr = src.as_ptr();
+ let heap: BinaryHeap<_> = src.into_iter().map(std::convert::identity).collect();
+ let heap_ptr = heap.iter().next().unwrap() as *const usize;
+ assert_eq!(src_ptr, heap_ptr);
+ let sink: Vec<_> = heap.into_iter().map(std::convert::identity).collect();
+ let sink_ptr = sink.as_ptr();
+ assert_eq!(heap_ptr, sink_ptr);
+}
+
+#[test]
+fn test_empty_pop() {
+ let mut heap = BinaryHeap::<i32>::new();
+ assert!(heap.pop().is_none());
+}
+
+#[test]
+fn test_empty_peek() {
+ let empty = BinaryHeap::<i32>::new();
+ assert!(empty.peek().is_none());
+}
+
+#[test]
+fn test_empty_peek_mut() {
+ let mut empty = BinaryHeap::<i32>::new();
+ assert!(empty.peek_mut().is_none());
+}
+
+#[test]
+fn test_from_iter() {
+ let xs = vec![9, 8, 7, 6, 5, 4, 3, 2, 1];
+
+ let mut q: BinaryHeap<_> = xs.iter().rev().cloned().collect();
+
+ for &x in &xs {
+ assert_eq!(q.pop().unwrap(), x);
+ }
+}
+
+#[test]
+fn test_drain() {
+ let mut q: BinaryHeap<_> = [9, 8, 7, 6, 5, 4, 3, 2, 1].iter().cloned().collect();
+
+ assert_eq!(q.drain().take(5).count(), 5);
+
+ assert!(q.is_empty());
+}
+
+#[test]
+fn test_drain_sorted() {
+ let mut q: BinaryHeap<_> = [9, 8, 7, 6, 5, 4, 3, 2, 1].iter().cloned().collect();
+
+ assert_eq!(q.drain_sorted().take(5).collect::<Vec<_>>(), vec![9, 8, 7, 6, 5]);
+
+ assert!(q.is_empty());
+}
+
+#[test]
+fn test_drain_sorted_leak() {
+ static DROPS: AtomicU32 = AtomicU32::new(0);
+
+ #[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
+ struct D(u32, bool);
+
+ impl Drop for D {
+ fn drop(&mut self) {
+ DROPS.fetch_add(1, Ordering::SeqCst);
+
+ if self.1 {
+ panic!("panic in `drop`");
+ }
+ }
+ }
+
+ let mut q = BinaryHeap::from(vec![
+ D(0, false),
+ D(1, false),
+ D(2, false),
+ D(3, true),
+ D(4, false),
+ D(5, false),
+ ]);
+
+ catch_unwind(AssertUnwindSafe(|| drop(q.drain_sorted()))).ok();
+
+ assert_eq!(DROPS.load(Ordering::SeqCst), 6);
+}
+
+#[test]
+fn test_extend_ref() {
+ let mut a = BinaryHeap::new();
+ a.push(1);
+ a.push(2);
+
+ a.extend(&[3, 4, 5]);
+
+ assert_eq!(a.len(), 5);
+ assert_eq!(a.into_sorted_vec(), [1, 2, 3, 4, 5]);
+
+ let mut a = BinaryHeap::new();
+ a.push(1);
+ a.push(2);
+ let mut b = BinaryHeap::new();
+ b.push(3);
+ b.push(4);
+ b.push(5);
+
+ a.extend(&b);
+
+ assert_eq!(a.len(), 5);
+ assert_eq!(a.into_sorted_vec(), [1, 2, 3, 4, 5]);
+}
+
+#[test]
+fn test_append() {
+ let mut a = BinaryHeap::from(vec![-10, 1, 2, 3, 3]);
+ let mut b = BinaryHeap::from(vec![-20, 5, 43]);
+
+ a.append(&mut b);
+
+ assert_eq!(a.into_sorted_vec(), [-20, -10, 1, 2, 3, 3, 5, 43]);
+ assert!(b.is_empty());
+}
+
+#[test]
+fn test_append_to_empty() {
+ let mut a = BinaryHeap::new();
+ let mut b = BinaryHeap::from(vec![-20, 5, 43]);
+
+ a.append(&mut b);
+
+ assert_eq!(a.into_sorted_vec(), [-20, 5, 43]);
+ assert!(b.is_empty());
+}
+
+#[test]
+fn test_extend_specialization() {
+ let mut a = BinaryHeap::from(vec![-10, 1, 2, 3, 3]);
+ let b = BinaryHeap::from(vec![-20, 5, 43]);
+
+ a.extend(b);
+
+ assert_eq!(a.into_sorted_vec(), [-20, -10, 1, 2, 3, 3, 5, 43]);
+}
+
+#[allow(dead_code)]
+fn assert_covariance() {
+ fn drain<'new>(d: Drain<'static, &'static str>) -> Drain<'new, &'new str> {
+ d
+ }
+}
+
+#[test]
+fn test_retain() {
+ let mut a = BinaryHeap::from(vec![100, 10, 50, 1, 2, 20, 30]);
+ a.retain(|&x| x != 2);
+
+ // Check that 20 moved into 10's place.
+ assert_eq!(a.clone().into_vec(), [100, 20, 50, 1, 10, 30]);
+
+ a.retain(|_| true);
+
+ assert_eq!(a.clone().into_vec(), [100, 20, 50, 1, 10, 30]);
+
+ a.retain(|&x| x < 50);
+
+ assert_eq!(a.clone().into_vec(), [30, 20, 10, 1]);
+
+ a.retain(|_| false);
+
+ assert!(a.is_empty());
+}
+
+// old binaryheap failed this test
+//
+// Integrity means that all elements are present after a comparison panics,
+// even if the order might not be correct.
+//
+// Destructors must be called exactly once per element.
+// FIXME: re-enable emscripten once it can unwind again
+#[test]
+#[cfg(not(target_os = "emscripten"))]
+fn panic_safe() {
+ use rand::{seq::SliceRandom, thread_rng};
+ use std::cmp;
+ use std::panic::{self, AssertUnwindSafe};
+ use std::sync::atomic::{AtomicUsize, Ordering};
+
+ static DROP_COUNTER: AtomicUsize = AtomicUsize::new(0);
+
+ #[derive(Eq, PartialEq, Ord, Clone, Debug)]
+ struct PanicOrd<T>(T, bool);
+
+ impl<T> Drop for PanicOrd<T> {
+ fn drop(&mut self) {
+ // update global drop count
+ DROP_COUNTER.fetch_add(1, Ordering::SeqCst);
+ }
+ }
+
+ impl<T: PartialOrd> PartialOrd for PanicOrd<T> {
+ fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
+ if self.1 || other.1 {
+ panic!("Panicking comparison");
+ }
+ self.0.partial_cmp(&other.0)
+ }
+ }
+ let mut rng = thread_rng();
+ const DATASZ: usize = 32;
+ // Miri is too slow
+ let ntest = if cfg!(miri) { 1 } else { 10 };
+
+ // don't use 0 in the data -- we want to catch the zeroed-out case.
+ let data = (1..=DATASZ).collect::<Vec<_>>();
+
+ // since it's a fuzzy test, run several tries.
+ for _ in 0..ntest {
+ for i in 1..=DATASZ {
+ DROP_COUNTER.store(0, Ordering::SeqCst);
+
+ let mut panic_ords: Vec<_> =
+ data.iter().filter(|&&x| x != i).map(|&x| PanicOrd(x, false)).collect();
+ let panic_item = PanicOrd(i, true);
+
+ // heapify the sane items
+ panic_ords.shuffle(&mut rng);
+ let mut heap = BinaryHeap::from(panic_ords);
+ let inner_data;
+
+ {
+ // push the panicking item to the heap and catch the panic
+ let thread_result = {
+ let mut heap_ref = AssertUnwindSafe(&mut heap);
+ panic::catch_unwind(move || {
+ heap_ref.push(panic_item);
+ })
+ };
+ assert!(thread_result.is_err());
+
+ // Assert no elements were dropped
+ let drops = DROP_COUNTER.load(Ordering::SeqCst);
+ assert!(drops == 0, "Must not drop items. drops={}", drops);
+ inner_data = heap.clone().into_vec();
+ drop(heap);
+ }
+ let drops = DROP_COUNTER.load(Ordering::SeqCst);
+ assert_eq!(drops, DATASZ);
+
+ let mut data_sorted = inner_data.into_iter().map(|p| p.0).collect::<Vec<_>>();
+ data_sorted.sort();
+ assert_eq!(data_sorted, data);
+ }
+ }
+}
/// Returns `true` if the `LinkedList` contains an element equal to the
/// given value.
///
- /// This operation should compute in *O*(*n*) time.
+ /// This operation should compute linearly in *O*(*n*) time.
///
/// # Examples
///
/// Appends an element to the front of the cursor's parent list. The node
/// that the cursor points to is unchanged, even if it is the "ghost" node.
///
- /// This operation should compute in O(1) time.
+ /// This operation should compute in *O*(1) time.
// `push_front` continues to point to "ghost" when it addes a node to mimic
// the behavior of `insert_before` on an empty list.
#[unstable(feature = "linked_list_cursors", issue = "58533")]
/// Appends an element to the back of the cursor's parent list. The node
/// that the cursor points to is unchanged, even if it is the "ghost" node.
///
- /// This operation should compute in O(1) time.
+ /// This operation should compute in *O*(1) time.
#[unstable(feature = "linked_list_cursors", issue = "58533")]
pub fn push_back(&mut self, elt: T) {
// Safety: We know that `push_back` does not change the position in
/// unchanged, unless it was pointing to the front element. In that case, it
/// points to the new front element.
///
- /// This operation should compute in O(1) time.
+ /// This operation should compute in *O*(1) time.
#[unstable(feature = "linked_list_cursors", issue = "58533")]
pub fn pop_front(&mut self) -> Option<T> {
// We can't check if current is empty, we must check the list directly.
/// unchanged, unless it was pointing to the back element. In that case, it
/// points to the "ghost" element.
///
- /// This operation should compute in O(1) time.
+ /// This operation should compute in *O*(1) time.
#[unstable(feature = "linked_list_cursors", issue = "58533")]
pub fn pop_back(&mut self) -> Option<T> {
if self.list.is_empty() {
use super::*;
+use crate::vec::Vec;
+use std::panic::{catch_unwind, AssertUnwindSafe};
use std::thread;
-use std::vec::Vec;
use rand::{thread_rng, RngCore};
+#[test]
+fn test_basic() {
+ let mut m = LinkedList::<Box<_>>::new();
+ assert_eq!(m.pop_front(), None);
+ assert_eq!(m.pop_back(), None);
+ assert_eq!(m.pop_front(), None);
+ m.push_front(box 1);
+ assert_eq!(m.pop_front(), Some(box 1));
+ m.push_back(box 2);
+ m.push_back(box 3);
+ assert_eq!(m.len(), 2);
+ assert_eq!(m.pop_front(), Some(box 2));
+ assert_eq!(m.pop_front(), Some(box 3));
+ assert_eq!(m.len(), 0);
+ assert_eq!(m.pop_front(), None);
+ m.push_back(box 1);
+ m.push_back(box 3);
+ m.push_back(box 5);
+ m.push_back(box 7);
+ assert_eq!(m.pop_front(), Some(box 1));
+
+ let mut n = LinkedList::new();
+ n.push_front(2);
+ n.push_front(3);
+ {
+ assert_eq!(n.front().unwrap(), &3);
+ let x = n.front_mut().unwrap();
+ assert_eq!(*x, 3);
+ *x = 0;
+ }
+ {
+ assert_eq!(n.back().unwrap(), &2);
+ let y = n.back_mut().unwrap();
+ assert_eq!(*y, 2);
+ *y = 1;
+ }
+ assert_eq!(n.pop_front(), Some(0));
+ assert_eq!(n.pop_front(), Some(1));
+}
+
+fn generate_test() -> LinkedList<i32> {
+ list_from(&[0, 1, 2, 3, 4, 5, 6])
+}
+
fn list_from<T: Clone>(v: &[T]) -> LinkedList<T> {
v.iter().cloned().collect()
}
check_links(&n);
}
+#[test]
+fn test_iterator() {
+ let m = generate_test();
+ for (i, elt) in m.iter().enumerate() {
+ assert_eq!(i as i32, *elt);
+ }
+ let mut n = LinkedList::new();
+ assert_eq!(n.iter().next(), None);
+ n.push_front(4);
+ let mut it = n.iter();
+ assert_eq!(it.size_hint(), (1, Some(1)));
+ assert_eq!(it.next().unwrap(), &4);
+ assert_eq!(it.size_hint(), (0, Some(0)));
+ assert_eq!(it.next(), None);
+}
+
+#[test]
+fn test_iterator_clone() {
+ let mut n = LinkedList::new();
+ n.push_back(2);
+ n.push_back(3);
+ n.push_back(4);
+ let mut it = n.iter();
+ it.next();
+ let mut jt = it.clone();
+ assert_eq!(it.next(), jt.next());
+ assert_eq!(it.next_back(), jt.next_back());
+ assert_eq!(it.next(), jt.next());
+}
+
+#[test]
+fn test_iterator_double_end() {
+ let mut n = LinkedList::new();
+ assert_eq!(n.iter().next(), None);
+ n.push_front(4);
+ n.push_front(5);
+ n.push_front(6);
+ let mut it = n.iter();
+ assert_eq!(it.size_hint(), (3, Some(3)));
+ assert_eq!(it.next().unwrap(), &6);
+ assert_eq!(it.size_hint(), (2, Some(2)));
+ assert_eq!(it.next_back().unwrap(), &4);
+ assert_eq!(it.size_hint(), (1, Some(1)));
+ assert_eq!(it.next_back().unwrap(), &5);
+ assert_eq!(it.next_back(), None);
+ assert_eq!(it.next(), None);
+}
+
+#[test]
+fn test_rev_iter() {
+ let m = generate_test();
+ for (i, elt) in m.iter().rev().enumerate() {
+ assert_eq!((6 - i) as i32, *elt);
+ }
+ let mut n = LinkedList::new();
+ assert_eq!(n.iter().rev().next(), None);
+ n.push_front(4);
+ let mut it = n.iter().rev();
+ assert_eq!(it.size_hint(), (1, Some(1)));
+ assert_eq!(it.next().unwrap(), &4);
+ assert_eq!(it.size_hint(), (0, Some(0)));
+ assert_eq!(it.next(), None);
+}
+
+#[test]
+fn test_mut_iter() {
+ let mut m = generate_test();
+ let mut len = m.len();
+ for (i, elt) in m.iter_mut().enumerate() {
+ assert_eq!(i as i32, *elt);
+ len -= 1;
+ }
+ assert_eq!(len, 0);
+ let mut n = LinkedList::new();
+ assert!(n.iter_mut().next().is_none());
+ n.push_front(4);
+ n.push_back(5);
+ let mut it = n.iter_mut();
+ assert_eq!(it.size_hint(), (2, Some(2)));
+ assert!(it.next().is_some());
+ assert!(it.next().is_some());
+ assert_eq!(it.size_hint(), (0, Some(0)));
+ assert!(it.next().is_none());
+}
+
+#[test]
+fn test_iterator_mut_double_end() {
+ let mut n = LinkedList::new();
+ assert!(n.iter_mut().next_back().is_none());
+ n.push_front(4);
+ n.push_front(5);
+ n.push_front(6);
+ let mut it = n.iter_mut();
+ assert_eq!(it.size_hint(), (3, Some(3)));
+ assert_eq!(*it.next().unwrap(), 6);
+ assert_eq!(it.size_hint(), (2, Some(2)));
+ assert_eq!(*it.next_back().unwrap(), 4);
+ assert_eq!(it.size_hint(), (1, Some(1)));
+ assert_eq!(*it.next_back().unwrap(), 5);
+ assert!(it.next_back().is_none());
+ assert!(it.next().is_none());
+}
+
+#[test]
+fn test_mut_rev_iter() {
+ let mut m = generate_test();
+ for (i, elt) in m.iter_mut().rev().enumerate() {
+ assert_eq!((6 - i) as i32, *elt);
+ }
+ let mut n = LinkedList::new();
+ assert!(n.iter_mut().rev().next().is_none());
+ n.push_front(4);
+ let mut it = n.iter_mut().rev();
+ assert!(it.next().is_some());
+ assert!(it.next().is_none());
+}
+
#[test]
fn test_clone_from() {
// Short cloned from long
}
#[test]
-fn test_fuzz() {
- for _ in 0..25 {
- fuzz_test(3);
- fuzz_test(16);
- #[cfg(not(miri))] // Miri is too slow
- fuzz_test(189);
- }
+fn test_eq() {
+ let mut n = list_from(&[]);
+ let mut m = list_from(&[]);
+ assert!(n == m);
+ n.push_front(1);
+ assert!(n != m);
+ m.push_back(1);
+ assert!(n == m);
+
+ let n = list_from(&[2, 3, 4]);
+ let m = list_from(&[1, 2, 3]);
+ assert!(n != m);
+}
+
+#[test]
+fn test_ord() {
+ let n = list_from(&[]);
+ let m = list_from(&[1, 2, 3]);
+ assert!(n < m);
+ assert!(m > n);
+ assert!(n <= n);
+ assert!(n >= n);
+}
+
+#[test]
+fn test_ord_nan() {
+ let nan = 0.0f64 / 0.0;
+ let n = list_from(&[nan]);
+ let m = list_from(&[nan]);
+ assert!(!(n < m));
+ assert!(!(n > m));
+ assert!(!(n <= m));
+ assert!(!(n >= m));
+
+ let n = list_from(&[nan]);
+ let one = list_from(&[1.0f64]);
+ assert!(!(n < one));
+ assert!(!(n > one));
+ assert!(!(n <= one));
+ assert!(!(n >= one));
+
+ let u = list_from(&[1.0f64, 2.0, nan]);
+ let v = list_from(&[1.0f64, 2.0, 3.0]);
+ assert!(!(u < v));
+ assert!(!(u > v));
+ assert!(!(u <= v));
+ assert!(!(u >= v));
+
+ let s = list_from(&[1.0f64, 2.0, 4.0, 2.0]);
+ let t = list_from(&[1.0f64, 2.0, 3.0, 2.0]);
+ assert!(!(s < t));
+ assert!(s > one);
+ assert!(!(s <= one));
+ assert!(s >= one);
}
#[test]
}
}
+#[test]
+fn test_split_off_2() {
+ // singleton
+ {
+ let mut m = LinkedList::new();
+ m.push_back(1);
+
+ let p = m.split_off(0);
+ assert_eq!(m.len(), 0);
+ assert_eq!(p.len(), 1);
+ assert_eq!(p.back(), Some(&1));
+ assert_eq!(p.front(), Some(&1));
+ }
+
+ // not singleton, forwards
+ {
+ let u = vec![1, 2, 3, 4, 5];
+ let mut m = list_from(&u);
+ let mut n = m.split_off(2);
+ assert_eq!(m.len(), 2);
+ assert_eq!(n.len(), 3);
+ for elt in 1..3 {
+ assert_eq!(m.pop_front(), Some(elt));
+ }
+ for elt in 3..6 {
+ assert_eq!(n.pop_front(), Some(elt));
+ }
+ }
+ // not singleton, backwards
+ {
+ let u = vec![1, 2, 3, 4, 5];
+ let mut m = list_from(&u);
+ let mut n = m.split_off(4);
+ assert_eq!(m.len(), 4);
+ assert_eq!(n.len(), 1);
+ for elt in 1..5 {
+ assert_eq!(m.pop_front(), Some(elt));
+ }
+ for elt in 5..6 {
+ assert_eq!(n.pop_front(), Some(elt));
+ }
+ }
+
+ // no-op on the last index
+ {
+ let mut m = LinkedList::new();
+ m.push_back(1);
+
+ let p = m.split_off(1);
+ assert_eq!(m.len(), 1);
+ assert_eq!(p.len(), 0);
+ assert_eq!(m.back(), Some(&1));
+ assert_eq!(m.front(), Some(&1));
+ }
+}
+
fn fuzz_test(sz: i32) {
let mut m: LinkedList<_> = LinkedList::new();
let mut v = vec![];
assert_eq!(i, v.len());
}
+#[test]
+fn test_fuzz() {
+ for _ in 0..25 {
+ fuzz_test(3);
+ fuzz_test(16);
+ #[cfg(not(miri))] // Miri is too slow
+ fuzz_test(189);
+ }
+}
+
+#[test]
+fn test_show() {
+ let list: LinkedList<_> = (0..10).collect();
+ assert_eq!(format!("{list:?}"), "[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]");
+
+ let list: LinkedList<_> = ["just", "one", "test", "more"].into_iter().collect();
+ assert_eq!(format!("{list:?}"), "[\"just\", \"one\", \"test\", \"more\"]");
+}
+
#[test]
fn drain_filter_test() {
let mut m: LinkedList<u32> = LinkedList::new();
assert_eq!(c.current(), None);
assert_eq!(c.index, 2);
}
+
+#[test]
+fn test_extend_ref() {
+ let mut a = LinkedList::new();
+ a.push_back(1);
+
+ a.extend(&[2, 3, 4]);
+
+ assert_eq!(a.len(), 4);
+ assert_eq!(a, list_from(&[1, 2, 3, 4]));
+
+ let mut b = LinkedList::new();
+ b.push_back(5);
+ b.push_back(6);
+ a.extend(&b);
+
+ assert_eq!(a.len(), 6);
+ assert_eq!(a, list_from(&[1, 2, 3, 4, 5, 6]));
+}
+
+#[test]
+fn test_extend() {
+ let mut a = LinkedList::new();
+ a.push_back(1);
+ a.extend(vec![2, 3, 4]); // uses iterator
+
+ assert_eq!(a.len(), 4);
+ assert!(a.iter().eq(&[1, 2, 3, 4]));
+
+ let b: LinkedList<_> = [5, 6, 7].into_iter().collect();
+ a.extend(b); // specializes to `append`
+
+ assert_eq!(a.len(), 7);
+ assert!(a.iter().eq(&[1, 2, 3, 4, 5, 6, 7]));
+}
+
+#[test]
+fn test_contains() {
+ let mut l = LinkedList::new();
+ l.extend(&[2, 3, 4]);
+
+ assert!(l.contains(&3));
+ assert!(!l.contains(&1));
+
+ l.clear();
+
+ assert!(!l.contains(&3));
+}
+
+#[test]
+fn drain_filter_empty() {
+ let mut list: LinkedList<i32> = LinkedList::new();
+
+ {
+ let mut iter = list.drain_filter(|_| true);
+ assert_eq!(iter.size_hint(), (0, Some(0)));
+ assert_eq!(iter.next(), None);
+ assert_eq!(iter.size_hint(), (0, Some(0)));
+ assert_eq!(iter.next(), None);
+ assert_eq!(iter.size_hint(), (0, Some(0)));
+ }
+
+ assert_eq!(list.len(), 0);
+ assert_eq!(list.into_iter().collect::<Vec<_>>(), vec![]);
+}
+
+#[test]
+fn drain_filter_zst() {
+ let mut list: LinkedList<_> = [(), (), (), (), ()].into_iter().collect();
+ let initial_len = list.len();
+ let mut count = 0;
+
+ {
+ let mut iter = list.drain_filter(|_| true);
+ assert_eq!(iter.size_hint(), (0, Some(initial_len)));
+ while let Some(_) = iter.next() {
+ count += 1;
+ assert_eq!(iter.size_hint(), (0, Some(initial_len - count)));
+ }
+ assert_eq!(iter.size_hint(), (0, Some(0)));
+ assert_eq!(iter.next(), None);
+ assert_eq!(iter.size_hint(), (0, Some(0)));
+ }
+
+ assert_eq!(count, initial_len);
+ assert_eq!(list.len(), 0);
+ assert_eq!(list.into_iter().collect::<Vec<_>>(), vec![]);
+}
+
+#[test]
+fn drain_filter_false() {
+ let mut list: LinkedList<_> = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].into_iter().collect();
+
+ let initial_len = list.len();
+ let mut count = 0;
+
+ {
+ let mut iter = list.drain_filter(|_| false);
+ assert_eq!(iter.size_hint(), (0, Some(initial_len)));
+ for _ in iter.by_ref() {
+ count += 1;
+ }
+ assert_eq!(iter.size_hint(), (0, Some(0)));
+ assert_eq!(iter.next(), None);
+ assert_eq!(iter.size_hint(), (0, Some(0)));
+ }
+
+ assert_eq!(count, 0);
+ assert_eq!(list.len(), initial_len);
+ assert_eq!(list.into_iter().collect::<Vec<_>>(), vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
+}
+
+#[test]
+fn drain_filter_true() {
+ let mut list: LinkedList<_> = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].into_iter().collect();
+
+ let initial_len = list.len();
+ let mut count = 0;
+
+ {
+ let mut iter = list.drain_filter(|_| true);
+ assert_eq!(iter.size_hint(), (0, Some(initial_len)));
+ while let Some(_) = iter.next() {
+ count += 1;
+ assert_eq!(iter.size_hint(), (0, Some(initial_len - count)));
+ }
+ assert_eq!(iter.size_hint(), (0, Some(0)));
+ assert_eq!(iter.next(), None);
+ assert_eq!(iter.size_hint(), (0, Some(0)));
+ }
+
+ assert_eq!(count, initial_len);
+ assert_eq!(list.len(), 0);
+ assert_eq!(list.into_iter().collect::<Vec<_>>(), vec![]);
+}
+
+#[test]
+fn drain_filter_complex() {
+ {
+ // [+xxx++++++xxxxx++++x+x++]
+ let mut list = [
+ 1, 2, 4, 6, 7, 9, 11, 13, 15, 17, 18, 20, 22, 24, 26, 27, 29, 31, 33, 34, 35, 36, 37,
+ 39,
+ ]
+ .into_iter()
+ .collect::<LinkedList<_>>();
+
+ let removed = list.drain_filter(|x| *x % 2 == 0).collect::<Vec<_>>();
+ assert_eq!(removed.len(), 10);
+ assert_eq!(removed, vec![2, 4, 6, 18, 20, 22, 24, 26, 34, 36]);
+
+ assert_eq!(list.len(), 14);
+ assert_eq!(
+ list.into_iter().collect::<Vec<_>>(),
+ vec![1, 7, 9, 11, 13, 15, 17, 27, 29, 31, 33, 35, 37, 39]
+ );
+ }
+
+ {
+ // [xxx++++++xxxxx++++x+x++]
+ let mut list =
+ [2, 4, 6, 7, 9, 11, 13, 15, 17, 18, 20, 22, 24, 26, 27, 29, 31, 33, 34, 35, 36, 37, 39]
+ .into_iter()
+ .collect::<LinkedList<_>>();
+
+ let removed = list.drain_filter(|x| *x % 2 == 0).collect::<Vec<_>>();
+ assert_eq!(removed.len(), 10);
+ assert_eq!(removed, vec![2, 4, 6, 18, 20, 22, 24, 26, 34, 36]);
+
+ assert_eq!(list.len(), 13);
+ assert_eq!(
+ list.into_iter().collect::<Vec<_>>(),
+ vec![7, 9, 11, 13, 15, 17, 27, 29, 31, 33, 35, 37, 39]
+ );
+ }
+
+ {
+ // [xxx++++++xxxxx++++x+x]
+ let mut list =
+ [2, 4, 6, 7, 9, 11, 13, 15, 17, 18, 20, 22, 24, 26, 27, 29, 31, 33, 34, 35, 36]
+ .into_iter()
+ .collect::<LinkedList<_>>();
+
+ let removed = list.drain_filter(|x| *x % 2 == 0).collect::<Vec<_>>();
+ assert_eq!(removed.len(), 10);
+ assert_eq!(removed, vec![2, 4, 6, 18, 20, 22, 24, 26, 34, 36]);
+
+ assert_eq!(list.len(), 11);
+ assert_eq!(
+ list.into_iter().collect::<Vec<_>>(),
+ vec![7, 9, 11, 13, 15, 17, 27, 29, 31, 33, 35]
+ );
+ }
+
+ {
+ // [xxxxxxxxxx+++++++++++]
+ let mut list = [2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 1, 3, 5, 7, 9, 11, 13, 15, 17, 19]
+ .into_iter()
+ .collect::<LinkedList<_>>();
+
+ let removed = list.drain_filter(|x| *x % 2 == 0).collect::<Vec<_>>();
+ assert_eq!(removed.len(), 10);
+ assert_eq!(removed, vec![2, 4, 6, 8, 10, 12, 14, 16, 18, 20]);
+
+ assert_eq!(list.len(), 10);
+ assert_eq!(list.into_iter().collect::<Vec<_>>(), vec![1, 3, 5, 7, 9, 11, 13, 15, 17, 19]);
+ }
+
+ {
+ // [+++++++++++xxxxxxxxxx]
+ let mut list = [1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
+ .into_iter()
+ .collect::<LinkedList<_>>();
+
+ let removed = list.drain_filter(|x| *x % 2 == 0).collect::<Vec<_>>();
+ assert_eq!(removed.len(), 10);
+ assert_eq!(removed, vec![2, 4, 6, 8, 10, 12, 14, 16, 18, 20]);
+
+ assert_eq!(list.len(), 10);
+ assert_eq!(list.into_iter().collect::<Vec<_>>(), vec![1, 3, 5, 7, 9, 11, 13, 15, 17, 19]);
+ }
+}
+
+#[test]
+fn drain_filter_drop_panic_leak() {
+ static mut DROPS: i32 = 0;
+
+ struct D(bool);
+
+ impl Drop for D {
+ fn drop(&mut self) {
+ unsafe {
+ DROPS += 1;
+ }
+
+ if self.0 {
+ panic!("panic in `drop`");
+ }
+ }
+ }
+
+ let mut q = LinkedList::new();
+ q.push_back(D(false));
+ q.push_back(D(false));
+ q.push_back(D(false));
+ q.push_back(D(false));
+ q.push_back(D(false));
+ q.push_front(D(false));
+ q.push_front(D(true));
+ q.push_front(D(false));
+
+ catch_unwind(AssertUnwindSafe(|| drop(q.drain_filter(|_| true)))).ok();
+
+ assert_eq!(unsafe { DROPS }, 8);
+ assert!(q.is_empty());
+}
+
+#[test]
+fn drain_filter_pred_panic_leak() {
+ static mut DROPS: i32 = 0;
+
+ #[derive(Debug)]
+ struct D(u32);
+
+ impl Drop for D {
+ fn drop(&mut self) {
+ unsafe {
+ DROPS += 1;
+ }
+ }
+ }
+
+ let mut q = LinkedList::new();
+ q.push_back(D(3));
+ q.push_back(D(4));
+ q.push_back(D(5));
+ q.push_back(D(6));
+ q.push_back(D(7));
+ q.push_front(D(2));
+ q.push_front(D(1));
+ q.push_front(D(0));
+
+ catch_unwind(AssertUnwindSafe(|| {
+ drop(q.drain_filter(|item| if item.0 >= 2 { panic!() } else { true }))
+ }))
+ .ok();
+
+ assert_eq!(unsafe { DROPS }, 2); // 0 and 1
+ assert_eq!(q.len(), 6);
+}
+
+#[test]
+fn test_drop() {
+ static mut DROPS: i32 = 0;
+ struct Elem;
+ impl Drop for Elem {
+ fn drop(&mut self) {
+ unsafe {
+ DROPS += 1;
+ }
+ }
+ }
+
+ let mut ring = LinkedList::new();
+ ring.push_back(Elem);
+ ring.push_front(Elem);
+ ring.push_back(Elem);
+ ring.push_front(Elem);
+ drop(ring);
+
+ assert_eq!(unsafe { DROPS }, 4);
+}
+
+#[test]
+fn test_drop_with_pop() {
+ static mut DROPS: i32 = 0;
+ struct Elem;
+ impl Drop for Elem {
+ fn drop(&mut self) {
+ unsafe {
+ DROPS += 1;
+ }
+ }
+ }
+
+ let mut ring = LinkedList::new();
+ ring.push_back(Elem);
+ ring.push_front(Elem);
+ ring.push_back(Elem);
+ ring.push_front(Elem);
+
+ drop(ring.pop_back());
+ drop(ring.pop_front());
+ assert_eq!(unsafe { DROPS }, 2);
+
+ drop(ring);
+ assert_eq!(unsafe { DROPS }, 4);
+}
+
+#[test]
+fn test_drop_clear() {
+ static mut DROPS: i32 = 0;
+ struct Elem;
+ impl Drop for Elem {
+ fn drop(&mut self) {
+ unsafe {
+ DROPS += 1;
+ }
+ }
+ }
+
+ let mut ring = LinkedList::new();
+ ring.push_back(Elem);
+ ring.push_front(Elem);
+ ring.push_back(Elem);
+ ring.push_front(Elem);
+ ring.clear();
+ assert_eq!(unsafe { DROPS }, 4);
+
+ drop(ring);
+ assert_eq!(unsafe { DROPS }, 4);
+}
+
+#[test]
+fn test_drop_panic() {
+ static mut DROPS: i32 = 0;
+
+ struct D(bool);
+
+ impl Drop for D {
+ fn drop(&mut self) {
+ unsafe {
+ DROPS += 1;
+ }
+
+ if self.0 {
+ panic!("panic in `drop`");
+ }
+ }
+ }
+
+ let mut q = LinkedList::new();
+ q.push_back(D(false));
+ q.push_back(D(false));
+ q.push_back(D(false));
+ q.push_back(D(false));
+ q.push_back(D(false));
+ q.push_front(D(false));
+ q.push_front(D(false));
+ q.push_front(D(true));
+
+ catch_unwind(move || drop(q)).ok();
+
+ assert_eq!(unsafe { DROPS }, 8);
+}
mod ring_slices;
+use self::spec_extend::SpecExtend;
+
+mod spec_extend;
+
#[cfg(test)]
mod tests;
/// Returns `true` if the deque contains an element equal to the
/// given value.
///
+ /// This operation is *O*(*n*).
+ ///
+ /// Note that if you have a sorted `VecDeque`, [`binary_search`] may be faster.
+ ///
+ /// [`binary_search`]: VecDeque::binary_search
+ ///
/// # Examples
///
/// ```
}
}
- /// Binary searches the sorted deque for a given element.
+ /// Binary searches this `VecDeque` for a given element.
+ /// This behaves similarly to [`contains`] if this `VecDeque` is sorted.
///
/// If the value is found then [`Result::Ok`] is returned, containing the
/// index of the matching element. If there are multiple matches, then any
///
/// See also [`binary_search_by`], [`binary_search_by_key`], and [`partition_point`].
///
+ /// [`contains`]: VecDeque::contains
/// [`binary_search_by`]: VecDeque::binary_search_by
/// [`binary_search_by_key`]: VecDeque::binary_search_by_key
/// [`partition_point`]: VecDeque::partition_point
self.binary_search_by(|e| e.cmp(x))
}
- /// Binary searches the sorted deque with a comparator function.
+ /// Binary searches this `VecDeque` with a comparator function.
+ /// This behaves similarly to [`contains`] if this `VecDeque` is sorted.
///
/// The comparator function should implement an order consistent
/// with the sort order of the deque, returning an order code that
///
/// See also [`binary_search`], [`binary_search_by_key`], and [`partition_point`].
///
+ /// [`contains`]: VecDeque::contains
/// [`binary_search`]: VecDeque::binary_search
/// [`binary_search_by_key`]: VecDeque::binary_search_by_key
/// [`partition_point`]: VecDeque::partition_point
}
}
- /// Binary searches the sorted deque with a key extraction function.
+ /// Binary searches this `VecDeque` with a key extraction function.
+ /// This behaves similarly to [`contains`] if this `VecDeque` is sorted.
///
/// Assumes that the deque is sorted by the key, for instance with
/// [`make_contiguous().sort_by_key()`] using the same key extraction function.
///
/// See also [`binary_search`], [`binary_search_by`], and [`partition_point`].
///
+ /// [`contains`]: VecDeque::contains
/// [`make_contiguous().sort_by_key()`]: VecDeque::make_contiguous
/// [`binary_search`]: VecDeque::binary_search
/// [`binary_search_by`]: VecDeque::binary_search_by
#[stable(feature = "rust1", since = "1.0.0")]
impl<T, A: Allocator> Extend<T> for VecDeque<T, A> {
fn extend<I: IntoIterator<Item = T>>(&mut self, iter: I) {
- // This function should be the moral equivalent of:
- //
- // for item in iter.into_iter() {
- // self.push_back(item);
- // }
- let mut iter = iter.into_iter();
- while let Some(element) = iter.next() {
- if self.len() == self.capacity() {
- let (lower, _) = iter.size_hint();
- self.reserve(lower.saturating_add(1));
- }
-
- let head = self.head;
- self.head = self.wrap_add(self.head, 1);
- unsafe {
- self.buffer_write(head, element);
- }
- }
+ <Self as SpecExtend<T, I::IntoIter>>::spec_extend(self, iter.into_iter());
}
#[inline]
#[stable(feature = "extend_ref", since = "1.2.0")]
impl<'a, T: 'a + Copy, A: Allocator> Extend<&'a T> for VecDeque<T, A> {
fn extend<I: IntoIterator<Item = &'a T>>(&mut self, iter: I) {
- self.extend(iter.into_iter().cloned());
+ self.spec_extend(iter.into_iter());
}
#[inline]
--- /dev/null
+use crate::alloc::Allocator;
+use crate::vec;
+use core::slice;
+
+use super::VecDeque;
+
+// Specialization trait used for VecDeque::extend
+pub(super) trait SpecExtend<T, I> {
+ fn spec_extend(&mut self, iter: I);
+}
+
+impl<T, I, A: Allocator> SpecExtend<T, I> for VecDeque<T, A>
+where
+ I: Iterator<Item = T>,
+{
+ default fn spec_extend(&mut self, mut iter: I) {
+ // This function should be the moral equivalent of:
+ //
+ // for item in iter {
+ // self.push_back(item);
+ // }
+ while let Some(element) = iter.next() {
+ if self.len() == self.capacity() {
+ let (lower, _) = iter.size_hint();
+ self.reserve(lower.saturating_add(1));
+ }
+
+ let head = self.head;
+ self.head = self.wrap_add(self.head, 1);
+ unsafe {
+ self.buffer_write(head, element);
+ }
+ }
+ }
+}
+
+impl<T, A: Allocator> SpecExtend<T, vec::IntoIter<T>> for VecDeque<T, A> {
+ fn spec_extend(&mut self, mut iterator: vec::IntoIter<T>) {
+ let slice = iterator.as_slice();
+ self.reserve(slice.len());
+
+ unsafe {
+ self.copy_slice(self.head, slice);
+ self.head = self.wrap_add(self.head, slice.len());
+ }
+ iterator.forget_remaining_elements();
+ }
+}
+
+impl<'a, T: 'a, I, A: Allocator> SpecExtend<&'a T, I> for VecDeque<T, A>
+where
+ I: Iterator<Item = &'a T>,
+ T: Copy,
+{
+ default fn spec_extend(&mut self, iterator: I) {
+ self.spec_extend(iterator.copied())
+ }
+}
+
+impl<'a, T: 'a, A: Allocator> SpecExtend<&'a T, slice::Iter<'a, T>> for VecDeque<T, A>
+where
+ T: Copy,
+{
+ fn spec_extend(&mut self, iterator: slice::Iter<'a, T>) {
+ let slice = iterator.as_slice();
+ self.reserve(slice.len());
+
+ unsafe {
+ self.copy_slice(self.head, slice);
+ self.head = self.wrap_add(self.head, slice.len());
+ }
+ }
+}
}
}
+#[test]
+fn test_get() {
+ let mut tester = VecDeque::new();
+ tester.push_back(1);
+ tester.push_back(2);
+ tester.push_back(3);
+
+ assert_eq!(tester.len(), 3);
+
+ assert_eq!(tester.get(1), Some(&2));
+ assert_eq!(tester.get(2), Some(&3));
+ assert_eq!(tester.get(0), Some(&1));
+ assert_eq!(tester.get(3), None);
+
+ tester.remove(0);
+
+ assert_eq!(tester.len(), 2);
+ assert_eq!(tester.get(0), Some(&2));
+ assert_eq!(tester.get(1), Some(&3));
+ assert_eq!(tester.get(2), None);
+}
+
+#[test]
+fn test_get_mut() {
+ let mut tester = VecDeque::new();
+ tester.push_back(1);
+ tester.push_back(2);
+ tester.push_back(3);
+
+ assert_eq!(tester.len(), 3);
+
+ if let Some(elem) = tester.get_mut(0) {
+ assert_eq!(*elem, 1);
+ *elem = 10;
+ }
+
+ if let Some(elem) = tester.get_mut(2) {
+ assert_eq!(*elem, 3);
+ *elem = 30;
+ }
+
+ assert_eq!(tester.get(0), Some(&10));
+ assert_eq!(tester.get(2), Some(&30));
+ assert_eq!(tester.get_mut(3), None);
+
+ tester.remove(2);
+
+ assert_eq!(tester.len(), 2);
+ assert_eq!(tester.get(0), Some(&10));
+ assert_eq!(tester.get(1), Some(&2));
+ assert_eq!(tester.get(2), None);
+}
+
+#[test]
+fn test_swap() {
+ let mut tester = VecDeque::new();
+ tester.push_back(1);
+ tester.push_back(2);
+ tester.push_back(3);
+
+ assert_eq!(tester, [1, 2, 3]);
+
+ tester.swap(0, 0);
+ assert_eq!(tester, [1, 2, 3]);
+ tester.swap(0, 1);
+ assert_eq!(tester, [2, 1, 3]);
+ tester.swap(2, 1);
+ assert_eq!(tester, [2, 3, 1]);
+ tester.swap(1, 2);
+ assert_eq!(tester, [2, 1, 3]);
+ tester.swap(0, 2);
+ assert_eq!(tester, [3, 1, 2]);
+ tester.swap(2, 2);
+ assert_eq!(tester, [3, 1, 2]);
+}
+
+#[test]
+#[should_panic = "assertion failed: j < self.len()"]
+fn test_swap_panic() {
+ let mut tester = VecDeque::new();
+ tester.push_back(1);
+ tester.push_back(2);
+ tester.push_back(3);
+ tester.swap(2, 3);
+}
+
+#[test]
+fn test_reserve_exact() {
+ let mut tester: VecDeque<i32> = VecDeque::with_capacity(1);
+ assert!(tester.capacity() == 1);
+ tester.reserve_exact(50);
+ assert!(tester.capacity() >= 51);
+ tester.reserve_exact(40);
+ assert!(tester.capacity() >= 51);
+ tester.reserve_exact(200);
+ assert!(tester.capacity() >= 200);
+}
+
+#[test]
+#[should_panic = "capacity overflow"]
+fn test_reserve_exact_panic() {
+ let mut tester: VecDeque<i32> = VecDeque::new();
+ tester.reserve_exact(usize::MAX);
+}
+
+#[test]
+fn test_try_reserve_exact() {
+ let mut tester: VecDeque<i32> = VecDeque::with_capacity(1);
+ assert!(tester.capacity() == 1);
+ assert_eq!(tester.try_reserve_exact(100), Ok(()));
+ assert!(tester.capacity() >= 100);
+ assert_eq!(tester.try_reserve_exact(50), Ok(()));
+ assert!(tester.capacity() >= 100);
+ assert_eq!(tester.try_reserve_exact(200), Ok(()));
+ assert!(tester.capacity() >= 200);
+ assert_eq!(tester.try_reserve_exact(0), Ok(()));
+ assert!(tester.capacity() >= 200);
+ assert!(tester.try_reserve_exact(usize::MAX).is_err());
+}
+
+#[test]
+fn test_try_reserve() {
+ let mut tester: VecDeque<i32> = VecDeque::with_capacity(1);
+ assert!(tester.capacity() == 1);
+ assert_eq!(tester.try_reserve(100), Ok(()));
+ assert!(tester.capacity() >= 100);
+ assert_eq!(tester.try_reserve(50), Ok(()));
+ assert!(tester.capacity() >= 100);
+ assert_eq!(tester.try_reserve(200), Ok(()));
+ assert!(tester.capacity() >= 200);
+ assert_eq!(tester.try_reserve(0), Ok(()));
+ assert!(tester.capacity() >= 200);
+ assert!(tester.try_reserve(usize::MAX).is_err());
+}
+
+#[test]
+fn test_contains() {
+ let mut tester = VecDeque::new();
+ tester.push_back(1);
+ tester.push_back(2);
+ tester.push_back(3);
+
+ assert!(tester.contains(&1));
+ assert!(tester.contains(&3));
+ assert!(!tester.contains(&0));
+ assert!(!tester.contains(&4));
+ tester.remove(0);
+ assert!(!tester.contains(&1));
+ assert!(tester.contains(&2));
+ assert!(tester.contains(&3));
+}
+
+#[test]
+fn test_rotate_left_right() {
+ let mut tester: VecDeque<_> = (1..=10).collect();
+
+ assert_eq!(tester.len(), 10);
+
+ tester.rotate_left(0);
+ assert_eq!(tester, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
+
+ tester.rotate_right(0);
+ assert_eq!(tester, [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
+
+ tester.rotate_left(3);
+ assert_eq!(tester, [4, 5, 6, 7, 8, 9, 10, 1, 2, 3]);
+
+ tester.rotate_right(5);
+ assert_eq!(tester, [9, 10, 1, 2, 3, 4, 5, 6, 7, 8]);
+
+ tester.rotate_left(tester.len());
+ assert_eq!(tester, [9, 10, 1, 2, 3, 4, 5, 6, 7, 8]);
+
+ tester.rotate_right(tester.len());
+ assert_eq!(tester, [9, 10, 1, 2, 3, 4, 5, 6, 7, 8]);
+
+ tester.rotate_left(1);
+ assert_eq!(tester, [10, 1, 2, 3, 4, 5, 6, 7, 8, 9]);
+}
+
+#[test]
+#[should_panic = "assertion failed: mid <= self.len()"]
+fn test_rotate_left_panic() {
+ let mut tester: VecDeque<_> = (1..=10).collect();
+ tester.rotate_left(tester.len() + 1);
+}
+
+#[test]
+#[should_panic = "assertion failed: k <= self.len()"]
+fn test_rotate_right_panic() {
+ let mut tester: VecDeque<_> = (1..=10).collect();
+ tester.rotate_right(tester.len() + 1);
+}
+
+#[test]
+fn test_binary_search() {
+ // If the givin VecDeque is not sorted, the returned result is unspecified and meaningless,
+ // as this method performs a binary search.
+
+ let tester: VecDeque<_> = [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55].into();
+
+ assert_eq!(tester.binary_search(&0), Ok(0));
+ assert_eq!(tester.binary_search(&5), Ok(5));
+ assert_eq!(tester.binary_search(&55), Ok(10));
+ assert_eq!(tester.binary_search(&4), Err(5));
+ assert_eq!(tester.binary_search(&-1), Err(0));
+ assert!(matches!(tester.binary_search(&1), Ok(1..=2)));
+
+ let tester: VecDeque<_> = [1, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3].into();
+ assert_eq!(tester.binary_search(&1), Ok(0));
+ assert!(matches!(tester.binary_search(&2), Ok(1..=4)));
+ assert!(matches!(tester.binary_search(&3), Ok(5..=13)));
+ assert_eq!(tester.binary_search(&-2), Err(0));
+ assert_eq!(tester.binary_search(&0), Err(0));
+ assert_eq!(tester.binary_search(&4), Err(14));
+ assert_eq!(tester.binary_search(&5), Err(14));
+}
+
+#[test]
+fn test_binary_search_by() {
+ // If the givin VecDeque is not sorted, the returned result is unspecified and meaningless,
+ // as this method performs a binary search.
+
+ let tester: VecDeque<_> = [0, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55].into();
+
+ assert_eq!(tester.binary_search_by(|x| x.cmp(&0)), Ok(0));
+ assert_eq!(tester.binary_search_by(|x| x.cmp(&5)), Ok(5));
+ assert_eq!(tester.binary_search_by(|x| x.cmp(&55)), Ok(10));
+ assert_eq!(tester.binary_search_by(|x| x.cmp(&4)), Err(5));
+ assert_eq!(tester.binary_search_by(|x| x.cmp(&-1)), Err(0));
+ assert!(matches!(tester.binary_search_by(|x| x.cmp(&1)), Ok(1..=2)));
+}
+
+#[test]
+fn test_binary_search_key() {
+ // If the givin VecDeque is not sorted, the returned result is unspecified and meaningless,
+ // as this method performs a binary search.
+
+ let tester: VecDeque<_> = [
+ (-1, 0),
+ (2, 10),
+ (6, 5),
+ (7, 1),
+ (8, 10),
+ (10, 2),
+ (20, 3),
+ (24, 5),
+ (25, 18),
+ (28, 13),
+ (31, 21),
+ (32, 4),
+ (54, 25),
+ ]
+ .into();
+
+ assert_eq!(tester.binary_search_by_key(&-1, |&(a, _b)| a), Ok(0));
+ assert_eq!(tester.binary_search_by_key(&8, |&(a, _b)| a), Ok(4));
+ assert_eq!(tester.binary_search_by_key(&25, |&(a, _b)| a), Ok(8));
+ assert_eq!(tester.binary_search_by_key(&54, |&(a, _b)| a), Ok(12));
+ assert_eq!(tester.binary_search_by_key(&-2, |&(a, _b)| a), Err(0));
+ assert_eq!(tester.binary_search_by_key(&1, |&(a, _b)| a), Err(1));
+ assert_eq!(tester.binary_search_by_key(&4, |&(a, _b)| a), Err(2));
+ assert_eq!(tester.binary_search_by_key(&13, |&(a, _b)| a), Err(6));
+ assert_eq!(tester.binary_search_by_key(&55, |&(a, _b)| a), Err(13));
+ assert_eq!(tester.binary_search_by_key(&100, |&(a, _b)| a), Err(13));
+
+ let tester: VecDeque<_> = [
+ (0, 0),
+ (2, 1),
+ (6, 1),
+ (5, 1),
+ (3, 1),
+ (1, 2),
+ (2, 3),
+ (4, 5),
+ (5, 8),
+ (8, 13),
+ (1, 21),
+ (2, 34),
+ (4, 55),
+ ]
+ .into();
+
+ assert_eq!(tester.binary_search_by_key(&0, |&(_a, b)| b), Ok(0));
+ assert!(matches!(tester.binary_search_by_key(&1, |&(_a, b)| b), Ok(1..=4)));
+ assert_eq!(tester.binary_search_by_key(&8, |&(_a, b)| b), Ok(8));
+ assert_eq!(tester.binary_search_by_key(&13, |&(_a, b)| b), Ok(9));
+ assert_eq!(tester.binary_search_by_key(&55, |&(_a, b)| b), Ok(12));
+ assert_eq!(tester.binary_search_by_key(&-1, |&(_a, b)| b), Err(0));
+ assert_eq!(tester.binary_search_by_key(&4, |&(_a, b)| b), Err(7));
+ assert_eq!(tester.binary_search_by_key(&56, |&(_a, b)| b), Err(13));
+ assert_eq!(tester.binary_search_by_key(&100, |&(_a, b)| b), Err(13));
+}
+
#[test]
fn make_contiguous_big_tail() {
let mut tester = VecDeque::with_capacity(15);
//!
//! 3. An asterisk `.*`:
//!
-//! `.*` means that this `{...}` is associated with *two* format inputs rather than one: the
-//! first input holds the `usize` precision, and the second holds the value to print. Note that
-//! in this case, if one uses the format string `{<arg>:<spec>.*}`, then the `<arg>` part refers
-//! to the *value* to print, and the `precision` must come in the input preceding `<arg>`.
+//! `.*` means that this `{...}` is associated with *two* format inputs rather than one:
+//! - If a format string in the fashion of `{:<spec>.*}` is used, then the first input holds
+//! the `usize` precision, and the second holds the value to print.
+//! - If a format string in the fashion of `{<arg>:<spec>.*}` is used, then the `<arg>` part
+//! refers to the value to print, and the `precision` is taken like it was specified with an
+//! omitted positional parameter (`{}` instead of `{<arg>:}`).
//!
//! For example, the following calls all print the same thing `Hello x is 0.01000`:
//!
//! // Hello {arg 0 ("x")} is {arg 2 (0.01) with precision specified in arg 1 (5)}
//! println!("Hello {0} is {2:.1$}", "x", 5, 0.01);
//!
-//! // Hello {next arg ("x")} is {second of next two args (0.01) with precision
-//! // specified in first of next two args (5)}
+//! // Hello {next arg -> arg 0 ("x")} is {second of next two args -> arg 2 (0.01) with precision
+//! // specified in first of next two args -> arg 1 (5)}
//! println!("Hello {} is {:.*}", "x", 5, 0.01);
//!
-//! // Hello {next arg ("x")} is {arg 2 (0.01) with precision
-//! // specified in its predecessor (5)}
+//! // Hello {arg 1 ("x")} is {arg 2 (0.01) with precision
+//! // specified in next arg -> arg 0 (5)}
+//! println!("Hello {1} is {2:.*}", 5, "x", 0.01);
+//!
+//! // Hello {next arg -> arg 0 ("x")} is {arg 2 (0.01) with precision
+//! // specified in next arg -> arg 1 (5)}
//! println!("Hello {} is {2:.*}", "x", 5, 0.01);
//!
-//! // Hello {next arg ("x")} is {arg "number" (0.01) with precision specified
+//! // Hello {next arg -> arg 0 ("x")} is {arg "number" (0.01) with precision specified
//! // in arg "prec" (5)}
//! println!("Hello {} is {number:.prec$}", "x", prec = 5, number = 0.01);
//! ```
//! ```text
//! format_string := text [ maybe_format text ] *
//! maybe_format := '{' '{' | '}' '}' | format
-//! format := '{' [ argument ] [ ':' format_spec ] '}'
+//! format := '{' [ argument ] [ ':' format_spec ] [ ws ] * '}'
//! argument := integer | identifier
//!
//! format_spec := [[fill]align][sign]['#']['0'][width]['.' precision]type
//! count := parameter | integer
//! parameter := argument '$'
//! ```
-//! In the above grammar, `text` must not contain any `'{'` or `'}'` characters.
+//! In the above grammar,
+//! - `text` must not contain any `'{'` or `'}'` characters,
+//! - `ws` is any character for which [`char::is_whitespace`] returns `true`, has no semantic
+//! meaning and is completely optional,
+//! - `integer` is a decimal integer that may contain leading zeroes and
+//! - `identifier` is an `IDENTIFIER_OR_KEYWORD` (not an `IDENTIFIER`) as defined by the [Rust language reference](https://doc.rust-lang.org/reference/identifiers.html).
//!
//! # Formatting traits
//!
//! ```
//!
//! Your type will be passed as `self` by-reference, and then the function
-//! should emit output into the `f.buf` stream. It is up to each format trait
-//! implementation to correctly adhere to the requested formatting parameters.
-//! The values of these parameters will be listed in the fields of the
+//! should emit output into the Formatter `f` which implements `fmt::Write`. It is up to each
+//! format trait implementation to correctly adhere to the requested formatting parameters.
+//! The values of these parameters can be accessed with methods of the
//! [`Formatter`] struct. In order to help with this, the [`Formatter`] struct also
//! provides some helper methods.
//!
//!
//! ```ignore (only-for-syntax-highlight)
//! format! // described above
-//! write! // first argument is a &mut io::Write, the destination
+//! write! // first argument is either a &mut io::Write or a &mut fmt::Write, the destination
//! writeln! // same as write but appends a newline
//! print! // the format string is printed to the standard output
//! println! // same as print but appends a newline
//!
//! ### `write!`
//!
-//! This and [`writeln!`] are two macros which are used to emit the format string
+//! [`write!`] and [`writeln!`] are two macros which are used to emit the format string
//! to a specified stream. This is used to prevent intermediate allocations of
//! format strings and instead directly write the output. Under the hood, this
//! function is actually invoking the [`write_fmt`] function defined on the
-//! [`std::io::Write`] trait. Example usage is:
+//! [`std::io::Write`] and the [`std::fmt::Write`] trait. Example usage is:
//!
//! ```
//! # #![allow(unused_must_use)]
//!
//! ### `format_args!`
//!
-//! This is a curious macro used to safely pass around
+//! [`format_args!`] is a curious macro used to safely pass around
//! an opaque object describing the format string. This object
//! does not require any heap allocations to create, and it only
//! references information on the stack. Under the hood, all of
//! [`to_string`]: crate::string::ToString::to_string "ToString::to_string"
//! [`write_fmt`]: ../../std/io/trait.Write.html#method.write_fmt
//! [`std::io::Write`]: ../../std/io/trait.Write.html
+//! [`std::fmt::Write`]: ../../std/fmt/trait.Write.html
//! [`print!`]: ../../std/macro.print.html "print!"
//! [`println!`]: ../../std/macro.println.html "println!"
//! [`eprint!`]: ../../std/macro.eprint.html "eprint!"
//! [`eprintln!`]: ../../std/macro.eprintln.html "eprintln!"
+//! [`format_args!`]: ../../std/macro.format_args.html "format_args!"
//! [`fmt::Arguments`]: Arguments "fmt::Arguments"
//! [`format`]: format() "fmt::format"
}
}
+#[stable(feature = "shared_from_str", since = "1.62.0")]
+impl From<Rc<str>> for Rc<[u8]> {
+ /// Converts a reference-counted string slice into a byte slice.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// # use std::rc::Rc;
+ /// let string: Rc<str> = Rc::from("eggplant");
+ /// let bytes: Rc<[u8]> = Rc::from(string);
+ /// assert_eq!("eggplant".as_bytes(), bytes.as_ref());
+ /// ```
+ #[inline]
+ fn from(rc: Rc<str>) -> Self {
+ // SAFETY: `str` has the same layout as `[u8]`.
+ unsafe { Rc::from_raw(Rc::into_raw(rc) as *const [u8]) }
+ }
+}
+
#[stable(feature = "boxed_slice_try_from", since = "1.43.0")]
impl<T, const N: usize> TryFrom<Rc<[T]>> for Rc<[T; N]> {
type Error = Rc<[T]>;
/// * The first `length` bytes at `buf` need to be valid UTF-8.
///
/// Violating these may cause problems like corrupting the allocator's
- /// internal data structures.
+ /// internal data structures. For example, it is normally **not** safe to
+ /// build a `String` from a pointer to a C `char` array containing UTF-8
+ /// _unless_ you are certain that array was originally allocated by the
+ /// Rust standard library's allocator.
///
/// The ownership of `buf` is effectively transferred to the
/// `String` which may then deallocate, reallocate or change the
}
}
+#[stable(feature = "shared_from_str", since = "1.62.0")]
+impl From<Arc<str>> for Arc<[u8]> {
+ /// Converts an atomically reference-counted string slice into a byte slice.
+ ///
+ /// # Example
+ ///
+ /// ```
+ /// # use std::sync::Arc;
+ /// let string: Arc<str> = Arc::from("eggplant");
+ /// let bytes: Arc<[u8]> = Arc::from(string);
+ /// assert_eq!("eggplant".as_bytes(), bytes.as_ref());
+ /// ```
+ #[inline]
+ fn from(rc: Arc<str>) -> Self {
+ // SAFETY: `str` has the same layout as `[u8]`.
+ unsafe { Arc::from_raw(Arc::into_raw(rc) as *const [u8]) }
+ }
+}
+
#[stable(feature = "boxed_slice_try_from", since = "1.43.0")]
impl<T, const N: usize> TryFrom<Arc<[T]>> for Arc<[T; N]> {
type Error = Arc<[T]>;
ptr::drop_in_place(remaining);
}
}
+
+ /// Forgets to Drop the remaining elements while still allowing the backing allocation to be freed.
+ pub(crate) fn forget_remaining_elements(&mut self) {
+ self.ptr = self.end;
+ }
}
#[stable(feature = "vec_intoiter_as_ref", since = "1.46.0")]
#[rustc_specialization_trait]
pub(super) unsafe trait IsZero {
- /// Whether this value is zero
+ /// Whether this value's representation is all zeros
fn is_zero(&self) -> bool;
}
}
}
+unsafe impl<T: IsZero, const N: usize> IsZero for [T; N] {
+ #[inline]
+ fn is_zero(&self) -> bool {
+ // Because this is generated as a runtime check, it's not obvious that
+ // it's worth doing if the array is really long. The threshold here
+ // is largely arbitrary, but was picked because as of 2022-05-01 LLVM
+ // can const-fold the check in `vec![[0; 32]; n]` but not in
+ // `vec![[0; 64]; n]`: https://godbolt.org/z/WTzjzfs5b
+ // Feel free to tweak if you have better evidence.
+
+ N <= 32 && self.iter().all(IsZero::is_zero)
+ }
+}
+
// `Option<&T>` and `Option<Box<T>>` are guaranteed to represent `None` as null.
// For fat pointers, the bytes that would be the pointer metadata in the `Some`
// variant are padding in the `None` variant, so ignoring them and
/// * `length` needs to be less than or equal to `capacity`.
///
/// Violating these may cause problems like corrupting the allocator's
- /// internal data structures. For example it is **not** safe
- /// to build a `Vec<u8>` from a pointer to a C `char` array with length `size_t`.
+ /// internal data structures. For example it is normally **not** safe
+ /// to build a `Vec<u8>` from a pointer to a C `char` array with length
+ /// `size_t`, doing so is only safe if the array was initially allocated by
+ /// a `Vec` or `String`.
/// It's also not safe to build one from a `Vec<u16>` and its length, because
/// the allocator cares about the alignment, and these two types have different
/// alignments. The buffer was allocated with alignment 2 (for `u16`), but after
}
}
-#[cfg(not(no_global_oom_handling))]
-#[stable(feature = "vec_from_array_ref", since = "1.61.0")]
-impl<T: Clone, const N: usize> From<&[T; N]> for Vec<T> {
- /// Allocate a `Vec<T>` and fill it by cloning `s`'s items.
- ///
- /// # Examples
- ///
- /// ```
- /// assert_eq!(Vec::from(b"raw"), vec![b'r', b'a', b'w']);
- /// ```
- #[cfg(not(test))]
- fn from(s: &[T; N]) -> Vec<T> {
- s.to_vec()
- }
-
- #[cfg(test)]
- fn from(s: &[T; N]) -> Vec<T> {
- crate::slice::to_vec(s, Global)
- }
-}
-
-#[cfg(not(no_global_oom_handling))]
-#[stable(feature = "vec_from_array_ref", since = "1.61.0")]
-impl<T: Clone, const N: usize> From<&mut [T; N]> for Vec<T> {
- /// Allocate a `Vec<T>` and fill it by cloning `s`'s items.
- ///
- /// # Examples
- ///
- /// ```
- /// assert_eq!(Vec::from(&mut [1, 2, 3]), vec![1, 2, 3]);
- /// ```
- #[cfg(not(test))]
- fn from(s: &mut [T; N]) -> Vec<T> {
- s.to_vec()
- }
-
- #[cfg(test)]
- fn from(s: &mut [T; N]) -> Vec<T> {
- crate::slice::to_vec(s, Global)
- }
-}
-
#[stable(feature = "vec_from_cow_slice", since = "1.14.0")]
impl<'a, T> From<Cow<'a, [T]>> for Vec<T>
where
unsafe {
self.append_elements(iterator.as_slice() as _);
}
- iterator.ptr = iterator.end;
+ iterator.forget_remaining_elements();
}
}
+++ /dev/null
-use std::collections::binary_heap::{Drain, PeekMut};
-use std::collections::BinaryHeap;
-use std::iter::TrustedLen;
-use std::panic::{catch_unwind, AssertUnwindSafe};
-use std::sync::atomic::{AtomicU32, Ordering};
-
-#[test]
-fn test_iterator() {
- let data = vec![5, 9, 3];
- let iterout = [9, 5, 3];
- let heap = BinaryHeap::from(data);
- let mut i = 0;
- for el in &heap {
- assert_eq!(*el, iterout[i]);
- i += 1;
- }
-}
-
-#[test]
-fn test_iter_rev_cloned_collect() {
- let data = vec![5, 9, 3];
- let iterout = vec![3, 5, 9];
- let pq = BinaryHeap::from(data);
-
- let v: Vec<_> = pq.iter().rev().cloned().collect();
- assert_eq!(v, iterout);
-}
-
-#[test]
-fn test_into_iter_collect() {
- let data = vec![5, 9, 3];
- let iterout = vec![9, 5, 3];
- let pq = BinaryHeap::from(data);
-
- let v: Vec<_> = pq.into_iter().collect();
- assert_eq!(v, iterout);
-}
-
-#[test]
-fn test_into_iter_size_hint() {
- let data = vec![5, 9];
- let pq = BinaryHeap::from(data);
-
- let mut it = pq.into_iter();
-
- assert_eq!(it.size_hint(), (2, Some(2)));
- assert_eq!(it.next(), Some(9));
-
- assert_eq!(it.size_hint(), (1, Some(1)));
- assert_eq!(it.next(), Some(5));
-
- assert_eq!(it.size_hint(), (0, Some(0)));
- assert_eq!(it.next(), None);
-}
-
-#[test]
-fn test_into_iter_rev_collect() {
- let data = vec![5, 9, 3];
- let iterout = vec![3, 5, 9];
- let pq = BinaryHeap::from(data);
-
- let v: Vec<_> = pq.into_iter().rev().collect();
- assert_eq!(v, iterout);
-}
-
-#[test]
-fn test_into_iter_sorted_collect() {
- let heap = BinaryHeap::from(vec![2, 4, 6, 2, 1, 8, 10, 3, 5, 7, 0, 9, 1]);
- let it = heap.into_iter_sorted();
- let sorted = it.collect::<Vec<_>>();
- assert_eq!(sorted, vec![10, 9, 8, 7, 6, 5, 4, 3, 2, 2, 1, 1, 0]);
-}
-
-#[test]
-fn test_drain_sorted_collect() {
- let mut heap = BinaryHeap::from(vec![2, 4, 6, 2, 1, 8, 10, 3, 5, 7, 0, 9, 1]);
- let it = heap.drain_sorted();
- let sorted = it.collect::<Vec<_>>();
- assert_eq!(sorted, vec![10, 9, 8, 7, 6, 5, 4, 3, 2, 2, 1, 1, 0]);
-}
-
-fn check_exact_size_iterator<I: ExactSizeIterator>(len: usize, it: I) {
- let mut it = it;
-
- for i in 0..it.len() {
- let (lower, upper) = it.size_hint();
- assert_eq!(Some(lower), upper);
- assert_eq!(lower, len - i);
- assert_eq!(it.len(), len - i);
- it.next();
- }
- assert_eq!(it.len(), 0);
- assert!(it.is_empty());
-}
-
-#[test]
-fn test_exact_size_iterator() {
- let heap = BinaryHeap::from(vec![2, 4, 6, 2, 1, 8, 10, 3, 5, 7, 0, 9, 1]);
- check_exact_size_iterator(heap.len(), heap.iter());
- check_exact_size_iterator(heap.len(), heap.clone().into_iter());
- check_exact_size_iterator(heap.len(), heap.clone().into_iter_sorted());
- check_exact_size_iterator(heap.len(), heap.clone().drain());
- check_exact_size_iterator(heap.len(), heap.clone().drain_sorted());
-}
-
-fn check_trusted_len<I: TrustedLen>(len: usize, it: I) {
- let mut it = it;
- for i in 0..len {
- let (lower, upper) = it.size_hint();
- if upper.is_some() {
- assert_eq!(Some(lower), upper);
- assert_eq!(lower, len - i);
- }
- it.next();
- }
-}
-
-#[test]
-fn test_trusted_len() {
- let heap = BinaryHeap::from(vec![2, 4, 6, 2, 1, 8, 10, 3, 5, 7, 0, 9, 1]);
- check_trusted_len(heap.len(), heap.clone().into_iter_sorted());
- check_trusted_len(heap.len(), heap.clone().drain_sorted());
-}
-
-#[test]
-fn test_peek_and_pop() {
- let data = vec![2, 4, 6, 2, 1, 8, 10, 3, 5, 7, 0, 9, 1];
- let mut sorted = data.clone();
- sorted.sort();
- let mut heap = BinaryHeap::from(data);
- while !heap.is_empty() {
- assert_eq!(heap.peek().unwrap(), sorted.last().unwrap());
- assert_eq!(heap.pop().unwrap(), sorted.pop().unwrap());
- }
-}
-
-#[test]
-fn test_peek_mut() {
- let data = vec![2, 4, 6, 2, 1, 8, 10, 3, 5, 7, 0, 9, 1];
- let mut heap = BinaryHeap::from(data);
- assert_eq!(heap.peek(), Some(&10));
- {
- let mut top = heap.peek_mut().unwrap();
- *top -= 2;
- }
- assert_eq!(heap.peek(), Some(&9));
-}
-
-#[test]
-fn test_peek_mut_pop() {
- let data = vec![2, 4, 6, 2, 1, 8, 10, 3, 5, 7, 0, 9, 1];
- let mut heap = BinaryHeap::from(data);
- assert_eq!(heap.peek(), Some(&10));
- {
- let mut top = heap.peek_mut().unwrap();
- *top -= 2;
- assert_eq!(PeekMut::pop(top), 8);
- }
- assert_eq!(heap.peek(), Some(&9));
-}
-
-#[test]
-fn test_push() {
- let mut heap = BinaryHeap::from(vec![2, 4, 9]);
- assert_eq!(heap.len(), 3);
- assert!(*heap.peek().unwrap() == 9);
- heap.push(11);
- assert_eq!(heap.len(), 4);
- assert!(*heap.peek().unwrap() == 11);
- heap.push(5);
- assert_eq!(heap.len(), 5);
- assert!(*heap.peek().unwrap() == 11);
- heap.push(27);
- assert_eq!(heap.len(), 6);
- assert!(*heap.peek().unwrap() == 27);
- heap.push(3);
- assert_eq!(heap.len(), 7);
- assert!(*heap.peek().unwrap() == 27);
- heap.push(103);
- assert_eq!(heap.len(), 8);
- assert!(*heap.peek().unwrap() == 103);
-}
-
-#[test]
-fn test_push_unique() {
- let mut heap = BinaryHeap::<Box<_>>::from(vec![box 2, box 4, box 9]);
- assert_eq!(heap.len(), 3);
- assert!(**heap.peek().unwrap() == 9);
- heap.push(box 11);
- assert_eq!(heap.len(), 4);
- assert!(**heap.peek().unwrap() == 11);
- heap.push(box 5);
- assert_eq!(heap.len(), 5);
- assert!(**heap.peek().unwrap() == 11);
- heap.push(box 27);
- assert_eq!(heap.len(), 6);
- assert!(**heap.peek().unwrap() == 27);
- heap.push(box 3);
- assert_eq!(heap.len(), 7);
- assert!(**heap.peek().unwrap() == 27);
- heap.push(box 103);
- assert_eq!(heap.len(), 8);
- assert!(**heap.peek().unwrap() == 103);
-}
-
-fn check_to_vec(mut data: Vec<i32>) {
- let heap = BinaryHeap::from(data.clone());
- let mut v = heap.clone().into_vec();
- v.sort();
- data.sort();
-
- assert_eq!(v, data);
- assert_eq!(heap.into_sorted_vec(), data);
-}
-
-#[test]
-fn test_to_vec() {
- check_to_vec(vec![]);
- check_to_vec(vec![5]);
- check_to_vec(vec![3, 2]);
- check_to_vec(vec![2, 3]);
- check_to_vec(vec![5, 1, 2]);
- check_to_vec(vec![1, 100, 2, 3]);
- check_to_vec(vec![1, 3, 5, 7, 9, 2, 4, 6, 8, 0]);
- check_to_vec(vec![2, 4, 6, 2, 1, 8, 10, 3, 5, 7, 0, 9, 1]);
- check_to_vec(vec![9, 11, 9, 9, 9, 9, 11, 2, 3, 4, 11, 9, 0, 0, 0, 0]);
- check_to_vec(vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
- check_to_vec(vec![10, 9, 8, 7, 6, 5, 4, 3, 2, 1, 0]);
- check_to_vec(vec![0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 1, 2]);
- check_to_vec(vec![5, 4, 3, 2, 1, 5, 4, 3, 2, 1, 5, 4, 3, 2, 1]);
-}
-
-#[test]
-fn test_in_place_iterator_specialization() {
- let src: Vec<usize> = vec![1, 2, 3];
- let src_ptr = src.as_ptr();
- let heap: BinaryHeap<_> = src.into_iter().map(std::convert::identity).collect();
- let heap_ptr = heap.iter().next().unwrap() as *const usize;
- assert_eq!(src_ptr, heap_ptr);
- let sink: Vec<_> = heap.into_iter().map(std::convert::identity).collect();
- let sink_ptr = sink.as_ptr();
- assert_eq!(heap_ptr, sink_ptr);
-}
-
-#[test]
-fn test_empty_pop() {
- let mut heap = BinaryHeap::<i32>::new();
- assert!(heap.pop().is_none());
-}
-
-#[test]
-fn test_empty_peek() {
- let empty = BinaryHeap::<i32>::new();
- assert!(empty.peek().is_none());
-}
-
-#[test]
-fn test_empty_peek_mut() {
- let mut empty = BinaryHeap::<i32>::new();
- assert!(empty.peek_mut().is_none());
-}
-
-#[test]
-fn test_from_iter() {
- let xs = vec![9, 8, 7, 6, 5, 4, 3, 2, 1];
-
- let mut q: BinaryHeap<_> = xs.iter().rev().cloned().collect();
-
- for &x in &xs {
- assert_eq!(q.pop().unwrap(), x);
- }
-}
-
-#[test]
-fn test_drain() {
- let mut q: BinaryHeap<_> = [9, 8, 7, 6, 5, 4, 3, 2, 1].iter().cloned().collect();
-
- assert_eq!(q.drain().take(5).count(), 5);
-
- assert!(q.is_empty());
-}
-
-#[test]
-fn test_drain_sorted() {
- let mut q: BinaryHeap<_> = [9, 8, 7, 6, 5, 4, 3, 2, 1].iter().cloned().collect();
-
- assert_eq!(q.drain_sorted().take(5).collect::<Vec<_>>(), vec![9, 8, 7, 6, 5]);
-
- assert!(q.is_empty());
-}
-
-#[test]
-fn test_drain_sorted_leak() {
- static DROPS: AtomicU32 = AtomicU32::new(0);
-
- #[derive(Clone, PartialEq, Eq, PartialOrd, Ord)]
- struct D(u32, bool);
-
- impl Drop for D {
- fn drop(&mut self) {
- DROPS.fetch_add(1, Ordering::SeqCst);
-
- if self.1 {
- panic!("panic in `drop`");
- }
- }
- }
-
- let mut q = BinaryHeap::from(vec![
- D(0, false),
- D(1, false),
- D(2, false),
- D(3, true),
- D(4, false),
- D(5, false),
- ]);
-
- catch_unwind(AssertUnwindSafe(|| drop(q.drain_sorted()))).ok();
-
- assert_eq!(DROPS.load(Ordering::SeqCst), 6);
-}
-
-#[test]
-fn test_extend_ref() {
- let mut a = BinaryHeap::new();
- a.push(1);
- a.push(2);
-
- a.extend(&[3, 4, 5]);
-
- assert_eq!(a.len(), 5);
- assert_eq!(a.into_sorted_vec(), [1, 2, 3, 4, 5]);
-
- let mut a = BinaryHeap::new();
- a.push(1);
- a.push(2);
- let mut b = BinaryHeap::new();
- b.push(3);
- b.push(4);
- b.push(5);
-
- a.extend(&b);
-
- assert_eq!(a.len(), 5);
- assert_eq!(a.into_sorted_vec(), [1, 2, 3, 4, 5]);
-}
-
-#[test]
-fn test_append() {
- let mut a = BinaryHeap::from(vec![-10, 1, 2, 3, 3]);
- let mut b = BinaryHeap::from(vec![-20, 5, 43]);
-
- a.append(&mut b);
-
- assert_eq!(a.into_sorted_vec(), [-20, -10, 1, 2, 3, 3, 5, 43]);
- assert!(b.is_empty());
-}
-
-#[test]
-fn test_append_to_empty() {
- let mut a = BinaryHeap::new();
- let mut b = BinaryHeap::from(vec![-20, 5, 43]);
-
- a.append(&mut b);
-
- assert_eq!(a.into_sorted_vec(), [-20, 5, 43]);
- assert!(b.is_empty());
-}
-
-#[test]
-fn test_extend_specialization() {
- let mut a = BinaryHeap::from(vec![-10, 1, 2, 3, 3]);
- let b = BinaryHeap::from(vec![-20, 5, 43]);
-
- a.extend(b);
-
- assert_eq!(a.into_sorted_vec(), [-20, -10, 1, 2, 3, 3, 5, 43]);
-}
-
-#[allow(dead_code)]
-fn assert_covariance() {
- fn drain<'new>(d: Drain<'static, &'static str>) -> Drain<'new, &'new str> {
- d
- }
-}
-
-#[test]
-fn test_retain() {
- let mut a = BinaryHeap::from(vec![100, 10, 50, 1, 2, 20, 30]);
- a.retain(|&x| x != 2);
-
- // Check that 20 moved into 10's place.
- assert_eq!(a.clone().into_vec(), [100, 20, 50, 1, 10, 30]);
-
- a.retain(|_| true);
-
- assert_eq!(a.clone().into_vec(), [100, 20, 50, 1, 10, 30]);
-
- a.retain(|&x| x < 50);
-
- assert_eq!(a.clone().into_vec(), [30, 20, 10, 1]);
-
- a.retain(|_| false);
-
- assert!(a.is_empty());
-}
-
-// old binaryheap failed this test
-//
-// Integrity means that all elements are present after a comparison panics,
-// even if the order might not be correct.
-//
-// Destructors must be called exactly once per element.
-// FIXME: re-enable emscripten once it can unwind again
-#[test]
-#[cfg(not(target_os = "emscripten"))]
-fn panic_safe() {
- use rand::{seq::SliceRandom, thread_rng};
- use std::cmp;
- use std::panic::{self, AssertUnwindSafe};
- use std::sync::atomic::{AtomicUsize, Ordering};
-
- static DROP_COUNTER: AtomicUsize = AtomicUsize::new(0);
-
- #[derive(Eq, PartialEq, Ord, Clone, Debug)]
- struct PanicOrd<T>(T, bool);
-
- impl<T> Drop for PanicOrd<T> {
- fn drop(&mut self) {
- // update global drop count
- DROP_COUNTER.fetch_add(1, Ordering::SeqCst);
- }
- }
-
- impl<T: PartialOrd> PartialOrd for PanicOrd<T> {
- fn partial_cmp(&self, other: &Self) -> Option<cmp::Ordering> {
- if self.1 || other.1 {
- panic!("Panicking comparison");
- }
- self.0.partial_cmp(&other.0)
- }
- }
- let mut rng = thread_rng();
- const DATASZ: usize = 32;
- // Miri is too slow
- let ntest = if cfg!(miri) { 1 } else { 10 };
-
- // don't use 0 in the data -- we want to catch the zeroed-out case.
- let data = (1..=DATASZ).collect::<Vec<_>>();
-
- // since it's a fuzzy test, run several tries.
- for _ in 0..ntest {
- for i in 1..=DATASZ {
- DROP_COUNTER.store(0, Ordering::SeqCst);
-
- let mut panic_ords: Vec<_> =
- data.iter().filter(|&&x| x != i).map(|&x| PanicOrd(x, false)).collect();
- let panic_item = PanicOrd(i, true);
-
- // heapify the sane items
- panic_ords.shuffle(&mut rng);
- let mut heap = BinaryHeap::from(panic_ords);
- let inner_data;
-
- {
- // push the panicking item to the heap and catch the panic
- let thread_result = {
- let mut heap_ref = AssertUnwindSafe(&mut heap);
- panic::catch_unwind(move || {
- heap_ref.push(panic_item);
- })
- };
- assert!(thread_result.is_err());
-
- // Assert no elements were dropped
- let drops = DROP_COUNTER.load(Ordering::SeqCst);
- assert!(drops == 0, "Must not drop items. drops={}", drops);
- inner_data = heap.clone().into_vec();
- drop(heap);
- }
- let drops = DROP_COUNTER.load(Ordering::SeqCst);
- assert_eq!(drops, DATASZ);
-
- let mut data_sorted = inner_data.into_iter().map(|p| p.0).collect::<Vec<_>>();
- data_sorted.sort();
- assert_eq!(data_sorted, data);
- }
- }
-}
use std::borrow::Cow::{Borrowed, Owned};
-use std::ffi::{c_char, CStr};
+use std::ffi::CStr;
+use std::os::raw::c_char;
#[test]
fn to_str() {
use std::hash::{Hash, Hasher};
mod arc;
-mod binary_heap;
mod borrow;
mod boxed;
mod btree_set_hash;
use std::collections::LinkedList;
-use std::panic::{catch_unwind, AssertUnwindSafe};
-
-#[test]
-fn test_basic() {
- let mut m = LinkedList::<Box<_>>::new();
- assert_eq!(m.pop_front(), None);
- assert_eq!(m.pop_back(), None);
- assert_eq!(m.pop_front(), None);
- m.push_front(box 1);
- assert_eq!(m.pop_front(), Some(box 1));
- m.push_back(box 2);
- m.push_back(box 3);
- assert_eq!(m.len(), 2);
- assert_eq!(m.pop_front(), Some(box 2));
- assert_eq!(m.pop_front(), Some(box 3));
- assert_eq!(m.len(), 0);
- assert_eq!(m.pop_front(), None);
- m.push_back(box 1);
- m.push_back(box 3);
- m.push_back(box 5);
- m.push_back(box 7);
- assert_eq!(m.pop_front(), Some(box 1));
-
- let mut n = LinkedList::new();
- n.push_front(2);
- n.push_front(3);
- {
- assert_eq!(n.front().unwrap(), &3);
- let x = n.front_mut().unwrap();
- assert_eq!(*x, 3);
- *x = 0;
- }
- {
- assert_eq!(n.back().unwrap(), &2);
- let y = n.back_mut().unwrap();
- assert_eq!(*y, 2);
- *y = 1;
- }
- assert_eq!(n.pop_front(), Some(0));
- assert_eq!(n.pop_front(), Some(1));
-}
-
-fn generate_test() -> LinkedList<i32> {
- list_from(&[0, 1, 2, 3, 4, 5, 6])
-}
-
-fn list_from<T: Clone>(v: &[T]) -> LinkedList<T> {
- v.iter().cloned().collect()
-}
-
-#[test]
-fn test_split_off() {
- // singleton
- {
- let mut m = LinkedList::new();
- m.push_back(1);
-
- let p = m.split_off(0);
- assert_eq!(m.len(), 0);
- assert_eq!(p.len(), 1);
- assert_eq!(p.back(), Some(&1));
- assert_eq!(p.front(), Some(&1));
- }
-
- // not singleton, forwards
- {
- let u = vec![1, 2, 3, 4, 5];
- let mut m = list_from(&u);
- let mut n = m.split_off(2);
- assert_eq!(m.len(), 2);
- assert_eq!(n.len(), 3);
- for elt in 1..3 {
- assert_eq!(m.pop_front(), Some(elt));
- }
- for elt in 3..6 {
- assert_eq!(n.pop_front(), Some(elt));
- }
- }
- // not singleton, backwards
- {
- let u = vec![1, 2, 3, 4, 5];
- let mut m = list_from(&u);
- let mut n = m.split_off(4);
- assert_eq!(m.len(), 4);
- assert_eq!(n.len(), 1);
- for elt in 1..5 {
- assert_eq!(m.pop_front(), Some(elt));
- }
- for elt in 5..6 {
- assert_eq!(n.pop_front(), Some(elt));
- }
- }
-
- // no-op on the last index
- {
- let mut m = LinkedList::new();
- m.push_back(1);
-
- let p = m.split_off(1);
- assert_eq!(m.len(), 1);
- assert_eq!(p.len(), 0);
- assert_eq!(m.back(), Some(&1));
- assert_eq!(m.front(), Some(&1));
- }
-}
-
-#[test]
-fn test_iterator() {
- let m = generate_test();
- for (i, elt) in m.iter().enumerate() {
- assert_eq!(i as i32, *elt);
- }
- let mut n = LinkedList::new();
- assert_eq!(n.iter().next(), None);
- n.push_front(4);
- let mut it = n.iter();
- assert_eq!(it.size_hint(), (1, Some(1)));
- assert_eq!(it.next().unwrap(), &4);
- assert_eq!(it.size_hint(), (0, Some(0)));
- assert_eq!(it.next(), None);
-}
-
-#[test]
-fn test_iterator_clone() {
- let mut n = LinkedList::new();
- n.push_back(2);
- n.push_back(3);
- n.push_back(4);
- let mut it = n.iter();
- it.next();
- let mut jt = it.clone();
- assert_eq!(it.next(), jt.next());
- assert_eq!(it.next_back(), jt.next_back());
- assert_eq!(it.next(), jt.next());
-}
-
-#[test]
-fn test_iterator_double_end() {
- let mut n = LinkedList::new();
- assert_eq!(n.iter().next(), None);
- n.push_front(4);
- n.push_front(5);
- n.push_front(6);
- let mut it = n.iter();
- assert_eq!(it.size_hint(), (3, Some(3)));
- assert_eq!(it.next().unwrap(), &6);
- assert_eq!(it.size_hint(), (2, Some(2)));
- assert_eq!(it.next_back().unwrap(), &4);
- assert_eq!(it.size_hint(), (1, Some(1)));
- assert_eq!(it.next_back().unwrap(), &5);
- assert_eq!(it.next_back(), None);
- assert_eq!(it.next(), None);
-}
-
-#[test]
-fn test_rev_iter() {
- let m = generate_test();
- for (i, elt) in m.iter().rev().enumerate() {
- assert_eq!((6 - i) as i32, *elt);
- }
- let mut n = LinkedList::new();
- assert_eq!(n.iter().rev().next(), None);
- n.push_front(4);
- let mut it = n.iter().rev();
- assert_eq!(it.size_hint(), (1, Some(1)));
- assert_eq!(it.next().unwrap(), &4);
- assert_eq!(it.size_hint(), (0, Some(0)));
- assert_eq!(it.next(), None);
-}
-
-#[test]
-fn test_mut_iter() {
- let mut m = generate_test();
- let mut len = m.len();
- for (i, elt) in m.iter_mut().enumerate() {
- assert_eq!(i as i32, *elt);
- len -= 1;
- }
- assert_eq!(len, 0);
- let mut n = LinkedList::new();
- assert!(n.iter_mut().next().is_none());
- n.push_front(4);
- n.push_back(5);
- let mut it = n.iter_mut();
- assert_eq!(it.size_hint(), (2, Some(2)));
- assert!(it.next().is_some());
- assert!(it.next().is_some());
- assert_eq!(it.size_hint(), (0, Some(0)));
- assert!(it.next().is_none());
-}
-
-#[test]
-fn test_iterator_mut_double_end() {
- let mut n = LinkedList::new();
- assert!(n.iter_mut().next_back().is_none());
- n.push_front(4);
- n.push_front(5);
- n.push_front(6);
- let mut it = n.iter_mut();
- assert_eq!(it.size_hint(), (3, Some(3)));
- assert_eq!(*it.next().unwrap(), 6);
- assert_eq!(it.size_hint(), (2, Some(2)));
- assert_eq!(*it.next_back().unwrap(), 4);
- assert_eq!(it.size_hint(), (1, Some(1)));
- assert_eq!(*it.next_back().unwrap(), 5);
- assert!(it.next_back().is_none());
- assert!(it.next().is_none());
-}
-
-#[test]
-fn test_mut_rev_iter() {
- let mut m = generate_test();
- for (i, elt) in m.iter_mut().rev().enumerate() {
- assert_eq!((6 - i) as i32, *elt);
- }
- let mut n = LinkedList::new();
- assert!(n.iter_mut().rev().next().is_none());
- n.push_front(4);
- let mut it = n.iter_mut().rev();
- assert!(it.next().is_some());
- assert!(it.next().is_none());
-}
-
-#[test]
-fn test_eq() {
- let mut n = list_from(&[]);
- let mut m = list_from(&[]);
- assert!(n == m);
- n.push_front(1);
- assert!(n != m);
- m.push_back(1);
- assert!(n == m);
-
- let n = list_from(&[2, 3, 4]);
- let m = list_from(&[1, 2, 3]);
- assert!(n != m);
-}
#[test]
fn test_hash() {
assert!(hash(&x) == hash(&y));
}
-
-#[test]
-fn test_ord() {
- let n = list_from(&[]);
- let m = list_from(&[1, 2, 3]);
- assert!(n < m);
- assert!(m > n);
- assert!(n <= n);
- assert!(n >= n);
-}
-
-#[test]
-fn test_ord_nan() {
- let nan = 0.0f64 / 0.0;
- let n = list_from(&[nan]);
- let m = list_from(&[nan]);
- assert!(!(n < m));
- assert!(!(n > m));
- assert!(!(n <= m));
- assert!(!(n >= m));
-
- let n = list_from(&[nan]);
- let one = list_from(&[1.0f64]);
- assert!(!(n < one));
- assert!(!(n > one));
- assert!(!(n <= one));
- assert!(!(n >= one));
-
- let u = list_from(&[1.0f64, 2.0, nan]);
- let v = list_from(&[1.0f64, 2.0, 3.0]);
- assert!(!(u < v));
- assert!(!(u > v));
- assert!(!(u <= v));
- assert!(!(u >= v));
-
- let s = list_from(&[1.0f64, 2.0, 4.0, 2.0]);
- let t = list_from(&[1.0f64, 2.0, 3.0, 2.0]);
- assert!(!(s < t));
- assert!(s > one);
- assert!(!(s <= one));
- assert!(s >= one);
-}
-
-#[test]
-fn test_show() {
- let list: LinkedList<_> = (0..10).collect();
- assert_eq!(format!("{list:?}"), "[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]");
-
- let list: LinkedList<_> = ["just", "one", "test", "more"].into_iter().collect();
- assert_eq!(format!("{list:?}"), "[\"just\", \"one\", \"test\", \"more\"]");
-}
-
-#[test]
-fn test_extend_ref() {
- let mut a = LinkedList::new();
- a.push_back(1);
-
- a.extend(&[2, 3, 4]);
-
- assert_eq!(a.len(), 4);
- assert_eq!(a, list_from(&[1, 2, 3, 4]));
-
- let mut b = LinkedList::new();
- b.push_back(5);
- b.push_back(6);
- a.extend(&b);
-
- assert_eq!(a.len(), 6);
- assert_eq!(a, list_from(&[1, 2, 3, 4, 5, 6]));
-}
-
-#[test]
-fn test_extend() {
- let mut a = LinkedList::new();
- a.push_back(1);
- a.extend(vec![2, 3, 4]); // uses iterator
-
- assert_eq!(a.len(), 4);
- assert!(a.iter().eq(&[1, 2, 3, 4]));
-
- let b: LinkedList<_> = [5, 6, 7].into_iter().collect();
- a.extend(b); // specializes to `append`
-
- assert_eq!(a.len(), 7);
- assert!(a.iter().eq(&[1, 2, 3, 4, 5, 6, 7]));
-}
-
-#[test]
-fn test_contains() {
- let mut l = LinkedList::new();
- l.extend(&[2, 3, 4]);
-
- assert!(l.contains(&3));
- assert!(!l.contains(&1));
-
- l.clear();
-
- assert!(!l.contains(&3));
-}
-
-#[test]
-fn drain_filter_empty() {
- let mut list: LinkedList<i32> = LinkedList::new();
-
- {
- let mut iter = list.drain_filter(|_| true);
- assert_eq!(iter.size_hint(), (0, Some(0)));
- assert_eq!(iter.next(), None);
- assert_eq!(iter.size_hint(), (0, Some(0)));
- assert_eq!(iter.next(), None);
- assert_eq!(iter.size_hint(), (0, Some(0)));
- }
-
- assert_eq!(list.len(), 0);
- assert_eq!(list.into_iter().collect::<Vec<_>>(), vec![]);
-}
-
-#[test]
-fn drain_filter_zst() {
- let mut list: LinkedList<_> = [(), (), (), (), ()].into_iter().collect();
- let initial_len = list.len();
- let mut count = 0;
-
- {
- let mut iter = list.drain_filter(|_| true);
- assert_eq!(iter.size_hint(), (0, Some(initial_len)));
- while let Some(_) = iter.next() {
- count += 1;
- assert_eq!(iter.size_hint(), (0, Some(initial_len - count)));
- }
- assert_eq!(iter.size_hint(), (0, Some(0)));
- assert_eq!(iter.next(), None);
- assert_eq!(iter.size_hint(), (0, Some(0)));
- }
-
- assert_eq!(count, initial_len);
- assert_eq!(list.len(), 0);
- assert_eq!(list.into_iter().collect::<Vec<_>>(), vec![]);
-}
-
-#[test]
-fn drain_filter_false() {
- let mut list: LinkedList<_> = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].into_iter().collect();
-
- let initial_len = list.len();
- let mut count = 0;
-
- {
- let mut iter = list.drain_filter(|_| false);
- assert_eq!(iter.size_hint(), (0, Some(initial_len)));
- for _ in iter.by_ref() {
- count += 1;
- }
- assert_eq!(iter.size_hint(), (0, Some(0)));
- assert_eq!(iter.next(), None);
- assert_eq!(iter.size_hint(), (0, Some(0)));
- }
-
- assert_eq!(count, 0);
- assert_eq!(list.len(), initial_len);
- assert_eq!(list.into_iter().collect::<Vec<_>>(), vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]);
-}
-
-#[test]
-fn drain_filter_true() {
- let mut list: LinkedList<_> = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10].into_iter().collect();
-
- let initial_len = list.len();
- let mut count = 0;
-
- {
- let mut iter = list.drain_filter(|_| true);
- assert_eq!(iter.size_hint(), (0, Some(initial_len)));
- while let Some(_) = iter.next() {
- count += 1;
- assert_eq!(iter.size_hint(), (0, Some(initial_len - count)));
- }
- assert_eq!(iter.size_hint(), (0, Some(0)));
- assert_eq!(iter.next(), None);
- assert_eq!(iter.size_hint(), (0, Some(0)));
- }
-
- assert_eq!(count, initial_len);
- assert_eq!(list.len(), 0);
- assert_eq!(list.into_iter().collect::<Vec<_>>(), vec![]);
-}
-
-#[test]
-fn drain_filter_complex() {
- {
- // [+xxx++++++xxxxx++++x+x++]
- let mut list = [
- 1, 2, 4, 6, 7, 9, 11, 13, 15, 17, 18, 20, 22, 24, 26, 27, 29, 31, 33, 34, 35, 36, 37,
- 39,
- ]
- .into_iter()
- .collect::<LinkedList<_>>();
-
- let removed = list.drain_filter(|x| *x % 2 == 0).collect::<Vec<_>>();
- assert_eq!(removed.len(), 10);
- assert_eq!(removed, vec![2, 4, 6, 18, 20, 22, 24, 26, 34, 36]);
-
- assert_eq!(list.len(), 14);
- assert_eq!(
- list.into_iter().collect::<Vec<_>>(),
- vec![1, 7, 9, 11, 13, 15, 17, 27, 29, 31, 33, 35, 37, 39]
- );
- }
-
- {
- // [xxx++++++xxxxx++++x+x++]
- let mut list =
- [2, 4, 6, 7, 9, 11, 13, 15, 17, 18, 20, 22, 24, 26, 27, 29, 31, 33, 34, 35, 36, 37, 39]
- .into_iter()
- .collect::<LinkedList<_>>();
-
- let removed = list.drain_filter(|x| *x % 2 == 0).collect::<Vec<_>>();
- assert_eq!(removed.len(), 10);
- assert_eq!(removed, vec![2, 4, 6, 18, 20, 22, 24, 26, 34, 36]);
-
- assert_eq!(list.len(), 13);
- assert_eq!(
- list.into_iter().collect::<Vec<_>>(),
- vec![7, 9, 11, 13, 15, 17, 27, 29, 31, 33, 35, 37, 39]
- );
- }
-
- {
- // [xxx++++++xxxxx++++x+x]
- let mut list =
- [2, 4, 6, 7, 9, 11, 13, 15, 17, 18, 20, 22, 24, 26, 27, 29, 31, 33, 34, 35, 36]
- .into_iter()
- .collect::<LinkedList<_>>();
-
- let removed = list.drain_filter(|x| *x % 2 == 0).collect::<Vec<_>>();
- assert_eq!(removed.len(), 10);
- assert_eq!(removed, vec![2, 4, 6, 18, 20, 22, 24, 26, 34, 36]);
-
- assert_eq!(list.len(), 11);
- assert_eq!(
- list.into_iter().collect::<Vec<_>>(),
- vec![7, 9, 11, 13, 15, 17, 27, 29, 31, 33, 35]
- );
- }
-
- {
- // [xxxxxxxxxx+++++++++++]
- let mut list = [2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 1, 3, 5, 7, 9, 11, 13, 15, 17, 19]
- .into_iter()
- .collect::<LinkedList<_>>();
-
- let removed = list.drain_filter(|x| *x % 2 == 0).collect::<Vec<_>>();
- assert_eq!(removed.len(), 10);
- assert_eq!(removed, vec![2, 4, 6, 8, 10, 12, 14, 16, 18, 20]);
-
- assert_eq!(list.len(), 10);
- assert_eq!(list.into_iter().collect::<Vec<_>>(), vec![1, 3, 5, 7, 9, 11, 13, 15, 17, 19]);
- }
-
- {
- // [+++++++++++xxxxxxxxxx]
- let mut list = [1, 3, 5, 7, 9, 11, 13, 15, 17, 19, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20]
- .into_iter()
- .collect::<LinkedList<_>>();
-
- let removed = list.drain_filter(|x| *x % 2 == 0).collect::<Vec<_>>();
- assert_eq!(removed.len(), 10);
- assert_eq!(removed, vec![2, 4, 6, 8, 10, 12, 14, 16, 18, 20]);
-
- assert_eq!(list.len(), 10);
- assert_eq!(list.into_iter().collect::<Vec<_>>(), vec![1, 3, 5, 7, 9, 11, 13, 15, 17, 19]);
- }
-}
-
-#[test]
-fn drain_filter_drop_panic_leak() {
- static mut DROPS: i32 = 0;
-
- struct D(bool);
-
- impl Drop for D {
- fn drop(&mut self) {
- unsafe {
- DROPS += 1;
- }
-
- if self.0 {
- panic!("panic in `drop`");
- }
- }
- }
-
- let mut q = LinkedList::new();
- q.push_back(D(false));
- q.push_back(D(false));
- q.push_back(D(false));
- q.push_back(D(false));
- q.push_back(D(false));
- q.push_front(D(false));
- q.push_front(D(true));
- q.push_front(D(false));
-
- catch_unwind(AssertUnwindSafe(|| drop(q.drain_filter(|_| true)))).ok();
-
- assert_eq!(unsafe { DROPS }, 8);
- assert!(q.is_empty());
-}
-
-#[test]
-fn drain_filter_pred_panic_leak() {
- static mut DROPS: i32 = 0;
-
- #[derive(Debug)]
- struct D(u32);
-
- impl Drop for D {
- fn drop(&mut self) {
- unsafe {
- DROPS += 1;
- }
- }
- }
-
- let mut q = LinkedList::new();
- q.push_back(D(3));
- q.push_back(D(4));
- q.push_back(D(5));
- q.push_back(D(6));
- q.push_back(D(7));
- q.push_front(D(2));
- q.push_front(D(1));
- q.push_front(D(0));
-
- catch_unwind(AssertUnwindSafe(|| {
- drop(q.drain_filter(|item| if item.0 >= 2 { panic!() } else { true }))
- }))
- .ok();
-
- assert_eq!(unsafe { DROPS }, 2); // 0 and 1
- assert_eq!(q.len(), 6);
-}
-
-#[test]
-fn test_drop() {
- static mut DROPS: i32 = 0;
- struct Elem;
- impl Drop for Elem {
- fn drop(&mut self) {
- unsafe {
- DROPS += 1;
- }
- }
- }
-
- let mut ring = LinkedList::new();
- ring.push_back(Elem);
- ring.push_front(Elem);
- ring.push_back(Elem);
- ring.push_front(Elem);
- drop(ring);
-
- assert_eq!(unsafe { DROPS }, 4);
-}
-
-#[test]
-fn test_drop_with_pop() {
- static mut DROPS: i32 = 0;
- struct Elem;
- impl Drop for Elem {
- fn drop(&mut self) {
- unsafe {
- DROPS += 1;
- }
- }
- }
-
- let mut ring = LinkedList::new();
- ring.push_back(Elem);
- ring.push_front(Elem);
- ring.push_back(Elem);
- ring.push_front(Elem);
-
- drop(ring.pop_back());
- drop(ring.pop_front());
- assert_eq!(unsafe { DROPS }, 2);
-
- drop(ring);
- assert_eq!(unsafe { DROPS }, 4);
-}
-
-#[test]
-fn test_drop_clear() {
- static mut DROPS: i32 = 0;
- struct Elem;
- impl Drop for Elem {
- fn drop(&mut self) {
- unsafe {
- DROPS += 1;
- }
- }
- }
-
- let mut ring = LinkedList::new();
- ring.push_back(Elem);
- ring.push_front(Elem);
- ring.push_back(Elem);
- ring.push_front(Elem);
- ring.clear();
- assert_eq!(unsafe { DROPS }, 4);
-
- drop(ring);
- assert_eq!(unsafe { DROPS }, 4);
-}
-
-#[test]
-fn test_drop_panic() {
- static mut DROPS: i32 = 0;
-
- struct D(bool);
-
- impl Drop for D {
- fn drop(&mut self) {
- unsafe {
- DROPS += 1;
- }
-
- if self.0 {
- panic!("panic in `drop`");
- }
- }
- }
-
- let mut q = LinkedList::new();
- q.push_back(D(false));
- q.push_back(D(false));
- q.push_back(D(false));
- q.push_back(D(false));
- q.push_back(D(false));
- q.push_front(D(false));
- q.push_front(D(false));
- q.push_front(D(true));
-
- catch_unwind(move || drop(q)).ok();
-
- assert_eq!(unsafe { DROPS }, 8);
-}
fn bench_lt(b: &mut Bencher) {
b.iter(|| (0..100000).map(black_box).lt((0..100000).map(black_box)))
}
+
+#[bench]
+fn bench_trusted_random_access_adapters(b: &mut Bencher) {
+ let vec1: Vec<_> = (0usize..100000).collect();
+ let vec2 = black_box(vec1.clone());
+ b.iter(|| {
+ let mut iter = vec1
+ .iter()
+ .copied()
+ .enumerate()
+ .map(|(idx, e)| idx.wrapping_add(e))
+ .zip(vec2.iter().copied())
+ .map(|(a, b)| a.wrapping_add(b))
+ .fuse();
+ let mut acc: usize = 0;
+ let size = iter.size();
+ for i in 0..size {
+ // SAFETY: TRA requirements are satisfied by 0..size iteration and then dropping the
+ // iterator.
+ acc = acc.wrapping_add(unsafe { iter.__iterator_get_unchecked(i) });
+ }
+ acc
+ })
+}
#![feature(flt2dec)]
#![feature(int_log)]
#![feature(test)]
+#![feature(trusted_random_access)]
extern crate test;
// implements the unary operator "op &T"
// based on "op T" where T is expected to be `Copy`able
macro_rules! forward_ref_unop {
- (impl $imp:ident, $method:ident for $t:ty) => {
- forward_ref_unop!(impl $imp, $method for $t,
- #[stable(feature = "rust1", since = "1.0.0")]);
- };
(impl const $imp:ident, $method:ident for $t:ty) => {
forward_ref_unop!(impl const $imp, $method for $t,
#[stable(feature = "rust1", since = "1.0.0")]);
// implements binary operators "&T op U", "T op &U", "&T op &U"
// based on "T op U" where T and U are expected to be `Copy`able
macro_rules! forward_ref_binop {
- (impl $imp:ident, $method:ident for $t:ty, $u:ty) => {
- forward_ref_binop!(impl $imp, $method for $t, $u,
- #[stable(feature = "rust1", since = "1.0.0")]);
- };
(impl const $imp:ident, $method:ident for $t:ty, $u:ty) => {
forward_ref_binop!(impl const $imp, $method for $t, $u,
#[stable(feature = "rust1", since = "1.0.0")]);
}
};
- // match if/else chains lacking a final `else`
- (
- if #[cfg( $i_meta:meta )] { $( $i_tokens:tt )* }
- $(
- else if #[cfg( $e_meta:meta )] { $( $e_tokens:tt )* }
- )*
- ) => {
- cfg_if! {
- @__items () ;
- (( $i_meta ) ( $( $i_tokens )* )) ,
- $(
- (( $e_meta ) ( $( $e_tokens )* )) ,
- )*
- }
- };
-
// Internal and recursive macro to emit all the items
//
// Collects all the previous cfgs in a list at the beginning, so they can be
#[rustc_inherit_overflow_checks]
#[doc(hidden)]
+ #[inline]
unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> <Self as Iterator>::Item
where
Self: TrustedRandomAccessNoCoerce,
}
#[doc(hidden)]
+ #[inline]
unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> B
where
Self: TrustedRandomAccessNoCoerce,
///
/// Same requirements calling `get_unchecked` directly.
#[doc(hidden)]
+#[inline]
pub(in crate::iter::adapters) unsafe fn try_get_unchecked<I>(it: &mut I, idx: usize) -> I::Item
where
I: Iterator,
}
unsafe impl<I: Iterator + TrustedRandomAccessNoCoerce> SpecTrustedRandomAccess for I {
+ #[inline]
unsafe fn try_get_unchecked(&mut self, index: usize) -> Self::Item {
// SAFETY: the caller must uphold the contract for
// `Iterator::__iterator_get_unchecked`.
/// use std::iter;
///
/// // let's assume we have some value of a type that is not `Clone`
-/// // or which don't want to have in memory just yet because it is expensive:
+/// // or which we don't want to have in memory just yet because it is expensive:
/// #[derive(PartialEq, Debug)]
/// struct Expensive;
///
#[rustc_on_unimplemented(
on(
_Self = "[{A}]",
- message = "a value of type `{Self}` cannot be built since `{Self}` has no definite size",
+ message = "a slice of type `{Self}` cannot be built since `{Self}` has no definite size",
label = "try explicitly collecting into a `Vec<{A}>`",
),
on(
- all(
- A = "{integer}",
- any(
- _Self = "[i8]",
- _Self = "[i16]",
- _Self = "[i32]",
- _Self = "[i64]",
- _Self = "[i128]",
- _Self = "[isize]",
- _Self = "[u8]",
- _Self = "[u16]",
- _Self = "[u32]",
- _Self = "[u64]",
- _Self = "[u128]",
- _Self = "[usize]"
- )
- ),
- message = "a value of type `{Self}` cannot be built since `{Self}` has no definite size",
+ all(A = "{integer}", any(_Self = "[{integral}]",)),
+ message = "a slice of type `{Self}` cannot be built since `{Self}` has no definite size",
label = "try explicitly collecting into a `Vec<{A}>`",
),
+ on(
+ _Self = "[{A}; _]",
+ message = "an array of type `{Self}` cannot be built directly from an iterator",
+ label = "try collecting into a `Vec<{A}>`, then using `.try_into()`",
+ ),
+ on(
+ all(A = "{integer}", any(_Self = "[{integral}; _]",)),
+ message = "an array of type `{Self}` cannot be built directly from an iterator",
+ label = "try collecting into a `Vec<{A}>`, then using `.try_into()`",
+ ),
message = "a value of type `{Self}` cannot be built from an iterator \
over elements of type `{A}`",
label = "value of type `{Self}` cannot be built from `std::iter::Iterator<Item={A}>`"
fn into_iter(self) -> Self::IntoIter;
}
+#[rustc_const_unstable(feature = "const_intoiterator_identity", issue = "90603")]
#[stable(feature = "rust1", since = "1.0.0")]
-impl<I: Iterator> IntoIterator for I {
+impl<I: ~const Iterator> const IntoIterator for I {
type Item = I::Item;
type IntoIter = I;
///
/// Like `panic!`, this macro has a second form for displaying custom values.
///
+/// [`todo!`]: crate::todo
+///
/// # Examples
///
/// Say we have a trait `Foo`:
#[inline]
#[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
pub(crate) const fn abs_private(self) -> f32 {
- f32::from_bits(self.to_bits() & 0x7fff_ffff)
+ // SAFETY: This transmutation is fine. Probably. For the reasons std is using it.
+ unsafe { mem::transmute::<u32, f32>(mem::transmute::<f32, u32>(self) & 0x7fff_ffff) }
}
/// Returns `true` if this value is positive infinity or negative infinity, and
#[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
#[inline]
pub const fn is_infinite(self) -> bool {
- self.abs_private() == Self::INFINITY
+ // Getting clever with transmutation can result in incorrect answers on some FPUs
+ // FIXME: alter the Rust <-> Rust calling convention to prevent this problem.
+ // See https://github.com/rust-lang/rust/issues/72327
+ (self == f32::INFINITY) | (self == f32::NEG_INFINITY)
}
/// Returns `true` if this number is neither infinite nor `NaN`.
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
pub const fn classify(self) -> FpCategory {
+ // A previous implementation tried to only use bitmask-based checks,
+ // using f32::to_bits to transmute the float to its bit repr and match on that.
+ // Unfortunately, floating point numbers can be much worse than that.
+ // This also needs to not result in recursive evaluations of f64::to_bits.
+ //
+ // On some processors, in some cases, LLVM will "helpfully" lower floating point ops,
+ // in spite of a request for them using f32 and f64, to things like x87 operations.
+ // These have an f64's mantissa, but can have a larger than normal exponent.
+ // FIXME(jubilee): Using x87 operations is never necessary in order to function
+ // on x86 processors for Rust-to-Rust calls, so this issue should not happen.
+ // Code generation should be adjusted to use non-C calling conventions, avoiding this.
+ //
+ if self.is_infinite() {
+ // Thus, a value may compare unequal to infinity, despite having a "full" exponent mask.
+ FpCategory::Infinite
+ } else if self.is_nan() {
+ // And it may not be NaN, as it can simply be an "overextended" finite value.
+ FpCategory::Nan
+ } else {
+ // However, std can't simply compare to zero to check for zero, either,
+ // as correctness requires avoiding equality tests that may be Subnormal == -0.0
+ // because it may be wrong under "denormals are zero" and "flush to zero" modes.
+ // Most of std's targets don't use those, but they are used for thumbv7neon.
+ // So, this does use bitpattern matching for the rest.
+
+ // SAFETY: f32 to u32 is fine. Usually.
+ // If classify has gotten this far, the value is definitely in one of these categories.
+ unsafe { f32::partial_classify(self) }
+ }
+ }
+
+ // This doesn't actually return a right answer for NaN on purpose,
+ // seeing as how it cannot correctly discern between a floating point NaN,
+ // and some normal floating point numbers truncated from an x87 FPU.
+ // FIXME(jubilee): This probably could at least answer things correctly for Infinity,
+ // like the f64 version does, but I need to run more checks on how things go on x86.
+ // I fear losing mantissa data that would have answered that differently.
+ //
+ // # Safety
+ // This requires making sure you call this function for values it answers correctly on,
+ // otherwise it returns a wrong answer. This is not important for memory safety per se,
+ // but getting floats correct is important for not accidentally leaking const eval
+ // runtime-deviating logic which may or may not be acceptable.
+ #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
+ const unsafe fn partial_classify(self) -> FpCategory {
const EXP_MASK: u32 = 0x7f800000;
const MAN_MASK: u32 = 0x007fffff;
- let bits = self.to_bits();
- match (bits & MAN_MASK, bits & EXP_MASK) {
+ // SAFETY: The caller is not asking questions for which this will tell lies.
+ let b = unsafe { mem::transmute::<f32, u32>(self) };
+ match (b & MAN_MASK, b & EXP_MASK) {
(0, 0) => FpCategory::Zero,
(_, 0) => FpCategory::Subnormal,
+ _ => FpCategory::Normal,
+ }
+ }
+
+ // This operates on bits, and only bits, so it can ignore concerns about weird FPUs.
+ // FIXME(jubilee): In a just world, this would be the entire impl for classify,
+ // plus a transmute. We do not live in a just world, but we can make it more so.
+ #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
+ const fn classify_bits(b: u32) -> FpCategory {
+ const EXP_MASK: u32 = 0x7f800000;
+ const MAN_MASK: u32 = 0x007fffff;
+
+ match (b & MAN_MASK, b & EXP_MASK) {
(0, EXP_MASK) => FpCategory::Infinite,
(_, EXP_MASK) => FpCategory::Nan,
+ (0, 0) => FpCategory::Zero,
+ (_, 0) => FpCategory::Subnormal,
_ => FpCategory::Normal,
}
}
pub const fn is_sign_negative(self) -> bool {
// IEEE754 says: isSignMinus(x) is true if and only if x has negative sign. isSignMinus
// applies to zeros and NaNs as well.
- self.to_bits() & 0x8000_0000 != 0
+ // SAFETY: This is just transmuting to get the sign bit, it's fine.
+ unsafe { mem::transmute::<f32, u32>(self) & 0x8000_0000 != 0 }
}
/// Takes the reciprocal (inverse) of a number, `1/x`.
#[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
#[inline]
pub const fn to_bits(self) -> u32 {
- // SAFETY: `u32` is a plain old datatype so we can always transmute to it
- unsafe { mem::transmute(self) }
+ // SAFETY: `u32` is a plain old datatype so we can always transmute to it.
+ // ...sorta.
+ //
+ // It turns out that at runtime, it is possible for a floating point number
+ // to be subject to a floating point mode that alters nonzero subnormal numbers
+ // to zero on reads and writes, aka "denormals are zero" and "flush to zero".
+ // This is not a problem per se, but at least one tier2 platform for Rust
+ // actually exhibits this behavior by default.
+ //
+ // In addition, on x86 targets with SSE or SSE2 disabled and the x87 FPU enabled,
+ // i.e. not soft-float, the way Rust does parameter passing can actually alter
+ // a number that is "not infinity" to have the same exponent as infinity,
+ // in a slightly unpredictable manner.
+ //
+ // And, of course evaluating to a NaN value is fairly nondeterministic.
+ // More precisely: when NaN should be returned is knowable, but which NaN?
+ // So far that's defined by a combination of LLVM and the CPU, not Rust.
+ // This function, however, allows observing the bitstring of a NaN,
+ // thus introspection on CTFE.
+ //
+ // In order to preserve, at least for the moment, const-to-runtime equivalence,
+ // we reject any of these possible situations from happening.
+ #[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
+ const fn ct_f32_to_u32(ct: f32) -> u32 {
+ match ct.classify() {
+ FpCategory::Nan => {
+ panic!("const-eval error: cannot use f32::to_bits on a NaN")
+ }
+ FpCategory::Subnormal => {
+ panic!("const-eval error: cannot use f32::to_bits on a subnormal number")
+ }
+ FpCategory::Infinite | FpCategory::Normal | FpCategory::Zero => {
+ // SAFETY: We have a normal floating point number. Now we transmute, i.e. do a bitcopy.
+ unsafe { mem::transmute::<f32, u32>(ct) }
+ }
+ }
+ }
+ // SAFETY: `u32` is a plain old datatype so we can always... uh...
+ // ...look, just pretend you forgot what you just read.
+ // Stability concerns.
+ let rt_f32_to_u32 = |rt| unsafe { mem::transmute::<f32, u32>(rt) };
+ // SAFETY: We use internal implementations that either always work or fail at compile time.
+ unsafe { intrinsics::const_eval_select((self,), ct_f32_to_u32, rt_f32_to_u32) }
}
/// Raw transmutation from `u32`.
#[must_use]
#[inline]
pub const fn from_bits(v: u32) -> Self {
- // SAFETY: `u32` is a plain old datatype so we can always transmute from it
// It turns out the safety issues with sNaN were overblown! Hooray!
- unsafe { mem::transmute(v) }
+ // SAFETY: `u32` is a plain old datatype so we can always transmute from it
+ // ...sorta.
+ //
+ // It turns out that at runtime, it is possible for a floating point number
+ // to be subject to floating point modes that alter nonzero subnormal numbers
+ // to zero on reads and writes, aka "denormals are zero" and "flush to zero".
+ // This is not a problem usually, but at least one tier2 platform for Rust
+ // actually exhibits this behavior by default: thumbv7neon
+ // aka "the Neon FPU in AArch32 state"
+ //
+ // In addition, on x86 targets with SSE or SSE2 disabled and the x87 FPU enabled,
+ // i.e. not soft-float, the way Rust does parameter passing can actually alter
+ // a number that is "not infinity" to have the same exponent as infinity,
+ // in a slightly unpredictable manner.
+ //
+ // And, of course evaluating to a NaN value is fairly nondeterministic.
+ // More precisely: when NaN should be returned is knowable, but which NaN?
+ // So far that's defined by a combination of LLVM and the CPU, not Rust.
+ // This function, however, allows observing the bitstring of a NaN,
+ // thus introspection on CTFE.
+ //
+ // In order to preserve, at least for the moment, const-to-runtime equivalence,
+ // reject any of these possible situations from happening.
+ #[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
+ const fn ct_u32_to_f32(ct: u32) -> f32 {
+ match f32::classify_bits(ct) {
+ FpCategory::Subnormal => {
+ panic!("const-eval error: cannot use f32::from_bits on a subnormal number")
+ }
+ FpCategory::Nan => {
+ panic!("const-eval error: cannot use f32::from_bits on NaN")
+ }
+ FpCategory::Infinite | FpCategory::Normal | FpCategory::Zero => {
+ // SAFETY: It's not a frumious number
+ unsafe { mem::transmute::<u32, f32>(ct) }
+ }
+ }
+ }
+ // SAFETY: `u32` is a plain old datatype so we can always... uh...
+ // ...look, just pretend you forgot what you just read.
+ // Stability concerns.
+ let rt_u32_to_f32 = |rt| unsafe { mem::transmute::<u32, f32>(rt) };
+ // SAFETY: We use internal implementations that either always work or fail at compile time.
+ unsafe { intrinsics::const_eval_select((v,), ct_u32_to_f32, rt_u32_to_f32) }
}
/// Return the memory representation of this floating point number as a byte array in
#[inline]
#[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
pub(crate) const fn abs_private(self) -> f64 {
- f64::from_bits(self.to_bits() & 0x7fff_ffff_ffff_ffff)
+ // SAFETY: This transmutation is fine. Probably. For the reasons std is using it.
+ unsafe {
+ mem::transmute::<u64, f64>(mem::transmute::<f64, u64>(self) & 0x7fff_ffff_ffff_ffff)
+ }
}
/// Returns `true` if this value is positive infinity or negative infinity, and
#[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
#[inline]
pub const fn is_infinite(self) -> bool {
- self.abs_private() == Self::INFINITY
+ // Getting clever with transmutation can result in incorrect answers on some FPUs
+ // FIXME: alter the Rust <-> Rust calling convention to prevent this problem.
+ // See https://github.com/rust-lang/rust/issues/72327
+ (self == f64::INFINITY) | (self == f64::NEG_INFINITY)
}
/// Returns `true` if this number is neither infinite nor `NaN`.
#[stable(feature = "rust1", since = "1.0.0")]
#[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
pub const fn classify(self) -> FpCategory {
+ // A previous implementation tried to only use bitmask-based checks,
+ // using f64::to_bits to transmute the float to its bit repr and match on that.
+ // Unfortunately, floating point numbers can be much worse than that.
+ // This also needs to not result in recursive evaluations of f64::to_bits.
+ //
+ // On some processors, in some cases, LLVM will "helpfully" lower floating point ops,
+ // in spite of a request for them using f32 and f64, to things like x87 operations.
+ // These have an f64's mantissa, but can have a larger than normal exponent.
+ // FIXME(jubilee): Using x87 operations is never necessary in order to function
+ // on x86 processors for Rust-to-Rust calls, so this issue should not happen.
+ // Code generation should be adjusted to use non-C calling conventions, avoiding this.
+ //
+ // Thus, a value may compare unequal to infinity, despite having a "full" exponent mask.
+ // And it may not be NaN, as it can simply be an "overextended" finite value.
+ if self.is_nan() {
+ FpCategory::Nan
+ } else {
+ // However, std can't simply compare to zero to check for zero, either,
+ // as correctness requires avoiding equality tests that may be Subnormal == -0.0
+ // because it may be wrong under "denormals are zero" and "flush to zero" modes.
+ // Most of std's targets don't use those, but they are used for thumbv7neon.
+ // So, this does use bitpattern matching for the rest.
+
+ // SAFETY: f64 to u64 is fine. Usually.
+ // If control flow has gotten this far, the value is definitely in one of the categories
+ // that f64::partial_classify can correctly analyze.
+ unsafe { f64::partial_classify(self) }
+ }
+ }
+
+ // This doesn't actually return a right answer for NaN on purpose,
+ // seeing as how it cannot correctly discern between a floating point NaN,
+ // and some normal floating point numbers truncated from an x87 FPU.
+ #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
+ const unsafe fn partial_classify(self) -> FpCategory {
const EXP_MASK: u64 = 0x7ff0000000000000;
const MAN_MASK: u64 = 0x000fffffffffffff;
- let bits = self.to_bits();
- match (bits & MAN_MASK, bits & EXP_MASK) {
+ // SAFETY: The caller is not asking questions for which this will tell lies.
+ let b = unsafe { mem::transmute::<f64, u64>(self) };
+ match (b & MAN_MASK, b & EXP_MASK) {
+ (0, EXP_MASK) => FpCategory::Infinite,
(0, 0) => FpCategory::Zero,
(_, 0) => FpCategory::Subnormal,
+ _ => FpCategory::Normal,
+ }
+ }
+
+ // This operates on bits, and only bits, so it can ignore concerns about weird FPUs.
+ // FIXME(jubilee): In a just world, this would be the entire impl for classify,
+ // plus a transmute. We do not live in a just world, but we can make it more so.
+ #[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
+ const fn classify_bits(b: u64) -> FpCategory {
+ const EXP_MASK: u64 = 0x7ff0000000000000;
+ const MAN_MASK: u64 = 0x000fffffffffffff;
+
+ match (b & MAN_MASK, b & EXP_MASK) {
(0, EXP_MASK) => FpCategory::Infinite,
(_, EXP_MASK) => FpCategory::Nan,
+ (0, 0) => FpCategory::Zero,
+ (_, 0) => FpCategory::Subnormal,
_ => FpCategory::Normal,
}
}
#[rustc_const_unstable(feature = "const_float_classify", issue = "72505")]
#[inline]
pub const fn is_sign_negative(self) -> bool {
- self.to_bits() & 0x8000_0000_0000_0000 != 0
+ // IEEE754 says: isSignMinus(x) is true if and only if x has negative sign. isSignMinus
+ // applies to zeros and NaNs as well.
+ // SAFETY: This is just transmuting to get the sign bit, it's fine.
+ unsafe { mem::transmute::<f64, u64>(self) & 0x8000_0000_0000_0000 != 0 }
}
#[must_use]
#[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
#[inline]
pub const fn to_bits(self) -> u64 {
- // SAFETY: `u64` is a plain old datatype so we can always transmute to it
- unsafe { mem::transmute(self) }
+ // SAFETY: `u64` is a plain old datatype so we can always transmute to it.
+ // ...sorta.
+ //
+ // See the SAFETY comment in f64::from_bits for more.
+ #[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
+ const fn ct_f64_to_u64(ct: f64) -> u64 {
+ match ct.classify() {
+ FpCategory::Nan => {
+ panic!("const-eval error: cannot use f64::to_bits on a NaN")
+ }
+ FpCategory::Subnormal => {
+ panic!("const-eval error: cannot use f64::to_bits on a subnormal number")
+ }
+ FpCategory::Infinite | FpCategory::Normal | FpCategory::Zero => {
+ // SAFETY: We have a normal floating point number. Now we transmute, i.e. do a bitcopy.
+ unsafe { mem::transmute::<f64, u64>(ct) }
+ }
+ }
+ }
+ // SAFETY: `u64` is a plain old datatype so we can always... uh...
+ // ...look, just pretend you forgot what you just read.
+ // Stability concerns.
+ let rt_f64_to_u64 = |rt| unsafe { mem::transmute::<f64, u64>(rt) };
+ // SAFETY: We use internal implementations that either always work or fail at compile time.
+ unsafe { intrinsics::const_eval_select((self,), ct_f64_to_u64, rt_f64_to_u64) }
}
/// Raw transmutation from `u64`.
#[must_use]
#[inline]
pub const fn from_bits(v: u64) -> Self {
- // SAFETY: `u64` is a plain old datatype so we can always transmute from it
// It turns out the safety issues with sNaN were overblown! Hooray!
- unsafe { mem::transmute(v) }
+ // SAFETY: `u64` is a plain old datatype so we can always transmute from it
+ // ...sorta.
+ //
+ // It turns out that at runtime, it is possible for a floating point number
+ // to be subject to floating point modes that alter nonzero subnormal numbers
+ // to zero on reads and writes, aka "denormals are zero" and "flush to zero".
+ // This is not a problem usually, but at least one tier2 platform for Rust
+ // actually exhibits an FTZ behavior by default: thumbv7neon
+ // aka "the Neon FPU in AArch32 state"
+ //
+ // Even with this, not all instructions exhibit the FTZ behaviors on thumbv7neon,
+ // so this should load the same bits if LLVM emits the "correct" instructions,
+ // but LLVM sometimes makes interesting choices about float optimization,
+ // and other FPUs may do similar. Thus, it is wise to indulge luxuriously in caution.
+ //
+ // In addition, on x86 targets with SSE or SSE2 disabled and the x87 FPU enabled,
+ // i.e. not soft-float, the way Rust does parameter passing can actually alter
+ // a number that is "not infinity" to have the same exponent as infinity,
+ // in a slightly unpredictable manner.
+ //
+ // And, of course evaluating to a NaN value is fairly nondeterministic.
+ // More precisely: when NaN should be returned is knowable, but which NaN?
+ // So far that's defined by a combination of LLVM and the CPU, not Rust.
+ // This function, however, allows observing the bitstring of a NaN,
+ // thus introspection on CTFE.
+ //
+ // In order to preserve, at least for the moment, const-to-runtime equivalence,
+ // reject any of these possible situations from happening.
+ #[rustc_const_unstable(feature = "const_float_bits_conv", issue = "72447")]
+ const fn ct_u64_to_f64(ct: u64) -> f64 {
+ match f64::classify_bits(ct) {
+ FpCategory::Subnormal => {
+ panic!("const-eval error: cannot use f64::from_bits on a subnormal number")
+ }
+ FpCategory::Nan => {
+ panic!("const-eval error: cannot use f64::from_bits on NaN")
+ }
+ FpCategory::Infinite | FpCategory::Normal | FpCategory::Zero => {
+ // SAFETY: It's not a frumious number
+ unsafe { mem::transmute::<u64, f64>(ct) }
+ }
+ }
+ }
+ // SAFETY: `u64` is a plain old datatype so we can always... uh...
+ // ...look, just pretend you forgot what you just read.
+ // Stability concerns.
+ let rt_u64_to_f64 = |rt| unsafe { mem::transmute::<u64, f64>(rt) };
+ // SAFETY: We use internal implementations that either always work or fail at compile time.
+ unsafe { intrinsics::const_eval_select((v,), ct_u64_to_f64, rt_u64_to_f64) }
}
/// Return the memory representation of this floating point number as a byte array in
///
/// # Panics
///
- /// When the number is zero, or if the base is not at least 2; it
+ /// When the number is negative, zero, or if the base is not at least 2; it
/// panics in debug mode and the return value is 0 in release
/// mode.
///
///
/// # Panics
///
- /// When the number is zero it panics in debug mode and the return value
+ /// When the number is negative or zero it panics in debug mode and the return value
/// is 0 in release mode.
///
/// # Examples
///
/// # Panics
///
- /// When the number is zero it panics in debug mode and the return value
+ /// When the number is negative or zero it panics in debug mode and the return value
/// is 0 in release mode.
///
/// # Example
///
/// # Panics
///
- /// When the number is negative, zero, or if the base is not at least 2;
+ /// When the number is zero, or if the base is not at least 2;
/// it panics in debug mode and the return value is 0 in release mode.
///
/// # Examples
///
/// # Panics
///
- /// When the number is negative or zero it panics in debug mode and
+ /// When the number is zero it panics in debug mode and
/// the return value is 0 in release mode.
///
/// # Examples
///
/// # Panics
///
- /// When the number is negative or zero it panics in debug mode and the
+ /// When the number is zero it panics in debug mode and the
/// return value is 0 in release mode.
///
/// # Example
#[unstable(feature = "try_trait_v2", issue = "84277")]
pub use self::try_trait::{FromResidual, Try};
+#[unstable(feature = "try_trait_v2_yeet", issue = "96374")]
+pub use self::try_trait::Yeet;
+
#[unstable(feature = "try_trait_v2_residual", issue = "91285")]
pub use self::try_trait::Residual;
fn from_residual(residual: R) -> Self;
}
+#[cfg(not(bootstrap))]
+#[unstable(
+ feature = "yeet_desugar_details",
+ issue = "none",
+ reason = "just here to simplify the desugaring; will never be stabilized"
+)]
+#[inline]
+#[track_caller] // because `Result::from_residual` has it
+#[lang = "from_yeet"]
+pub fn from_yeet<T, Y>(yeeted: Y) -> T
+where
+ T: FromResidual<Yeet<Y>>,
+{
+ FromResidual::from_residual(Yeet(yeeted))
+}
+
/// Allows retrieving the canonical type implementing [`Try`] that has this type
/// as its residual and allows it to hold an `O` as its output.
///
impl<T> Residual<T> for NeverShortCircuitResidual {
type TryType = NeverShortCircuit<T>;
}
+
+/// Implement `FromResidual<Yeet<T>>` on your type to enable
+/// `do yeet expr` syntax in functions returning your type.
+#[unstable(feature = "try_trait_v2_yeet", issue = "96374")]
+#[derive(Debug)]
+pub struct Yeet<T>(pub T);
}
}
+#[unstable(feature = "try_trait_v2_yeet", issue = "96374")]
+impl<T> ops::FromResidual<ops::Yeet<()>> for Option<T> {
+ #[inline]
+ fn from_residual(ops::Yeet(()): ops::Yeet<()>) -> Self {
+ None
+ }
+}
+
#[unstable(feature = "try_trait_v2_residual", issue = "91285")]
impl<T> ops::Residual<T> for Option<convert::Infallible> {
type TryType = Option<T>;
#[stable(feature = "track_caller", since = "1.46.0")]
#[rustc_const_unstable(feature = "const_caller_location", issue = "76156")]
#[track_caller]
+ #[inline]
pub const fn caller() -> &'static Location<'static> {
crate::intrinsics::caller_location()
}
/// ```
#[must_use]
#[stable(feature = "panic_hooks", since = "1.10.0")]
+ #[inline]
pub fn file(&self) -> &str {
self.file
}
/// ```
#[must_use]
#[stable(feature = "panic_hooks", since = "1.10.0")]
+ #[inline]
pub fn line(&self) -> u32 {
self.line
}
/// ```
#[must_use]
#[stable(feature = "panic_col", since = "1.25.0")]
+ #[inline]
pub fn column(&self) -> u32 {
self.col
}
}
}
+#[stable(feature = "assertunwindsafe_default", since = "1.62.0")]
+impl<T: Default> Default for AssertUnwindSafe<T> {
+ fn default() -> Self {
+ Self(Default::default())
+ }
+}
+
#[stable(feature = "futures_api", since = "1.36.0")]
impl<F: Future> Future for AssertUnwindSafe<F> {
type Output = F::Output;
}
}
+#[unstable(feature = "try_trait_v2_yeet", issue = "96374")]
+impl<T, E, F: From<E>> ops::FromResidual<ops::Yeet<E>> for Result<T, F> {
+ #[inline]
+ fn from_residual(ops::Yeet(e): ops::Yeet<E>) -> Self {
+ Err(From::from(e))
+ }
+}
+
#[unstable(feature = "try_trait_v2_residual", issue = "91285")]
impl<T, E> ops::Residual<T> for Result<convert::Infallible, E> {
type TryType = Result<T, E>;
}
#[doc(hidden)]
+ #[inline]
unsafe fn __iterator_get_unchecked(&mut self, idx: usize) -> Self::Item {
// SAFETY: the caller must guarantee that `i` is in bounds of
// the underlying slice, so `i` cannot overflow an `isize`, and
/// Returns `true` if the slice contains an element with the given value.
///
+ /// This operation is *O*(*n*).
+ ///
+ /// Note that if you have a sorted slice, [`binary_search`] may be faster.
+ ///
+ /// [`binary_search`]: slice::binary_search
+ ///
/// # Examples
///
/// ```
None
}
- /// Binary searches this sorted slice for a given element.
+ /// Binary searches this slice for a given element.
+ /// This behaves similary to [`contains`] if this slice is sorted.
///
/// If the value is found then [`Result::Ok`] is returned, containing the
/// index of the matching element. If there are multiple matches, then any
///
/// See also [`binary_search_by`], [`binary_search_by_key`], and [`partition_point`].
///
+ /// [`contains`]: slice::contains
/// [`binary_search_by`]: slice::binary_search_by
/// [`binary_search_by_key`]: slice::binary_search_by_key
/// [`partition_point`]: slice::partition_point
self.binary_search_by(|p| p.cmp(x))
}
- /// Binary searches this sorted slice with a comparator function.
+ /// Binary searches this slice with a comparator function.
+ /// This behaves similarly to [`contains`] if this slice is sorted.
///
/// The comparator function should implement an order consistent
/// with the sort order of the underlying slice, returning an
///
/// See also [`binary_search`], [`binary_search_by_key`], and [`partition_point`].
///
+ /// [`contains`]: slice::contains
/// [`binary_search`]: slice::binary_search
/// [`binary_search_by_key`]: slice::binary_search_by_key
/// [`partition_point`]: slice::partition_point
Err(left)
}
- /// Binary searches this sorted slice with a key extraction function.
+ /// Binary searches this slice with a key extraction function.
+ /// This behaves similarly to [`contains`] if this slice is sorted.
///
/// Assumes that the slice is sorted by the key, for instance with
/// [`sort_by_key`] using the same key extraction function.
///
/// See also [`binary_search`], [`binary_search_by`], and [`partition_point`].
///
+ /// [`contains`]: slice::contains
/// [`sort_by_key`]: slice::sort_by_key
/// [`binary_search`]: slice::binary_search
/// [`binary_search_by`]: slice::binary_search_by
}
macro_rules! test_op {
- ($fn_name:ident, $op:ident::$method:ident($lhs:literal, $rhs:literal), $result:literal, $($t:ty),+) => {
- #[test]
- fn $fn_name() {
- impls_defined!($op, $method($lhs, $rhs), $result, $($t),+);
- }
- };
- ($fn_name:ident, $op:ident::$method:ident(&mut $lhs:literal, $rhs:literal), $result:literal, $($t:ty),+) => {
- #[test]
- fn $fn_name() {
- impls_defined!($op, $method(&mut $lhs, $rhs), $result, $($t),+);
- }
- };
($fn_name:ident, $op:ident::$method:ident($lhs:literal), $result:literal, $($t:ty),+) => {
#[test]
fn $fn_name() {
#[stable(feature = "proc_macro_lib2", since = "1.29.0")]
Bracket,
/// `Ø ... Ø`
- /// An implicit delimiter, that may, for example, appear around tokens coming from a
+ /// An invisible delimiter, that may, for example, appear around tokens coming from a
/// "macro variable" `$var`. It is important to preserve operator priorities in cases like
/// `$var * 3` where `$var` is `1 + 2`.
- /// Implicit delimiters might not survive roundtrip of a token stream through a string.
+ /// Invisible delimiters might not survive roundtrip of a token stream through a string.
#[stable(feature = "proc_macro_lib2", since = "1.29.0")]
None,
}
({$($t:tt)*}) => { Group::new(Delimiter::Brace, quote!($($t)*)) };
(,) => { Punct::new(',', Spacing::Alone) };
(.) => { Punct::new('.', Spacing::Alone) };
- (:) => { Punct::new(':', Spacing::Alone) };
(;) => { Punct::new(';', Spacing::Alone) };
(!) => { Punct::new('!', Spacing::Alone) };
(<) => { Punct::new('<', Spacing::Alone) };
/// ]);
/// ```
///
-/// `HashMap` implements an [`Entry API`](#method.entry), which allows
+/// `HashMap` implements an [`Entry` API](#method.entry), which allows
/// for complex methods of getting, setting, updating and removing keys and
/// their values:
///
#[stable(feature = "core_c_void", since = "1.30.0")]
pub use core::ffi::c_void;
-#[unstable(feature = "core_ffi_c", issue = "94501")]
-pub use core::ffi::{
- c_char, c_double, c_float, c_int, c_long, c_longlong, c_schar, c_short, c_uchar, c_uint,
- c_ulong, c_ulonglong, c_ushort,
-};
-
-#[unstable(feature = "c_size_t", issue = "88345")]
-pub use core::ffi::{c_ptrdiff_t, c_size_t, c_ssize_t};
-
#[unstable(
feature = "c_variadic",
reason = "the `c_variadic` feature has not been properly tested on \
#![feature(exhaustive_patterns)]
#![feature(intra_doc_pointers)]
#![feature(lang_items)]
+#![feature(let_chains)]
#![feature(linkage)]
#![feature(min_specialization)]
#![feature(must_not_suspend)]
// Only for re-exporting:
#![feature(assert_matches)]
#![feature(async_iterator)]
-#![feature(c_size_t)]
#![feature(c_variadic)]
#![feature(cfg_accessible)]
#![feature(cfg_eval)]
/// [`eprint!`] instead to print error and progress messages.
///
/// [flush]: crate::io::Write::flush
+/// [`println!`]: crate::println
+/// [`eprint!`]: crate::eprint
///
/// # Panics
///
/// [`eprintln!`] instead to print error and progress messages.
///
/// [`std::fmt`]: crate::fmt
+/// [`eprintln!`]: crate::eprintln
///
/// # Panics
///
() => {
$crate::print!("\n")
};
- ($($arg:tt)*) => {
- $crate::io::_print($crate::format_args_nl!($($arg)*))
- };
+ ($($arg:tt)*) => {{
+ $crate::io::_print($crate::format_args_nl!($($arg)*));
+ }};
}
/// Prints to the standard error.
///
/// [`io::stderr`]: crate::io::stderr
/// [`io::stdout`]: crate::io::stdout
+/// [`println!`]: crate::println
///
/// # Panics
///
() => {
$crate::eprint!("\n")
};
- ($($arg:tt)*) => {
- $crate::io::_eprint($crate::format_args_nl!($($arg)*))
- };
+ ($($arg:tt)*) => {{
+ $crate::io::_eprint($crate::format_args_nl!($($arg)*));
+ }};
}
/// Prints and returns the value of a given expression for quick and dirty
#[stable(feature = "rust1", since = "1.0.0")]
fn uid(
&mut self,
- #[cfg(not(target_os = "vxworks"))] id: u32,
- #[cfg(target_os = "vxworks")] id: u16,
+ #[cfg(not(any(target_os = "vxworks", target_os = "espidf")))] id: u32,
+ #[cfg(any(target_os = "vxworks", target_os = "espidf"))] id: u16,
) -> &mut process::Command;
/// Similar to `uid`, but sets the group ID of the child process. This has
#[stable(feature = "rust1", since = "1.0.0")]
fn gid(
&mut self,
- #[cfg(not(target_os = "vxworks"))] id: u32,
- #[cfg(target_os = "vxworks")] id: u16,
+ #[cfg(not(any(target_os = "vxworks", target_os = "espidf")))] id: u32,
+ #[cfg(any(target_os = "vxworks", target_os = "espidf"))] id: u16,
) -> &mut process::Command;
/// Sets the supplementary group IDs for the calling process. Translates to
#[unstable(feature = "setgroups", issue = "90747")]
fn groups(
&mut self,
- #[cfg(not(target_os = "vxworks"))] groups: &[u32],
- #[cfg(target_os = "vxworks")] groups: &[u16],
+ #[cfg(not(any(target_os = "vxworks", target_os = "espidf")))] groups: &[u32],
+ #[cfg(any(target_os = "vxworks", target_os = "espidf"))] groups: &[u16],
) -> &mut process::Command;
/// Schedules a closure to be run just before the `exec` function is
impl CommandExt for process::Command {
fn uid(
&mut self,
- #[cfg(not(target_os = "vxworks"))] id: u32,
- #[cfg(target_os = "vxworks")] id: u16,
+ #[cfg(not(any(target_os = "vxworks", target_os = "espidf")))] id: u32,
+ #[cfg(any(target_os = "vxworks", target_os = "espidf"))] id: u16,
) -> &mut process::Command {
self.as_inner_mut().uid(id);
self
fn gid(
&mut self,
- #[cfg(not(target_os = "vxworks"))] id: u32,
- #[cfg(target_os = "vxworks")] id: u16,
+ #[cfg(not(any(target_os = "vxworks", target_os = "espidf")))] id: u32,
+ #[cfg(any(target_os = "vxworks", target_os = "espidf"))] id: u16,
) -> &mut process::Command {
self.as_inner_mut().gid(id);
self
fn groups(
&mut self,
- #[cfg(not(target_os = "vxworks"))] groups: &[u32],
- #[cfg(target_os = "vxworks")] groups: &[u16],
+ #[cfg(not(any(target_os = "vxworks", target_os = "espidf")))] groups: &[u32],
+ #[cfg(any(target_os = "vxworks", target_os = "espidf"))] groups: &[u16],
) -> &mut process::Command {
self.as_inner_mut().groups(groups);
self
/// `NULL`. This ensures that such FFI calls cannot start using the handle without
/// checking for `NULL` first.
///
-/// This type concerns any value other than `NULL` to be valid, including `INVALID_HANDLE_VALUE`.
+/// This type considers any value other than `NULL` to be valid, including `INVALID_HANDLE_VALUE`.
/// This is because APIs that use `NULL` as their sentry value don't treat `INVALID_HANDLE_VALUE`
/// as special.
///
/// `INVALID_HANDLE_VALUE`. This ensures that such FFI calls cannot start using the handle without
/// checking for `INVALID_HANDLE_VALUE` first.
///
-/// This type concerns any value other than `INVALID_HANDLE_VALUE` to be valid, including `NULL`.
+/// This type considers any value other than `INVALID_HANDLE_VALUE` to be valid, including `NULL`.
/// This is because APIs that use `INVALID_HANDLE_VALUE` as their sentry value may return `NULL`
/// under `windows_subsystem = "windows"` or other situations where I/O devices are detached.
///
}
impl TryFrom<HandleOrNull> for OwnedHandle {
- type Error = ();
+ type Error = NullHandleError;
#[inline]
- fn try_from(handle_or_null: HandleOrNull) -> Result<Self, ()> {
+ fn try_from(handle_or_null: HandleOrNull) -> Result<Self, NullHandleError> {
let owned_handle = handle_or_null.0;
if owned_handle.handle.is_null() {
// Don't call `CloseHandle`; it'd be harmless, except that it could
// overwrite the `GetLastError` error.
forget(owned_handle);
- Err(())
+ Err(NullHandleError(()))
} else {
Ok(owned_handle)
}
})?;
unsafe { Ok(Self::from_raw_handle(ret)) }
}
+
+ /// Allow child processes to inherit the handle.
+ pub(crate) fn set_inheritable(&self) -> io::Result<()> {
+ cvt(unsafe {
+ c::SetHandleInformation(
+ self.as_raw_handle(),
+ c::HANDLE_FLAG_INHERIT,
+ c::HANDLE_FLAG_INHERIT,
+ )
+ })?;
+ Ok(())
+ }
}
impl TryFrom<HandleOrInvalid> for OwnedHandle {
- type Error = ();
+ type Error = InvalidHandleError;
#[inline]
- fn try_from(handle_or_invalid: HandleOrInvalid) -> Result<Self, ()> {
+ fn try_from(handle_or_invalid: HandleOrInvalid) -> Result<Self, InvalidHandleError> {
let owned_handle = handle_or_invalid.0;
if owned_handle.handle == c::INVALID_HANDLE_VALUE {
// Don't call `CloseHandle`; it'd be harmless, except that it could
// overwrite the `GetLastError` error.
forget(owned_handle);
- Err(())
+ Err(InvalidHandleError(()))
} else {
Ok(owned_handle)
}
}
}
+/// This is the error type used by [`HandleOrNull`] when attempting to convert
+/// into a handle, to indicate that the value is null.
+// The empty field prevents constructing this, and allows extending it in the future.
+#[unstable(feature = "io_safety", issue = "87074")]
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct NullHandleError(());
+
+#[unstable(feature = "io_safety", issue = "87074")]
+impl fmt::Display for NullHandleError {
+ fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+ "A HandleOrNull could not be converted to a handle because it was null".fmt(fmt)
+ }
+}
+
+#[unstable(feature = "io_safety", issue = "87074")]
+impl crate::error::Error for NullHandleError {}
+
+/// This is the error type used by [`HandleOrInvalid`] when attempting to
+/// convert into a handle, to indicate that the value is
+/// `INVALID_HANDLE_VALUE`.
+// The empty field prevents constructing this, and allows extending it in the future.
+#[unstable(feature = "io_safety", issue = "87074")]
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct InvalidHandleError(());
+
+#[unstable(feature = "io_safety", issue = "87074")]
+impl fmt::Display for InvalidHandleError {
+ fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+ "A HandleOrInvalid could not be converted to a handle because it was INVALID_HANDLE_VALUE"
+ .fmt(fmt)
+ }
+}
+
+#[unstable(feature = "io_safety", issue = "87074")]
+impl crate::error::Error for InvalidHandleError {}
+
impl AsRawHandle for BorrowedHandle<'_> {
#[inline]
fn as_raw_handle(&self) -> RawHandle {
/// Device namespace prefix, e.g., `\\.\COM42`.
///
- /// Device namespace prefixes consist of `\\.\` immediately followed by the
- /// device name.
+ /// Device namespace prefixes consist of `\\.\` (possibly using `/`
+ /// instead of `\`), immediately followed by the device name.
#[stable(feature = "rust1", since = "1.0.0")]
DeviceNS(#[stable(feature = "rust1", since = "1.0.0")] &'a OsStr),
fn len(&self) -> usize {
use self::Prefix::*;
fn os_str_len(s: &OsStr) -> usize {
- os_str_as_u8_slice(s).len()
+ s.bytes().len()
}
match *self {
Verbatim(x) => 4 + os_str_len(x),
}
}
-// See note at the top of this module to understand why these are used:
-//
-// These casts are safe as OsStr is internally a wrapper around [u8] on all
-// platforms.
-//
-// Note that currently this relies on the special knowledge that libstd has;
-// these types are single-element structs but are not marked repr(transparent)
-// or repr(C) which would make these casts allowable outside std.
-fn os_str_as_u8_slice(s: &OsStr) -> &[u8] {
- unsafe { &*(s as *const OsStr as *const [u8]) }
-}
unsafe fn u8_slice_as_os_str(s: &[u8]) -> &OsStr {
- // SAFETY: see the comment of `os_str_as_u8_slice`
+ // SAFETY: See note at the top of this module to understand why this and
+ // `OsStr::bytes` are used:
+ //
+ // This casts are safe as OsStr is internally a wrapper around [u8] on all
+ // platforms.
+ //
+ // Note that currently this relies on the special knowledge that libstd has;
+ // these types are single-element structs but are not marked
+ // repr(transparent) or repr(C) which would make these casts not allowable
+ // outside std.
unsafe { &*(s as *const [u8] as *const OsStr) }
}
// basic workhorse for splitting stem and extension
fn rsplit_file_at_dot(file: &OsStr) -> (Option<&OsStr>, Option<&OsStr>) {
- if os_str_as_u8_slice(file) == b".." {
+ if file.bytes() == b".." {
return (Some(file), None);
}
// and back. This is safe to do because (1) we only look at ASCII
// contents of the encoding and (2) new &OsStr values are produced
// only from ASCII-bounded slices of existing &OsStr values.
- let mut iter = os_str_as_u8_slice(file).rsplitn(2, |b| *b == b'.');
+ let mut iter = file.bytes().rsplitn(2, |b| *b == b'.');
let after = iter.next();
let before = iter.next();
if before == Some(b"") {
}
fn split_file_at_dot(file: &OsStr) -> (&OsStr, Option<&OsStr>) {
- let slice = os_str_as_u8_slice(file);
+ let slice = file.bytes();
if slice == b".." {
return (file, None);
}
fn _set_extension(&mut self, extension: &OsStr) -> bool {
let file_stem = match self.file_stem() {
None => return false,
- Some(f) => os_str_as_u8_slice(f),
+ Some(f) => f.bytes(),
};
// truncate until right after the file stem
let end_file_stem = file_stem[file_stem.len()..].as_ptr().addr();
- let start = os_str_as_u8_slice(&self.inner).as_ptr().addr();
+ let start = self.inner.bytes().as_ptr().addr();
let v = self.as_mut_vec();
v.truncate(end_file_stem.wrapping_sub(start));
// add the new extension, if any
- let new = os_str_as_u8_slice(extension);
+ let new = extension.bytes();
if !new.is_empty() {
v.reserve_exact(new.len() + 1);
v.push(b'.');
}
// The following (private!) function reveals the byte encoding used for OsStr.
fn as_u8_slice(&self) -> &[u8] {
- os_str_as_u8_slice(&self.inner)
+ self.inner.bytes()
}
/// Directly wraps a string slice as a `Path` slice.
file_prefix: None
);
- t!("\\\\?\\C:/foo",
- iter: ["\\\\?\\C:/foo"],
+ t!("\\\\?\\C:/foo/bar",
+ iter: ["\\\\?\\C:", "\\", "foo/bar"],
has_root: true,
is_absolute: true,
- parent: None,
- file_name: None,
- file_stem: None,
+ parent: Some("\\\\?\\C:/"),
+ file_name: Some("foo/bar"),
+ file_stem: Some("foo/bar"),
extension: None,
- file_prefix: None
+ file_prefix: Some("foo/bar")
);
t!("\\\\.\\foo\\bar",
assert!(output.status.success());
assert_eq!(String::from_utf8_lossy(&output.stdout).trim(), "Hello, fellow Rustaceans!");
}
+
+// See issue #95178
+#[test]
+#[cfg(windows)]
+fn run_canonical_bat_script() {
+ let tempdir = crate::sys_common::io::test::tmpdir();
+ let script_path = tempdir.join("hello.cmd");
+
+ crate::fs::write(&script_path, "@echo Hello, %~1!").unwrap();
+
+ // Try using a canonical path
+ let output = Command::new(&script_path.canonicalize().unwrap())
+ .arg("fellow Rustaceans")
+ .stdout(crate::process::Stdio::piped())
+ .spawn()
+ .unwrap()
+ .wait_with_output()
+ .unwrap();
+ assert!(output.status.success());
+ assert_eq!(String::from_utf8_lossy(&output.stdout).trim(), "Hello, fellow Rustaceans!");
+}
use libc::{c_int, c_void};
+#[cfg(any(
+ target_os = "android",
+ target_os = "linux",
+ target_os = "emscripten",
+ target_os = "l4re"
+))]
+use libc::off64_t;
+#[cfg(not(any(
+ target_os = "linux",
+ target_os = "emscripten",
+ target_os = "l4re",
+ target_os = "android"
+)))]
+use libc::off_t as off64_t;
+
#[derive(Debug)]
pub struct FileDesc(OwnedFd);
self.as_raw_fd(),
buf.as_mut_ptr() as *mut c_void,
cmp::min(buf.len(), READ_LIMIT),
- offset as i64,
+ offset as off64_t,
))
.map(|n| n as usize)
}
self.as_raw_fd(),
buf.as_ptr() as *const c_void,
cmp::min(buf.len(), READ_LIMIT),
- offset as i64,
+ offset as off64_t,
))
.map(|n| n as usize)
}
SeekFrom::End(off) => (libc::SEEK_END, off),
SeekFrom::Current(off) => (libc::SEEK_CUR, off),
};
- let n = cvt(unsafe { lseek64(self.as_raw_fd(), pos, whence) })?;
+ let n = cvt(unsafe { lseek64(self.as_raw_fd(), pos as off64_t, whence) })?;
Ok(n as u64)
}
pub mod thread;
pub mod thread_local_dtor;
pub mod thread_local_key;
+pub mod thread_parker;
pub mod time;
#[cfg(target_os = "espidf")]
// Android with api less than 21 define sig* functions inline, so it is not
// available for dynamic link. Implementing sigemptyset and sigaddset allow us
// to support older Android version (independent of libc version).
-// The following implementations are based on https://git.io/vSkNf
+// The following implementations are based on
+// https://github.com/aosp-mirror/platform_bionic/blob/ad8dcd6023294b646e5a8288c0ed431b0845da49/libc/include/android/legacy_signal_inlines.h
cfg_if::cfg_if! {
if #[cfg(target_os = "android")] {
pub unsafe fn sigemptyset(set: *mut libc::sigset_t) -> libc::c_int {
--- /dev/null
+//! Thread parking without `futex` using the `pthread` synchronization primitives.
+
+#![cfg(not(any(
+ target_os = "linux",
+ target_os = "android",
+ all(target_os = "emscripten", target_feature = "atomics")
+)))]
+
+use crate::cell::UnsafeCell;
+use crate::marker::PhantomPinned;
+use crate::pin::Pin;
+use crate::ptr::addr_of_mut;
+use crate::sync::atomic::AtomicUsize;
+use crate::sync::atomic::Ordering::SeqCst;
+use crate::time::Duration;
+
+const EMPTY: usize = 0;
+const PARKED: usize = 1;
+const NOTIFIED: usize = 2;
+
+unsafe fn lock(lock: *mut libc::pthread_mutex_t) {
+ let r = libc::pthread_mutex_lock(lock);
+ debug_assert_eq!(r, 0);
+}
+
+unsafe fn unlock(lock: *mut libc::pthread_mutex_t) {
+ let r = libc::pthread_mutex_unlock(lock);
+ debug_assert_eq!(r, 0);
+}
+
+unsafe fn notify_one(cond: *mut libc::pthread_cond_t) {
+ let r = libc::pthread_cond_signal(cond);
+ debug_assert_eq!(r, 0);
+}
+
+unsafe fn wait(cond: *mut libc::pthread_cond_t, lock: *mut libc::pthread_mutex_t) {
+ let r = libc::pthread_cond_wait(cond, lock);
+ debug_assert_eq!(r, 0);
+}
+
+const TIMESPEC_MAX: libc::timespec =
+ libc::timespec { tv_sec: <libc::time_t>::MAX, tv_nsec: 1_000_000_000 - 1 };
+
+unsafe fn wait_timeout(
+ cond: *mut libc::pthread_cond_t,
+ lock: *mut libc::pthread_mutex_t,
+ dur: Duration,
+) {
+ // Use the system clock on systems that do not support pthread_condattr_setclock.
+ // This unfortunately results in problems when the system time changes.
+ #[cfg(any(target_os = "macos", target_os = "ios", target_os = "espidf"))]
+ let (now, dur) = {
+ use super::time::SystemTime;
+ use crate::cmp::min;
+
+ // OSX implementation of `pthread_cond_timedwait` is buggy
+ // with super long durations. When duration is greater than
+ // 0x100_0000_0000_0000 seconds, `pthread_cond_timedwait`
+ // in macOS Sierra return error 316.
+ //
+ // This program demonstrates the issue:
+ // https://gist.github.com/stepancheg/198db4623a20aad2ad7cddb8fda4a63c
+ //
+ // To work around this issue, and possible bugs of other OSes, timeout
+ // is clamped to 1000 years, which is allowable per the API of `park_timeout`
+ // because of spurious wakeups.
+ let dur = min(dur, Duration::from_secs(1000 * 365 * 86400));
+ let now = SystemTime::now().t;
+ (now, dur)
+ };
+ // Use the monotonic clock on other systems.
+ #[cfg(not(any(target_os = "macos", target_os = "ios", target_os = "espidf")))]
+ let (now, dur) = {
+ use super::time::Timespec;
+
+ (Timespec::now(libc::CLOCK_MONOTONIC), dur)
+ };
+
+ let timeout = now.checked_add_duration(&dur).map(|t| t.t).unwrap_or(TIMESPEC_MAX);
+ let r = libc::pthread_cond_timedwait(cond, lock, &timeout);
+ debug_assert!(r == libc::ETIMEDOUT || r == 0);
+}
+
+pub struct Parker {
+ state: AtomicUsize,
+ lock: UnsafeCell<libc::pthread_mutex_t>,
+ cvar: UnsafeCell<libc::pthread_cond_t>,
+ // The `pthread` primitives require a stable address, so make this struct `!Unpin`.
+ _pinned: PhantomPinned,
+}
+
+impl Parker {
+ /// Construct the UNIX parker in-place.
+ ///
+ /// # Safety
+ /// The constructed parker must never be moved.
+ pub unsafe fn new(parker: *mut Parker) {
+ // Use the default mutex implementation to allow for simpler initialization.
+ // This could lead to undefined behaviour when deadlocking. This is avoided
+ // by not deadlocking. Note in particular the unlocking operation before any
+ // panic, as code after the panic could try to park again.
+ addr_of_mut!((*parker).state).write(AtomicUsize::new(EMPTY));
+ addr_of_mut!((*parker).lock).write(UnsafeCell::new(libc::PTHREAD_MUTEX_INITIALIZER));
+
+ cfg_if::cfg_if! {
+ if #[cfg(any(
+ target_os = "macos",
+ target_os = "ios",
+ target_os = "l4re",
+ target_os = "android",
+ target_os = "redox"
+ ))] {
+ addr_of_mut!((*parker).cvar).write(UnsafeCell::new(libc::PTHREAD_COND_INITIALIZER));
+ } else if #[cfg(target_os = "espidf")] {
+ let r = libc::pthread_cond_init(addr_of_mut!((*parker).cvar).cast(), crate::ptr::null());
+ assert_eq!(r, 0);
+ } else {
+ use crate::mem::MaybeUninit;
+ let mut attr = MaybeUninit::<libc::pthread_condattr_t>::uninit();
+ let r = libc::pthread_condattr_init(attr.as_mut_ptr());
+ assert_eq!(r, 0);
+ let r = libc::pthread_condattr_setclock(attr.as_mut_ptr(), libc::CLOCK_MONOTONIC);
+ assert_eq!(r, 0);
+ let r = libc::pthread_cond_init(addr_of_mut!((*parker).cvar).cast(), attr.as_ptr());
+ assert_eq!(r, 0);
+ let r = libc::pthread_condattr_destroy(attr.as_mut_ptr());
+ assert_eq!(r, 0);
+ }
+ }
+ }
+
+ // This implementation doesn't require `unsafe`, but other implementations
+ // may assume this is only called by the thread that owns the Parker.
+ pub unsafe fn park(self: Pin<&Self>) {
+ // If we were previously notified then we consume this notification and
+ // return quickly.
+ if self.state.compare_exchange(NOTIFIED, EMPTY, SeqCst, SeqCst).is_ok() {
+ return;
+ }
+
+ // Otherwise we need to coordinate going to sleep
+ lock(self.lock.get());
+ match self.state.compare_exchange(EMPTY, PARKED, SeqCst, SeqCst) {
+ Ok(_) => {}
+ Err(NOTIFIED) => {
+ // We must read here, even though we know it will be `NOTIFIED`.
+ // This is because `unpark` may have been called again since we read
+ // `NOTIFIED` in the `compare_exchange` above. We must perform an
+ // acquire operation that synchronizes with that `unpark` to observe
+ // any writes it made before the call to unpark. To do that we must
+ // read from the write it made to `state`.
+ let old = self.state.swap(EMPTY, SeqCst);
+
+ unlock(self.lock.get());
+
+ assert_eq!(old, NOTIFIED, "park state changed unexpectedly");
+ return;
+ } // should consume this notification, so prohibit spurious wakeups in next park.
+ Err(_) => {
+ unlock(self.lock.get());
+
+ panic!("inconsistent park state")
+ }
+ }
+
+ loop {
+ wait(self.cvar.get(), self.lock.get());
+
+ match self.state.compare_exchange(NOTIFIED, EMPTY, SeqCst, SeqCst) {
+ Ok(_) => break, // got a notification
+ Err(_) => {} // spurious wakeup, go back to sleep
+ }
+ }
+
+ unlock(self.lock.get());
+ }
+
+ // This implementation doesn't require `unsafe`, but other implementations
+ // may assume this is only called by the thread that owns the Parker. Use
+ // `Pin` to guarantee a stable address for the mutex and condition variable.
+ pub unsafe fn park_timeout(self: Pin<&Self>, dur: Duration) {
+ // Like `park` above we have a fast path for an already-notified thread, and
+ // afterwards we start coordinating for a sleep.
+ // return quickly.
+ if self.state.compare_exchange(NOTIFIED, EMPTY, SeqCst, SeqCst).is_ok() {
+ return;
+ }
+
+ lock(self.lock.get());
+ match self.state.compare_exchange(EMPTY, PARKED, SeqCst, SeqCst) {
+ Ok(_) => {}
+ Err(NOTIFIED) => {
+ // We must read again here, see `park`.
+ let old = self.state.swap(EMPTY, SeqCst);
+ unlock(self.lock.get());
+
+ assert_eq!(old, NOTIFIED, "park state changed unexpectedly");
+ return;
+ } // should consume this notification, so prohibit spurious wakeups in next park.
+ Err(_) => {
+ unlock(self.lock.get());
+ panic!("inconsistent park_timeout state")
+ }
+ }
+
+ // Wait with a timeout, and if we spuriously wake up or otherwise wake up
+ // from a notification we just want to unconditionally set the state back to
+ // empty, either consuming a notification or un-flagging ourselves as
+ // parked.
+ wait_timeout(self.cvar.get(), self.lock.get(), dur);
+
+ match self.state.swap(EMPTY, SeqCst) {
+ NOTIFIED => unlock(self.lock.get()), // got a notification, hurray!
+ PARKED => unlock(self.lock.get()), // no notification, alas
+ n => {
+ unlock(self.lock.get());
+ panic!("inconsistent park_timeout state: {n}")
+ }
+ }
+ }
+
+ pub fn unpark(self: Pin<&Self>) {
+ // To ensure the unparked thread will observe any writes we made
+ // before this call, we must perform a release operation that `park`
+ // can synchronize with. To do that we must write `NOTIFIED` even if
+ // `state` is already `NOTIFIED`. That is why this must be a swap
+ // rather than a compare-and-swap that returns if it reads `NOTIFIED`
+ // on failure.
+ match self.state.swap(NOTIFIED, SeqCst) {
+ EMPTY => return, // no one was waiting
+ NOTIFIED => return, // already unparked
+ PARKED => {} // gotta go wake someone up
+ _ => panic!("inconsistent state in unpark"),
+ }
+
+ // There is a period between when the parked thread sets `state` to
+ // `PARKED` (or last checked `state` in the case of a spurious wake
+ // up) and when it actually waits on `cvar`. If we were to notify
+ // during this period it would be ignored and then when the parked
+ // thread went to sleep it would never wake up. Fortunately, it has
+ // `lock` locked at this stage so we can acquire `lock` to wait until
+ // it is ready to receive the notification.
+ //
+ // Releasing `lock` before the call to `notify_one` means that when the
+ // parked thread wakes it doesn't get woken only to have to wait for us
+ // to release `lock`.
+ unsafe {
+ lock(self.lock.get());
+ unlock(self.lock.get());
+ notify_one(self.cvar.get());
+ }
+ }
+}
+
+impl Drop for Parker {
+ fn drop(&mut self) {
+ unsafe {
+ libc::pthread_cond_destroy(self.cvar.get_mut());
+ libc::pthread_mutex_destroy(self.lock.get_mut());
+ }
+ }
+}
+
+unsafe impl Sync for Parker {}
+unsafe impl Send for Parker {}
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct SystemTime {
- t: Timespec,
+ pub(in crate::sys::unix) t: Timespec,
}
pub const UNIX_EPOCH: SystemTime = SystemTime { t: Timespec::zero() };
#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct SystemTime {
- t: Timespec,
+ pub(in crate::sys::unix) t: Timespec,
}
pub const UNIX_EPOCH: SystemTime = SystemTime { t: Timespec::zero() };
use crate::ffi::OsString;
use crate::fmt;
+use crate::io;
use crate::marker::PhantomData;
use crate::num::NonZeroU16;
use crate::os::windows::prelude::*;
use crate::path::PathBuf;
use crate::ptr::NonNull;
use crate::sys::c;
+use crate::sys::process::ensure_no_nuls;
use crate::sys::windows::os::current_exe;
use crate::vec;
}
}
}
+
+#[derive(Debug)]
+pub(crate) enum Arg {
+ /// Add quotes (if needed)
+ Regular(OsString),
+ /// Append raw string without quoting
+ Raw(OsString),
+}
+
+enum Quote {
+ // Every arg is quoted
+ Always,
+ // Whitespace and empty args are quoted
+ Auto,
+ // Arg appended without any changes (#29494)
+ Never,
+}
+
+pub(crate) fn append_arg(cmd: &mut Vec<u16>, arg: &Arg, force_quotes: bool) -> io::Result<()> {
+ let (arg, quote) = match arg {
+ Arg::Regular(arg) => (arg, if force_quotes { Quote::Always } else { Quote::Auto }),
+ Arg::Raw(arg) => (arg, Quote::Never),
+ };
+
+ // If an argument has 0 characters then we need to quote it to ensure
+ // that it actually gets passed through on the command line or otherwise
+ // it will be dropped entirely when parsed on the other end.
+ ensure_no_nuls(arg)?;
+ let arg_bytes = arg.bytes();
+ let (quote, escape) = match quote {
+ Quote::Always => (true, true),
+ Quote::Auto => {
+ (arg_bytes.iter().any(|c| *c == b' ' || *c == b'\t') || arg_bytes.is_empty(), true)
+ }
+ Quote::Never => (false, false),
+ };
+ if quote {
+ cmd.push('"' as u16);
+ }
+
+ let mut backslashes: usize = 0;
+ for x in arg.encode_wide() {
+ if escape {
+ if x == '\\' as u16 {
+ backslashes += 1;
+ } else {
+ if x == '"' as u16 {
+ // Add n+1 backslashes to total 2n+1 before internal '"'.
+ cmd.extend((0..=backslashes).map(|_| '\\' as u16));
+ }
+ backslashes = 0;
+ }
+ }
+ cmd.push(x);
+ }
+
+ if quote {
+ // Add n backslashes to total 2n before ending '"'.
+ cmd.extend((0..backslashes).map(|_| '\\' as u16));
+ cmd.push('"' as u16);
+ }
+ Ok(())
+}
+
+pub(crate) fn make_bat_command_line(
+ script: &[u16],
+ args: &[Arg],
+ force_quotes: bool,
+) -> io::Result<Vec<u16>> {
+ // Set the start of the command line to `cmd.exe /c "`
+ // It is necessary to surround the command in an extra pair of quotes,
+ // hence the trailing quote here. It will be closed after all arguments
+ // have been added.
+ let mut cmd: Vec<u16> = "cmd.exe /c \"".encode_utf16().collect();
+
+ // Push the script name surrounded by its quote pair.
+ cmd.push(b'"' as u16);
+ // Windows file names cannot contain a `"` character or end with `\\`.
+ // If the script name does then return an error.
+ if script.contains(&(b'"' as u16)) || script.last() == Some(&(b'\\' as u16)) {
+ return Err(io::const_io_error!(
+ io::ErrorKind::InvalidInput,
+ "Windows file names may not contain `\"` or end with `\\`"
+ ));
+ }
+ cmd.extend_from_slice(script.strip_suffix(&[0]).unwrap_or(script));
+ cmd.push(b'"' as u16);
+
+ // Append the arguments.
+ // FIXME: This needs tests to ensure that the arguments are properly
+ // reconstructed by the batch script by default.
+ for arg in args {
+ cmd.push(' ' as u16);
+ append_arg(&mut cmd, arg, force_quotes)?;
+ }
+
+ // Close the quote we left opened earlier.
+ cmd.push(b'"' as u16);
+
+ Ok(cmd)
+}
+
+/// Takes a path and tries to return a non-verbatim path.
+///
+/// This is necessary because cmd.exe does not support verbatim paths.
+pub(crate) fn to_user_path(mut path: Vec<u16>) -> io::Result<Vec<u16>> {
+ use crate::ptr;
+ use crate::sys::windows::fill_utf16_buf;
+
+ // UTF-16 encoded code points, used in parsing and building UTF-16 paths.
+ // All of these are in the ASCII range so they can be cast directly to `u16`.
+ const SEP: u16 = b'\\' as _;
+ const QUERY: u16 = b'?' as _;
+ const COLON: u16 = b':' as _;
+ const U: u16 = b'U' as _;
+ const N: u16 = b'N' as _;
+ const C: u16 = b'C' as _;
+
+ // Early return if the path is too long to remove the verbatim prefix.
+ const LEGACY_MAX_PATH: usize = 260;
+ if path.len() > LEGACY_MAX_PATH {
+ return Ok(path);
+ }
+
+ match &path[..] {
+ // `\\?\C:\...` => `C:\...`
+ [SEP, SEP, QUERY, SEP, _, COLON, SEP, ..] => unsafe {
+ let lpfilename = path[4..].as_ptr();
+ fill_utf16_buf(
+ |buffer, size| c::GetFullPathNameW(lpfilename, size, buffer, ptr::null_mut()),
+ |full_path: &[u16]| {
+ if full_path == &path[4..path.len() - 1] { full_path.into() } else { path }
+ },
+ )
+ },
+ // `\\?\UNC\...` => `\\...`
+ [SEP, SEP, QUERY, SEP, U, N, C, SEP, ..] => unsafe {
+ // Change the `C` in `UNC\` to `\` so we can get a slice that starts with `\\`.
+ path[6] = b'\\' as u16;
+ let lpfilename = path[6..].as_ptr();
+ fill_utf16_buf(
+ |buffer, size| c::GetFullPathNameW(lpfilename, size, buffer, ptr::null_mut()),
+ |full_path: &[u16]| {
+ if full_path == &path[6..path.len() - 1] {
+ full_path.into()
+ } else {
+ // Restore the 'C' in "UNC".
+ path[6] = b'C' as u16;
+ path
+ }
+ },
+ )
+ },
+ // For everything else, leave the path unchanged.
+ _ => Ok(path),
+ }
+}
bWaitAll: BOOL,
dwMilliseconds: DWORD,
) -> DWORD;
+ pub fn CreatePipe(
+ hReadPipe: *mut HANDLE,
+ hWritePipe: *mut HANDLE,
+ lpPipeAttributes: *const SECURITY_ATTRIBUTES,
+ nSize: DWORD,
+ ) -> BOOL;
pub fn CreateNamedPipeW(
lpName: LPCWSTR,
dwOpenMode: DWORD,
Ok(Self(self.0.duplicate(access, inherit, options)?))
}
+ pub(crate) fn set_inheritable(&self) -> io::Result<()> {
+ self.0.set_inheritable()
+ }
+
/// Performs a synchronous read.
///
/// If the handle is opened for asynchronous I/O then this abort the process.
pub fn to_u16s<S: AsRef<OsStr>>(s: S) -> crate::io::Result<Vec<u16>> {
fn inner(s: &OsStr) -> crate::io::Result<Vec<u16>> {
- let mut maybe_result: Vec<u16> = s.encode_wide().collect();
+ // Most paths are ASCII, so reserve capacity for as much as there are bytes
+ // in the OsStr plus one for the null-terminating character. We are not
+ // wasting bytes here as paths created by this function are primarily used
+ // in an ephemeral fashion.
+ let mut maybe_result = Vec::with_capacity(s.len() + 1);
+ maybe_result.extend(s.encode_wide());
+
if unrolled_find_u16s(0, &maybe_result).is_some() {
return Err(crate::io::const_io_error!(
ErrorKind::InvalidInput,
{
// Start off with a stack buf but then spill over to the heap if we end up
// needing more space.
+ //
+ // This initial size also works around `GetFullPathNameW` returning
+ // incorrect size hints for some short paths:
+ // https://github.com/dylni/normpath/issues/5
let mut stack_buf = [0u16; 512];
let mut heap_buf = Vec::new();
unsafe {
path.into()
}
+struct PrefixParser<'a, const LEN: usize> {
+ path: &'a OsStr,
+ prefix: [u8; LEN],
+}
+
+impl<'a, const LEN: usize> PrefixParser<'a, LEN> {
+ #[inline]
+ fn get_prefix(path: &OsStr) -> [u8; LEN] {
+ let mut prefix = [0; LEN];
+ // SAFETY: Only ASCII characters are modified.
+ for (i, &ch) in path.bytes().iter().take(LEN).enumerate() {
+ prefix[i] = if ch == b'/' { b'\\' } else { ch };
+ }
+ prefix
+ }
+
+ fn new(path: &'a OsStr) -> Self {
+ Self { path, prefix: Self::get_prefix(path) }
+ }
+
+ fn as_slice(&self) -> PrefixParserSlice<'a, '_> {
+ PrefixParserSlice {
+ path: self.path,
+ prefix: &self.prefix[..LEN.min(self.path.len())],
+ index: 0,
+ }
+ }
+}
+
+struct PrefixParserSlice<'a, 'b> {
+ path: &'a OsStr,
+ prefix: &'b [u8],
+ index: usize,
+}
+
+impl<'a> PrefixParserSlice<'a, '_> {
+ fn strip_prefix(&self, prefix: &str) -> Option<Self> {
+ self.prefix[self.index..]
+ .starts_with(prefix.as_bytes())
+ .then(|| Self { index: self.index + prefix.len(), ..*self })
+ }
+
+ fn prefix_bytes(&self) -> &'a [u8] {
+ &self.path.bytes()[..self.index]
+ }
+
+ fn finish(self) -> &'a OsStr {
+ // SAFETY: The unsafety here stems from converting between &OsStr and
+ // &[u8] and back. This is safe to do because (1) we only look at ASCII
+ // contents of the encoding and (2) new &OsStr values are produced only
+ // from ASCII-bounded slices of existing &OsStr values.
+ unsafe { bytes_as_os_str(&self.path.bytes()[self.index..]) }
+ }
+}
+
pub fn parse_prefix(path: &OsStr) -> Option<Prefix<'_>> {
use Prefix::{DeviceNS, Disk, Verbatim, VerbatimDisk, VerbatimUNC, UNC};
- if let Some(path) = strip_prefix(path, r"\\") {
+ let parser = PrefixParser::<8>::new(path);
+ let parser = parser.as_slice();
+ if let Some(parser) = parser.strip_prefix(r"\\") {
// \\
- if let Some(path) = strip_prefix(path, r"?\") {
+
+ // The meaning of verbatim paths can change when they use a different
+ // separator.
+ if let Some(parser) = parser.strip_prefix(r"?\") && !parser.prefix_bytes().iter().any(|&x| x == b'/') {
// \\?\
- if let Some(path) = strip_prefix(path, r"UNC\") {
+ if let Some(parser) = parser.strip_prefix(r"UNC\") {
// \\?\UNC\server\share
+ let path = parser.finish();
let (server, path) = parse_next_component(path, true);
let (share, _) = parse_next_component(path, true);
Some(VerbatimUNC(server, share))
} else {
- let (prefix, _) = parse_next_component(path, true);
+ let path = parser.finish();
// in verbatim paths only recognize an exact drive prefix
- if let Some(drive) = parse_drive_exact(prefix) {
+ if let Some(drive) = parse_drive_exact(path) {
// \\?\C:
Some(VerbatimDisk(drive))
} else {
// \\?\prefix
+ let (prefix, _) = parse_next_component(path, true);
Some(Verbatim(prefix))
}
}
- } else if let Some(path) = strip_prefix(path, r".\") {
+ } else if let Some(parser) = parser.strip_prefix(r".\") {
// \\.\COM42
+ let path = parser.finish();
let (prefix, _) = parse_next_component(path, false);
Some(DeviceNS(prefix))
} else {
+ let path = parser.finish();
let (server, path) = parse_next_component(path, false);
let (share, _) = parse_next_component(path, false);
}
// Parses a drive prefix, e.g. "C:" and "C:\whatever"
-fn parse_drive(prefix: &OsStr) -> Option<u8> {
+fn parse_drive(path: &OsStr) -> Option<u8> {
// In most DOS systems, it is not possible to have more than 26 drive letters.
// See <https://en.wikipedia.org/wiki/Drive_letter_assignment#Common_assignments>.
fn is_valid_drive_letter(drive: &u8) -> bool {
drive.is_ascii_alphabetic()
}
- match prefix.bytes() {
+ match path.bytes() {
[drive, b':', ..] if is_valid_drive_letter(drive) => Some(drive.to_ascii_uppercase()),
_ => None,
}
}
// Parses a drive prefix exactly, e.g. "C:"
-fn parse_drive_exact(prefix: &OsStr) -> Option<u8> {
+fn parse_drive_exact(path: &OsStr) -> Option<u8> {
// only parse two bytes: the drive letter and the drive separator
- if prefix.len() == 2 { parse_drive(prefix) } else { None }
-}
-
-fn strip_prefix<'a>(path: &'a OsStr, prefix: &str) -> Option<&'a OsStr> {
- // `path` and `prefix` are valid wtf8 and utf8 encoded slices respectively, `path[prefix.len()]`
- // is thus a code point boundary and `path[prefix.len()..]` is a valid wtf8 encoded slice.
- match path.bytes().strip_prefix(prefix.as_bytes()) {
- Some(path) => unsafe { Some(bytes_as_os_str(path)) },
- None => None,
+ if path.bytes().get(2).map(|&x| is_sep_byte(x)).unwrap_or(true) {
+ parse_drive(path)
+ } else {
+ None
}
}
// SAFETY: `fill_utf16_buf` ensures the `buffer` and `size` are valid.
// `lpfilename` is a pointer to a null terminated string that is not
// invalidated until after `GetFullPathNameW` returns successfully.
- |buffer, size| unsafe {
- // While the docs for `GetFullPathNameW` have the standard note
- // about needing a `\\?\` path for a long lpfilename, this does not
- // appear to be true in practice.
- // See:
- // https://stackoverflow.com/questions/38036943/getfullpathnamew-and-long-windows-file-paths
- // https://googleprojectzero.blogspot.com/2016/02/the-definitive-guide-on-win32-to-nt.html
- c::GetFullPathNameW(lpfilename, size, buffer, ptr::null_mut())
- },
+ |buffer, size| unsafe { c::GetFullPathNameW(lpfilename, size, buffer, ptr::null_mut()) },
|mut absolute| {
path.clear();
/// Make a Windows path absolute.
pub(crate) fn absolute(path: &Path) -> io::Result<PathBuf> {
- if path.as_os_str().bytes().starts_with(br"\\?\") {
- return Ok(path.into());
+ let path = path.as_os_str();
+ let prefix = parse_prefix(path);
+ // Verbatim paths should not be modified.
+ if prefix.map(|x| x.is_verbatim()).unwrap_or(false) {
+ // NULs in verbatim paths are rejected for consistency.
+ if path.bytes().contains(&0) {
+ return Err(io::const_io_error!(
+ io::ErrorKind::InvalidInput,
+ "strings passed to WinAPI cannot contain NULs",
+ ));
+ }
+ return Ok(path.to_owned().into());
}
+
let path = to_u16s(path)?;
let lpfilename = path.as_ptr();
fill_utf16_buf(
// A path that contains null is not a valid path.
assert!(maybe_verbatim(Path::new("\0")).is_err());
}
+
+fn parse_prefix(path: &str) -> Option<Prefix<'_>> {
+ super::parse_prefix(OsStr::new(path))
+}
+
+#[test]
+fn test_parse_prefix_verbatim() {
+ let prefix = Some(Prefix::VerbatimDisk(b'C'));
+ assert_eq!(prefix, parse_prefix(r"\\?\C:/windows/system32/notepad.exe"));
+ assert_eq!(prefix, parse_prefix(r"\\?\C:\windows\system32\notepad.exe"));
+}
+
+#[test]
+fn test_parse_prefix_verbatim_device() {
+ let prefix = Some(Prefix::UNC(OsStr::new("?"), OsStr::new("C:")));
+ assert_eq!(prefix, parse_prefix(r"//?/C:/windows/system32/notepad.exe"));
+ assert_eq!(prefix, parse_prefix(r"//?/C:\windows\system32\notepad.exe"));
+ assert_eq!(prefix, parse_prefix(r"/\?\C:\windows\system32\notepad.exe"));
+ assert_eq!(prefix, parse_prefix(r"\\?/C:\windows\system32\notepad.exe"));
+}
// Anonymous pipes
////////////////////////////////////////////////////////////////////////////////
-pub struct AnonPipe {
- inner: Handle,
+// A 64kb pipe capacity is the same as a typical Linux default.
+const PIPE_BUFFER_CAPACITY: u32 = 64 * 1024;
+
+pub enum AnonPipe {
+ Sync(Handle),
+ Async(Handle),
}
impl IntoInner<Handle> for AnonPipe {
fn into_inner(self) -> Handle {
- self.inner
+ match self {
+ Self::Sync(handle) => handle,
+ Self::Async(handle) => handle,
+ }
}
}
pub ours: AnonPipe,
pub theirs: AnonPipe,
}
+impl Pipes {
+ /// Create a new pair of pipes where both pipes are synchronous.
+ ///
+ /// These must not be used asynchronously.
+ pub fn new_synchronous(
+ ours_readable: bool,
+ their_handle_inheritable: bool,
+ ) -> io::Result<Self> {
+ unsafe {
+ // If `CreatePipe` succeeds, these will be our pipes.
+ let mut read = ptr::null_mut();
+ let mut write = ptr::null_mut();
+
+ if c::CreatePipe(&mut read, &mut write, ptr::null(), PIPE_BUFFER_CAPACITY) == 0 {
+ Err(io::Error::last_os_error())
+ } else {
+ let (ours, theirs) = if ours_readable { (read, write) } else { (write, read) };
+ let ours = Handle::from_raw_handle(ours);
+ let theirs = Handle::from_raw_handle(theirs);
+
+ if their_handle_inheritable {
+ theirs.set_inheritable()?;
+ }
+
+ Ok(Pipes { ours: AnonPipe::Sync(ours), theirs: AnonPipe::Sync(theirs) })
+ }
+ }
+ }
+}
/// Although this looks similar to `anon_pipe` in the Unix module it's actually
/// subtly different. Here we'll return two pipes in the `Pipes` return value,
/// with `OVERLAPPED` instances, but also works out ok if it's only ever used
/// once at a time (which we do indeed guarantee).
pub fn anon_pipe(ours_readable: bool, their_handle_inheritable: bool) -> io::Result<Pipes> {
- // A 64kb pipe capacity is the same as a typical Linux default.
- const PIPE_BUFFER_CAPACITY: u32 = 64 * 1024;
-
// Note that we specifically do *not* use `CreatePipe` here because
// unfortunately the anonymous pipes returned do not support overlapped
// operations. Instead, we create a "hopefully unique" name and create a
};
opts.security_attributes(&mut sa);
let theirs = File::open(Path::new(&name), &opts)?;
- let theirs = AnonPipe { inner: theirs.into_inner() };
+ let theirs = AnonPipe::Sync(theirs.into_inner());
- Ok(Pipes {
- ours: AnonPipe { inner: ours },
- theirs: AnonPipe { inner: theirs.into_inner() },
- })
+ Ok(Pipes { ours: AnonPipe::Async(ours), theirs })
}
}
/// This is achieved by creating a new set of pipes and spawning a thread that
/// relays messages between the source and the synchronous pipe.
pub fn spawn_pipe_relay(
- source: &AnonPipe,
+ source: &Handle,
ours_readable: bool,
their_handle_inheritable: bool,
) -> io::Result<AnonPipe> {
// We need this handle to live for the lifetime of the thread spawned below.
- let source = source.duplicate()?;
+ let source = AnonPipe::Async(source.duplicate(0, true, c::DUPLICATE_SAME_ACCESS)?);
// create a new pair of anon pipes.
let Pipes { theirs, ours } = anon_pipe(ours_readable, their_handle_inheritable)?;
impl AnonPipe {
pub fn handle(&self) -> &Handle {
- &self.inner
+ match self {
+ Self::Async(ref handle) => handle,
+ Self::Sync(ref handle) => handle,
+ }
}
pub fn into_handle(self) -> Handle {
- self.inner
- }
- fn duplicate(&self) -> io::Result<Self> {
- self.inner.duplicate(0, false, c::DUPLICATE_SAME_ACCESS).map(|inner| AnonPipe { inner })
+ self.into_inner()
}
pub fn read(&self, buf: &mut [u8]) -> io::Result<usize> {
let result = unsafe {
let len = crate::cmp::min(buf.len(), c::DWORD::MAX as usize) as c::DWORD;
- self.alertable_io_internal(c::ReadFileEx, buf.as_mut_ptr() as _, len)
+ match self {
+ Self::Sync(ref handle) => handle.read(buf),
+ Self::Async(_) => {
+ self.alertable_io_internal(c::ReadFileEx, buf.as_mut_ptr() as _, len)
+ }
+ }
};
match result {
}
pub fn read_vectored(&self, bufs: &mut [IoSliceMut<'_>]) -> io::Result<usize> {
- self.inner.read_vectored(bufs)
+ io::default_read_vectored(|buf| self.read(buf), bufs)
}
#[inline]
pub fn is_read_vectored(&self) -> bool {
- self.inner.is_read_vectored()
+ false
}
pub fn write(&self, buf: &[u8]) -> io::Result<usize> {
unsafe {
let len = crate::cmp::min(buf.len(), c::DWORD::MAX as usize) as c::DWORD;
- self.alertable_io_internal(c::WriteFileEx, buf.as_ptr() as _, len)
+ match self {
+ Self::Sync(ref handle) => handle.write(buf),
+ Self::Async(_) => {
+ self.alertable_io_internal(c::WriteFileEx, buf.as_ptr() as _, len)
+ }
+ }
}
}
pub fn write_vectored(&self, bufs: &[IoSlice<'_>]) -> io::Result<usize> {
- self.inner.write_vectored(bufs)
+ io::default_write_vectored(|buf| self.write(buf), bufs)
}
#[inline]
pub fn is_write_vectored(&self) -> bool {
- self.inner.is_write_vectored()
+ false
}
/// Synchronizes asynchronous reads or writes using our anonymous pipe.
// Asynchronous read of the pipe.
// If successful, `callback` will be called once it completes.
- let result = io(self.inner.as_handle(), buf, len, &mut overlapped, callback);
+ let result = io(self.handle().as_handle(), buf, len, &mut overlapped, callback);
if result == c::FALSE {
// We can return here because the call failed.
// After this we must not return until the I/O completes.
use crate::os::windows::io::{AsRawHandle, FromRawHandle, IntoRawHandle};
use crate::path::{Path, PathBuf};
use crate::ptr;
+use crate::sys::args::{self, Arg};
use crate::sys::c;
use crate::sys::c::NonZeroDWORD;
use crate::sys::cvt;
use crate::sys::fs::{File, OpenOptions};
use crate::sys::handle::Handle;
use crate::sys::path;
-use crate::sys::pipe::{self, AnonPipe};
+use crate::sys::pipe::{self, AnonPipe, Pipes};
use crate::sys::stdio;
use crate::sys_common::mutex::StaticMutex;
use crate::sys_common::process::{CommandEnv, CommandEnvs};
-use crate::sys_common::{AsInner, IntoInner};
+use crate::sys_common::IntoInner;
use libc::{c_void, EXIT_FAILURE, EXIT_SUCCESS};
}
}
-fn ensure_no_nuls<T: AsRef<OsStr>>(str: T) -> io::Result<T> {
+pub(crate) fn ensure_no_nuls<T: AsRef<OsStr>>(str: T) -> io::Result<T> {
if str.as_ref().encode_wide().any(|b| b == 0) {
Err(io::const_io_error!(ErrorKind::InvalidInput, "nul byte found in provided data"))
} else {
Inherit,
Null,
MakePipe,
- Pipe(AnonPipe),
+ AsyncPipe(Handle),
Handle(Handle),
}
pub stderr: Option<AnonPipe>,
}
-#[derive(Debug)]
-enum Arg {
- /// Add quotes (if needed)
- Regular(OsString),
- /// Append raw string without quoting
- Raw(OsString),
-}
-
impl Command {
pub fn new(program: &OsStr) -> Command {
Command {
program.len().checked_sub(5).and_then(|i| program.get(i..)),
Some([46, 98 | 66, 97 | 65, 116 | 84, 0] | [46, 99 | 67, 109 | 77, 100 | 68, 0])
);
- let mut cmd_str =
- make_command_line(&program, &self.args, self.force_quotes_enabled, is_batch_file)?;
+ let (program, mut cmd_str) = if is_batch_file {
+ (
+ command_prompt()?,
+ args::make_bat_command_line(
+ &args::to_user_path(program)?,
+ &self.args,
+ self.force_quotes_enabled,
+ )?,
+ )
+ } else {
+ let cmd_str = make_command_line(&self.program, &self.args, self.force_quotes_enabled)?;
+ (program, cmd_str)
+ };
cmd_str.push(0); // add null terminator
// stolen from the libuv code.
},
Stdio::MakePipe => {
- let ours_readable = stdio_id != c::STD_INPUT_HANDLE;
- let pipes = pipe::anon_pipe(ours_readable, true)?;
+ // Handles that are passed to a child process must be synchronous
+ // because they will be read synchronously (see #95759).
+ // Therefore we prefer to make both ends of a pipe synchronous
+ // just in case our end of the pipe is passed to another process.
+ //
+ // However, we may need to read from both the child's stdout and
+ // stderr simultaneously when waiting for output. This requires
+ // async reads so as to avoid blocking either pipe.
+ //
+ // The solution used here is to make handles synchronous
+ // except for our side of the stdout and sterr pipes.
+ // If our side of those pipes do end up being given to another
+ // process then we use a "pipe relay" to synchronize access
+ // (see `Stdio::AsyncPipe` below).
+ let pipes = if stdio_id == c::STD_INPUT_HANDLE {
+ // For stdin both sides of the pipe are synchronous.
+ Pipes::new_synchronous(false, true)?
+ } else {
+ // For stdout/stderr our side of the pipe is async and their side is synchronous.
+ pipe::anon_pipe(true, true)?
+ };
*pipe = Some(pipes.ours);
Ok(pipes.theirs.into_handle())
}
- Stdio::Pipe(ref source) => {
+ Stdio::AsyncPipe(ref source) => {
+ // We need to synchronize asynchronous pipes by using a pipe relay.
let ours_readable = stdio_id != c::STD_INPUT_HANDLE;
pipe::spawn_pipe_relay(source, ours_readable, true).map(AnonPipe::into_handle)
}
impl From<AnonPipe> for Stdio {
fn from(pipe: AnonPipe) -> Stdio {
- Stdio::Pipe(pipe)
+ // Note that it's very important we don't give async handles to child processes.
+ // Therefore if the pipe is asynchronous we must have a way to turn it synchronous.
+ // See #95759.
+ match pipe {
+ AnonPipe::Sync(handle) => Stdio::Handle(handle),
+ AnonPipe::Async(handle) => Stdio::AsyncPipe(handle),
+ }
}
}
}
}
-enum Quote {
- // Every arg is quoted
- Always,
- // Whitespace and empty args are quoted
- Auto,
- // Arg appended without any changes (#29494)
- Never,
-}
-
// Produces a wide string *without terminating null*; returns an error if
// `prog` or any of the `args` contain a nul.
-fn make_command_line(
- prog: &[u16],
- args: &[Arg],
- force_quotes: bool,
- is_batch_file: bool,
-) -> io::Result<Vec<u16>> {
+fn make_command_line(argv0: &OsStr, args: &[Arg], force_quotes: bool) -> io::Result<Vec<u16>> {
// Encode the command and arguments in a command line string such
// that the spawned process may recover them using CommandLineToArgvW.
let mut cmd: Vec<u16> = Vec::new();
- // CreateFileW has special handling for .bat and .cmd files, which means we
- // need to add an extra pair of quotes surrounding the whole command line
- // so they are properly passed on to the script.
- // See issue #91991.
- if is_batch_file {
- cmd.push(b'"' as u16);
- }
-
// Always quote the program name so CreateProcess to avoid ambiguity when
// the child process parses its arguments.
// Note that quotes aren't escaped here because they can't be used in arg0.
// But that's ok because file paths can't contain quotes.
cmd.push(b'"' as u16);
- cmd.extend_from_slice(prog.strip_suffix(&[0]).unwrap_or(prog));
+ cmd.extend(argv0.encode_wide());
cmd.push(b'"' as u16);
for arg in args {
cmd.push(' ' as u16);
- let (arg, quote) = match arg {
- Arg::Regular(arg) => (arg, if force_quotes { Quote::Always } else { Quote::Auto }),
- Arg::Raw(arg) => (arg, Quote::Never),
- };
- append_arg(&mut cmd, arg, quote)?;
- }
- if is_batch_file {
- cmd.push(b'"' as u16);
- }
- return Ok(cmd);
-
- fn append_arg(cmd: &mut Vec<u16>, arg: &OsStr, quote: Quote) -> io::Result<()> {
- // If an argument has 0 characters then we need to quote it to ensure
- // that it actually gets passed through on the command line or otherwise
- // it will be dropped entirely when parsed on the other end.
- ensure_no_nuls(arg)?;
- let arg_bytes = &arg.as_inner().inner.as_inner();
- let (quote, escape) = match quote {
- Quote::Always => (true, true),
- Quote::Auto => {
- (arg_bytes.iter().any(|c| *c == b' ' || *c == b'\t') || arg_bytes.is_empty(), true)
- }
- Quote::Never => (false, false),
- };
- if quote {
- cmd.push('"' as u16);
- }
-
- let mut backslashes: usize = 0;
- for x in arg.encode_wide() {
- if escape {
- if x == '\\' as u16 {
- backslashes += 1;
- } else {
- if x == '"' as u16 {
- // Add n+1 backslashes to total 2n+1 before internal '"'.
- cmd.extend((0..=backslashes).map(|_| '\\' as u16));
- }
- backslashes = 0;
- }
- }
- cmd.push(x);
- }
-
- if quote {
- // Add n backslashes to total 2n before ending '"'.
- cmd.extend((0..backslashes).map(|_| '\\' as u16));
- cmd.push('"' as u16);
- }
- Ok(())
+ args::append_arg(&mut cmd, arg, force_quotes)?;
}
+ Ok(cmd)
+}
+
+// Get `cmd.exe` for use with bat scripts, encoded as a UTF-16 string.
+fn command_prompt() -> io::Result<Vec<u16>> {
+ let mut system: Vec<u16> = super::fill_utf16_buf(
+ |buf, size| unsafe { c::GetSystemDirectoryW(buf, size) },
+ |buf| buf.into(),
+ )?;
+ system.extend("\\cmd.exe".encode_utf16().chain([0]));
+ Ok(system)
}
fn make_envp(maybe_env: Option<BTreeMap<EnvKey, OsString>>) -> io::Result<(*mut c_void, Vec<u16>)> {
use crate::env;
use crate::ffi::{OsStr, OsString};
use crate::process::Command;
-use crate::sys::to_u16s;
#[test]
fn test_raw_args() {
let command_line = &make_command_line(
- &to_u16s("quoted exe").unwrap(),
+ OsStr::new("quoted exe"),
&[
Arg::Regular(OsString::from("quote me")),
Arg::Raw(OsString::from("quote me *not*")),
Arg::Regular(OsString::from("optional-quotes")),
],
false,
- false,
)
.unwrap();
assert_eq!(
fn test_make_command_line() {
fn test_wrapper(prog: &str, args: &[&str], force_quotes: bool) -> String {
let command_line = &make_command_line(
- &to_u16s(prog).unwrap(),
+ OsStr::new(prog),
&args.iter().map(|a| Arg::Regular(OsString::from(a))).collect::<Vec<_>>(),
force_quotes,
- false,
)
.unwrap();
String::from_utf16(command_line).unwrap()
// [4]: Windows Internals, Part 1, ISBN 9780735671300
use crate::convert::TryFrom;
+use crate::pin::Pin;
use crate::ptr;
use crate::sync::atomic::{
AtomicI8, AtomicPtr,
// Ordering::Release when writing NOTIFIED (the 'token') in unpark(), and using
// Ordering::Acquire when reading this state in park() after waking up.
impl Parker {
- pub fn new() -> Self {
- Self { state: AtomicI8::new(EMPTY) }
+ /// Construct the Windows parker. The UNIX parker implementation
+ /// requires this to happen in-place.
+ pub unsafe fn new(parker: *mut Parker) {
+ parker.write(Self { state: AtomicI8::new(EMPTY) });
}
// Assumes this is only called by the thread that owns the Parker,
- // which means that `self.state != PARKED`.
- pub unsafe fn park(&self) {
+ // which means that `self.state != PARKED`. This implementation doesn't require `Pin`,
+ // but other implementations do.
+ pub unsafe fn park(self: Pin<&Self>) {
// Change NOTIFIED=>EMPTY or EMPTY=>PARKED, and directly return in the
// first case.
if self.state.fetch_sub(1, Acquire) == NOTIFIED {
}
// Assumes this is only called by the thread that owns the Parker,
- // which means that `self.state != PARKED`.
- pub unsafe fn park_timeout(&self, timeout: Duration) {
+ // which means that `self.state != PARKED`. This implementation doesn't require `Pin`,
+ // but other implementations do.
+ pub unsafe fn park_timeout(self: Pin<&Self>, timeout: Duration) {
// Change NOTIFIED=>EMPTY or EMPTY=>PARKED, and directly return in the
// first case.
if self.state.fetch_sub(1, Acquire) == NOTIFIED {
}
}
- pub fn unpark(&self) {
+ // This implementation doesn't require `Pin`, but other implementations do.
+ pub fn unpark(self: Pin<&Self>) {
// Change PARKED=>NOTIFIED, EMPTY=>NOTIFIED, or NOTIFIED=>NOTIFIED, and
// wake the thread in the first case.
//
+use crate::pin::Pin;
use crate::sync::atomic::AtomicU32;
use crate::sync::atomic::Ordering::{Acquire, Release};
use crate::sys::futex::{futex_wait, futex_wake};
// Ordering::Release when writing NOTIFIED (the 'token') in unpark(), and using
// Ordering::Acquire when checking for this state in park().
impl Parker {
- #[inline]
- pub const fn new() -> Self {
- Parker { state: AtomicU32::new(EMPTY) }
+ /// Construct the futex parker. The UNIX parker implementation
+ /// requires this to happen in-place.
+ pub unsafe fn new(parker: *mut Parker) {
+ parker.write(Self { state: AtomicU32::new(EMPTY) });
}
// Assumes this is only called by the thread that owns the Parker,
// which means that `self.state != PARKED`.
- pub unsafe fn park(&self) {
+ pub unsafe fn park(self: Pin<&Self>) {
// Change NOTIFIED=>EMPTY or EMPTY=>PARKED, and directly return in the
// first case.
if self.state.fetch_sub(1, Acquire) == NOTIFIED {
}
// Assumes this is only called by the thread that owns the Parker,
- // which means that `self.state != PARKED`.
- pub unsafe fn park_timeout(&self, timeout: Duration) {
+ // which means that `self.state != PARKED`. This implementation doesn't
+ // require `Pin`, but other implementations do.
+ pub unsafe fn park_timeout(self: Pin<&Self>, timeout: Duration) {
// Change NOTIFIED=>EMPTY or EMPTY=>PARKED, and directly return in the
// first case.
if self.state.fetch_sub(1, Acquire) == NOTIFIED {
}
}
+ // This implementation doesn't require `Pin`, but other implementations do.
#[inline]
- pub fn unpark(&self) {
+ pub fn unpark(self: Pin<&Self>) {
// Change PARKED=>NOTIFIED, EMPTY=>NOTIFIED, or NOTIFIED=>NOTIFIED, and
// wake the thread in the first case.
//
//! Parker implementation based on a Mutex and Condvar.
+use crate::pin::Pin;
use crate::sync::atomic::AtomicUsize;
use crate::sync::atomic::Ordering::SeqCst;
use crate::sync::{Condvar, Mutex};
}
impl Parker {
- pub fn new() -> Self {
- Parker { state: AtomicUsize::new(EMPTY), lock: Mutex::new(()), cvar: Condvar::new() }
+ /// Construct the generic parker. The UNIX parker implementation
+ /// requires this to happen in-place.
+ pub unsafe fn new(parker: *mut Parker) {
+ parker.write(Parker {
+ state: AtomicUsize::new(EMPTY),
+ lock: Mutex::new(()),
+ cvar: Condvar::new(),
+ });
}
- // This implementation doesn't require `unsafe`, but other implementations
- // may assume this is only called by the thread that owns the Parker.
- pub unsafe fn park(&self) {
+ // This implementation doesn't require `unsafe` and `Pin`, but other implementations do.
+ pub unsafe fn park(self: Pin<&Self>) {
// If we were previously notified then we consume this notification and
// return quickly.
if self.state.compare_exchange(NOTIFIED, EMPTY, SeqCst, SeqCst).is_ok() {
}
}
- // This implementation doesn't require `unsafe`, but other implementations
- // may assume this is only called by the thread that owns the Parker.
- pub unsafe fn park_timeout(&self, dur: Duration) {
+ // This implementation doesn't require `unsafe` and `Pin`, but other implementations do.
+ pub unsafe fn park_timeout(self: Pin<&Self>, dur: Duration) {
// Like `park` above we have a fast path for an already-notified thread, and
// afterwards we start coordinating for a sleep.
// return quickly.
}
}
- pub fn unpark(&self) {
+ // This implementation doesn't require `Pin`, but other implementations do.
+ pub fn unpark(self: Pin<&Self>) {
// To ensure the unparked thread will observe any writes we made
// before this call, we must perform a release operation that `park`
// can synchronize with. To do that we must write `NOTIFIED` even if
pub use futex::Parker;
} else if #[cfg(windows)] {
pub use crate::sys::thread_parker::Parker;
+ } else if #[cfg(target_family = "unix")] {
+ pub use crate::sys::thread_parker::Parker;
} else {
mod generic;
pub use generic::Parker;
use crate::collections::TryReserveError;
use crate::fmt;
use crate::hash::{Hash, Hasher};
-use crate::iter::FromIterator;
+use crate::iter::{FromIterator, FusedIterator};
use crate::mem;
use crate::ops;
use crate::rc::Rc;
}
}
+#[stable(feature = "encode_wide_fused_iterator", since = "1.62.0")]
+impl FusedIterator for EncodeWide<'_> {}
+
impl Hash for CodePoint {
#[inline]
fn hash<H: Hasher>(&self, state: &mut H) {
/// The [`with`] method yields a reference to the contained value which cannot be
/// sent across threads or escape the given closure.
///
+/// [`thread_local!`]: crate::thread_local
+///
/// # Initialization and Destruction
///
/// Initialization is dynamically performed on the first call to [`with`]
//! [`Cell`]: crate::cell::Cell
//! [`RefCell`]: crate::cell::RefCell
//! [`with`]: LocalKey::with
+//! [`thread_local!`]: crate::thread_local
#![stable(feature = "rust1", since = "1.0.0")]
#![deny(unsafe_op_in_unsafe_fn)]
use crate::num::NonZeroUsize;
use crate::panic;
use crate::panicking;
+use crate::pin::Pin;
+use crate::ptr::addr_of_mut;
use crate::str;
use crate::sync::Arc;
use crate::sys::thread as imp;
pub fn park() {
// SAFETY: park_timeout is called on the parker owned by this thread.
unsafe {
- current().inner.parker.park();
+ current().inner.as_ref().parker().park();
}
}
pub fn park_timeout(dur: Duration) {
// SAFETY: park_timeout is called on the parker owned by this thread.
unsafe {
- current().inner.parker.park_timeout(dur);
+ current().inner.as_ref().parker().park_timeout(dur);
}
}
parker: Parker,
}
+impl Inner {
+ fn parker(self: Pin<&Self>) -> Pin<&Parker> {
+ unsafe { Pin::map_unchecked(self, |inner| &inner.parker) }
+ }
+}
+
#[derive(Clone)]
#[stable(feature = "rust1", since = "1.0.0")]
/// A handle to a thread.
///
/// [`thread::current`]: current
pub struct Thread {
- inner: Arc<Inner>,
+ inner: Pin<Arc<Inner>>,
}
impl Thread {
// Used only internally to construct a thread object without spawning
// Panics if the name contains nuls.
pub(crate) fn new(name: Option<CString>) -> Thread {
- Thread { inner: Arc::new(Inner { name, id: ThreadId::new(), parker: Parker::new() }) }
+ // We have to use `unsafe` here to constuct the `Parker` in-place,
+ // which is required for the UNIX implementation.
+ //
+ // SAFETY: We pin the Arc immediately after creation, so its address never
+ // changes.
+ let inner = unsafe {
+ let mut arc = Arc::<Inner>::new_uninit();
+ let ptr = Arc::get_mut_unchecked(&mut arc).as_mut_ptr();
+ addr_of_mut!((*ptr).name).write(name);
+ addr_of_mut!((*ptr).id).write(ThreadId::new());
+ Parker::new(addr_of_mut!((*ptr).parker));
+ Pin::new_unchecked(arc.assume_init())
+ };
+
+ Thread { inner }
}
/// Atomically makes the handle's token available if it is not already.
#[stable(feature = "rust1", since = "1.0.0")]
#[inline]
pub fn unpark(&self) {
- self.inner.parker.unpark();
+ self.inner.as_ref().parker().unpark();
}
/// Gets the thread's unique identifier.
libc = "0.2"
serde = { version = "1.0.8", features = ["derive"] }
serde_json = "1.0.2"
+tar = "0.4"
toml = "0.5"
ignore = "0.4.10"
opener = "0.5"
once_cell = "1.7.2"
+xz2 = "0.1"
[target.'cfg(windows)'.dependencies.winapi]
version = "0.3"
let mut cmd = Command::new(rustdoc);
- // I am not actually sure why it's necessary to pass the sysroot for `--test`,
- // but `test --doc --stage 0` is broken without it :(
+ // cfg(bootstrap)
+ // NOTE: the `--test` special-casing can be removed when https://github.com/rust-lang/cargo/pull/10594 lands on beta.
if target.is_some() || args.iter().any(|x| x == "--test") {
// The stage0 compiler has a special sysroot distinct from what we
// actually downloaded, so we just always pass the `--sysroot` option,
}
}
- // Needed to be able to run all rustdoc tests.
- if let Some(ref x) = env::var_os("RUSTDOC_RESOURCE_SUFFIX") {
- // This "unstable-options" can be removed when `--resource-suffix` is stabilized
- cmd.arg("-Z").arg("unstable-options");
- cmd.arg("--resource-suffix").arg(x);
- }
-
if verbose > 1 {
eprintln!(
"rustdoc command: {:?}={:?} {:?}",
with output(self.rustfmt_stamp()) as rustfmt_stamp:
rustfmt_stamp.write(self.stage0_rustfmt.channel())
- # Avoid downloading LLVM twice (once for stage0 and once for the master rustc)
- if self.downloading_llvm() and stage0:
- # We want the most recent LLVM submodule update to avoid downloading
- # LLVM more often than necessary.
- #
- # This git command finds that commit SHA, looking for bors-authored
- # commits that modified src/llvm-project or other relevant version
- # stamp files.
- #
- # This works even in a repository that has not yet initialized
- # submodules.
- top_level = subprocess.check_output([
- "git", "rev-parse", "--show-toplevel",
- ]).decode(sys.getdefaultencoding()).strip()
- llvm_sha = subprocess.check_output([
- "git", "rev-list", "--author=bors@rust-lang.org", "-n1",
- "--first-parent", "HEAD",
- "--",
- "{}/src/llvm-project".format(top_level),
- "{}/src/bootstrap/download-ci-llvm-stamp".format(top_level),
- # the LLVM shared object file is named `LLVM-12-rust-{version}-nightly`
- "{}/src/version".format(top_level)
- ]).decode(sys.getdefaultencoding()).strip()
- llvm_assertions = self.get_toml('assertions', 'llvm') == 'true'
- llvm_root = self.llvm_root()
- llvm_lib = os.path.join(llvm_root, "lib")
- if self.program_out_of_date(self.llvm_stamp(), llvm_sha + str(llvm_assertions)):
- self._download_ci_llvm(llvm_sha, llvm_assertions)
- for binary in ["llvm-config", "FileCheck"]:
- self.fix_bin_or_dylib(os.path.join(llvm_root, "bin", binary))
- for lib in os.listdir(llvm_lib):
- if lib.endswith(".so"):
- self.fix_bin_or_dylib(os.path.join(llvm_lib, lib))
- with output(self.llvm_stamp()) as llvm_stamp:
- llvm_stamp.write(llvm_sha + str(llvm_assertions))
-
- def downloading_llvm(self):
- opt = self.get_toml('download-ci-llvm', 'llvm')
- # This is currently all tier 1 targets and tier 2 targets with host tools
- # (since others may not have CI artifacts)
- # https://doc.rust-lang.org/rustc/platform-support.html#tier-1
- supported_platforms = [
- # tier 1
- "aarch64-unknown-linux-gnu",
- "i686-pc-windows-gnu",
- "i686-pc-windows-msvc",
- "i686-unknown-linux-gnu",
- "x86_64-unknown-linux-gnu",
- "x86_64-apple-darwin",
- "x86_64-pc-windows-gnu",
- "x86_64-pc-windows-msvc",
- # tier 2 with host tools
- "aarch64-apple-darwin",
- "aarch64-pc-windows-msvc",
- "aarch64-unknown-linux-musl",
- "arm-unknown-linux-gnueabi",
- "arm-unknown-linux-gnueabihf",
- "armv7-unknown-linux-gnueabihf",
- "mips-unknown-linux-gnu",
- "mips64-unknown-linux-gnuabi64",
- "mips64el-unknown-linux-gnuabi64",
- "mipsel-unknown-linux-gnu",
- "powerpc-unknown-linux-gnu",
- "powerpc64-unknown-linux-gnu",
- "powerpc64le-unknown-linux-gnu",
- "riscv64gc-unknown-linux-gnu",
- "s390x-unknown-linux-gnu",
- "x86_64-unknown-freebsd",
- "x86_64-unknown-illumos",
- "x86_64-unknown-linux-musl",
- "x86_64-unknown-netbsd",
- ]
- return opt == "true" \
- or (opt == "if-available" and self.build in supported_platforms)
-
def _download_component_helper(
self, filename, pattern, tarball_suffix, stage0=True, key=None
):
)
unpack(tarball, tarball_suffix, self.bin_root(stage0), match=pattern, verbose=self.verbose)
- def _download_ci_llvm(self, llvm_sha, llvm_assertions):
- if not llvm_sha:
- print("error: could not find commit hash for downloading LLVM")
- print("help: maybe your repository history is too shallow?")
- print("help: consider disabling `download-ci-llvm`")
- print("help: or fetch enough history to include one upstream commit")
- exit(1)
- cache_prefix = "llvm-{}-{}".format(llvm_sha, llvm_assertions)
- cache_dst = os.path.join(self.build_dir, "cache")
- rustc_cache = os.path.join(cache_dst, cache_prefix)
- if not os.path.exists(rustc_cache):
- os.makedirs(rustc_cache)
-
- base = "https://ci-artifacts.rust-lang.org"
- url = "rustc-builds/{}".format(llvm_sha)
- if llvm_assertions:
- url = url.replace('rustc-builds', 'rustc-builds-alt')
- # ci-artifacts are only stored as .xz, not .gz
- if not support_xz():
- print("error: XZ support is required to download LLVM")
- print("help: consider disabling `download-ci-llvm` or using python3")
- exit(1)
- tarball_suffix = '.tar.xz'
- filename = "rust-dev-nightly-" + self.build + tarball_suffix
- tarball = os.path.join(rustc_cache, filename)
- if not os.path.exists(tarball):
- help_on_error = "error: failed to download llvm from ci"
- help_on_error += "\nhelp: old builds get deleted after a certain time"
- help_on_error += "\nhelp: if trying to compile an old commit of rustc,"
- help_on_error += " disable `download-ci-llvm` in config.toml:"
- help_on_error += "\n"
- help_on_error += "\n[llvm]"
- help_on_error += "\ndownload-ci-llvm = false"
- help_on_error += "\n"
- get(
- base,
- "{}/{}".format(url, filename),
- tarball,
- self.checksums_sha256,
- verbose=self.verbose,
- do_verify=False,
- help_on_error=help_on_error,
- )
- unpack(tarball, tarball_suffix, self.llvm_root(),
- match="rust-dev",
- verbose=self.verbose)
-
def fix_bin_or_dylib(self, fname):
"""Modifies the interpreter section of 'fname' to fix the dynamic linker,
or the RPATH section, to fix the dynamic library search path
"""
return os.path.join(self.bin_root(True), '.rustfmt-stamp')
- def llvm_stamp(self):
- """Return the path for .llvm-stamp
-
- >>> rb = RustBuild()
- >>> rb.build_dir = "build"
- >>> rb.llvm_stamp() == os.path.join("build", "ci-llvm", ".llvm-stamp")
- True
- """
- return os.path.join(self.llvm_root(), '.llvm-stamp')
-
-
def program_out_of_date(self, stamp_path, key):
"""Check if the given program stamp is out of date"""
if not os.path.exists(stamp_path) or self.clean:
subdir = "ci-rustc"
return os.path.join(self.build_dir, self.build, subdir)
- def llvm_root(self):
- """Return the CI LLVM root directory
-
- >>> rb = RustBuild()
- >>> rb.build_dir = "build"
- >>> rb.llvm_root() == os.path.join("build", "ci-llvm")
- True
-
- When the 'build' property is given should be a nested directory:
-
- >>> rb.build = "devel"
- >>> rb.llvm_root() == os.path.join("build", "devel", "ci-llvm")
- True
- """
- return os.path.join(self.build_dir, self.build, "ci-llvm")
-
def get_toml(self, key, section=None):
"""Returns the value of the given key in config.toml, otherwise returns None
-use std::any::Any;
+use std::any::{type_name, Any};
use std::cell::{Cell, RefCell};
use std::collections::BTreeSet;
use std::env;
use std::ffi::OsStr;
-use std::fmt::Debug;
+use std::fmt::{Debug, Write};
use std::fs;
use std::hash::Hash;
use std::ops::Deref;
use std::time::{Duration, Instant};
use crate::cache::{Cache, Interned, INTERNER};
-use crate::check;
use crate::compile;
use crate::config::{SplitDebuginfo, TargetSelection};
use crate::dist;
use crate::tool::{self, SourceType};
use crate::util::{self, add_dylib_path, add_link_lib_path, exe, libdir, output, t};
use crate::EXTRA_CHECK_CFGS;
+use crate::{check, Config};
use crate::{Build, CLang, DocTests, GitRepo, Mode};
pub use crate::Compiler;
if found_kind.is_empty() {
panic!("empty kind in task path {}", path.display());
}
- kind = Some(Kind::parse(found_kind));
+ kind = Kind::parse(found_kind);
+ assert!(kind.is_some());
path = Path::new(found_prefix).join(components.as_path());
}
}
Check,
Clippy,
Fix,
+ Format,
Test,
Bench,
- Dist,
Doc,
+ Clean,
+ Dist,
Install,
Run,
+ Setup,
}
impl Kind {
- fn parse(string: &str) -> Kind {
- match string {
- "build" => Kind::Build,
- "check" => Kind::Check,
+ pub fn parse(string: &str) -> Option<Kind> {
+ // these strings, including the one-letter aliases, must match the x.py help text
+ Some(match string {
+ "build" | "b" => Kind::Build,
+ "check" | "c" => Kind::Check,
"clippy" => Kind::Clippy,
"fix" => Kind::Fix,
- "test" => Kind::Test,
+ "fmt" => Kind::Format,
+ "test" | "t" => Kind::Test,
"bench" => Kind::Bench,
+ "doc" | "d" => Kind::Doc,
+ "clean" => Kind::Clean,
"dist" => Kind::Dist,
- "doc" => Kind::Doc,
"install" => Kind::Install,
- "run" => Kind::Run,
- other => panic!("unknown kind: {}", other),
- }
+ "run" | "r" => Kind::Run,
+ "setup" => Kind::Setup,
+ _ => return None,
+ })
}
- fn as_str(&self) -> &'static str {
+ pub fn as_str(&self) -> &'static str {
match self {
Kind::Build => "build",
Kind::Check => "check",
Kind::Clippy => "clippy",
Kind::Fix => "fix",
+ Kind::Format => "fmt",
Kind::Test => "test",
Kind::Bench => "bench",
- Kind::Dist => "dist",
Kind::Doc => "doc",
+ Kind::Clean => "clean",
+ Kind::Dist => "dist",
Kind::Install => "install",
Kind::Run => "run",
+ Kind::Setup => "setup",
}
}
}
native::Lld,
native::CrtBeginEnd
),
- Kind::Check | Kind::Clippy { .. } | Kind::Fix => describe!(
+ Kind::Check => describe!(
check::Std,
check::Rustc,
check::Rustdoc,
dist::RustcDocs,
dist::Mingw,
dist::Rustc,
- dist::DebuggerScripts,
dist::Std,
dist::RustcDev,
dist::Analysis,
install::Rustc
),
Kind::Run => describe!(run::ExpandYamlAnchors, run::BuildManifest, run::BumpStage0),
+ // These commands either don't use paths, or they're special-cased in Build::build()
+ Kind::Clean | Kind::Clippy | Kind::Fix | Kind::Format | Kind::Setup => vec![],
}
}
- pub fn get_help(build: &Build, subcommand: &str) -> Option<String> {
- let kind = match subcommand {
- "build" | "b" => Kind::Build,
- "doc" | "d" => Kind::Doc,
- "test" | "t" => Kind::Test,
- "bench" => Kind::Bench,
- "dist" => Kind::Dist,
- "install" => Kind::Install,
- _ => return None,
- };
+ pub fn get_help(build: &Build, kind: Kind) -> Option<String> {
+ let step_descriptions = Builder::get_step_descriptions(kind);
+ if step_descriptions.is_empty() {
+ return None;
+ }
let builder = Self::new_internal(build, kind, vec![]);
let builder = &builder;
// The "build" kind here is just a placeholder, it will be replaced with something else in
// the following statement.
let mut should_run = ShouldRun::new(builder, Kind::Build);
- for desc in Builder::get_step_descriptions(builder.kind) {
+ for desc in step_descriptions {
should_run.kind = desc.kind;
should_run = (desc.should_run)(should_run);
}
let mut help = String::from("Available paths:\n");
let mut add_path = |path: &Path| {
- help.push_str(&format!(" ./x.py {} {}\n", subcommand, path.display()));
+ t!(write!(help, " ./x.py {} {}\n", kind.as_str(), path.display()));
};
for pathset in should_run.paths {
match pathset {
None
}
+ /// Convenience wrapper to allow `builder.llvm_link_shared()` instead of `builder.config.llvm_link_shared(&builder)`.
+ pub(crate) fn llvm_link_shared(&self) -> bool {
+ Config::llvm_link_shared(self)
+ }
+
/// Prepares an invocation of `cargo` to be run.
///
/// This will create a `Command` that represents a pending execution of
};
if self.config.print_step_timings && !self.config.dry_run {
- println!("[TIMING] {:?} -- {}.{:03}", step, dur.as_secs(), dur.subsec_millis());
+ let step_string = format!("{:?}", step);
+ let brace_index = step_string.find("{").unwrap_or(0);
+ let type_string = type_name::<S>();
+ println!(
+ "[TIMING] {} {} -- {}.{:03}",
+ &type_string.strip_prefix("bootstrap::").unwrap_or(type_string),
+ &step_string[brace_index..],
+ dur.as_secs(),
+ dur.subsec_millis()
+ );
}
{
// don't save toolstates
config.save_toolstates = None;
config.dry_run = true;
+ config.submodules = Some(false);
config.ninja_in_file = false;
// try to avoid spurious failures in dist where we create/delete each others file
// HACK: rather than pull in `tempdir`, use the one that cargo has conveniently created for us
v.into_iter().map(|(a, _)| a).collect::<Vec<_>>()
}
+fn run_build(paths: &[PathBuf], config: Config) -> Cache {
+ let kind = config.cmd.kind();
+ let build = Build::new(config);
+ let builder = Builder::new(&build);
+ builder.run_step_descriptions(&Builder::get_step_descriptions(kind), paths);
+ builder.cache
+}
+
+#[test]
+fn test_exclude() {
+ let mut config = configure("test", &["A"], &["A"]);
+ config.exclude = vec![TaskPath::parse("src/tools/tidy")];
+ let cache = run_build(&[], config);
+
+ // Ensure we have really excluded tidy
+ assert!(!cache.contains::<test::Tidy>());
+
+ // Ensure other tests are not affected.
+ assert!(cache.contains::<test::RustdocUi>());
+}
+
+#[test]
+fn test_exclude_kind() {
+ let path = PathBuf::from("src/tools/cargotest");
+ let exclude = TaskPath::parse("test::src/tools/cargotest");
+ assert_eq!(exclude, TaskPath { kind: Some(Kind::Test), path: path.clone() });
+
+ let mut config = configure("test", &["A"], &["A"]);
+ // Ensure our test is valid, and `test::Cargotest` would be run without the exclude.
+ assert!(run_build(&[path.clone()], config.clone()).contains::<test::Cargotest>());
+ // Ensure tests for cargotest are skipped.
+ config.exclude = vec![exclude.clone()];
+ assert!(!run_build(&[path.clone()], config).contains::<test::Cargotest>());
+
+ // Ensure builds for cargotest are not skipped.
+ let mut config = configure("build", &["A"], &["A"]);
+ config.exclude = vec![exclude];
+ assert!(run_build(&[path], config).contains::<tool::CargoTest>());
+}
+
mod defaults {
- use super::{configure, first};
+ use super::{configure, first, run_build};
use crate::builder::*;
use crate::Config;
use pretty_assertions::assert_eq;
#[test]
fn build_default() {
- let build = Build::new(configure("build", &["A"], &["A"]));
- let mut builder = Builder::new(&build);
- builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Build), &[]);
+ let mut cache = run_build(&[], configure("build", &["A"], &["A"]));
let a = TargetSelection::from_user("A");
assert_eq!(
- first(builder.cache.all::<compile::Std>()),
+ first(cache.all::<compile::Std>()),
&[
compile::Std { compiler: Compiler { host: a, stage: 0 }, target: a },
compile::Std { compiler: Compiler { host: a, stage: 1 }, target: a },
]
);
- assert!(!builder.cache.all::<compile::Assemble>().is_empty());
+ assert!(!cache.all::<compile::Assemble>().is_empty());
// Make sure rustdoc is only built once.
assert_eq!(
- first(builder.cache.all::<tool::Rustdoc>()),
+ first(cache.all::<tool::Rustdoc>()),
// Recall that rustdoc stages are off-by-one
// - this is the compiler it's _linked_ to, not built with.
&[tool::Rustdoc { compiler: Compiler { host: a, stage: 1 } }],
);
assert_eq!(
- first(builder.cache.all::<compile::Rustc>()),
+ first(cache.all::<compile::Rustc>()),
&[compile::Rustc { compiler: Compiler { host: a, stage: 0 }, target: a },]
);
}
#[test]
fn build_stage_0() {
let config = Config { stage: 0, ..configure("build", &["A"], &["A"]) };
- let build = Build::new(config);
- let mut builder = Builder::new(&build);
- builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Build), &[]);
+ let mut cache = run_build(&[], config);
let a = TargetSelection::from_user("A");
assert_eq!(
- first(builder.cache.all::<compile::Std>()),
+ first(cache.all::<compile::Std>()),
&[compile::Std { compiler: Compiler { host: a, stage: 0 }, target: a },]
);
- assert!(!builder.cache.all::<compile::Assemble>().is_empty());
+ assert!(!cache.all::<compile::Assemble>().is_empty());
assert_eq!(
- first(builder.cache.all::<tool::Rustdoc>()),
+ first(cache.all::<tool::Rustdoc>()),
// This is the beta rustdoc.
// Add an assert here to make sure this is the only rustdoc built.
&[tool::Rustdoc { compiler: Compiler { host: a, stage: 0 } }],
);
- assert!(builder.cache.all::<compile::Rustc>().is_empty());
+ assert!(cache.all::<compile::Rustc>().is_empty());
}
#[test]
fn build_cross_compile() {
let config = Config { stage: 1, ..configure("build", &["A", "B"], &["A", "B"]) };
- let build = Build::new(config);
- let mut builder = Builder::new(&build);
- builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Build), &[]);
+ let mut cache = run_build(&[], config);
let a = TargetSelection::from_user("A");
let b = TargetSelection::from_user("B");
// (since we're producing stage 1 libraries/binaries). But currently
// rustbuild is just a bit buggy here; this should be fixed though.
assert_eq!(
- first(builder.cache.all::<compile::Std>()),
+ first(cache.all::<compile::Std>()),
&[
compile::Std { compiler: Compiler { host: a, stage: 0 }, target: a },
compile::Std { compiler: Compiler { host: a, stage: 1 }, target: a },
]
);
assert_eq!(
- first(builder.cache.all::<compile::Assemble>()),
+ first(cache.all::<compile::Assemble>()),
&[
compile::Assemble { target_compiler: Compiler { host: a, stage: 0 } },
compile::Assemble { target_compiler: Compiler { host: a, stage: 1 } },
]
);
assert_eq!(
- first(builder.cache.all::<tool::Rustdoc>()),
+ first(cache.all::<tool::Rustdoc>()),
&[
tool::Rustdoc { compiler: Compiler { host: a, stage: 1 } },
tool::Rustdoc { compiler: Compiler { host: b, stage: 1 } },
],
);
assert_eq!(
- first(builder.cache.all::<compile::Rustc>()),
+ first(cache.all::<compile::Rustc>()),
&[
compile::Rustc { compiler: Compiler { host: a, stage: 0 }, target: a },
compile::Rustc { compiler: Compiler { host: a, stage: 0 }, target: b },
let mut config = configure("doc", &["A"], &["A"]);
config.compiler_docs = true;
config.cmd = Subcommand::Doc { paths: Vec::new(), open: false };
- let build = Build::new(config);
- let mut builder = Builder::new(&build);
- builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Doc), &[]);
+ let mut cache = run_build(&[], config);
let a = TargetSelection::from_user("A");
// error_index_generator uses stage 0 to share rustdoc artifacts with the
// rustdoc tool.
+ assert_eq!(first(cache.all::<doc::ErrorIndex>()), &[doc::ErrorIndex { target: a },]);
assert_eq!(
- first(builder.cache.all::<doc::ErrorIndex>()),
- &[doc::ErrorIndex { target: a },]
- );
- assert_eq!(
- first(builder.cache.all::<tool::ErrorIndex>()),
+ first(cache.all::<tool::ErrorIndex>()),
&[tool::ErrorIndex { compiler: Compiler { host: a, stage: 0 } }]
);
// docs should be built with the beta compiler, not with the stage0 artifacts.
// recall that rustdoc is off-by-one: `stage` is the compiler rustdoc is _linked_ to,
// not the one it was built by.
assert_eq!(
- first(builder.cache.all::<tool::Rustdoc>()),
+ first(cache.all::<tool::Rustdoc>()),
&[tool::Rustdoc { compiler: Compiler { host: a, stage: 0 } },]
);
}
}
mod dist {
- use super::{first, Config};
+ use super::{first, run_build, Config};
use crate::builder::*;
use pretty_assertions::assert_eq;
#[test]
fn dist_baseline() {
- let build = Build::new(configure(&["A"], &["A"]));
- let mut builder = Builder::new(&build);
- builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Dist), &[]);
+ let mut cache = run_build(&[], configure(&["A"], &["A"]));
let a = TargetSelection::from_user("A");
- assert_eq!(first(builder.cache.all::<dist::Docs>()), &[dist::Docs { host: a },]);
- assert_eq!(first(builder.cache.all::<dist::Mingw>()), &[dist::Mingw { host: a },]);
+ assert_eq!(first(cache.all::<dist::Docs>()), &[dist::Docs { host: a },]);
+ assert_eq!(first(cache.all::<dist::Mingw>()), &[dist::Mingw { host: a },]);
assert_eq!(
- first(builder.cache.all::<dist::Rustc>()),
+ first(cache.all::<dist::Rustc>()),
&[dist::Rustc { compiler: Compiler { host: a, stage: 2 } },]
);
assert_eq!(
- first(builder.cache.all::<dist::Std>()),
+ first(cache.all::<dist::Std>()),
&[dist::Std { compiler: Compiler { host: a, stage: 1 }, target: a },]
);
- assert_eq!(first(builder.cache.all::<dist::Src>()), &[dist::Src]);
+ assert_eq!(first(cache.all::<dist::Src>()), &[dist::Src]);
// Make sure rustdoc is only built once.
assert_eq!(
- first(builder.cache.all::<tool::Rustdoc>()),
+ first(cache.all::<tool::Rustdoc>()),
&[tool::Rustdoc { compiler: Compiler { host: a, stage: 2 } },]
);
}
#[test]
fn dist_with_targets() {
- let build = Build::new(configure(&["A"], &["A", "B"]));
- let mut builder = Builder::new(&build);
- builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Dist), &[]);
+ let mut cache = run_build(&[], configure(&["A"], &["A", "B"]));
let a = TargetSelection::from_user("A");
let b = TargetSelection::from_user("B");
assert_eq!(
- first(builder.cache.all::<dist::Docs>()),
+ first(cache.all::<dist::Docs>()),
&[dist::Docs { host: a }, dist::Docs { host: b },]
);
assert_eq!(
- first(builder.cache.all::<dist::Mingw>()),
+ first(cache.all::<dist::Mingw>()),
&[dist::Mingw { host: a }, dist::Mingw { host: b },]
);
assert_eq!(
- first(builder.cache.all::<dist::Rustc>()),
+ first(cache.all::<dist::Rustc>()),
&[dist::Rustc { compiler: Compiler { host: a, stage: 2 } },]
);
assert_eq!(
- first(builder.cache.all::<dist::Std>()),
+ first(cache.all::<dist::Std>()),
&[
dist::Std { compiler: Compiler { host: a, stage: 1 }, target: a },
dist::Std { compiler: Compiler { host: a, stage: 2 }, target: b },
]
);
- assert_eq!(first(builder.cache.all::<dist::Src>()), &[dist::Src]);
+ assert_eq!(first(cache.all::<dist::Src>()), &[dist::Src]);
}
#[test]
fn dist_with_hosts() {
- let build = Build::new(configure(&["A", "B"], &["A", "B"]));
- let mut builder = Builder::new(&build);
- builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Dist), &[]);
+ let mut cache = run_build(&[], configure(&["A", "B"], &["A", "B"]));
let a = TargetSelection::from_user("A");
let b = TargetSelection::from_user("B");
assert_eq!(
- first(builder.cache.all::<dist::Docs>()),
+ first(cache.all::<dist::Docs>()),
&[dist::Docs { host: a }, dist::Docs { host: b },]
);
assert_eq!(
- first(builder.cache.all::<dist::Mingw>()),
+ first(cache.all::<dist::Mingw>()),
&[dist::Mingw { host: a }, dist::Mingw { host: b },]
);
assert_eq!(
- first(builder.cache.all::<dist::Rustc>()),
+ first(cache.all::<dist::Rustc>()),
&[
dist::Rustc { compiler: Compiler { host: a, stage: 2 } },
dist::Rustc { compiler: Compiler { host: b, stage: 2 } },
]
);
assert_eq!(
- first(builder.cache.all::<dist::Std>()),
+ first(cache.all::<dist::Std>()),
&[
dist::Std { compiler: Compiler { host: a, stage: 1 }, target: a },
dist::Std { compiler: Compiler { host: a, stage: 1 }, target: b },
]
);
assert_eq!(
- first(builder.cache.all::<compile::Std>()),
+ first(cache.all::<compile::Std>()),
&[
compile::Std { compiler: Compiler { host: a, stage: 0 }, target: a },
compile::Std { compiler: Compiler { host: a, stage: 1 }, target: a },
compile::Std { compiler: Compiler { host: a, stage: 2 }, target: b },
],
);
- assert_eq!(first(builder.cache.all::<dist::Src>()), &[dist::Src]);
+ assert_eq!(first(cache.all::<dist::Src>()), &[dist::Src]);
}
#[test]
fn dist_only_cross_host() {
let a = TargetSelection::from_user("A");
let b = TargetSelection::from_user("B");
- let mut build = Build::new(configure(&["A", "B"], &["A", "B"]));
- build.config.docs = false;
- build.config.extended = true;
- build.hosts = vec![b];
- let mut builder = Builder::new(&build);
- builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Dist), &[]);
+ let mut config = configure(&["A", "B"], &["A", "B"]);
+ config.docs = false;
+ config.extended = true;
+ config.hosts = vec![b];
+ let mut cache = run_build(&[], config);
assert_eq!(
- first(builder.cache.all::<dist::Rustc>()),
+ first(cache.all::<dist::Rustc>()),
&[dist::Rustc { compiler: Compiler { host: b, stage: 2 } },]
);
assert_eq!(
- first(builder.cache.all::<compile::Rustc>()),
+ first(cache.all::<compile::Rustc>()),
&[
compile::Rustc { compiler: Compiler { host: a, stage: 0 }, target: a },
compile::Rustc { compiler: Compiler { host: a, stage: 1 }, target: b },
#[test]
fn dist_with_targets_and_hosts() {
- let build = Build::new(configure(&["A", "B"], &["A", "B", "C"]));
- let mut builder = Builder::new(&build);
- builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Dist), &[]);
+ let mut cache = run_build(&[], configure(&["A", "B"], &["A", "B", "C"]));
let a = TargetSelection::from_user("A");
let b = TargetSelection::from_user("B");
let c = TargetSelection::from_user("C");
assert_eq!(
- first(builder.cache.all::<dist::Docs>()),
+ first(cache.all::<dist::Docs>()),
&[dist::Docs { host: a }, dist::Docs { host: b }, dist::Docs { host: c },]
);
assert_eq!(
- first(builder.cache.all::<dist::Mingw>()),
+ first(cache.all::<dist::Mingw>()),
&[dist::Mingw { host: a }, dist::Mingw { host: b }, dist::Mingw { host: c },]
);
assert_eq!(
- first(builder.cache.all::<dist::Rustc>()),
+ first(cache.all::<dist::Rustc>()),
&[
dist::Rustc { compiler: Compiler { host: a, stage: 2 } },
dist::Rustc { compiler: Compiler { host: b, stage: 2 } },
]
);
assert_eq!(
- first(builder.cache.all::<dist::Std>()),
+ first(cache.all::<dist::Std>()),
&[
dist::Std { compiler: Compiler { host: a, stage: 1 }, target: a },
dist::Std { compiler: Compiler { host: a, stage: 1 }, target: b },
dist::Std { compiler: Compiler { host: a, stage: 2 }, target: c },
]
);
- assert_eq!(first(builder.cache.all::<dist::Src>()), &[dist::Src]);
+ assert_eq!(first(cache.all::<dist::Src>()), &[dist::Src]);
}
#[test]
fn dist_with_empty_host() {
let config = configure(&[], &["C"]);
- let build = Build::new(config);
- let mut builder = Builder::new(&build);
- builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Dist), &[]);
+ let mut cache = run_build(&[], config);
let a = TargetSelection::from_user("A");
let c = TargetSelection::from_user("C");
- assert_eq!(first(builder.cache.all::<dist::Docs>()), &[dist::Docs { host: c },]);
- assert_eq!(first(builder.cache.all::<dist::Mingw>()), &[dist::Mingw { host: c },]);
+ assert_eq!(first(cache.all::<dist::Docs>()), &[dist::Docs { host: c },]);
+ assert_eq!(first(cache.all::<dist::Mingw>()), &[dist::Mingw { host: c },]);
assert_eq!(
- first(builder.cache.all::<dist::Std>()),
+ first(cache.all::<dist::Std>()),
&[dist::Std { compiler: Compiler { host: a, stage: 2 }, target: c },]
);
}
#[test]
fn dist_with_same_targets_and_hosts() {
- let build = Build::new(configure(&["A", "B"], &["A", "B"]));
- let mut builder = Builder::new(&build);
- builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Dist), &[]);
+ let mut cache = run_build(&[], configure(&["A", "B"], &["A", "B"]));
let a = TargetSelection::from_user("A");
let b = TargetSelection::from_user("B");
assert_eq!(
- first(builder.cache.all::<dist::Docs>()),
+ first(cache.all::<dist::Docs>()),
&[dist::Docs { host: a }, dist::Docs { host: b },]
);
assert_eq!(
- first(builder.cache.all::<dist::Mingw>()),
+ first(cache.all::<dist::Mingw>()),
&[dist::Mingw { host: a }, dist::Mingw { host: b },]
);
assert_eq!(
- first(builder.cache.all::<dist::Rustc>()),
+ first(cache.all::<dist::Rustc>()),
&[
dist::Rustc { compiler: Compiler { host: a, stage: 2 } },
dist::Rustc { compiler: Compiler { host: b, stage: 2 } },
]
);
assert_eq!(
- first(builder.cache.all::<dist::Std>()),
+ first(cache.all::<dist::Std>()),
&[
dist::Std { compiler: Compiler { host: a, stage: 1 }, target: a },
dist::Std { compiler: Compiler { host: a, stage: 1 }, target: b },
]
);
- assert_eq!(first(builder.cache.all::<dist::Src>()), &[dist::Src]);
+ assert_eq!(first(cache.all::<dist::Src>()), &[dist::Src]);
assert_eq!(
- first(builder.cache.all::<compile::Std>()),
+ first(cache.all::<compile::Std>()),
&[
compile::Std { compiler: Compiler { host: a, stage: 0 }, target: a },
compile::Std { compiler: Compiler { host: a, stage: 1 }, target: a },
]
);
assert_eq!(
- first(builder.cache.all::<compile::Assemble>()),
+ first(cache.all::<compile::Assemble>()),
&[
compile::Assemble { target_compiler: Compiler { host: a, stage: 0 } },
compile::Assemble { target_compiler: Compiler { host: a, stage: 1 } },
);
}
- #[test]
- fn test_exclude() {
- let mut config = configure(&["A"], &["A"]);
- config.exclude = vec![TaskPath::parse("src/tools/tidy")];
- config.cmd = Subcommand::Test {
- paths: Vec::new(),
- test_args: Vec::new(),
- rustc_args: Vec::new(),
- fail_fast: true,
- doc_tests: DocTests::No,
- bless: false,
- force_rerun: false,
- compare_mode: None,
- rustfix_coverage: false,
- pass: None,
- run: None,
- };
-
- let build = Build::new(config);
- let builder = Builder::new(&build);
- builder.run_step_descriptions(&Builder::get_step_descriptions(Kind::Test), &[]);
-
- // Ensure we have really excluded tidy
- assert!(!builder.cache.contains::<test::Tidy>());
-
- // Ensure other tests are not affected.
- assert!(builder.cache.contains::<test::RustdocUi>());
- }
-
#[test]
fn doc_ci() {
let mut config = configure(&["A"], &["A"]);
const DEFAULT: bool = true;
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
- run.all_krates("test")
+ run.all_krates("test").path("library")
}
fn make_run(run: RunConfig<'_>) {
const DEFAULT: bool = true;
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
- run.all_krates("rustc-main")
+ run.all_krates("rustc-main").path("compiler")
}
fn make_run(run: RunConfig<'_>) {
// When downloading stage1, the standard library has already been copied to the sysroot, so
// there's no need to rebuild it.
let download_rustc = run.builder.config.download_rustc;
- run.all_krates("test").default_condition(!download_rustc)
+ run.all_krates("test").path("library").default_condition(!download_rustc)
}
fn make_run(run: RunConfig<'_>) {
);
cargo.env("LLVM_STATIC_STDCPP", file);
}
- if builder.config.llvm_link_shared {
+ if builder.llvm_link_shared() {
cargo.env("LLVM_LINK_SHARED", "1");
}
if builder.config.llvm_use_libcxx {
const ONLY_HOSTS: bool = true;
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
- run.path("compiler/rustc")
+ run.path("compiler/rustc").path("compiler")
}
fn make_run(run: RunConfig<'_>) {
//! This module implements parsing `config.toml` configuration files to tweak
//! how the build runs.
+use std::cell::Cell;
use std::cmp;
use std::collections::{HashMap, HashSet};
use std::env;
use std::path::{Path, PathBuf};
use std::str::FromStr;
-use crate::builder::TaskPath;
+use crate::builder::{Builder, TaskPath};
use crate::cache::{Interned, INTERNER};
use crate::channel::GitInfo;
pub use crate::flags::Subcommand;
/// each field, see the corresponding fields in
/// `config.toml.example`.
#[derive(Default)]
+#[cfg_attr(test, derive(Clone))]
pub struct Config {
pub changelog_seen: Option<usize>,
pub ccache: Option<String>,
pub test_compare_mode: bool,
pub llvm_libunwind: LlvmLibunwind,
pub color: Color,
+ pub patch_binaries_for_nix: bool,
pub on_fail: Option<String>,
pub stage: u32,
pub keep_stage: Vec<u32>,
pub keep_stage_std: Vec<u32>,
pub src: PathBuf,
- // defaults to `config.toml`
+ /// defaults to `config.toml`
pub config: PathBuf,
pub jobs: Option<u32>,
pub cmd: Subcommand,
pub llvm_release_debuginfo: bool,
pub llvm_version_check: bool,
pub llvm_static_stdcpp: bool,
- pub llvm_link_shared: bool,
+ /// `None` if `llvm_from_ci` is true and we haven't yet downloaded llvm.
+ #[cfg(not(test))]
+ llvm_link_shared: Cell<Option<bool>>,
+ #[cfg(test)]
+ pub llvm_link_shared: Cell<Option<bool>>,
pub llvm_clang_cl: Option<String>,
pub llvm_targets: Option<String>,
pub llvm_experimental_targets: Option<String>,
/// Per-target configuration stored in the global configuration structure.
#[derive(Default)]
+#[cfg_attr(test, derive(Clone))]
pub struct Target {
/// Some(path to llvm-config) if using an external LLVM.
pub llvm_config: Option<PathBuf>,
set(&mut config.local_rebuild, build.local_rebuild);
set(&mut config.print_step_timings, build.print_step_timings);
set(&mut config.print_step_rusage, build.print_step_rusage);
+ set(&mut config.patch_binaries_for_nix, build.patch_binaries_for_nix);
config.verbose = cmp::max(config.verbose, flags.verbose);
set(&mut config.llvm_release_debuginfo, llvm.release_debuginfo);
set(&mut config.llvm_version_check, llvm.version_check);
set(&mut config.llvm_static_stdcpp, llvm.static_libstdcpp);
- set(&mut config.llvm_link_shared, llvm.link_shared);
+ if let Some(v) = llvm.link_shared {
+ config.llvm_link_shared.set(Some(v));
+ }
config.llvm_targets = llvm.targets.clone();
config.llvm_experimental_targets = llvm.experimental_targets.clone();
config.llvm_link_jobs = llvm.link_jobs;
check_ci_llvm!(llvm.optimize);
check_ci_llvm!(llvm.thin_lto);
check_ci_llvm!(llvm.release_debuginfo);
+ // CI-built LLVM can be either dynamic or static. We won't know until we download it.
check_ci_llvm!(llvm.link_shared);
check_ci_llvm!(llvm.static_libstdcpp);
check_ci_llvm!(llvm.targets);
check_ci_llvm!(llvm.clang);
check_ci_llvm!(llvm.build_config);
check_ci_llvm!(llvm.plugins);
-
- // CI-built LLVM can be either dynamic or static.
- let ci_llvm = config.out.join(&*config.build.triple).join("ci-llvm");
- config.llvm_link_shared = if config.dry_run {
- // just assume dynamic for now
- true
- } else {
- let link_type = t!(
- std::fs::read_to_string(ci_llvm.join("link-type.txt")),
- format!("CI llvm missing: {}", ci_llvm.display())
- );
- link_type == "dynamic"
- };
}
+ // NOTE: can never be hit when downloading from CI, since we call `check_ci_llvm!(thin_lto)` above.
if config.llvm_thin_lto && llvm.link_shared.is_none() {
// If we're building with ThinLTO on, by default we want to link
// to LLVM shared, to avoid re-doing ThinLTO (which happens in
// the link step) with each stage.
- config.llvm_link_shared = true;
+ config.llvm_link_shared.set(Some(true));
}
}
}
}
+ /// The absolute path to the downloaded LLVM artifacts.
+ pub(crate) fn ci_llvm_root(&self) -> PathBuf {
+ assert!(self.llvm_from_ci);
+ self.out.join(&*self.build.triple).join("ci-llvm")
+ }
+
+ /// Determine whether llvm should be linked dynamically.
+ ///
+ /// If `false`, llvm should be linked statically.
+ /// This is computed on demand since LLVM might have to first be downloaded from CI.
+ pub(crate) fn llvm_link_shared(builder: &Builder<'_>) -> bool {
+ let mut opt = builder.config.llvm_link_shared.get();
+ if opt.is_none() && builder.config.dry_run {
+ // just assume static for now - dynamic linking isn't supported on all platforms
+ return false;
+ }
+
+ let llvm_link_shared = *opt.get_or_insert_with(|| {
+ if builder.config.llvm_from_ci {
+ crate::native::maybe_download_ci_llvm(builder);
+ let ci_llvm = builder.config.ci_llvm_root();
+ let link_type = t!(
+ std::fs::read_to_string(ci_llvm.join("link-type.txt")),
+ format!("CI llvm missing: {}", ci_llvm.display())
+ );
+ link_type == "dynamic"
+ } else {
+ // unclear how thought-through this default is, but it maintains compatibility with
+ // previous behavior
+ false
+ }
+ });
+ builder.config.llvm_link_shared.set(opt);
+ llvm_link_shared
+ }
+
pub fn verbose(&self) -> bool {
self.verbose > 0
}
type Output = ();
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
- run.path("src/etc/lldb_batchmode.py")
- }
-
- fn make_run(run: RunConfig<'_>) {
- run.builder.ensure(DebuggerScripts {
- sysroot: run
- .builder
- .sysroot(run.builder.compiler(run.builder.top_stage, run.build_triple())),
- host: run.target,
- });
+ run.never()
}
/// Copies debugger scripts for `target` into the `sysroot` specified.
// clear why this is the case, though. llvm-config will emit the versioned
// paths and we don't want those in the sysroot (as we're expecting
// unversioned paths).
- if target.contains("apple-darwin") && builder.config.llvm_link_shared {
+ if target.contains("apple-darwin") && builder.llvm_link_shared() {
let src_libdir = builder.llvm_out(target).join("lib");
let llvm_dylib_path = src_libdir.join("libLLVM.dylib");
if llvm_dylib_path.exists() {
// We do not need to copy LLVM files into the sysroot if it is not
// dynamically linked; it is already included into librustc_llvm
// statically.
- if builder.config.llvm_link_shared {
+ if builder.llvm_link_shared() {
maybe_install_llvm(builder, target, &dst_libdir);
}
}
// We do not need to copy LLVM files into the sysroot if it is not
// dynamically linked; it is already included into librustc_llvm
// statically.
- if builder.config.llvm_link_shared {
+ if builder.llvm_link_shared() {
maybe_install_llvm(builder, target, &dst_libdir);
}
}
// compiler libraries.
let dst_libdir = tarball.image_dir().join("lib");
maybe_install_llvm(builder, target, &dst_libdir);
- let link_type = if builder.config.llvm_link_shared { "dynamic" } else { "static" };
+ let link_type = if builder.llvm_link_shared() { "dynamic" } else { "static" };
t!(std::fs::write(tarball.image_dir().join("link-type.txt"), link_type), dst_libdir);
Some(tarball.generate())
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
let builder = run.builder;
- run.all_krates("test").default_condition(builder.config.docs)
+ run.all_krates("test").path("library").default_condition(builder.config.docs)
}
fn make_run(run: RunConfig<'_>) {
.iter()
.map(components_simplified)
.filter_map(|path| {
- if path.get(0) == Some(&"library") {
+ if path.len() >= 2 && path.get(0) == Some(&"library") {
+ // single crate
Some(path[1].to_owned())
} else if !path.is_empty() {
+ // ??
Some(path[0].to_owned())
} else {
+ // all library crates
None
}
})
use getopts::Options;
-use crate::builder::Builder;
+use crate::builder::{Builder, Kind};
use crate::config::{Config, TargetSelection};
use crate::setup::Profile;
use crate::util::t;
use crate::{Build, DocTests};
+#[derive(Copy, Clone)]
pub enum Color {
Always,
Never,
pub llvm_profile_generate: bool,
}
+#[cfg_attr(test, derive(Clone))]
pub enum Subcommand {
Build {
paths: Vec<PathBuf>,
// the subcommand. Therefore we must manually identify the subcommand first, so that we can
// complete the definition of the options. Then we can use the getopt::Matches object from
// there on out.
- let subcommand = args.iter().find(|&s| {
- (s == "build")
- || (s == "b")
- || (s == "check")
- || (s == "c")
- || (s == "clippy")
- || (s == "fix")
- || (s == "fmt")
- || (s == "test")
- || (s == "t")
- || (s == "bench")
- || (s == "doc")
- || (s == "d")
- || (s == "clean")
- || (s == "dist")
- || (s == "install")
- || (s == "run")
- || (s == "r")
- || (s == "setup")
- });
- let subcommand = match subcommand {
+ let subcommand = match args.iter().find_map(|s| Kind::parse(&s)) {
Some(s) => s,
None => {
// No or an invalid subcommand -- show the general usage and subcommand help
};
// Some subcommands get extra options
- match subcommand.as_str() {
- "test" | "t" => {
+ match subcommand {
+ Kind::Test => {
opts.optflag("", "no-fail-fast", "Run all tests regardless of failure");
opts.optmulti(
"",
`/<build_base>/rustfix_missing_coverage.txt`",
);
}
- "check" | "c" => {
+ Kind::Check => {
opts.optflag("", "all-targets", "Check all targets");
}
- "bench" => {
+ Kind::Bench => {
opts.optmulti("", "test-args", "extra arguments", "ARGS");
}
- "clippy" => {
+ Kind::Clippy => {
opts.optflag("", "fix", "automatically apply lint suggestions");
}
- "doc" | "d" => {
+ Kind::Doc => {
opts.optflag("", "open", "open the docs in a browser");
}
- "clean" => {
+ Kind::Clean => {
opts.optflag("", "all", "clean all build artifacts");
}
- "fmt" => {
+ Kind::Format => {
opts.optflag("", "check", "check formatting instead of applying.");
}
_ => {}
// fn usage()
let usage = |exit_code: i32, opts: &Options, verbose: bool, subcommand_help: &str| -> ! {
- let mut extra_help = String::new();
-
- // All subcommands except `clean` can have an optional "Available paths" section
- if verbose {
- let config = Config::parse(&["build".to_string()]);
- let build = Build::new(config);
-
- let maybe_rules_help = Builder::get_help(&build, subcommand.as_str());
- extra_help.push_str(maybe_rules_help.unwrap_or_default().as_str());
- } else if !(subcommand.as_str() == "clean" || subcommand.as_str() == "fmt") {
- extra_help.push_str(
- format!("Run `./x.py {} -h -v` to see a list of available paths.", subcommand)
- .as_str(),
- );
- }
+ let config = Config::parse(&["build".to_string()]);
+ let build = Build::new(config);
+ let paths = Builder::get_help(&build, subcommand);
println!("{}", opts.usage(subcommand_help));
- if !extra_help.is_empty() {
- println!("{}", extra_help);
+ if let Some(s) = paths {
+ if verbose {
+ println!("{}", s);
+ } else {
+ println!(
+ "Run `./x.py {} -h -v` to see a list of available paths.",
+ subcommand.as_str()
+ );
+ }
+ } else if verbose {
+ panic!("No paths available for subcommand `{}`", subcommand.as_str());
}
process::exit(exit_code);
};
// ^-- option ^ ^- actual subcommand
// \_ arg to option could be mistaken as subcommand
let mut pass_sanity_check = true;
- match matches.free.get(0) {
+ match matches.free.get(0).and_then(|s| Kind::parse(&s)) {
Some(check_subcommand) => {
if check_subcommand != subcommand {
pass_sanity_check = false;
process::exit(1);
}
// Extra help text for some commands
- match subcommand.as_str() {
- "build" | "b" => {
+ match subcommand {
+ Kind::Build => {
subcommand_help.push_str(
"\n
Arguments:
./x.py build ",
);
}
- "check" | "c" => {
+ Kind::Check => {
subcommand_help.push_str(
"\n
Arguments:
If no arguments are passed then many artifacts are checked.",
);
}
- "clippy" => {
+ Kind::Clippy => {
subcommand_help.push_str(
"\n
Arguments:
./x.py clippy library/core library/proc_macro",
);
}
- "fix" => {
+ Kind::Fix => {
subcommand_help.push_str(
"\n
Arguments:
./x.py fix library/core library/proc_macro",
);
}
- "fmt" => {
+ Kind::Format => {
subcommand_help.push_str(
"\n
Arguments:
./x.py fmt --check",
);
}
- "test" | "t" => {
+ Kind::Test => {
subcommand_help.push_str(
"\n
Arguments:
./x.py test --stage 1",
);
}
- "doc" | "d" => {
+ Kind::Doc => {
subcommand_help.push_str(
"\n
Arguments:
./x.py doc --stage 1",
);
}
- "run" | "r" => {
+ Kind::Run => {
subcommand_help.push_str(
"\n
Arguments:
At least a tool needs to be called.",
);
}
- "setup" => {
+ Kind::Setup => {
subcommand_help.push_str(&format!(
"\n
x.py setup creates a `config.toml` which changes the defaults for x.py itself.
Profile::all_for_help(" ").trim_end()
));
}
- _ => {}
+ Kind::Bench | Kind::Clean | Kind::Dist | Kind::Install => {}
};
// Get any optional paths which occur after the subcommand
let mut paths = matches.free[1..].iter().map(|p| p.into()).collect::<Vec<PathBuf>>();
usage(0, &opts, verbose, &subcommand_help);
}
- let cmd = match subcommand.as_str() {
- "build" | "b" => Subcommand::Build { paths },
- "check" | "c" => {
+ let cmd = match subcommand {
+ Kind::Build => Subcommand::Build { paths },
+ Kind::Check => {
if matches.opt_present("all-targets") {
eprintln!(
"Warning: --all-targets is now on by default and does not need to be passed explicitly."
}
Subcommand::Check { paths }
}
- "clippy" => Subcommand::Clippy { paths, fix: matches.opt_present("fix") },
- "fix" => Subcommand::Fix { paths },
- "test" | "t" => Subcommand::Test {
+ Kind::Clippy => Subcommand::Clippy { paths, fix: matches.opt_present("fix") },
+ Kind::Fix => Subcommand::Fix { paths },
+ Kind::Test => Subcommand::Test {
paths,
bless: matches.opt_present("bless"),
force_rerun: matches.opt_present("force-rerun"),
DocTests::Yes
},
},
- "bench" => Subcommand::Bench { paths, test_args: matches.opt_strs("test-args") },
- "doc" | "d" => Subcommand::Doc { paths, open: matches.opt_present("open") },
- "clean" => {
+ Kind::Bench => Subcommand::Bench { paths, test_args: matches.opt_strs("test-args") },
+ Kind::Doc => Subcommand::Doc { paths, open: matches.opt_present("open") },
+ Kind::Clean => {
if !paths.is_empty() {
println!("\nclean does not take a path argument\n");
usage(1, &opts, verbose, &subcommand_help);
Subcommand::Clean { all: matches.opt_present("all") }
}
- "fmt" => Subcommand::Format { check: matches.opt_present("check"), paths },
- "dist" => Subcommand::Dist { paths },
- "install" => Subcommand::Install { paths },
- "run" | "r" => {
+ Kind::Format => Subcommand::Format { check: matches.opt_present("check"), paths },
+ Kind::Dist => Subcommand::Dist { paths },
+ Kind::Install => Subcommand::Install { paths },
+ Kind::Run => {
if paths.is_empty() {
println!("\nrun requires at least a path!\n");
usage(1, &opts, verbose, &subcommand_help);
}
Subcommand::Run { paths }
}
- "setup" => {
+ Kind::Setup => {
let profile = if paths.len() > 1 {
println!("\nat most one profile can be passed to setup\n");
usage(1, &opts, verbose, &subcommand_help)
};
Subcommand::Setup { profile }
}
- _ => {
- usage(1, &opts, verbose, &subcommand_help);
- }
};
if let Subcommand::Check { .. } = &cmd {
}
impl Subcommand {
+ pub fn kind(&self) -> Kind {
+ match self {
+ Subcommand::Bench { .. } => Kind::Bench,
+ Subcommand::Build { .. } => Kind::Build,
+ Subcommand::Check { .. } => Kind::Check,
+ Subcommand::Clippy { .. } => Kind::Clippy,
+ Subcommand::Doc { .. } => Kind::Doc,
+ Subcommand::Fix { .. } => Kind::Fix,
+ Subcommand::Format { .. } => Kind::Format,
+ Subcommand::Test { .. } => Kind::Test,
+ Subcommand::Clean { .. } => Kind::Clean,
+ Subcommand::Dist { .. } => Kind::Dist,
+ Subcommand::Install { .. } => Kind::Install,
+ Subcommand::Run { .. } => Kind::Run,
+ Subcommand::Setup { .. } => Kind::Setup,
+ }
+ }
+
pub fn test_args(&self) -> Vec<&str> {
match *self {
Subcommand::Test { ref test_args, .. } | Subcommand::Bench { ref test_args, .. } => {
paths
}
+ /// Create a temporary directory in `out` and return its path.
+ ///
+ /// NOTE: this temporary directory is shared between all steps;
+ /// if you need an empty directory, create a new subdirectory inside it.
+ fn tempdir(&self) -> PathBuf {
+ let tmp = self.out.join("tmp");
+ t!(fs::create_dir_all(&tmp));
+ tmp
+ }
+
/// Copies a file from `src` to `dst`
pub fn copy(&self, src: &Path, dst: &Path) {
if self.config.dry_run {
use std::env::consts::EXE_EXTENSION;
use std::ffi::{OsStr, OsString};
use std::fs::{self, File};
-use std::io;
+use std::io::{self, BufRead, BufReader, ErrorKind};
use std::path::{Path, PathBuf};
-use std::process::Command;
+use std::process::{Command, Stdio};
+
+use once_cell::sync::OnceCell;
+use xz2::bufread::XzDecoder;
use crate::builder::{Builder, RunConfig, ShouldRun, Step};
use crate::config::TargetSelection;
builder: &Builder<'_>,
target: TargetSelection,
) -> Result<PathBuf, Meta> {
+ maybe_download_ci_llvm(builder);
+
// If we're using a custom LLVM bail out here, but we can only use a
// custom LLVM for the build triple.
if let Some(config) = builder.config.target_config.get(&target) {
Err(Meta { stamp, build_llvm_config, out_dir, root: root.into() })
}
+pub(crate) fn maybe_download_ci_llvm(builder: &Builder<'_>) {
+ let config = &builder.config;
+ if !config.llvm_from_ci {
+ return;
+ }
+ let mut rev_list = Command::new("git");
+ rev_list.args(&[
+ PathBuf::from("rev-list"),
+ "--author=bors@rust-lang.org".into(),
+ "-n1".into(),
+ "--first-parent".into(),
+ "HEAD".into(),
+ "--".into(),
+ builder.src.join("src/llvm-project"),
+ builder.src.join("src/bootstrap/download-ci-llvm-stamp"),
+ // the LLVM shared object file is named `LLVM-12-rust-{version}-nightly`
+ builder.src.join("src/version"),
+ ]);
+ let llvm_sha = output(&mut rev_list);
+ let llvm_sha = llvm_sha.trim();
+
+ if llvm_sha == "" {
+ println!("error: could not find commit hash for downloading LLVM");
+ println!("help: maybe your repository history is too shallow?");
+ println!("help: consider disabling `download-ci-llvm`");
+ println!("help: or fetch enough history to include one upstream commit");
+ panic!();
+ }
+
+ let llvm_root = config.ci_llvm_root();
+ let llvm_stamp = llvm_root.join(".llvm-stamp");
+ let key = format!("{}{}", llvm_sha, config.llvm_assertions);
+ if program_out_of_date(&llvm_stamp, &key) && !config.dry_run {
+ download_ci_llvm(builder, &llvm_sha);
+ for binary in ["llvm-config", "FileCheck"] {
+ fix_bin_or_dylib(builder, &llvm_root.join("bin").join(binary));
+ }
+ let llvm_lib = llvm_root.join("lib");
+ for entry in t!(fs::read_dir(&llvm_lib)) {
+ let lib = t!(entry).path();
+ if lib.ends_with(".so") {
+ fix_bin_or_dylib(builder, &lib);
+ }
+ }
+ t!(fs::write(llvm_stamp, key));
+ }
+}
+
+fn download_ci_llvm(builder: &Builder<'_>, llvm_sha: &str) {
+ let llvm_assertions = builder.config.llvm_assertions;
+
+ let cache_prefix = format!("llvm-{}-{}", llvm_sha, llvm_assertions);
+ let cache_dst = builder.out.join("cache");
+ let rustc_cache = cache_dst.join(cache_prefix);
+ if !rustc_cache.exists() {
+ t!(fs::create_dir_all(&rustc_cache));
+ }
+ let base = "https://ci-artifacts.rust-lang.org";
+ let url = if llvm_assertions {
+ format!("rustc-builds-alt/{}", llvm_sha)
+ } else {
+ format!("rustc-builds/{}", llvm_sha)
+ };
+ let filename = format!("rust-dev-nightly-{}.tar.xz", builder.build.build.triple);
+ let tarball = rustc_cache.join(&filename);
+ if !tarball.exists() {
+ download_component(builder, base, &format!("{}/{}", url, filename), &tarball);
+ }
+ let llvm_root = builder.config.ci_llvm_root();
+ unpack(builder, &tarball, &llvm_root);
+}
+
+/// Modifies the interpreter section of 'fname' to fix the dynamic linker,
+/// or the RPATH section, to fix the dynamic library search path
+///
+/// This is only required on NixOS and uses the PatchELF utility to
+/// change the interpreter/RPATH of ELF executables.
+///
+/// Please see https://nixos.org/patchelf.html for more information
+fn fix_bin_or_dylib(builder: &Builder<'_>, fname: &Path) {
+ // FIXME: cache NixOS detection?
+ match Command::new("uname").arg("-s").stderr(Stdio::inherit()).output() {
+ Err(_) => return,
+ Ok(output) if !output.status.success() => return,
+ Ok(output) => {
+ let mut s = output.stdout;
+ if s.last() == Some(&b'\n') {
+ s.pop();
+ }
+ if s != b"Linux" {
+ return;
+ }
+ }
+ }
+
+ // If the user has asked binaries to be patched for Nix, then
+ // don't check for NixOS or `/lib`, just continue to the patching.
+ // FIXME: shouldn't this take precedence over the `uname` check above?
+ if !builder.config.patch_binaries_for_nix {
+ // Use `/etc/os-release` instead of `/etc/NIXOS`.
+ // The latter one does not exist on NixOS when using tmpfs as root.
+ const NIX_IDS: &[&str] = &["ID=nixos", "ID='nixos'", "ID=\"nixos\""];
+ let os_release = match File::open("/etc/os-release") {
+ Err(e) if e.kind() == ErrorKind::NotFound => return,
+ Err(e) => panic!("failed to access /etc/os-release: {}", e),
+ Ok(f) => f,
+ };
+ if !BufReader::new(os_release).lines().any(|l| NIX_IDS.contains(&t!(l).trim())) {
+ return;
+ }
+ if Path::new("/lib").exists() {
+ return;
+ }
+ }
+
+ // At this point we're pretty sure the user is running NixOS or using Nix
+ println!("info: you seem to be using Nix. Attempting to patch {}", fname.display());
+
+ // Only build `.nix-deps` once.
+ static NIX_DEPS_DIR: OnceCell<PathBuf> = OnceCell::new();
+ let mut nix_build_succeeded = true;
+ let nix_deps_dir = NIX_DEPS_DIR.get_or_init(|| {
+ // Run `nix-build` to "build" each dependency (which will likely reuse
+ // the existing `/nix/store` copy, or at most download a pre-built copy).
+ //
+ // Importantly, we create a gc-root called `.nix-deps` in the `build/`
+ // directory, but still reference the actual `/nix/store` path in the rpath
+ // as it makes it significantly more robust against changes to the location of
+ // the `.nix-deps` location.
+ //
+ // bintools: Needed for the path of `ld-linux.so` (via `nix-support/dynamic-linker`).
+ // zlib: Needed as a system dependency of `libLLVM-*.so`.
+ // patchelf: Needed for patching ELF binaries (see doc comment above).
+ let nix_deps_dir = builder.out.join(".nix-deps");
+ const NIX_EXPR: &str = "
+ with (import <nixpkgs> {});
+ symlinkJoin {
+ name = \"rust-stage0-dependencies\";
+ paths = [
+ zlib
+ patchelf
+ stdenv.cc.bintools
+ ];
+ }
+ ";
+ nix_build_succeeded = builder.try_run(Command::new("nix-build").args(&[
+ Path::new("-E"),
+ Path::new(NIX_EXPR),
+ Path::new("-o"),
+ &nix_deps_dir,
+ ]));
+ nix_deps_dir
+ });
+ if !nix_build_succeeded {
+ return;
+ }
+
+ let mut patchelf = Command::new(nix_deps_dir.join("bin/patchelf"));
+ let rpath_entries = {
+ // ORIGIN is a relative default, all binary and dynamic libraries we ship
+ // appear to have this (even when `../lib` is redundant).
+ // NOTE: there are only two paths here, delimited by a `:`
+ let mut entries = OsString::from("$ORIGIN/../lib:");
+ entries.push(t!(fs::canonicalize(nix_deps_dir)));
+ entries.push("/lib");
+ entries
+ };
+ patchelf.args(&[OsString::from("--set-rpath"), rpath_entries]);
+ if !fname.ends_with(".so") {
+ // Finally, set the corret .interp for binaries
+ let dynamic_linker_path = nix_deps_dir.join("nix-support/dynamic-linker");
+ // FIXME: can we support utf8 here? `args` doesn't accept Vec<u8>, only OsString ...
+ let dynamic_linker = t!(String::from_utf8(t!(fs::read(dynamic_linker_path))));
+ patchelf.args(&["--set-interpreter", dynamic_linker.trim_end()]);
+ }
+
+ builder.try_run(patchelf.arg(fname));
+}
+
+fn download_component(builder: &Builder<'_>, base: &str, url: &str, dest_path: &Path) {
+ // Use a temporary file in case we crash while downloading, to avoid a corrupt download in cache/.
+ let tempfile = builder.tempdir().join(dest_path.file_name().unwrap());
+ // FIXME: support `do_verify` (only really needed for nightly rustfmt)
+ // FIXME: support non-utf8 paths?
+ download_with_retries(builder, tempfile.to_str().unwrap(), &format!("{}/{}", base, url));
+ t!(std::fs::rename(&tempfile, dest_path));
+}
+
+fn download_with_retries(builder: &Builder<'_>, tempfile: &str, url: &str) {
+ println!("downloading {}", url);
+
+ // FIXME: check if curl is installed instead of skipping straight to powershell
+ if builder.build.build.contains("windows-msvc") {
+ for _ in 0..3 {
+ if builder.try_run(Command::new("PowerShell.exe").args(&[
+ "/nologo",
+ "-Command",
+ "[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12;",
+ &format!(
+ "(New-Object System.Net.WebClient).DownloadFile('{}', '{}')",
+ url, tempfile
+ ),
+ ])) {
+ return;
+ }
+ println!("\nspurious failure, trying again");
+ }
+ } else {
+ builder.run(Command::new("curl").args(&[
+ "-#",
+ "-y",
+ "30",
+ "-Y",
+ "10", // timeout if speed is < 10 bytes/sec for > 30 seconds
+ "--connect-timeout",
+ "30", // timeout if cannot connect within 30 seconds
+ "--retry",
+ "3",
+ "-Sf",
+ "-o",
+ tempfile,
+ url,
+ ]));
+ }
+}
+
+fn unpack(builder: &Builder<'_>, tarball: &Path, dst: &Path) {
+ println!("extracting {} to {}", tarball.display(), dst.display());
+ if !dst.exists() {
+ t!(fs::create_dir_all(dst));
+ }
+
+ // FIXME: will need to be a parameter once `download-rustc` is moved to rustbuild
+ const MATCH: &str = "rust-dev";
+
+ // `tarball` ends with `.tar.xz`; strip that suffix
+ // example: `rust-dev-nightly-x86_64-unknown-linux-gnu`
+ let uncompressed_filename =
+ Path::new(tarball.file_name().expect("missing tarball filename")).file_stem().unwrap();
+ let directory_prefix = Path::new(Path::new(uncompressed_filename).file_stem().unwrap());
+
+ // decompress the file
+ let data = t!(File::open(tarball));
+ let decompressor = XzDecoder::new(BufReader::new(data));
+
+ let mut tar = tar::Archive::new(decompressor);
+ for member in t!(tar.entries()) {
+ let mut member = t!(member);
+ let original_path = t!(member.path()).into_owned();
+ // skip the top-level directory
+ if original_path == directory_prefix {
+ continue;
+ }
+ let mut short_path = t!(original_path.strip_prefix(directory_prefix));
+ if !short_path.starts_with(MATCH) {
+ continue;
+ }
+ short_path = t!(short_path.strip_prefix(MATCH));
+ let dst_path = dst.join(short_path);
+ builder.verbose(&format!("extracting {} to {}", original_path.display(), dst.display()));
+ if !t!(member.unpack_in(dst)) {
+ panic!("path traversal attack ??");
+ }
+ let src_path = dst.join(original_path);
+ if src_path.is_dir() && dst_path.exists() {
+ continue;
+ }
+ t!(fs::rename(src_path, dst_path));
+ }
+ t!(fs::remove_dir_all(dst.join(directory_prefix)));
+}
+
+fn program_out_of_date(stamp: &Path, key: &str) -> bool {
+ if !stamp.exists() {
+ return true;
+ }
+ t!(fs::read_to_string(stamp)) != key
+}
+
#[derive(Debug, Copy, Clone, Hash, PartialEq, Eq)]
pub struct Llvm {
pub target: TargetSelection,
};
builder.update_submodule(&Path::new("src").join("llvm-project"));
- if builder.config.llvm_link_shared
+ if builder.llvm_link_shared()
&& (target.contains("windows") || target.contains("apple-darwin"))
{
panic!("shared linking to LLVM is not currently supported on {}", target.triple);
//
// If we're not linking rustc to a dynamic LLVM, though, then don't link
// tools to it.
- if builder.llvm_link_tools_dynamically(target) && builder.config.llvm_link_shared {
+ if builder.llvm_link_tools_dynamically(target) && builder.llvm_link_shared() {
cfg.define("LLVM_LINK_LLVM_DYLIB", "ON");
}
t!(std::fs::rename(&self.image_dir, &dest));
self.run(|this, cmd| {
+ let distdir = crate::dist::distdir(this.builder);
+ t!(std::fs::create_dir_all(&distdir));
cmd.arg("tarball")
.arg("--input")
.arg(&dest)
.arg("--output")
- .arg(crate::dist::distdir(this.builder).join(this.package_name()));
+ .arg(distdir.join(this.package_name()));
})
}
targetflags.extend(builder.lld_flags(target));
cmd.arg("--target-rustcflags").arg(targetflags.join(" "));
- cmd.arg("--docck-python").arg(builder.python());
-
- cmd.arg("--lldb-python").arg(builder.python());
+ cmd.arg("--python").arg(builder.python());
if let Some(ref gdb) = builder.config.gdb {
cmd.arg("--gdb").arg(gdb);
cmd.env("RUSTC_PROFILER_SUPPORT", "1");
}
- let tmp = builder.out.join("tmp");
- std::fs::create_dir_all(&tmp).unwrap();
- cmd.env("RUST_TEST_TMPDIR", tmp);
+ cmd.env("RUST_TEST_TMPDIR", builder.tempdir());
cmd.arg("--adb-path").arg("adb");
cmd.arg("--adb-test-dir").arg(ADB_TEST_DIR);
builder.ensure(compile::Std { compiler, target });
builder.info(&format!("REMOTE copy libs to emulator ({})", target));
- t!(fs::create_dir_all(builder.out.join("tmp")));
let server = builder.ensure(tool::RemoteTestServer { compiler, target });
// Spawn the emulator and wait for it to come online
let tool = builder.tool_exe(Tool::RemoteTestClient);
let mut cmd = Command::new(&tool);
- cmd.arg("spawn-emulator").arg(target.triple).arg(&server).arg(builder.out.join("tmp"));
+ cmd.arg("spawn-emulator").arg(target.triple).arg(&server).arg(builder.tempdir());
if let Some(rootfs) = builder.qemu_rootfs(target) {
cmd.arg(rootfs);
}
/// Runs "distcheck", a 'make check' from a tarball
fn run(self, builder: &Builder<'_>) {
builder.info("Distcheck");
- let dir = builder.out.join("tmp").join("distcheck");
+ let dir = builder.tempdir().join("distcheck");
let _ = fs::remove_dir_all(&dir);
t!(fs::create_dir_all(&dir));
// Now make sure that rust-src has all of libstd's dependencies
builder.info("Distcheck rust-src");
- let dir = builder.out.join("tmp").join("distcheck-src");
+ let dir = builder.tempdir().join("distcheck-src");
let _ = fs::remove_dir_all(&dir);
t!(fs::create_dir_all(&dir));
.env("RUSTFLAGS", "-Cdebuginfo=2")
.env("CARGO_TARGET_DIR", builder.out.join("bootstrap"))
.env("RUSTC_BOOTSTRAP", "1")
+ .env("RUSTDOC", builder.rustdoc(builder.compiler(0, builder.build.build)))
.env("RUSTC", &builder.initial_rustc);
if let Some(flags) = option_env!("RUSTFLAGS") {
// Use the same rustc flags for testing as for "normal" compilation,
if !builder.fail_fast {
cmd.arg("--no-fail-fast");
}
+ match builder.doc_tests {
+ DocTests::Only => {
+ cmd.arg("--doc");
+ }
+ DocTests::No => {
+ cmd.args(&["--lib", "--bins", "--examples", "--tests", "--benches"]);
+ }
+ DocTests::Yes => {}
+ }
+
cmd.arg("--").args(&builder.config.cmd.test_args());
// rustbuild tests are racy on directory creation so just run them one at a time.
// Since there's not many this shouldn't be a problem.
source shared.sh
-LLVM=llvmorg-13.0.0
+LLVM=llvmorg-14.0.2
mkdir llvm-project
cd llvm-project
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX=/rustroot \
-DCOMPILER_RT_BUILD_SANITIZERS=OFF \
+ -DCOMPILER_RT_BUILD_XRAY=OFF \
+ -DCOMPILER_RT_BUILD_MEMPROF=OFF \
-DLLVM_TARGETS_TO_BUILD=X86 \
+ -DLLVM_INCLUDE_BENCHMARKS=OFF \
+ -DLLVM_INCLUDE_TESTS=OFF \
+ -DLLVM_INCLUDE_EXAMPLES=OFF \
-DLLVM_ENABLE_PROJECTS="clang;lld;compiler-rt" \
-DC_INCLUDE_DIRS="$INC"
source shared.sh
-GCC=5.5.0
+GCC=7.5.0
curl https://ftp.gnu.org/gnu/gcc/gcc-$GCC/gcc-$GCC.tar.xz | xzcat | tar xf -
cd gcc-$GCC
-0.8.5
\ No newline at end of file
+0.9.0
\ No newline at end of file
-Subproject commit 765318b844569a642ceef7bf1adab9639cbf6af3
+Subproject commit de0dbffc5812fd885700874e8d258dd334733ac4
-Subproject commit a6de8b6e3ea5d4f0de8b7b9a7e5c1405dc2c2ddb
+Subproject commit f7cefbb995eec8c6148f213235e9e2e03268e775
-Subproject commit c2a98d9fc5d29c481d42052fbeccfde15ed03116
+Subproject commit 44a80e8d8bfc5881c9bd69a2cb3a570776ee4181
-Subproject commit eeb5a83c15b6ae60df3e4f19207376b22c6fbc4c
+Subproject commit 043e60f4f191651e9f8bf52fa32df14defbb23d9
The modifier does nothing for linkers that don't support it.
The default for this modifier is `-whole-archive`. \
-NOTE: The default may currently be different when building dylibs for some targets,
-but it is not guaranteed.
+NOTE: The default may currently be different in some cases for backward compatibility,
+but it is not guaranteed. If you need whole archive semantics use `+whole-archive` explicitly.
<a id="option-crate-type"></a>
## `--crate-type`: a list of types of crates for the compiler to emit
modules with a `cargo` command like:
```sh
-$ RUSTFLAGS="-Z cf-protection=full" RUSTC="rustc-custom" cargo +nightly build -Z build-std --target x86_64-unknown-linux-gnu
+$ RUSTFLAGS="-Z cf-protection=full" cargo +nightly build -Z build-std --target x86_64-unknown-linux-gnu
```
### Detection
--- /dev/null
+# `yeet_expr`
+
+The tracking issue for this feature is: [#96373]
+
+[#96373]: https://github.com/rust-lang/rust/issues/96373
+
+------------------------
+
+The `yeet_expr` feature adds support for `do yeet` expressions,
+which can be used to early-exit from a function or `try` block.
+
+These are highly experimental, thus the placeholder syntax.
+
+```rust,edition2021
+#![feature(yeet_expr)]
+
+fn foo() -> Result<String, i32> {
+ do yeet 4;
+}
+assert_eq!(foo(), Err(4));
+
+fn bar() -> Option<String> {
+ do yeet;
+}
+assert_eq!(bar(), None);
+```
ty = param["kind"]["type"]
if ty["default"]:
check_type(ty["default"])
- for bound in ty["bounds"]:
- check_generic_bound(bound)
elif "const" in param["kind"]:
check_type(param["kind"]["const"])
rustdoc-json-types = { path = "../rustdoc-json-types" }
tracing = "0.1"
tracing-tree = "0.2.0"
+once_cell = "1.10.0"
[dependencies.tracing-subscriber]
version = "0.3.3"
}
}
-impl Clean<WherePredicate> for hir::WherePredicate<'_> {
- fn clean(&self, cx: &mut DocContext<'_>) -> WherePredicate {
- match *self {
+impl Clean<Option<WherePredicate>> for hir::WherePredicate<'_> {
+ fn clean(&self, cx: &mut DocContext<'_>) -> Option<WherePredicate> {
+ if !self.in_where_clause() {
+ return None;
+ }
+ Some(match *self {
hir::WherePredicate::BoundPredicate(ref wbp) => {
let bound_params = wbp
.bound_generic_params
// Higher-ranked lifetimes can't have bounds.
assert_matches!(
param,
- hir::GenericParam {
- kind: hir::GenericParamKind::Lifetime { .. },
- bounds: [],
- ..
- }
+ hir::GenericParam { kind: hir::GenericParamKind::Lifetime { .. }, .. }
);
Lifetime(param.name.ident().name)
})
lhs: wrp.lhs_ty.clean(cx),
rhs: wrp.rhs_ty.clean(cx).into(),
},
- }
+ })
}
}
// `self_def_id` set, we override it here.
// See https://github.com/rust-lang/rust/issues/85454
if let QPath { ref mut self_def_id, .. } = default {
- *self_def_id = cx.tcx.parent(self.def_id);
+ *self_def_id = Some(cx.tcx.parent(self.def_id));
}
Some(default)
}
}
-impl Clean<GenericParamDef> for hir::GenericParam<'_> {
- fn clean(&self, cx: &mut DocContext<'_>) -> GenericParamDef {
- let (name, kind) = match self.kind {
- hir::GenericParamKind::Lifetime { .. } => {
- let outlives = self
- .bounds
+fn clean_generic_param(
+ cx: &mut DocContext<'_>,
+ generics: Option<&hir::Generics<'_>>,
+ param: &hir::GenericParam<'_>,
+) -> GenericParamDef {
+ let (name, kind) = match param.kind {
+ hir::GenericParamKind::Lifetime { .. } => {
+ let outlives = if let Some(generics) = generics {
+ generics
+ .predicates
.iter()
+ .flat_map(|pred| {
+ match pred {
+ hir::WherePredicate::RegionPredicate(rp)
+ if rp.lifetime.name == hir::LifetimeName::Param(param.name)
+ && !rp.in_where_clause =>
+ {
+ rp.bounds
+ }
+ _ => &[],
+ }
+ .iter()
+ })
.map(|bound| match bound {
hir::GenericBound::Outlives(lt) => lt.clean(cx),
_ => panic!(),
})
- .collect();
- (self.name.ident().name, GenericParamDefKind::Lifetime { outlives })
- }
- hir::GenericParamKind::Type { ref default, synthetic } => (
- self.name.ident().name,
+ .collect()
+ } else {
+ Vec::new()
+ };
+ (param.name.ident().name, GenericParamDefKind::Lifetime { outlives })
+ }
+ hir::GenericParamKind::Type { ref default, synthetic } => {
+ let did = cx.tcx.hir().local_def_id(param.hir_id);
+ let bounds = if let Some(generics) = generics {
+ generics
+ .bounds_for_param(did)
+ .filter(|bp| !bp.in_where_clause)
+ .flat_map(|bp| bp.bounds)
+ .filter_map(|x| x.clean(cx))
+ .collect()
+ } else {
+ Vec::new()
+ };
+ (
+ param.name.ident().name,
GenericParamDefKind::Type {
- did: cx.tcx.hir().local_def_id(self.hir_id).to_def_id(),
- bounds: self.bounds.iter().filter_map(|x| x.clean(cx)).collect(),
+ did: did.to_def_id(),
+ bounds,
default: default.map(|t| t.clean(cx)).map(Box::new),
synthetic,
},
- ),
- hir::GenericParamKind::Const { ref ty, default } => (
- self.name.ident().name,
- GenericParamDefKind::Const {
- did: cx.tcx.hir().local_def_id(self.hir_id).to_def_id(),
- ty: Box::new(ty.clean(cx)),
- default: default.map(|ct| {
- let def_id = cx.tcx.hir().local_def_id(ct.hir_id);
- Box::new(ty::Const::from_anon_const(cx.tcx, def_id).to_string())
- }),
- },
- ),
- };
+ )
+ }
+ hir::GenericParamKind::Const { ref ty, default } => (
+ param.name.ident().name,
+ GenericParamDefKind::Const {
+ did: cx.tcx.hir().local_def_id(param.hir_id).to_def_id(),
+ ty: Box::new(ty.clean(cx)),
+ default: default.map(|ct| {
+ let def_id = cx.tcx.hir().local_def_id(ct.hir_id);
+ Box::new(ty::Const::from_anon_const(cx.tcx, def_id).to_string())
+ }),
+ },
+ ),
+ };
- GenericParamDef { name, kind }
- }
+ GenericParamDef { name, kind }
}
impl Clean<Generics> for hir::Generics<'_> {
.iter()
.filter(|param| is_impl_trait(param))
.map(|param| {
- let param: GenericParamDef = param.clean(cx);
+ let param = clean_generic_param(cx, Some(self), param);
match param.kind {
GenericParamDefKind::Lifetime { .. } => unreachable!(),
GenericParamDefKind::Type { did, ref bounds, .. } => {
let mut params = Vec::with_capacity(self.params.len());
for p in self.params.iter().filter(|p| !is_impl_trait(p) && !is_elided_lifetime(p)) {
- let p = p.clean(cx);
+ let p = clean_generic_param(cx, Some(self), p);
params.push(p);
}
params.extend(impl_trait_params);
let mut generics = Generics {
params,
- where_predicates: self.where_clause.predicates.iter().map(|x| x.clean(cx)).collect(),
+ where_predicates: self.predicates.iter().filter_map(|x| x.clean(cx)).collect(),
};
// Some duplicates are generated for ?Sized bounds between type params and where
fn clean(&self, cx: &mut DocContext<'_>) -> PolyTrait {
PolyTrait {
trait_: self.trait_ref.clean(cx),
- generic_params: self.bound_generic_params.iter().map(|x| x.clean(cx)).collect(),
+ generic_params: self
+ .bound_generic_params
+ .iter()
+ .map(|x| clean_generic_param(cx, None, x))
+ .collect(),
}
}
}
let mut what_rustc_thinks =
Item::from_def_id_and_parts(local_did, Some(self.ident.name), inner, cx);
- let impl_ref = cx.tcx.parent(local_did).and_then(|did| cx.tcx.impl_trait_ref(did));
+ let impl_ref = cx.tcx.impl_trait_ref(cx.tcx.local_parent(self.def_id));
// Trait impl items always inherit the impl's visibility --
// we don't want to show `pub`.
let mut what_rustc_thinks =
Item::from_def_id_and_parts(self.def_id, Some(self.name), kind, cx);
- let impl_ref = tcx.parent(self.def_id).and_then(|did| tcx.impl_trait_ref(did));
+ let impl_ref = tcx.impl_trait_ref(tcx.parent(self.def_id));
// Trait impl items always inherit the impl's visibility --
// we don't want to show `pub`.
fn maybe_expand_private_type_alias(cx: &mut DocContext<'_>, path: &hir::Path<'_>) -> Option<Type> {
let Res::Def(DefKind::TyAlias, def_id) = path.res else { return None };
// Substitute private type aliases
- let Some(def_id) = def_id.as_local() else { return None };
+ let def_id = def_id.as_local()?;
let alias = if !cx.cache.access_levels.is_exported(def_id.to_def_id()) {
&cx.tcx.hir().expect_item(def_id).kind
} else {
}
fn is_field_vis_inherited(tcx: TyCtxt<'_>, def_id: DefId) -> bool {
- let parent = tcx
- .parent(def_id)
- .expect("is_field_vis_inherited can only be called on struct or variant fields");
+ let parent = tcx.parent(def_id);
match tcx.def_kind(parent) {
DefKind::Struct | DefKind::Union => false,
DefKind::Variant => true,
fn clean(&self, cx: &mut DocContext<'_>) -> BareFunctionDecl {
let (generic_params, decl) = enter_impl_trait(cx, |cx| {
// NOTE: generics must be cleaned before args
- let generic_params = self.generic_params.iter().map(|x| x.clean(cx)).collect();
+ let generic_params =
+ self.generic_params.iter().map(|x| clean_generic_param(cx, None, x)).collect();
let args = clean_args_from_types_and_names(cx, self.decl.inputs, self.param_names);
let decl = clean_fn_decl_with_args(cx, self.decl, args);
(generic_params, decl)
-use rustc_ast::token::{self, BinOpToken, DelimToken};
+use rustc_ast::token::{self, BinOpToken, Delimiter};
use rustc_ast::tokenstream::{TokenStream, TokenTree};
use rustc_ast_pretty::pprust::state::State as Printer;
use rustc_ast_pretty::pprust::PrintState;
let open_delim = printer.token_kind_to_string(&token::OpenDelim(*delim));
printer.word(open_delim);
if !tts.is_empty() {
- if *delim == DelimToken::Brace {
+ if *delim == Delimiter::Brace {
printer.space();
}
print_tts(printer, tts);
- if *delim == DelimToken::Brace {
+ if *delim == Delimiter::Brace {
printer.space();
}
}
(_, _) => (true, Other),
},
TokenTree::Delimited(_, delim, _) => match (state, delim) {
- (Dollar, DelimToken::Paren) => (false, DollarParen),
- (Pound | PoundBang, DelimToken::Bracket) => (false, Other),
- (Ident, DelimToken::Paren | DelimToken::Bracket) => (false, Other),
+ (Dollar, Delimiter::Parenthesis) => (false, DollarParen),
+ (Pound | PoundBang, Delimiter::Bracket) => (false, Other),
+ (Ident, Delimiter::Parenthesis | Delimiter::Bracket) => (false, Other),
(_, _) => (true, Other),
},
};
use std::cell::RefCell;
use std::default::Default;
-use std::fmt;
use std::hash::Hash;
-use std::iter;
use std::lazy::SyncOnceCell as OnceCell;
use std::path::PathBuf;
use std::rc::Rc;
use std::sync::Arc;
-use std::vec;
+use std::{cmp, fmt, iter};
use arrayvec::ArrayVec;
};
crate use self::Visibility::{Inherited, Public};
+#[cfg(test)]
+mod tests;
+
crate type ItemIdSet = FxHashSet<ItemId>;
#[derive(Debug, Clone, PartialEq, Eq, Hash, Copy)]
acc
}
+/// Removes excess indentation on comments in order for the Markdown
+/// to be parsed correctly. This is necessary because the convention for
+/// writing documentation is to provide a space between the /// or //! marker
+/// and the doc text, but Markdown is whitespace-sensitive. For example,
+/// a block of text with four-space indentation is parsed as a code block,
+/// so if we didn't unindent comments, these list items
+///
+/// /// A list:
+/// ///
+/// /// - Foo
+/// /// - Bar
+///
+/// would be parsed as if they were in a code block, which is likely not what the user intended.
+fn unindent_doc_fragments(docs: &mut Vec<DocFragment>) {
+ // `add` is used in case the most common sugared doc syntax is used ("/// "). The other
+ // fragments kind's lines are never starting with a whitespace unless they are using some
+ // markdown formatting requiring it. Therefore, if the doc block have a mix between the two,
+ // we need to take into account the fact that the minimum indent minus one (to take this
+ // whitespace into account).
+ //
+ // For example:
+ //
+ // /// hello!
+ // #[doc = "another"]
+ //
+ // In this case, you want "hello! another" and not "hello! another".
+ let add = if docs.windows(2).any(|arr| arr[0].kind != arr[1].kind)
+ && docs.iter().any(|d| d.kind == DocFragmentKind::SugaredDoc)
+ {
+ // In case we have a mix of sugared doc comments and "raw" ones, we want the sugared one to
+ // "decide" how much the minimum indent will be.
+ 1
+ } else {
+ 0
+ };
+
+ // `min_indent` is used to know how much whitespaces from the start of each lines must be
+ // removed. Example:
+ //
+ // /// hello!
+ // #[doc = "another"]
+ //
+ // In here, the `min_indent` is 1 (because non-sugared fragment are always counted with minimum
+ // 1 whitespace), meaning that "hello!" will be considered a codeblock because it starts with 4
+ // (5 - 1) whitespaces.
+ let Some(min_indent) = docs
+ .iter()
+ .map(|fragment| {
+ fragment.doc.as_str().lines().fold(usize::MAX, |min_indent, line| {
+ if line.chars().all(|c| c.is_whitespace()) {
+ min_indent
+ } else {
+ // Compare against either space or tab, ignoring whether they are
+ // mixed or not.
+ let whitespace = line.chars().take_while(|c| *c == ' ' || *c == '\t').count();
+ cmp::min(min_indent, whitespace)
+ + if fragment.kind == DocFragmentKind::SugaredDoc { 0 } else { add }
+ }
+ })
+ })
+ .min()
+ else {
+ return;
+ };
+
+ for fragment in docs {
+ if fragment.doc == kw::Empty {
+ continue;
+ }
+
+ let min_indent = if fragment.kind != DocFragmentKind::SugaredDoc && min_indent > 0 {
+ min_indent - add
+ } else {
+ min_indent
+ };
+
+ fragment.indent = min_indent;
+ }
+}
+
/// A link that has not yet been rendered.
///
/// This link will be turned into a rendered link by [`Item::links`].
}
}
+ unindent_doc_fragments(&mut doc_strings);
+
Attributes { doc_strings, other_attrs }
}
--- /dev/null
+use super::*;
+
+use crate::clean::collapse_doc_fragments;
+
+use rustc_span::create_default_session_globals_then;
+use rustc_span::source_map::DUMMY_SP;
+use rustc_span::symbol::Symbol;
+
+fn create_doc_fragment(s: &str) -> Vec<DocFragment> {
+ vec![DocFragment {
+ span: DUMMY_SP,
+ parent_module: None,
+ doc: Symbol::intern(s),
+ kind: DocFragmentKind::SugaredDoc,
+ indent: 0,
+ }]
+}
+
+#[track_caller]
+fn run_test(input: &str, expected: &str) {
+ create_default_session_globals_then(|| {
+ let mut s = create_doc_fragment(input);
+ unindent_doc_fragments(&mut s);
+ assert_eq!(collapse_doc_fragments(&s), expected);
+ });
+}
+
+#[test]
+fn should_unindent() {
+ run_test(" line1\n line2", "line1\nline2");
+}
+
+#[test]
+fn should_unindent_multiple_paragraphs() {
+ run_test(" line1\n\n line2", "line1\n\nline2");
+}
+
+#[test]
+fn should_leave_multiple_indent_levels() {
+ // Line 2 is indented another level beyond the
+ // base indentation and should be preserved
+ run_test(" line1\n\n line2", "line1\n\n line2");
+}
+
+#[test]
+fn should_ignore_first_line_indent() {
+ run_test("line1\n line2", "line1\n line2");
+}
+
+#[test]
+fn should_not_ignore_first_line_indent_in_a_single_line_para() {
+ run_test("line1\n\n line2", "line1\n\n line2");
+}
+
+#[test]
+fn should_unindent_tabs() {
+ run_test("\tline1\n\tline2", "line1\nline2");
+}
+
+#[test]
+fn should_trim_mixed_indentation() {
+ run_test("\t line1\n\t line2", "line1\nline2");
+ run_test(" \tline1\n \tline2", "line1\nline2");
+}
+
+#[test]
+fn should_not_trim() {
+ run_test("\t line1 \n\t line2", "line1 \nline2");
+ run_test(" \tline1 \n \tline2", "line1 \nline2");
+}
let mut current = def_id;
// The immediate parent might not always be a module.
// Find the first parent which is.
- while let Some(parent) = tcx.parent(current) {
+ while let Some(parent) = tcx.opt_parent(current) {
if tcx.def_kind(parent) == DefKind::Mod {
return Some(parent);
}
self, parse_crate_types_from_list, parse_externs, parse_target_triple, CrateType,
};
use rustc_session::config::{get_cmd_lint_options, nightly_options};
-use rustc_session::config::{CodegenOptions, DebuggingOptions, ErrorOutputType, Externs};
+use rustc_session::config::{
+ CodegenOptions, DebuggingOptions, ErrorOutputType, Externs, JsonUnusedExterns,
+};
use rustc_session::getopts;
use rustc_session::lint::Level;
use rustc_session::search_paths::SearchPath;
/// documentation.
crate run_check: bool,
/// Whether doctests should emit unused externs
- crate json_unused_externs: bool,
+ crate json_unused_externs: JsonUnusedExterns,
/// Whether to skip capturing stdout and stderr of tests.
crate nocapture: bool,
// Collect and warn about unused externs, but only if we've gotten
// reports for each doctest
- if json_unused_externs {
+ if json_unused_externs.is_enabled() {
let unused_extern_reports: Vec<_> =
std::mem::take(&mut unused_extern_reports.lock().unwrap());
if unused_extern_reports.len() == compiling_test_count {
if lang_string.test_harness {
compiler.arg("--test");
}
- if rustdoc_options.json_unused_externs && !lang_string.compile_fail {
+ if rustdoc_options.json_unused_externs.is_enabled() && !lang_string.compile_fail {
compiler.arg("--error-format=json");
compiler.arg("--json").arg("unused-externs");
compiler.arg("-Z").arg("unstable-options");
nested: F,
) {
let ast_attrs = self.tcx.hir().attrs(hir_id);
- let mut attrs = Attributes::from_ast(ast_attrs, None);
-
if let Some(ref cfg) = ast_attrs.cfg(self.tcx, &FxHashSet::default()) {
if !cfg.matches(&self.sess.parse_sess, Some(self.sess.features_untracked())) {
return;
self.collector.names.push(name);
}
- attrs.unindent_doc_comments();
// The collapse-docs pass won't combine sugared/raw doc attributes, or included files with
// anything else, this will combine them for us.
+ let attrs = Attributes::from_ast(ast_attrs, None);
if let Some(doc) = attrs.collapsed_doc_value() {
// Use the outermost invocation, so that doctest names come from where the docs were written.
let span = ast_attrs
use rustc_middle::ty;
use rustc_middle::ty::DefIdTree;
use rustc_middle::ty::TyCtxt;
+use rustc_span::symbol::kw;
use rustc_span::{sym, Symbol};
use rustc_target::spec::abi::Abi;
indent: usize,
end_newline: bool,
) -> impl fmt::Display + 'a + Captures<'tcx> {
+ use fmt::Write;
+
display_fn(move |f| {
let mut where_predicates = gens.where_predicates.iter().filter(|pred| {
!matches!(pred, clean::WherePredicate::BoundPredicate { bounds, .. } if bounds.is_empty())
match pred {
clean::WherePredicate::BoundPredicate { ty, bounds, bound_params } => {
- let bounds = bounds;
- let for_prefix = if bound_params.is_empty() {
- String::new()
- } else if f.alternate() {
- format!(
- "for<{:#}> ",
- comma_sep(bound_params.iter().map(|lt| lt.print()), true)
- )
- } else {
- format!(
- "for<{}> ",
- comma_sep(bound_params.iter().map(|lt| lt.print()), true)
- )
- };
+ let ty_cx = ty.print(cx);
+ let generic_bounds = print_generic_bounds(bounds, cx);
- if f.alternate() {
- write!(
- f,
- "{}{:#}: {:#}",
- for_prefix,
- ty.print(cx),
- print_generic_bounds(bounds, cx)
- )
+ if bound_params.is_empty() {
+ if f.alternate() {
+ write!(f, "{ty_cx:#}: {generic_bounds:#}")
+ } else {
+ write!(f, "{ty_cx}: {generic_bounds}")
+ }
} else {
- write!(
- f,
- "{}{}: {}",
- for_prefix,
- ty.print(cx),
- print_generic_bounds(bounds, cx)
- )
+ if f.alternate() {
+ write!(
+ f,
+ "for<{:#}> {ty_cx:#}: {generic_bounds:#}",
+ comma_sep(bound_params.iter().map(|lt| lt.print()), true)
+ )
+ } else {
+ write!(
+ f,
+ "for<{}> {ty_cx}: {generic_bounds}",
+ comma_sep(bound_params.iter().map(|lt| lt.print()), true)
+ )
+ }
}
}
clean::WherePredicate::RegionPredicate { lifetime, bounds } => {
- write!(
- f,
- "{}: {}",
- lifetime.print(),
- bounds
- .iter()
- .map(|b| b.print(cx).to_string())
- .collect::<Vec<_>>()
- .join(" + ")
- )
+ let mut bounds_display = String::new();
+ for bound in bounds.iter().map(|b| b.print(cx)) {
+ write!(bounds_display, "{bound} + ")?;
+ }
+ bounds_display.truncate(bounds_display.len() - " + ".len());
+ write!(f, "{}: {bounds_display}", lifetime.print())
}
clean::WherePredicate::EqPredicate { lhs, rhs } => {
if f.alternate() {
- write!(f, "{:#} == {:#}", lhs.print(cx), rhs.print(cx),)
+ write!(f, "{:#} == {:#}", lhs.print(cx), rhs.print(cx))
} else {
- write!(f, "{} == {}", lhs.print(cx), rhs.print(cx),)
+ write!(f, "{} == {}", lhs.print(cx), rhs.print(cx))
}
}
}
return Ok(());
}
- let mut clause = String::new();
-
- if f.alternate() {
- clause.push_str(" where");
- } else {
+ let where_preds = comma_sep(where_predicates, false);
+ let clause = if f.alternate() {
if end_newline {
- clause.push_str(" <span class=\"where fmt-newline\">where");
+ // add a space so stripping <br> tags and breaking spaces still renders properly
+ format!(" where{where_preds}, ")
} else {
- clause.push_str(" <span class=\"where\">where");
+ format!(" where{where_preds}")
}
- }
-
- clause.push_str(&comma_sep(where_predicates, false).to_string());
-
- if end_newline {
- clause.push(',');
- // add a space so stripping <br> tags and breaking spaces still renders properly
- if f.alternate() {
- clause.push(' ');
- } else {
- clause.push_str(" ");
+ } else {
+ let mut br_with_padding = String::with_capacity(6 * indent + 28);
+ br_with_padding.push_str("<br>");
+ for _ in 0..indent + 4 {
+ br_with_padding.push_str(" ");
}
- }
+ let where_preds = where_preds.to_string().replace("<br>", &br_with_padding);
- if !f.alternate() {
- clause.push_str("</span>");
- let padding = " ".repeat(indent + 4);
- clause = clause.replace("<br>", &format!("<br>{}", padding));
- clause.insert_str(0, &" ".repeat(indent.saturating_sub(1)));
- if !end_newline {
- // we insert the <br> after a single space but before multiple spaces at the start
- clause.insert_str(if indent == 0 { 1 } else { 0 }, "<br>");
+ if end_newline {
+ let mut clause = " ".repeat(indent.saturating_sub(1));
+ // add a space so stripping <br> tags and breaking spaces still renders properly
+ write!(
+ clause,
+ " <span class=\"where fmt-newline\">where{where_preds}, </span>"
+ )?;
+ clause
+ } else {
+ // insert a <br> tag after a single space but before multiple spaces at the start
+ if indent == 0 {
+ format!(" <br><span class=\"where\">where{where_preds}</span>")
+ } else {
+ let mut clause = br_with_padding;
+ clause.truncate(clause.len() - 5 * " ".len());
+ write!(clause, " <span class=\"where\">where{where_preds}</span>")?;
+ clause
+ }
}
- }
- write!(f, "{}", clause)
+ };
+ write!(f, "{clause}")
})
}
let did = match def_kind {
DefKind::AssocTy | DefKind::AssocFn | DefKind::AssocConst | DefKind::Variant => {
// documented on their parent's page
- tcx.parent(did).unwrap()
+ tcx.parent(did)
}
_ => did,
};
if print_all {
for seg in &path.segments[..path.segments.len() - 1] {
- write!(w, "{}::", seg.name)?;
+ write!(w, "{}::", if seg.name == kw::PathRoot { "" } else { seg.name.as_str() })?;
}
}
if w.alternate() {
use rustc_span::edition::Edition;
use rustc_span::Span;
+use once_cell::sync::Lazy;
use std::borrow::Cow;
use std::cell::RefCell;
use std::collections::VecDeque;
#[derive(Clone, Default, Debug)]
pub struct IdMap {
- map: FxHashMap<String, usize>,
+ map: FxHashMap<Cow<'static, str>, usize>,
}
-fn init_id_map() -> FxHashMap<String, usize> {
+// The map is pre-initialized and cloned each time to avoid reinitializing it repeatedly.
+static DEFAULT_ID_MAP: Lazy<FxHashMap<Cow<'static, str>, usize>> = Lazy::new(|| init_id_map());
+
+fn init_id_map() -> FxHashMap<Cow<'static, str>, usize> {
let mut map = FxHashMap::default();
// This is the list of IDs used in Javascript.
- map.insert("help".to_owned(), 1);
+ map.insert("help".into(), 1);
+ map.insert("settings".into(), 1);
+ map.insert("not-displayed".into(), 1);
+ map.insert("alternative-display".into(), 1);
+ map.insert("search".into(), 1);
// This is the list of IDs used in HTML generated in Rust (including the ones
// used in tera template files).
- map.insert("mainThemeStyle".to_owned(), 1);
- map.insert("themeStyle".to_owned(), 1);
- map.insert("theme-picker".to_owned(), 1);
- map.insert("theme-choices".to_owned(), 1);
- map.insert("settings-menu".to_owned(), 1);
- map.insert("help-button".to_owned(), 1);
- map.insert("main-content".to_owned(), 1);
- map.insert("search".to_owned(), 1);
- map.insert("crate-search".to_owned(), 1);
- map.insert("render-detail".to_owned(), 1);
- map.insert("toggle-all-docs".to_owned(), 1);
- map.insert("all-types".to_owned(), 1);
- map.insert("default-settings".to_owned(), 1);
- map.insert("rustdoc-vars".to_owned(), 1);
- map.insert("sidebar-vars".to_owned(), 1);
- map.insert("copy-path".to_owned(), 1);
- map.insert("TOC".to_owned(), 1);
+ map.insert("mainThemeStyle".into(), 1);
+ map.insert("themeStyle".into(), 1);
+ map.insert("theme-picker".into(), 1);
+ map.insert("theme-choices".into(), 1);
+ map.insert("settings-menu".into(), 1);
+ map.insert("help-button".into(), 1);
+ map.insert("main-content".into(), 1);
+ map.insert("crate-search".into(), 1);
+ map.insert("render-detail".into(), 1);
+ map.insert("toggle-all-docs".into(), 1);
+ map.insert("all-types".into(), 1);
+ map.insert("default-settings".into(), 1);
+ map.insert("rustdoc-vars".into(), 1);
+ map.insert("sidebar-vars".into(), 1);
+ map.insert("copy-path".into(), 1);
+ map.insert("TOC".into(), 1);
// This is the list of IDs used by rustdoc sections (but still generated by
// rustdoc).
- map.insert("fields".to_owned(), 1);
- map.insert("variants".to_owned(), 1);
- map.insert("implementors-list".to_owned(), 1);
- map.insert("synthetic-implementors-list".to_owned(), 1);
- map.insert("foreign-impls".to_owned(), 1);
- map.insert("implementations".to_owned(), 1);
- map.insert("trait-implementations".to_owned(), 1);
- map.insert("synthetic-implementations".to_owned(), 1);
- map.insert("blanket-implementations".to_owned(), 1);
- map.insert("required-associated-types".to_owned(), 1);
- map.insert("provided-associated-types".to_owned(), 1);
- map.insert("provided-associated-consts".to_owned(), 1);
- map.insert("required-associated-consts".to_owned(), 1);
- map.insert("required-methods".to_owned(), 1);
- 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);
- map.insert("deref-methods".to_owned(), 1);
+ map.insert("fields".into(), 1);
+ map.insert("variants".into(), 1);
+ map.insert("implementors-list".into(), 1);
+ map.insert("synthetic-implementors-list".into(), 1);
+ map.insert("foreign-impls".into(), 1);
+ map.insert("implementations".into(), 1);
+ map.insert("trait-implementations".into(), 1);
+ map.insert("synthetic-implementations".into(), 1);
+ map.insert("blanket-implementations".into(), 1);
+ map.insert("required-associated-types".into(), 1);
+ map.insert("provided-associated-types".into(), 1);
+ map.insert("provided-associated-consts".into(), 1);
+ map.insert("required-associated-consts".into(), 1);
+ map.insert("required-methods".into(), 1);
+ map.insert("provided-methods".into(), 1);
+ map.insert("implementors".into(), 1);
+ map.insert("synthetic-implementors".into(), 1);
+ map.insert("implementations-list".into(), 1);
+ map.insert("trait-implementations-list".into(), 1);
+ map.insert("synthetic-implementations-list".into(), 1);
+ map.insert("blanket-implementations-list".into(), 1);
+ map.insert("deref-methods".into(), 1);
map
}
impl IdMap {
pub fn new() -> Self {
- IdMap { map: init_id_map() }
+ IdMap { map: DEFAULT_ID_MAP.clone() }
}
crate fn derive<S: AsRef<str> + ToString>(&mut self, candidate: S) -> String {
}
};
- self.map.insert(id.clone(), 1);
+ self.map.insert(id.clone().into(), 1);
id
}
}
use super::search_index::build_index;
use super::write_shared::write_shared;
use super::{
- collect_spans_and_sources, print_sidebar, scrape_examples_help, settings, AllTypes,
- LinkFromSrc, NameDoc, StylePath, BASIC_KEYWORDS,
+ collect_spans_and_sources, print_sidebar, scrape_examples_help, AllTypes, LinkFromSrc, NameDoc,
+ StylePath, BASIC_KEYWORDS,
};
use crate::clean::{self, types::ExternalLocation, ExternalCrate};
page.root_path = "./";
let sidebar = "<h2 class=\"location\">Settings</h2><div class=\"sidebar-elems\"></div>";
- let theme_names: Vec<String> = self
- .shared
- .style_files
- .iter()
- .map(StylePath::basename)
- .collect::<Result<_, Error>>()?;
let v = layout::render(
&self.shared.layout,
&page,
sidebar,
- settings(
- self.shared.static_root_path.as_deref().unwrap_or("./"),
- &self.shared.resource_suffix,
- theme_names,
- )?,
+ |buf: &mut Buffer| {
+ write!(
+ buf,
+ "<script defer src=\"{}settings{}.js\"></script>",
+ page.static_root_path.unwrap_or(""),
+ page.resource_suffix
+ )
+ },
&self.shared.style_files,
);
self.shared.fs.write(settings_file, v)?;
}
}
-#[derive(Debug)]
-enum Setting {
- Section {
- description: &'static str,
- sub_settings: Vec<Setting>,
- },
- Toggle {
- js_data_name: &'static str,
- description: &'static str,
- default_value: bool,
- },
- Select {
- js_data_name: &'static str,
- description: &'static str,
- default_value: &'static str,
- options: Vec<String>,
- },
-}
-
-impl Setting {
- fn display(&self, root_path: &str, suffix: &str) -> String {
- match *self {
- Setting::Section { description, ref sub_settings } => format!(
- "<div class=\"setting-line\">\
- <div class=\"title\">{}</div>\
- <div class=\"sub-settings\">{}</div>
- </div>",
- description,
- sub_settings.iter().map(|s| s.display(root_path, suffix)).collect::<String>()
- ),
- Setting::Toggle { js_data_name, description, default_value } => format!(
- "<div class=\"setting-line\">\
- <label class=\"toggle\">\
- <input type=\"checkbox\" id=\"{}\" {}>\
- <span class=\"slider\"></span>\
- </label>\
- <div>{}</div>\
- </div>",
- js_data_name,
- if default_value { " checked" } else { "" },
- description,
- ),
- Setting::Select { js_data_name, description, default_value, ref options } => format!(
- "<div class=\"setting-line\"><div class=\"radio-line\" id=\"{}\"><span class=\"setting-name\">{}</span><div class=\"choices\">{}</div></div></div>",
- js_data_name,
- description,
- options
- .iter()
- .map(|opt| format!(
- "<label for=\"{js_data_name}-{name}\" class=\"choice\">
- <input type=\"radio\" name=\"{js_data_name}\" id=\"{js_data_name}-{name}\" value=\"{name}\" {checked}>\
- {name}\
- </label>",
- js_data_name = js_data_name,
- name = opt,
- checked = if opt == default_value { "checked" } else { "" },
- ))
- .collect::<String>(),
- ),
- }
- }
-}
-
-impl From<(&'static str, &'static str, bool)> for Setting {
- fn from(values: (&'static str, &'static str, bool)) -> Setting {
- Setting::Toggle { js_data_name: values.0, description: values.1, default_value: values.2 }
- }
-}
-
-impl<T: Into<Setting>> From<(&'static str, Vec<T>)> for Setting {
- fn from(values: (&'static str, Vec<T>)) -> Setting {
- Setting::Section {
- description: values.0,
- sub_settings: values.1.into_iter().map(|v| v.into()).collect::<Vec<_>>(),
- }
- }
-}
-
-fn settings(root_path: &str, suffix: &str, theme_names: Vec<String>) -> Result<String, Error> {
- // (id, explanation, default value)
- let settings: &[Setting] = &[
- Setting::from(("use-system-theme", "Use system theme", true)),
- Setting::Select {
- js_data_name: "theme",
- description: "Theme",
- default_value: "light",
- options: theme_names.clone(),
- },
- Setting::Select {
- js_data_name: "preferred-light-theme",
- description: "Preferred light theme",
- default_value: "light",
- options: theme_names.clone(),
- },
- Setting::Select {
- js_data_name: "preferred-dark-theme",
- description: "Preferred dark theme",
- default_value: "dark",
- options: theme_names,
- },
- ("auto-hide-large-items", "Auto-hide item contents for large items.", true).into(),
- ("auto-hide-method-docs", "Auto-hide item methods' documentation", false).into(),
- ("auto-hide-trait-implementations", "Auto-hide trait implementation documentation", false)
- .into(),
- ("go-to-only-result", "Directly go to item in search if there is only one result", false)
- .into(),
- ("line-numbers", "Show line numbers on code examples", false).into(),
- ("disable-shortcuts", "Disable keyboard shortcuts", false).into(),
- ];
-
- Ok(format!(
- "<div class=\"main-heading\">
- <h1 class=\"fqn\">\
- <span class=\"in-band\">Rustdoc settings</span>\
- </h1>\
- <span class=\"out-of-band\">\
- <a id=\"back\" href=\"javascript:void(0)\">Back</a>\
- </span>\
- </div>\
- <div class=\"settings\">{}</div>\
- <link rel=\"stylesheet\" href=\"{root_path}settings{suffix}.css\">\
- <script src=\"{root_path}settings{suffix}.js\"></script>",
- settings.iter().map(|s| s.display(root_path, suffix)).collect::<String>(),
- root_path = root_path,
- suffix = suffix
- ))
-}
-
fn scrape_examples_help(shared: &SharedContext<'_>) -> String {
let mut content = SCRAPE_EXAMPLES_HELP_MD.to_owned();
content.push_str(&format!(
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::DefId;
use rustc_hir::intravisit::{self, Visitor};
-use rustc_hir::{ExprKind, GenericParam, GenericParamKind, HirId, Mod, Node};
+use rustc_hir::{ExprKind, GenericParam, HirId, Mod, Node};
use rustc_middle::hir::nested_filter;
use rustc_middle::ty::TyCtxt;
use rustc_span::Span;
self.tcx.hir()
}
- fn visit_generic_param(&mut self, p: &'tcx GenericParam<'tcx>) {
- if !matches!(p.kind, GenericParamKind::Type { .. }) {
- return;
- }
- for bound in p.bounds {
- if let Some(trait_ref) = bound.trait_ref() {
- self.handle_path(trait_ref.path, None);
- }
- }
- }
+ fn visit_generic_param(&mut self, _: &'tcx GenericParam<'tcx>) {}
fn visit_path(&mut self, path: &'tcx rustc_hir::Path<'tcx>, _id: HirId) {
self.handle_path(path, None);
static FILES_UNVERSIONED: Lazy<FxHashMap<&str, &[u8]>> = Lazy::new(|| {
map! {
- "FiraSans-Regular.woff2" => static_files::fira_sans::REGULAR2,
- "FiraSans-Medium.woff2" => static_files::fira_sans::MEDIUM2,
- "FiraSans-Regular.woff" => static_files::fira_sans::REGULAR,
- "FiraSans-Medium.woff" => static_files::fira_sans::MEDIUM,
+ "FiraSans-Regular.woff2" => static_files::fira_sans::REGULAR,
+ "FiraSans-Medium.woff2" => static_files::fira_sans::MEDIUM,
"FiraSans-LICENSE.txt" => static_files::fira_sans::LICENSE,
- "SourceSerif4-Regular.ttf.woff2" => static_files::source_serif_4::REGULAR2,
- "SourceSerif4-Bold.ttf.woff2" => static_files::source_serif_4::BOLD2,
- "SourceSerif4-It.ttf.woff2" => static_files::source_serif_4::ITALIC2,
- "SourceSerif4-Regular.ttf.woff" => static_files::source_serif_4::REGULAR,
- "SourceSerif4-Bold.ttf.woff" => static_files::source_serif_4::BOLD,
- "SourceSerif4-It.ttf.woff" => static_files::source_serif_4::ITALIC,
+ "SourceSerif4-Regular.ttf.woff2" => static_files::source_serif_4::REGULAR,
+ "SourceSerif4-Bold.ttf.woff2" => static_files::source_serif_4::BOLD,
+ "SourceSerif4-It.ttf.woff2" => static_files::source_serif_4::ITALIC,
"SourceSerif4-LICENSE.md" => static_files::source_serif_4::LICENSE,
- "SourceCodePro-Regular.ttf.woff2" => static_files::source_code_pro::REGULAR2,
- "SourceCodePro-Semibold.ttf.woff2" => static_files::source_code_pro::SEMIBOLD2,
- "SourceCodePro-It.ttf.woff2" => static_files::source_code_pro::ITALIC2,
- "SourceCodePro-Regular.ttf.woff" => static_files::source_code_pro::REGULAR,
- "SourceCodePro-Semibold.ttf.woff" => static_files::source_code_pro::SEMIBOLD,
- "SourceCodePro-It.ttf.woff" => static_files::source_code_pro::ITALIC,
+ "SourceCodePro-Regular.ttf.woff2" => static_files::source_code_pro::REGULAR,
+ "SourceCodePro-Semibold.ttf.woff2" => static_files::source_code_pro::SEMIBOLD,
+ "SourceCodePro-It.ttf.woff2" => static_files::source_code_pro::ITALIC,
"SourceCodePro-LICENSE.txt" => static_files::source_code_pro::LICENSE,
- "NanumBarunGothic.ttf.woff2" => static_files::nanum_barun_gothic::REGULAR2,
- "NanumBarunGothic.ttf.woff" => static_files::nanum_barun_gothic::REGULAR,
+ "NanumBarunGothic.ttf.woff2" => static_files::nanum_barun_gothic::REGULAR,
"NanumBarunGothic-LICENSE.txt" => static_files::nanum_barun_gothic::LICENSE,
"LICENSE-MIT.txt" => static_files::LICENSE_MIT,
"LICENSE-APACHE.txt" => static_files::LICENSE_APACHE,
file applies only to those resources. The following third party resources are
included, and carry their own copyright notices and license terms:
-* Fira Sans (FiraSans-Regular.woff2, FiraSans-Medium.woff2,
- FiraSans-Regular.woff, FiraSans-Medium.woff):
+* Fira Sans (FiraSans-Regular.woff2, FiraSans-Medium.woff2):
Copyright (c) 2014, Mozilla Foundation https://mozilla.org/
with Reserved Font Name Fira Sans.
Licensed under the MIT license (see LICENSE-MIT.txt).
* Source Code Pro (SourceCodePro-Regular.ttf.woff2,
- SourceCodePro-Semibold.ttf.woff2, SourceCodePro-It.ttf.woff2,
- SourceCodePro-Regular.ttf.woff, SourceCodePro-Semibold.ttf.woff,
- SourceCodePro-It.ttf.woff):
+ SourceCodePro-Semibold.ttf.woff2, SourceCodePro-It.ttf.woff2):
Copyright 2010, 2012 Adobe Systems Incorporated (http://www.adobe.com/),
with Reserved Font Name 'Source'. All Rights Reserved. Source is a trademark
See SourceCodePro-LICENSE.txt.
* Source Serif 4 (SourceSerif4-Regular.ttf.woff2, SourceSerif4-Bold.ttf.woff2,
- SourceSerif4-It.ttf.woff2, SourceSerif4-Regular.ttf.woff,
- SourceSerif4-Bold.ttf.woff, SourceSerif4-It.ttf.woff):
+ SourceSerif4-It.ttf.woff2):
Copyright 2014-2021 Adobe (http://www.adobe.com/), with Reserved Font Name
'Source'. All Rights Reserved. Source is a trademark of Adobe in the United
font-style: normal;
font-weight: 400;
src: local('Fira Sans'),
- url("FiraSans-Regular.woff2") format("woff2"),
- url("FiraSans-Regular.woff") format('woff');
+ url("FiraSans-Regular.woff2") format("woff2");
font-display: swap;
}
@font-face {
font-style: normal;
font-weight: 500;
src: local('Fira Sans Medium'),
- url("FiraSans-Medium.woff2") format("woff2"),
- url("FiraSans-Medium.woff") format('woff');
+ url("FiraSans-Medium.woff2") format("woff2");
font-display: swap;
}
font-style: normal;
font-weight: 400;
src: local('Source Serif 4'),
- url("SourceSerif4-Regular.ttf.woff2") format("woff2"),
- url("SourceSerif4-Regular.ttf.woff") format("woff");
+ url("SourceSerif4-Regular.ttf.woff2") format("woff2");
font-display: swap;
}
@font-face {
font-style: italic;
font-weight: 400;
src: local('Source Serif 4 Italic'),
- url("SourceSerif4-It.ttf.woff2") format("woff2"),
- url("SourceSerif4-It.ttf.woff") format("woff");
+ url("SourceSerif4-It.ttf.woff2") format("woff2");
font-display: swap;
}
@font-face {
font-style: normal;
font-weight: 700;
src: local('Source Serif 4 Bold'),
- url("SourceSerif4-Bold.ttf.woff2") format("woff2"),
- url("SourceSerif4-Bold.ttf.woff") format("woff");
+ url("SourceSerif4-Bold.ttf.woff2") format("woff2");
font-display: swap;
}
font-weight: 400;
/* Avoid using locally installed font because bad versions are in circulation:
* see https://github.com/rust-lang/rust/issues/24355 */
- src: url("SourceCodePro-Regular.ttf.woff2") format("woff2"),
- url("SourceCodePro-Regular.ttf.woff") format("woff");
+ src: url("SourceCodePro-Regular.ttf.woff2") format("woff2");
font-display: swap;
}
@font-face {
font-family: 'Source Code Pro';
font-style: italic;
font-weight: 400;
- src: url("SourceCodePro-It.ttf.woff2") format("woff2"),
- url("SourceCodePro-It.ttf.woff") format("woff");
+ src: url("SourceCodePro-It.ttf.woff2") format("woff2");
font-display: swap;
}
@font-face {
font-family: 'Source Code Pro';
font-style: normal;
font-weight: 600;
- src: url("SourceCodePro-Semibold.ttf.woff2") format("woff2"),
- url("SourceCodePro-Semibold.ttf.woff") format("woff");
+ src: url("SourceCodePro-Semibold.ttf.woff2") format("woff2");
font-display: swap;
}
/* Avoid using legacy CJK serif fonts in Windows like Batang. */
@font-face {
font-family: 'NanumBarunGothic';
- src: url("NanumBarunGothic.ttf.woff2") format("woff2"),
- url("NanumBarunGothic.ttf.woff") format("woff");
+ src: url("NanumBarunGothic.ttf.woff2") format("woff2");
font-display: swap;
unicode-range: U+AC00-D7AF, U+1100-11FF, U+3130-318F, U+A960-A97F, U+D7B0-D7FF;
}
// This file contains type definitions that are processed by the Closure Compiler but are
// not put into the JavaScript we include as part of the documentation. It is used for
// type checking. See README.md in this directory for more info.
+/* eslint-env es6 */
+/* eslint no-var: "error" */
+/* eslint prefer-const: "error" */
/* eslint-disable */
-var searchState;
+let searchState;
function initSearch(searchIndex){}
/**
* generics: Array<QueryElement>,
* }}
*/
-var QueryElement;
+let QueryElement;
/**
* @typedef {{
* userQuery: string,
* }}
*/
-var ParserState;
+let ParserState;
/**
* @typedef {{
* foundElems: number,
* }}
*/
-var ParsedQuery;
+let ParsedQuery;
/**
* @typedef {{
* type: (Array<?>|null)
* }}
*/
-var Row;
+let Row;
/**
* @typedef {{
* query: ParsedQuery,
* }}
*/
-var ResultsTable;
+let ResultsTable;
/**
* @typedef {{
* ty: number,
* }}
*/
-var Results;
+let Results;
+/* eslint-env es6 */
+/* eslint no-var: "error" */
+/* eslint prefer-const: "error" */
// Local js definitions:
/* global addClass, getSettingValue, hasClass, searchState */
/* global onEach, onEachLazy, removeClass */
}
if (!String.prototype.endsWith) {
String.prototype.endsWith = function(suffix, length) {
- var l = length || this.length;
+ const l = length || this.length;
return this.indexOf(suffix, l - suffix.length) !== -1;
};
}
// Get a value from the rustdoc-vars div, which is used to convey data from
// Rust to the JS. If there is no such element, return null.
function getVar(name) {
- var el = document.getElementById("rustdoc-vars");
+ const el = document.getElementById("rustdoc-vars");
if (el) {
return el.attributes["data-" + name].value;
} else {
return getVar("root-path") + basename + getVar("resource-suffix") + extension;
}
+function hideMain() {
+ addClass(document.getElementById(MAIN_ID), "hidden");
+}
+
+function showMain() {
+ removeClass(document.getElementById(MAIN_ID), "hidden");
+}
+
(function () {
window.rootPath = getVar("root-path");
window.currentCrate = getVar("current-crate");
window.searchJS = resourcePath("search", ".js");
window.searchIndexJS = resourcePath("search-index", ".js");
- var sidebarVars = document.getElementById("sidebar-vars");
+ window.settingsJS = resourcePath("settings", ".js");
+ const sidebarVars = document.getElementById("sidebar-vars");
if (sidebarVars) {
window.sidebarCurrent = {
name: sidebarVars.attributes["data-name"].value,
};
// FIXME: It would be nicer to generate this text content directly in HTML,
// but with the current code it's hard to get the right information in the right place.
- var mobileLocationTitle = document.querySelector(".mobile-topbar h2.location");
- var locationTitle = document.querySelector(".sidebar h2.location");
+ const mobileLocationTitle = document.querySelector(".mobile-topbar h2.location");
+ const locationTitle = document.querySelector(".sidebar h2.location");
if (mobileLocationTitle && locationTitle) {
mobileLocationTitle.innerHTML = locationTitle.innerHTML;
}
return ev.key;
}
- var c = ev.charCode || ev.keyCode;
+ const c = ev.charCode || ev.keyCode;
if (c == 27) {
return "Escape";
}
return String.fromCharCode(c);
}
-var THEME_PICKER_ELEMENT_ID = "theme-picker";
-var THEMES_ELEMENT_ID = "theme-choices";
-var MAIN_ID = "main-content";
+const THEME_PICKER_ELEMENT_ID = "theme-picker";
+const THEMES_ELEMENT_ID = "theme-choices";
+const MAIN_ID = "main-content";
+const SETTINGS_BUTTON_ID = "settings-menu";
+const ALTERNATIVE_DISPLAY_ID = "alternative-display";
+const NOT_DISPLAYED_ID = "not-displayed";
function getThemesElement() {
return document.getElementById(THEMES_ELEMENT_ID);
return document.getElementById(THEME_PICKER_ELEMENT_ID);
}
+function getSettingsButton() {
+ return document.getElementById(SETTINGS_BUTTON_ID);
+}
+
// Returns the current URL without any query parameter or hash.
function getNakedUrl() {
return window.location.href.split("?")[0].split("#")[0];
}
function showThemeButtonState() {
- var themePicker = getThemePickerElement();
- var themeChoices = getThemesElement();
+ const themePicker = getThemePickerElement();
+ const themeChoices = getThemesElement();
themeChoices.style.display = "block";
themePicker.style.borderBottomRightRadius = "0";
}
function hideThemeButtonState() {
- var themePicker = getThemePickerElement();
- var themeChoices = getThemesElement();
+ const themePicker = getThemePickerElement();
+ const themeChoices = getThemesElement();
themeChoices.style.display = "none";
themePicker.style.borderBottomRightRadius = "3px";
themePicker.style.borderBottomLeftRadius = "3px";
}
+window.hideSettings = function() {
+ // Does nothing by default.
+};
+
// Set up the theme picker list.
(function () {
if (!document.location.href.startsWith("file:///")) {
return;
}
- var themeChoices = getThemesElement();
- var themePicker = getThemePickerElement();
- var availableThemes = getVar("themes").split(",");
+ const themeChoices = getThemesElement();
+ const themePicker = getThemePickerElement();
+ const availableThemes = getVar("themes").split(",");
removeClass(themeChoices.parentElement, "hidden");
}
function handleThemeButtonsBlur(e) {
- var active = document.activeElement;
- var related = e.relatedTarget;
+ const active = document.activeElement;
+ const related = e.relatedTarget;
if (active.id !== THEME_PICKER_ELEMENT_ID &&
(!active.parentNode || active.parentNode.id !== THEMES_ELEMENT_ID) &&
themePicker.onclick = switchThemeButtonState;
themePicker.onblur = handleThemeButtonsBlur;
availableThemes.forEach(function(item) {
- var but = document.createElement("button");
+ const but = document.createElement("button");
but.textContent = item;
but.onclick = function() {
switchTheme(window.currentTheme, window.mainTheme, item, true);
});
}());
+/**
+ * This function inserts `newNode` after `referenceNode`. It doesn't work if `referenceNode`
+ * doesn't have a parent node.
+ *
+ * @param {HTMLElement} newNode
+ * @param {HTMLElement} referenceNode
+ */
+function insertAfter(newNode, referenceNode) {
+ referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
+}
+
+/**
+ * This function creates a new `<section>` with the given `id` and `classes` if it doesn't already
+ * exist.
+ *
+ * More information about this in `switchDisplayedElement` documentation.
+ *
+ * @param {string} id
+ * @param {string} classes
+ */
+function getOrCreateSection(id, classes) {
+ let el = document.getElementById(id);
+
+ if (!el) {
+ el = document.createElement("section");
+ el.id = id;
+ el.className = classes;
+ insertAfter(el, document.getElementById(MAIN_ID));
+ }
+ return el;
+}
+
+/**
+ * Returns the `<section>` element which contains the displayed element.
+ *
+ * @return {HTMLElement}
+ */
+function getAlternativeDisplayElem() {
+ return getOrCreateSection(ALTERNATIVE_DISPLAY_ID, "content hidden");
+}
+
+/**
+ * Returns the `<section>` element which contains the not-displayed elements.
+ *
+ * @return {HTMLElement}
+ */
+function getNotDisplayedElem() {
+ return getOrCreateSection(NOT_DISPLAYED_ID, "hidden");
+}
+
+/**
+ * To nicely switch between displayed "extra" elements (such as search results or settings menu)
+ * and to alternate between the displayed and not displayed elements, we hold them in two different
+ * `<section>` elements. They work in pair: one holds the hidden elements while the other
+ * contains the displayed element (there can be only one at the same time!). So basically, we switch
+ * elements between the two `<section>` elements.
+ *
+ * @param {HTMLElement} elemToDisplay
+ */
+function switchDisplayedElement(elemToDisplay) {
+ const el = getAlternativeDisplayElem();
+
+ if (el.children.length > 0) {
+ getNotDisplayedElem().appendChild(el.firstElementChild);
+ }
+ if (elemToDisplay === null) {
+ addClass(el, "hidden");
+ showMain();
+ return;
+ }
+ el.appendChild(elemToDisplay);
+ hideMain();
+ removeClass(el, "hidden");
+}
+
+function browserSupportsHistoryApi() {
+ return window.history && typeof window.history.pushState === "function";
+}
+
+// eslint-disable-next-line no-unused-vars
+function loadCss(cssFileName) {
+ const link = document.createElement("link");
+ link.href = resourcePath(cssFileName, ".css");
+ link.type = "text/css";
+ link.rel = "stylesheet";
+ document.getElementsByTagName("head")[0].appendChild(link);
+}
+
(function() {
"use strict";
+ function loadScript(url) {
+ const script = document.createElement('script');
+ script.src = url;
+ document.head.append(script);
+ }
+
+
+ getSettingsButton().onclick = function(event) {
+ event.preventDefault();
+ loadScript(window.settingsJS);
+ };
+
window.searchState = {
loadingText: "Loading search results...",
input: document.getElementsByClassName("search-input")[0],
outputElement: function() {
- return document.getElementById("search");
+ let el = document.getElementById("search");
+ if (!el) {
+ el = document.createElement("section");
+ el.id = "search";
+ getNotDisplayedElem().appendChild(el);
+ }
+ return el;
},
title: document.title,
titleBeforeSearch: document.title,
searchState.timeout = null;
}
},
+ isDisplayed: function() {
+ return searchState.outputElement().parentElement.id === ALTERNATIVE_DISPLAY_ID;
+ },
// Sets the focus on the search bar at the top of the page
focus: function() {
searchState.input.focus();
if (search === null || typeof search === 'undefined') {
search = searchState.outputElement();
}
- addClass(main, "hidden");
- removeClass(search, "hidden");
+ switchDisplayedElement(search);
searchState.mouseMovedAfterSearch = false;
document.title = searchState.title;
},
- hideResults: function(search) {
- if (search === null || typeof search === 'undefined') {
- search = searchState.outputElement();
- }
- addClass(search, "hidden");
- removeClass(main, "hidden");
+ hideResults: function() {
+ switchDisplayedElement(null);
document.title = searchState.titleBeforeSearch;
// We also remove the query parameter from the URL.
- if (searchState.browserSupportsHistoryApi()) {
+ if (browserSupportsHistoryApi()) {
history.replaceState(null, window.currentCrate + " - Rust",
getNakedUrl() + window.location.hash);
}
},
getQueryStringParams: function() {
- var params = {};
+ const params = {};
window.location.search.substring(1).split("&").
map(function(s) {
- var pair = s.split("=");
+ const pair = s.split("=");
params[decodeURIComponent(pair[0])] =
typeof pair[1] === "undefined" ? null : decodeURIComponent(pair[1]);
});
return params;
},
- browserSupportsHistoryApi: function() {
- return window.history && typeof window.history.pushState === "function";
- },
setup: function() {
- var search_input = searchState.input;
+ const search_input = searchState.input;
if (!searchState.input) {
return;
}
- function loadScript(url) {
- var script = document.createElement('script');
- script.src = url;
- document.head.append(script);
- }
-
- var searchLoaded = false;
+ let searchLoaded = false;
function loadSearch() {
if (!searchLoaded) {
searchLoaded = true;
loadSearch();
}
- var params = searchState.getQueryStringParams();
+ const params = searchState.getQueryStringParams();
if (params.search !== undefined) {
- var search = searchState.outputElement();
+ const search = searchState.outputElement();
search.innerHTML = "<h3 class=\"search-loading\">" +
searchState.loadingText + "</h3>";
searchState.showResults(search);
function getPageId() {
if (window.location.hash) {
- var tmp = window.location.hash.replace(/^#/, "");
+ const tmp = window.location.hash.replace(/^#/, "");
if (tmp.length > 0) {
return tmp;
}
return null;
}
- var toggleAllDocsId = "toggle-all-docs";
- var main = document.getElementById(MAIN_ID);
- var savedHash = "";
+ const toggleAllDocsId = "toggle-all-docs";
+ let savedHash = "";
function handleHashes(ev) {
- var elem;
- var search = searchState.outputElement();
- if (ev !== null && search && !hasClass(search, "hidden") && ev.newURL) {
+ if (ev !== null && searchState.isDisplayed() && ev.newURL) {
// This block occurs when clicking on an element in the navbar while
// in a search.
- searchState.hideResults(search);
- var hash = ev.newURL.slice(ev.newURL.indexOf("#") + 1);
- if (searchState.browserSupportsHistoryApi()) {
+ switchDisplayedElement(null);
+ const hash = ev.newURL.slice(ev.newURL.indexOf("#") + 1);
+ if (browserSupportsHistoryApi()) {
// `window.location.search`` contains all the query parameters, not just `search`.
history.replaceState(null, "",
getNakedUrl() + window.location.search + "#" + hash);
}
- elem = document.getElementById(hash);
+ const elem = document.getElementById(hash);
if (elem) {
elem.scrollIntoView();
}
function onHashChange(ev) {
// If we're in mobile mode, we should hide the sidebar in any case.
- var sidebar = document.getElementsByClassName("sidebar")[0];
+ const sidebar = document.getElementsByClassName("sidebar")[0];
removeClass(sidebar, "shown");
handleHashes(ev);
}
}
function handleEscape(ev) {
- var help = getHelpElement(false);
- var search = searchState.outputElement();
+ searchState.clearInputTimeout();
+ const help = getHelpElement(false);
if (help && !hasClass(help, "hidden")) {
displayHelp(false, ev, help);
- } else if (search && !hasClass(search, "hidden")) {
- searchState.clearInputTimeout();
+ } else {
+ switchDisplayedElement(null);
+ if (browserSupportsHistoryApi()) {
+ history.replaceState(null, window.currentCrate + " - Rust",
+ getNakedUrl() + window.location.hash);
+ }
ev.preventDefault();
- searchState.hideResults(search);
}
searchState.defocus();
hideThemeButtonState();
}
- var disableShortcuts = getSettingValue("disable-shortcuts") === "true";
+ const disableShortcuts = getSettingValue("disable-shortcuts") === "true";
function handleShortcut(ev) {
// Don't interfere with browser shortcuts
if (ev.ctrlKey || ev.altKey || ev.metaKey || disableShortcuts) {
return;
}
+ let themePicker;
+
if (document.activeElement.tagName === "INPUT") {
switch (getVirtualKey(ev)) {
case "Escape":
case "T":
displayHelp(false, ev);
ev.preventDefault();
- var themePicker = getThemePickerElement();
+ themePicker = getThemePickerElement();
themePicker.click();
themePicker.focus();
break;
}
function handleThemeKeyDown(ev) {
- var active = document.activeElement;
- var themes = getThemesElement();
+ const active = document.activeElement;
+ const themes = getThemesElement();
switch (getVirtualKey(ev)) {
case "ArrowUp":
ev.preventDefault();
document.addEventListener("keypress", handleShortcut);
document.addEventListener("keydown", handleShortcut);
- (function() {
- var x = document.getElementsByClassName("version-selector");
- if (x.length > 0) {
- x[0].onchange = function() {
- var i, match,
- url = document.location.href,
- stripped = "",
- len = window.rootPath.match(/\.\.\//g).length + 1;
-
- for (i = 0; i < len; ++i) {
- match = url.match(/\/[^/]*$/);
- if (i < len - 1) {
- stripped = match[0] + stripped;
- }
- url = url.substring(0, url.length - match[0].length);
- }
-
- var selectedVersion = document.getElementsByClassName("version-selector")[0].value;
- url += "/" + selectedVersion + stripped;
-
- document.location.href = url;
- };
- }
- }());
-
// delayed sidebar rendering.
window.initSidebarItems = function(items) {
- var sidebar = document.getElementsByClassName("sidebar-elems")[0];
- var others;
- var current = window.sidebarCurrent;
+ const sidebar = document.getElementsByClassName("sidebar-elems")[0];
+ let others;
+ const current = window.sidebarCurrent;
function addSidebarCrates(crates) {
if (!hasClass(document.body, "crate")) {
return;
}
// Draw a convenient sidebar of known crates if we have a listing
- var div = document.createElement("div");
+ const div = document.createElement("div");
div.className = "block crate";
div.innerHTML = "<h3>Crates</h3>";
- var ul = document.createElement("ul");
+ const ul = document.createElement("ul");
div.appendChild(ul);
- for (var i = 0; i < crates.length; ++i) {
- var klass = "crate";
- if (window.rootPath !== "./" && crates[i] === window.currentCrate) {
+ for (const crate of crates) {
+ let klass = "crate";
+ if (window.rootPath !== "./" && crate === window.currentCrate) {
klass += " current";
}
- var link = document.createElement("a");
- link.href = window.rootPath + crates[i] + "/index.html";
+ const link = document.createElement("a");
+ link.href = window.rootPath + crate + "/index.html";
link.className = klass;
- link.textContent = crates[i];
+ link.textContent = crate;
- var li = document.createElement("li");
+ const li = document.createElement("li");
li.appendChild(link);
ul.appendChild(li);
}
* "Modules", or "Macros".
*/
function block(shortty, id, longty) {
- var filtered = items[shortty];
+ const filtered = items[shortty];
if (!filtered) {
return;
}
- var div = document.createElement("div");
+ const div = document.createElement("div");
div.className = "block " + shortty;
- var h3 = document.createElement("h3");
+ const h3 = document.createElement("h3");
h3.innerHTML = `<a href="index.html#${id}">${longty}</a>`;
div.appendChild(h3);
- var ul = document.createElement("ul");
+ const ul = document.createElement("ul");
- for (var i = 0, len = filtered.length; i < len; ++i) {
- var item = filtered[i];
- var name = item[0];
- var desc = item[1]; // can be null
+ for (const item of filtered) {
+ const name = item[0];
+ const desc = item[1]; // can be null
- var klass = shortty;
+ let klass = shortty;
if (name === current.name && shortty === current.ty) {
klass += " current";
}
- var path;
+ let path;
if (shortty === "mod") {
path = name + "/index.html";
} else {
path = shortty + "." + name + ".html";
}
- var link = document.createElement("a");
+ const link = document.createElement("a");
link.href = current.relpath + path;
link.title = desc;
link.className = klass;
link.textContent = name;
- var li = document.createElement("li");
+ const li = document.createElement("li");
li.appendChild(link);
ul.appendChild(li);
}
others.className = "others";
sidebar.appendChild(others);
- var isModule = hasClass(document.body, "mod");
+ const isModule = hasClass(document.body, "mod");
if (!isModule) {
block("primitive", "primitives", "Primitive Types");
block("mod", "modules", "Modules");
};
window.register_implementors = function(imp) {
- var implementors = document.getElementById("implementors-list");
- var synthetic_implementors = document.getElementById("synthetic-implementors-list");
+ const implementors = document.getElementById("implementors-list");
+ const synthetic_implementors = document.getElementById("synthetic-implementors-list");
+ const inlined_types = new Set();
if (synthetic_implementors) {
// This `inlined_types` variable is used to avoid having the same implementation
//
// By the way, this is only used by and useful for traits implemented automatically
// (like "Send" and "Sync").
- var inlined_types = new Set();
onEachLazy(synthetic_implementors.getElementsByClassName("impl"), function(el) {
- var aliases = el.getAttribute("data-aliases");
+ const aliases = el.getAttribute("data-aliases");
if (!aliases) {
return;
}
});
}
- var currentNbImpls = implementors.getElementsByClassName("impl").length;
- var traitName = document.querySelector("h1.fqn > .in-band > .trait").textContent;
- var baseIdName = "impl-" + traitName + "-";
- var libs = Object.getOwnPropertyNames(imp);
- for (var i = 0, llength = libs.length; i < llength; ++i) {
- if (libs[i] === window.currentCrate) { continue; }
- var structs = imp[libs[i]];
+ let currentNbImpls = implementors.getElementsByClassName("impl").length;
+ const traitName = document.querySelector("h1.fqn > .in-band > .trait").textContent;
+ const baseIdName = "impl-" + traitName + "-";
+ const libs = Object.getOwnPropertyNames(imp);
+ for (const lib of libs) {
+ if (lib === window.currentCrate) {
+ continue;
+ }
+ const structs = imp[lib];
struct_loop:
- for (var j = 0, slength = structs.length; j < slength; ++j) {
- var struct = structs[j];
-
- var list = struct.synthetic ? synthetic_implementors : implementors;
+ for (const struct of structs) {
+ const list = struct.synthetic ? synthetic_implementors : implementors;
if (struct.synthetic) {
- for (var k = 0, stlength = struct.types.length; k < stlength; k++) {
- if (inlined_types.has(struct.types[k])) {
+ for (const struct_type of struct.types) {
+ if (inlined_types.has(struct_type)) {
continue struct_loop;
}
- inlined_types.add(struct.types[k]);
+ inlined_types.add(struct_type);
}
}
- var code = document.createElement("h3");
+ const code = document.createElement("h3");
code.innerHTML = struct.text;
addClass(code, "code-header");
addClass(code, "in-band");
onEachLazy(code.getElementsByTagName("a"), function(elem) {
- var href = elem.getAttribute("href");
+ const href = elem.getAttribute("href");
if (href && href.indexOf("http") !== 0) {
elem.setAttribute("href", window.rootPath + href);
}
});
- var currentId = baseIdName + currentNbImpls;
- var anchor = document.createElement("a");
+ const currentId = baseIdName + currentNbImpls;
+ const anchor = document.createElement("a");
anchor.href = "#" + currentId;
addClass(anchor, "anchor");
- var display = document.createElement("div");
+ const display = document.createElement("div");
display.id = currentId;
addClass(display, "impl");
display.appendChild(anchor);
}
function toggleAllDocs() {
- var innerToggle = document.getElementById(toggleAllDocsId);
+ const innerToggle = document.getElementById(toggleAllDocsId);
if (!innerToggle) {
return;
}
- var sectionIsCollapsed = false;
+ let sectionIsCollapsed = false;
if (hasClass(innerToggle, "will-expand")) {
removeClass(innerToggle, "will-expand");
onEachLazy(document.getElementsByClassName("rustdoc-toggle"), function(e) {
innerToggle.children[0].innerText = labelForToggleButton(sectionIsCollapsed);
}
- function insertAfter(newNode, referenceNode) {
- referenceNode.parentNode.insertBefore(newNode, referenceNode.nextSibling);
- }
-
(function() {
- var toggles = document.getElementById(toggleAllDocsId);
+ const toggles = document.getElementById(toggleAllDocsId);
if (toggles) {
toggles.onclick = toggleAllDocs;
}
- var hideMethodDocs = getSettingValue("auto-hide-method-docs") === "true";
- var hideImplementations = getSettingValue("auto-hide-trait-implementations") === "true";
- var hideLargeItemContents = getSettingValue("auto-hide-large-items") !== "false";
+ const hideMethodDocs = getSettingValue("auto-hide-method-docs") === "true";
+ const hideImplementations = getSettingValue("auto-hide-trait-implementations") === "true";
+ const hideLargeItemContents = getSettingValue("auto-hide-large-items") !== "false";
function setImplementorsTogglesOpen(id, open) {
- var list = document.getElementById(id);
+ const list = document.getElementById(id);
if (list !== null) {
onEachLazy(list.getElementsByClassName("implementors-toggle"), function(e) {
e.open = open;
});
- var pageId = getPageId();
+ const pageId = getPageId();
if (pageId !== null) {
expandSection(pageId);
}
(function() {
// To avoid checking on "rustdoc-line-numbers" value on every loop...
- var lineNumbersFunc = function() {};
+ let lineNumbersFunc = function() {};
if (getSettingValue("line-numbers") === "true") {
lineNumbersFunc = function(x) {
- var count = x.textContent.split("\n").length;
- var elems = [];
- for (var i = 0; i < count; ++i) {
+ const count = x.textContent.split("\n").length;
+ const elems = [];
+ for (let i = 0; i < count; ++i) {
elems.push(i + 1);
}
- var node = document.createElement("pre");
+ const node = document.createElement("pre");
addClass(node, "line-number");
node.innerHTML = elems.join("\n");
x.parentNode.insertBefore(node, x);
}());
function hideSidebar() {
- var sidebar = document.getElementsByClassName("sidebar")[0];
+ const sidebar = document.getElementsByClassName("sidebar")[0];
removeClass(sidebar, "shown");
}
function handleClick(id, f) {
- var elem = document.getElementById(id);
+ const elem = document.getElementById(id);
if (elem) {
elem.addEventListener("click", f);
}
};
});
- var sidebar_menu_toggle = document.getElementsByClassName("sidebar-menu-toggle")[0];
+ const sidebar_menu_toggle = document.getElementsByClassName("sidebar-menu-toggle")[0];
if (sidebar_menu_toggle) {
sidebar_menu_toggle.addEventListener("click", function() {
- var sidebar = document.getElementsByClassName("sidebar")[0];
+ const sidebar = document.getElementsByClassName("sidebar")[0];
if (!hasClass(sidebar, "shown")) {
addClass(sidebar, "shown");
} else {
});
}
- var buildHelperPopup = function() {
- var popup = document.createElement("aside");
+ let buildHelperPopup = function() {
+ const popup = document.createElement("aside");
addClass(popup, "hidden");
popup.id = "help";
}
});
- var book_info = document.createElement("span");
+ const book_info = document.createElement("span");
book_info.className = "top";
book_info.innerHTML = "You can find more information in \
<a href=\"https://doc.rust-lang.org/rustdoc/\">the rustdoc book</a>.";
- var container = document.createElement("div");
- var shortcuts = [
+ const container = document.createElement("div");
+ const shortcuts = [
["?", "Show this help dialog"],
["S", "Focus the search field"],
["T", "Focus the theme picker menu"],
})
.join("") + "</dt><dd>" + x[1] + "</dd>";
}).join("");
- var div_shortcuts = document.createElement("div");
+ const div_shortcuts = document.createElement("div");
addClass(div_shortcuts, "shortcuts");
div_shortcuts.innerHTML = "<h2>Keyboard Shortcuts</h2><dl>" + shortcuts + "</dl></div>";
- var infos = [
+ const infos = [
"Prefix searches with a type followed by a colon (e.g., <code>fn:</code>) to \
restrict the search to a given item kind.",
"Accepted kinds are: <code>fn</code>, <code>mod</code>, <code>struct</code>, \
].map(function(x) {
return "<p>" + x + "</p>";
}).join("");
- var div_infos = document.createElement("div");
+ const div_infos = document.createElement("div");
addClass(div_infos, "infos");
div_infos.innerHTML = "<h2>Search Tricks</h2>" + infos;
container.appendChild(div_shortcuts);
container.appendChild(div_infos);
- var rustdoc_version = document.createElement("span");
+ const rustdoc_version = document.createElement("span");
rustdoc_version.className = "bottom";
- var rustdoc_version_code = document.createElement("code");
+ const rustdoc_version_code = document.createElement("code");
rustdoc_version_code.innerText = "rustdoc " + getVar("rustdoc-version");
rustdoc_version.appendChild(rustdoc_version_code);
}());
(function () {
- var reset_button_timeout = null;
+ let reset_button_timeout = null;
window.copy_path = function(but) {
- var parent = but.parentElement;
- var path = [];
+ const parent = but.parentElement;
+ const path = [];
onEach(parent.childNodes, function(child) {
if (child.tagName === 'A') {
}
});
- var el = document.createElement('textarea');
+ const el = document.createElement('textarea');
el.value = path.join('::');
el.setAttribute('readonly', '');
// To not make it appear on the screen.
// There is always one children, but multiple childNodes.
but.children[0].style.display = 'none';
- var tmp;
+ let tmp;
if (but.childNodes.length < 2) {
tmp = document.createTextNode('✓');
but.appendChild(tmp);
-/* global addClass, hasClass, removeClass, onEach */
+/* eslint-env es6 */
+/* eslint no-var: "error" */
+/* eslint prefer-const: "error" */
+/* global addClass, hasClass, removeClass, onEachLazy */
(function () {
// Number of lines shown when code viewer is not expanded
// Scroll code block to the given code location
function scrollToLoc(elt, loc) {
- var lines = elt.querySelector('.line-numbers');
- var scrollOffset;
+ const lines = elt.querySelector('.line-numbers');
+ let scrollOffset;
// If the block is greater than the size of the viewer,
// then scroll to the top of the block. Otherwise scroll
// to the middle of the block.
if (loc[1] - loc[0] > MAX_LINES) {
- var line = Math.max(0, loc[0] - 1);
+ const line = Math.max(0, loc[0] - 1);
scrollOffset = lines.children[line].offsetTop;
} else {
- var wrapper = elt.querySelector(".code-wrapper");
- var halfHeight = wrapper.offsetHeight / 2;
- var offsetMid = (lines.children[loc[0]].offsetTop
+ const wrapper = elt.querySelector(".code-wrapper");
+ const halfHeight = wrapper.offsetHeight / 2;
+ const offsetMid = (lines.children[loc[0]].offsetTop
+ lines.children[loc[1]].offsetTop) / 2;
scrollOffset = offsetMid - halfHeight;
}
}
function updateScrapedExample(example) {
- var locs = JSON.parse(example.attributes.getNamedItem("data-locs").textContent);
- var locIndex = 0;
- var highlights = example.querySelectorAll('.highlight');
- var link = example.querySelector('.scraped-example-title a');
+ const locs = JSON.parse(example.attributes.getNamedItem("data-locs").textContent);
+ let locIndex = 0;
+ const highlights = Array.prototype.slice.call(example.querySelectorAll('.highlight'));
+ const link = example.querySelector('.scraped-example-title a');
if (locs.length > 1) {
// Toggle through list of examples in a given file
- var onChangeLoc = function(changeIndex) {
+ const onChangeLoc = function(changeIndex) {
removeClass(highlights[locIndex], 'focus');
changeIndex();
scrollToLoc(example, locs[locIndex][0]);
addClass(highlights[locIndex], 'focus');
- var url = locs[locIndex][1];
- var title = locs[locIndex][2];
+ const url = locs[locIndex][1];
+ const title = locs[locIndex][2];
link.href = url;
link.innerHTML = title;
});
}
- var expandButton = example.querySelector('.expand');
+ const expandButton = example.querySelector('.expand');
if (expandButton) {
expandButton.addEventListener('click', function () {
if (hasClass(example, "expanded")) {
scrollToLoc(example, locs[0][0]);
}
- var firstExamples = document.querySelectorAll('.scraped-example-list > .scraped-example');
- onEach(firstExamples, updateScrapedExample);
- onEach(document.querySelectorAll('.more-examples-toggle'), function(toggle) {
+ const firstExamples = document.querySelectorAll('.scraped-example-list > .scraped-example');
+ onEachLazy(firstExamples, updateScrapedExample);
+ onEachLazy(document.querySelectorAll('.more-examples-toggle'), function(toggle) {
// Allow users to click the left border of the <details> section to close it,
// since the section can be large and finding the [+] button is annoying.
- toggle.querySelectorAll('.toggle-line, .hide-more').forEach(button => {
+ onEachLazy(toggle.querySelectorAll('.toggle-line, .hide-more'), button => {
button.addEventListener('click', function() {
toggle.open = false;
});
});
- var moreExamples = toggle.querySelectorAll('.scraped-example');
+ const moreExamples = toggle.querySelectorAll('.scraped-example');
toggle.querySelector('summary').addEventListener('click', function() {
// Wrapping in setTimeout ensures the update happens after the elements are actually
// visible. This is necessary since updateScrapedExample calls scrollToLoc which
// depends on offsetHeight, a property that requires an element to be visible to
// compute correctly.
- setTimeout(function() { onEach(moreExamples, updateScrapedExample); });
+ setTimeout(function() { onEachLazy(moreExamples, updateScrapedExample); });
}, {once: true});
});
})();
+/* eslint-env es6 */
+/* eslint no-var: "error" */
+/* eslint prefer-const: "error" */
/* global addClass, getNakedUrl, getSettingValue, hasOwnPropertyRustdoc, initSearch, onEach */
-/* global onEachLazy, removeClass, searchState, hasClass */
+/* global onEachLazy, removeClass, searchState, browserSupportsHistoryApi */
(function() {
// This mapping table should match the discriminants of
// `rustdoc::formats::item_type::ItemType` type in Rust.
-var itemTypes = [
+const itemTypes = [
"mod",
"externcrate",
"import",
];
// used for special search precedence
-var TY_PRIMITIVE = itemTypes.indexOf("primitive");
-var TY_KEYWORD = itemTypes.indexOf("keyword");
+const TY_PRIMITIVE = itemTypes.indexOf("primitive");
+const TY_KEYWORD = itemTypes.indexOf("keyword");
// In the search display, allows to switch between tabs.
function printTab(nb) {
if (nb === 0 || nb === 1 || nb === 2) {
searchState.currentTab = nb;
}
- var nb_copy = nb;
+ let nb_copy = nb;
onEachLazy(document.getElementById("titles").childNodes, function(elem) {
if (nb_copy === 0) {
addClass(elem, "selected");
* This code is an unmodified version of the code written by Marco de Wit
* and was found at https://stackoverflow.com/a/18514751/745719
*/
-var levenshtein_row2 = [];
+const levenshtein_row2 = [];
function levenshtein(s1, s2) {
if (s1 === s2) {
return 0;
}
- var s1_len = s1.length, s2_len = s2.length;
+ const s1_len = s1.length, s2_len = s2.length;
if (s1_len && s2_len) {
- var i1 = 0, i2 = 0, a, b, c, c2, row = levenshtein_row2;
+ let i1 = 0, i2 = 0, a, b, c, c2;
+ const row = levenshtein_row2;
while (i1 < s1_len) {
row[i1] = ++i1;
}
}
window.initSearch = function(rawSearchIndex) {
- var MAX_LEV_DISTANCE = 3;
- var MAX_RESULTS = 200;
- var GENERICS_DATA = 2;
- var NAME = 0;
- var INPUTS_DATA = 0;
- var OUTPUT_DATA = 1;
- var NO_TYPE_FILTER = -1;
+ const MAX_LEV_DISTANCE = 3;
+ const MAX_RESULTS = 200;
+ const GENERICS_DATA = 2;
+ const NAME = 0;
+ const INPUTS_DATA = 0;
+ const OUTPUT_DATA = 1;
+ const NO_TYPE_FILTER = -1;
/**
* @type {Array<Row>}
*/
- var searchIndex;
- /**
- * @type {Array<string>}
- */
- var searchWords;
- var currentResults;
- var ALIASES = {};
- var params = searchState.getQueryStringParams();
+ let searchIndex;
+ let currentResults;
+ const ALIASES = {};
+ const params = searchState.getQueryStringParams();
// Populate search bar with query string search term when provided,
// but only if the input bar is empty. This avoid the obnoxious issue
}
function itemTypeFromName(typename) {
- for (var i = 0, len = itemTypes.length; i < len; ++i) {
+ for (let i = 0, len = itemTypes.length; i < len; ++i) {
if (itemTypes[i] === typename) {
return i;
}
throw new Error("Cannot use literal search when there is more than one element");
}
parserState.pos += 1;
- var start = parserState.pos;
- var end = getIdentEndPosition(parserState);
+ const start = parserState.pos;
+ const end = getIdentEndPosition(parserState);
if (parserState.pos >= parserState.length) {
throw new Error("Unclosed `\"`");
} else if (parserState.userQuery[end] !== "\"") {
if (query.literalSearch && parserState.totalElems - parserState.genericsElems > 0) {
throw new Error("You cannot have more than one element if you use quotes");
}
- var pathSegments = name.split("::");
+ const pathSegments = name.split("::");
if (pathSegments.length > 1) {
- for (var i = 0, len = pathSegments.length; i < len; ++i) {
- var pathSegment = pathSegments[i];
+ for (let i = 0, len = pathSegments.length; i < len; ++i) {
+ const pathSegment = pathSegments[i];
if (pathSegment.length === 0) {
if (i === 0) {
* @return {integer}
*/
function getIdentEndPosition(parserState) {
- var end = parserState.pos;
+ let end = parserState.pos;
+ let foundExclamation = false;
while (parserState.pos < parserState.length) {
- var c = parserState.userQuery[parserState.pos];
+ const c = parserState.userQuery[parserState.pos];
if (!isIdentCharacter(c)) {
- if (isErrorCharacter(c)) {
+ if (c === "!") {
+ if (foundExclamation) {
+ throw new Error("Cannot have more than one `!` in an ident");
+ } else if (parserState.pos + 1 < parserState.length &&
+ isIdentCharacter(parserState.userQuery[parserState.pos + 1]))
+ {
+ throw new Error("`!` can only be at the end of an ident");
+ }
+ foundExclamation = true;
+ } else if (isErrorCharacter(c)) {
throw new Error(`Unexpected \`${c}\``);
} else if (
isStopCharacter(c) ||
}
// Skip current ":".
parserState.pos += 1;
+ foundExclamation = false;
} else {
throw new Error(`Unexpected \`${c}\``);
}
* @param {boolean} isInGenerics
*/
function getNextElem(query, parserState, elems, isInGenerics) {
- var generics = [];
+ const generics = [];
- var start = parserState.pos;
- var end;
+ let start = parserState.pos;
+ let end;
// We handle the strings on their own mostly to make code easier to follow.
if (parserState.userQuery[parserState.pos] === "\"") {
start += 1;
* character.
*/
function getItemsBefore(query, parserState, elems, endChar) {
- var foundStopChar = true;
+ let foundStopChar = true;
while (parserState.pos < parserState.length) {
- var c = parserState.userQuery[parserState.pos];
+ const c = parserState.userQuery[parserState.pos];
if (c === endChar) {
break;
} else if (isSeparatorCharacter(c)) {
} else if (c === ":" && isPathStart(parserState)) {
throw new Error("Unexpected `::`: paths cannot start with `::`");
} else if (c === ":" || isEndCharacter(c)) {
- var extra = "";
+ let extra = "";
if (endChar === ">") {
extra = "`<`";
} else if (endChar === "") {
}
throw new Error(`Expected \`,\` or \` \`, found \`${c}\``);
}
- var posBefore = parserState.pos;
+ const posBefore = parserState.pos;
getNextElem(query, parserState, elems, endChar === ">");
// This case can be encountered if `getNextElem` encounted a "stop character" right from
// the start. For example if you have `,,` or `<>`. In this case, we simply move up the
* @param {ParserState} parserState
*/
function checkExtraTypeFilterCharacters(parserState) {
- var query = parserState.userQuery;
+ const query = parserState.userQuery;
- for (var pos = 0; pos < parserState.pos; ++pos) {
+ for (let pos = 0; pos < parserState.pos; ++pos) {
if (!isIdentCharacter(query[pos]) && !isWhitespaceCharacter(query[pos])) {
throw new Error(`Unexpected \`${query[pos]}\` in type filter`);
}
* @param {ParserState} parserState
*/
function parseInput(query, parserState) {
- var c, before;
- var foundStopChar = true;
+ let c, before;
+ let foundStopChar = true;
while (parserState.pos < parserState.length) {
c = parserState.userQuery[parserState.pos];
* @return {string}
*/
function buildUrl(search, filterCrates) {
- var extra = "?search=" + encodeURIComponent(search);
+ let extra = "?search=" + encodeURIComponent(search);
if (filterCrates !== null) {
extra += "&filter-crate=" + encodeURIComponent(filterCrates);
* @return {string|null}
*/
function getFilterCrates() {
- var elem = document.getElementById("crate-search");
+ const elem = document.getElementById("crate-search");
if (elem &&
elem.value !== "All crates" &&
*
* The supported syntax by this parser is as follow:
*
- * ident = *(ALPHA / DIGIT / "_")
+ * ident = *(ALPHA / DIGIT / "_") [!]
* path = ident *(DOUBLE-COLON ident)
* arg = path [generics]
* arg-without-generic = path
*/
function parseQuery(userQuery) {
userQuery = userQuery.trim();
- var parserState = {
+ const parserState = {
length: userQuery.length,
pos: 0,
// Total number of elements (includes generics).
typeFilter: null,
userQuery: userQuery.toLowerCase(),
};
- var query = newParsedQuery(userQuery);
+ let query = newParsedQuery(userQuery);
try {
parseInput(query, parserState);
if (parserState.typeFilter !== null) {
- var typeFilter = parserState.typeFilter;
+ let typeFilter = parserState.typeFilter;
if (typeFilter === "const") {
typeFilter = "constant";
}
* @return {ResultsTable}
*/
function execQuery(parsedQuery, searchWords, filterCrates) {
- var results_others = {}, results_in_args = {}, results_returned = {};
+ const results_others = {}, results_in_args = {}, results_returned = {};
function transformResults(results) {
- var duplicates = {};
- var out = [];
-
- for (var i = 0, len = results.length; i < len; ++i) {
- var result = results[i];
+ const duplicates = {};
+ const out = [];
+ for (const result of results) {
if (result.id > -1) {
- var obj = searchIndex[result.id];
+ const obj = searchIndex[result.id];
obj.lev = result.lev;
- var res = buildHrefAndPath(obj);
+ const res = buildHrefAndPath(obj);
obj.displayPath = pathSplitter(res[0]);
obj.fullPath = obj.displayPath + obj.name;
// To be sure than it some items aren't considered as duplicate.
}
function sortResults(results, isType) {
- var userQuery = parsedQuery.userQuery;
- var ar = [];
- for (var entry in results) {
+ const userQuery = parsedQuery.userQuery;
+ const ar = [];
+ for (const entry in results) {
if (hasOwnPropertyRustdoc(results, entry)) {
- var result = results[entry];
+ const result = results[entry];
result.word = searchWords[result.id];
result.item = searchIndex[result.id] || {};
ar.push(result);
}
results.sort(function(aaa, bbb) {
- var a, b;
+ let a, b;
// sort by exact match with regard to the last word (mismatch goes later)
a = (aaa.word !== userQuery);
return 0;
});
- var nameSplit = null;
+ let nameSplit = null;
if (parsedQuery.elems.length === 1) {
- var hasPath = typeof parsedQuery.elems[0].path === "undefined";
+ const hasPath = typeof parsedQuery.elems[0].path === "undefined";
nameSplit = hasPath ? null : parsedQuery.elems[0].path;
}
- for (var i = 0, len = results.length; i < len; ++i) {
- result = results[i];
-
+ for (const result of results) {
// this validation does not make sense when searching by types
if (result.dontValidate) {
continue;
}
- var name = result.item.name.toLowerCase(),
+ const name = result.item.name.toLowerCase(),
path = result.item.path.toLowerCase(),
parent = result.item.parent;
}
// The names match, but we need to be sure that all generics kinda
// match as well.
- var elem_name;
+ let elem_name;
if (elem.generics.length > 0 && row[GENERICS_DATA].length >= elem.generics.length) {
- var elems = Object.create(null);
- for (var x = 0, length = row[GENERICS_DATA].length; x < length; ++x) {
- elem_name = row[GENERICS_DATA][x][NAME];
+ const elems = Object.create(null);
+ for (const entry of row[GENERICS_DATA]) {
+ elem_name = entry[NAME];
if (elem_name === "") {
// Pure generic, needs to check into it.
- if (checkGenerics(
- row[GENERICS_DATA][x], elem, MAX_LEV_DISTANCE + 1) !== 0) {
+ if (checkGenerics(entry, elem, MAX_LEV_DISTANCE + 1) !== 0) {
return MAX_LEV_DISTANCE + 1;
}
continue;
}
// We need to find the type that matches the most to remove it in order
// to move forward.
- for (x = 0, length = elem.generics.length; x < length; ++x) {
- var generic = elem.generics[x];
- var match = null;
+ for (const generic of elem.generics) {
+ let match = null;
if (elems[generic.name]) {
match = generic.name;
} else {
* @return {integer} - Returns a Levenshtein distance to the best match.
*/
function checkIfInGenerics(row, elem) {
- var lev = MAX_LEV_DISTANCE + 1;
- for (var x = 0, length = row[GENERICS_DATA].length; x < length && lev !== 0; ++x) {
- lev = Math.min(
- checkType(row[GENERICS_DATA][x], elem, true),
- lev
- );
+ let lev = MAX_LEV_DISTANCE + 1;
+ for (const entry of row[GENERICS_DATA]) {
+ lev = Math.min(checkType(entry, elem, true), lev);
+ if (lev === 0) {
+ break;
+ }
}
return lev;
}
return MAX_LEV_DISTANCE + 1;
}
- var lev = levenshtein(row[NAME], elem.name);
+ let lev = levenshtein(row[NAME], elem.name);
if (literalSearch) {
if (lev !== 0) {
// The name didn't match, let's try to check if the generics do.
if (elem.generics.length === 0) {
- var checkGeneric = (row.length > GENERICS_DATA &&
+ const checkGeneric = (row.length > GENERICS_DATA &&
row[GENERICS_DATA].length > 0);
if (checkGeneric && row[GENERICS_DATA].findIndex(function(tmp_elem) {
return tmp_elem[NAME] === elem.name;
} else {
// At this point, the name kinda match and we have generics to check, so
// let's go!
- var tmp_lev = checkGenerics(row, elem, lev);
+ const tmp_lev = checkGenerics(row, elem, lev);
if (tmp_lev > MAX_LEV_DISTANCE) {
return MAX_LEV_DISTANCE + 1;
}
* match, returns `MAX_LEV_DISTANCE + 1`.
*/
function findArg(row, elem, typeFilter) {
- var lev = MAX_LEV_DISTANCE + 1;
+ let lev = MAX_LEV_DISTANCE + 1;
if (row && row.type && row.type[INPUTS_DATA] && row.type[INPUTS_DATA].length > 0) {
- var length = row.type[INPUTS_DATA].length;
- for (var i = 0; i < length; i++) {
- var tmp = row.type[INPUTS_DATA][i];
- if (!typePassesFilter(typeFilter, tmp[1])) {
+ for (const input of row.type[INPUTS_DATA]) {
+ if (!typePassesFilter(typeFilter, input[1])) {
continue;
}
- lev = Math.min(lev, checkType(tmp, elem, parsedQuery.literalSearch));
+ lev = Math.min(lev, checkType(input, elem, parsedQuery.literalSearch));
if (lev === 0) {
return 0;
}
* match, returns `MAX_LEV_DISTANCE + 1`.
*/
function checkReturned(row, elem, typeFilter) {
- var lev = MAX_LEV_DISTANCE + 1;
+ let lev = MAX_LEV_DISTANCE + 1;
if (row && row.type && row.type.length > OUTPUT_DATA) {
- var ret = row.type[OUTPUT_DATA];
+ let ret = row.type[OUTPUT_DATA];
if (typeof ret[0] === "string") {
ret = [ret];
}
- for (var x = 0, len = ret.length; x < len; ++x) {
- var tmp = ret[x];
- if (!typePassesFilter(typeFilter, tmp[1])) {
+ for (const ret_ty of ret) {
+ if (!typePassesFilter(typeFilter, ret_ty[1])) {
continue;
}
- lev = Math.min(lev, checkType(tmp, elem, parsedQuery.literalSearch));
+ lev = Math.min(lev, checkType(ret_ty, elem, parsedQuery.literalSearch));
if (lev === 0) {
return 0;
}
if (contains.length === 0) {
return 0;
}
- var ret_lev = MAX_LEV_DISTANCE + 1;
- var path = ty.path.split("::");
+ let ret_lev = MAX_LEV_DISTANCE + 1;
+ const path = ty.path.split("::");
if (ty.parent && ty.parent.name) {
path.push(ty.parent.name.toLowerCase());
}
- var length = path.length;
- var clength = contains.length;
+ const length = path.length;
+ const clength = contains.length;
if (clength > length) {
return MAX_LEV_DISTANCE + 1;
}
- for (var i = 0; i < length; ++i) {
+ for (let i = 0; i < length; ++i) {
if (i + clength > length) {
break;
}
- var lev_total = 0;
- var aborted = false;
- for (var x = 0; x < clength; ++x) {
- var lev = levenshtein(path[i + x], contains[x]);
+ let lev_total = 0;
+ let aborted = false;
+ for (let x = 0; x < clength; ++x) {
+ const lev = levenshtein(path[i + x], contains[x]);
if (lev > MAX_LEV_DISTANCE) {
aborted = true;
break;
if (filter <= NO_TYPE_FILTER || filter === type) return true;
// Match related items
- var name = itemTypes[type];
+ const name = itemTypes[type];
switch (itemTypes[filter]) {
case "constant":
return name === "associatedconstant";
}
function handleAliases(ret, query, filterCrates) {
- var lowerQuery = query.toLowerCase();
+ const lowerQuery = query.toLowerCase();
// We separate aliases and crate aliases because we want to have current crate
// aliases to be before the others in the displayed results.
- var aliases = [];
- var crateAliases = [];
+ const aliases = [];
+ const crateAliases = [];
if (filterCrates !== null) {
if (ALIASES[filterCrates] && ALIASES[filterCrates][lowerQuery]) {
- var query_aliases = ALIASES[filterCrates][lowerQuery];
- var len = query_aliases.length;
- for (var i = 0; i < len; ++i) {
- aliases.push(createAliasFromItem(searchIndex[query_aliases[i]]));
+ const query_aliases = ALIASES[filterCrates][lowerQuery];
+ for (const alias of query_aliases) {
+ aliases.push(createAliasFromItem(searchIndex[alias]));
}
}
} else {
Object.keys(ALIASES).forEach(function(crate) {
if (ALIASES[crate][lowerQuery]) {
- var pushTo = crate === window.currentCrate ? crateAliases : aliases;
- var query_aliases = ALIASES[crate][lowerQuery];
- var len = query_aliases.length;
- for (var i = 0; i < len; ++i) {
- pushTo.push(createAliasFromItem(searchIndex[query_aliases[i]]));
+ const pushTo = crate === window.currentCrate ? crateAliases : aliases;
+ const query_aliases = ALIASES[crate][lowerQuery];
+ for (const alias of query_aliases) {
+ pushTo.push(createAliasFromItem(searchIndex[alias]));
}
}
});
}
- var sortFunc = function(aaa, bbb) {
+ const sortFunc = function(aaa, bbb) {
if (aaa.path < bbb.path) {
return 1;
} else if (aaa.path === bbb.path) {
crateAliases.sort(sortFunc);
aliases.sort(sortFunc);
- var pushFunc = function(alias) {
+ const pushFunc = function(alias) {
alias.alias = query;
- var res = buildHrefAndPath(alias);
+ const res = buildHrefAndPath(alias);
alias.displayPath = pathSplitter(res[0]);
alias.fullPath = alias.displayPath + alias.name;
alias.href = res[1];
function addIntoResults(results, fullId, id, index, lev) {
if (lev === 0 || (!parsedQuery.literalSearch && lev <= MAX_LEV_DISTANCE)) {
if (results[fullId] !== undefined) {
- var result = results[fullId];
+ const result = results[fullId];
if (result.dontValidate || result.lev <= lev) {
return;
}
if (!row || (filterCrates !== null && row.crate !== filterCrates)) {
return;
}
- var lev, lev_add = 0, index = -1;
- var fullId = row.id;
+ let lev, lev_add = 0, index = -1;
+ const fullId = row.id;
- var in_args = findArg(row, elem, parsedQuery.typeFilter);
- var returned = checkReturned(row, elem, parsedQuery.typeFilter);
+ const in_args = findArg(row, elem, parsedQuery.typeFilter);
+ const returned = checkReturned(row, elem, parsedQuery.typeFilter);
addIntoResults(results_in_args, fullId, pos, index, in_args);
addIntoResults(results_returned, fullId, pos, index, returned);
if (!typePassesFilter(parsedQuery.typeFilter, row.ty)) {
return;
}
- var searchWord = searchWords[pos];
+ const searchWord = searchWords[pos];
if (parsedQuery.literalSearch) {
if (searchWord === elem.name) {
return;
}
- var totalLev = 0;
- var nbLev = 0;
- var lev;
+ let totalLev = 0;
+ let nbLev = 0;
// If the result is too "bad", we return false and it ends this search.
function checkArgs(elems, callback) {
- for (var i = 0, len = elems.length; i < len; ++i) {
- var elem = elems[i];
+ for (const elem of elems) {
// There is more than one parameter to the query so all checks should be "exact"
- lev = callback(row, elem, NO_TYPE_FILTER);
+ const lev = callback(row, elem, NO_TYPE_FILTER);
if (lev <= 1) {
nbLev += 1;
totalLev += lev;
if (nbLev === 0) {
return;
}
- lev = Math.round(totalLev / nbLev);
+ const lev = Math.round(totalLev / nbLev);
addIntoResults(results, row.id, pos, 0, lev);
}
function innerRunQuery() {
- var elem, i, nSearchWords, in_returned, row;
+ let elem, i, nSearchWords, in_returned, row;
if (parsedQuery.foundElems === 1) {
if (parsedQuery.elems.length === 1) {
}
}
} else if (parsedQuery.foundElems > 0) {
- var container = results_others;
+ let container = results_others;
// In the special case where only a "returned" information is available, we want to
// put the information into the "results_returned" dict.
if (parsedQuery.returned.length !== 0 && parsedQuery.elems.length === 0) {
innerRunQuery();
}
- var ret = createQueryResults(
+ const ret = createQueryResults(
sortResults(results_in_args, true),
sortResults(results_returned, true),
sortResults(results_others, false),
if (!keys || !keys.length) {
return true;
}
- for (var i = 0, len = keys.length; i < len; ++i) {
+ for (const key of keys) {
// each check is for validation so we negate the conditions and invalidate
if (!(
// check for an exact name match
- name.indexOf(keys[i]) > -1 ||
+ name.indexOf(key) > -1 ||
// then an exact path match
- path.indexOf(keys[i]) > -1 ||
+ path.indexOf(key) > -1 ||
// next if there is a parent, check for exact parent match
(parent !== undefined && parent.name !== undefined &&
- parent.name.toLowerCase().indexOf(keys[i]) > -1) ||
+ parent.name.toLowerCase().indexOf(key) > -1) ||
// lastly check to see if the name was a levenshtein match
- levenshtein(name, keys[i]) <= MAX_LEV_DISTANCE)) {
+ levenshtein(name, key) <= MAX_LEV_DISTANCE)) {
return false;
}
}
}
function nextTab(direction) {
- var next = (searchState.currentTab + direction + 3) % searchState.focusedByTab.length;
+ const next = (searchState.currentTab + direction + 3) % searchState.focusedByTab.length;
searchState.focusedByTab[searchState.currentTab] = document.activeElement;
printTab(next);
focusSearchResult();
// Focus the first search result on the active tab, or the result that
// was focused last time this tab was active.
function focusSearchResult() {
- var target = searchState.focusedByTab[searchState.currentTab] ||
+ const target = searchState.focusedByTab[searchState.currentTab] ||
document.querySelectorAll(".search-results.active a").item(0) ||
document.querySelectorAll("#titles > button").item(searchState.currentTab);
if (target) {
}
function buildHrefAndPath(item) {
- var displayPath;
- var href;
- var type = itemTypes[item.ty];
- var name = item.name;
- var path = item.path;
+ let displayPath;
+ let href;
+ const type = itemTypes[item.ty];
+ const name = item.name;
+ let path = item.path;
if (type === "mod") {
displayPath = path + "::";
displayPath = "";
href = window.rootPath + name + "/index.html";
} else if (item.parent !== undefined) {
- var myparent = item.parent;
- var anchor = "#" + type + "." + name;
- var parentType = itemTypes[myparent.ty];
- var pageType = parentType;
- var pageName = myparent.name;
+ const myparent = item.parent;
+ let anchor = "#" + type + "." + name;
+ const parentType = itemTypes[myparent.ty];
+ let pageType = parentType;
+ let pageName = myparent.name;
if (parentType === "primitive") {
displayPath = myparent.name + "::";
} else if (type === "structfield" && parentType === "variant") {
// Structfields belonging to variants are special: the
// final path element is the enum name.
- var enumNameIdx = item.path.lastIndexOf("::");
- var enumName = item.path.substr(enumNameIdx + 2);
+ const enumNameIdx = item.path.lastIndexOf("::");
+ const enumName = item.path.substr(enumNameIdx + 2);
path = item.path.substr(0, enumNameIdx);
displayPath = path + "::" + enumName + "::" + myparent.name + "::";
anchor = "#variant." + myparent.name + ".field." + name;
}
function escape(content) {
- var h1 = document.createElement("h1");
+ const h1 = document.createElement("h1");
h1.textContent = content;
return h1.innerHTML;
}
function pathSplitter(path) {
- var tmp = "<span>" + path.replace(/::/g, "::</span><span>");
+ const tmp = "<span>" + path.replace(/::/g, "::</span><span>");
if (tmp.endsWith("<span>")) {
return tmp.slice(0, tmp.length - 6);
}
* @param {boolean} display - True if this is the active tab
*/
function addTab(array, query, display) {
- var extraClass = "";
+ let extraClass = "";
if (display === true) {
extraClass = " active";
}
- var output = document.createElement("div");
- var length = 0;
+ const output = document.createElement("div");
+ let length = 0;
if (array.length > 0) {
output.className = "search-results " + extraClass;
array.forEach(function(item) {
- var name = item.name;
- var type = itemTypes[item.ty];
+ const name = item.name;
+ const type = itemTypes[item.ty];
length += 1;
- var extra = "";
+ let extra = "";
if (type === "primitive") {
extra = " <i>(primitive type)</i>";
} else if (type === "keyword") {
extra = " <i>(keyword)</i>";
}
- var link = document.createElement("a");
+ const link = document.createElement("a");
link.className = "result-" + type;
link.href = item.href;
- var wrapper = document.createElement("div");
- var resultName = document.createElement("div");
+ const wrapper = document.createElement("div");
+ const resultName = document.createElement("div");
resultName.className = "result-name";
if (item.is_alias) {
- var alias = document.createElement("span");
+ const alias = document.createElement("span");
alias.className = "alias";
- var bold = document.createElement("b");
+ const bold = document.createElement("b");
bold.innerText = item.alias;
alias.appendChild(bold);
item.displayPath + "<span class=\"" + type + "\">" + name + extra + "</span>");
wrapper.appendChild(resultName);
- var description = document.createElement("div");
+ const description = document.createElement("div");
description.className = "desc";
- var spanDesc = document.createElement("span");
+ const spanDesc = document.createElement("span");
spanDesc.insertAdjacentHTML("beforeend", item.desc);
description.appendChild(spanDesc);
* @param {string} filterCrates
*/
function showResults(results, go_to_first, filterCrates) {
- var search = searchState.outputElement();
+ const search = searchState.outputElement();
if (go_to_first || (results.others.length === 1
&& getSettingValue("go-to-only-result") === "true"
// By default, the search DOM element is "empty" (meaning it has no children not
// ESC or empty the search input (which also "cancels" the search).
&& (!search.firstChild || search.firstChild.innerText !== searchState.loadingText)))
{
- var elem = document.createElement("a");
+ const elem = document.createElement("a");
elem.href = results.others[0].href;
removeClass(elem, "active");
// For firefox, we need the element to be in the DOM so it can be clicked.
currentResults = results.query.userQuery;
- var ret_others = addTab(results.others, results.query, true);
- var ret_in_args = addTab(results.in_args, results.query, false);
- var ret_returned = addTab(results.returned, results.query, false);
+ const ret_others = addTab(results.others, results.query, true);
+ const ret_in_args = addTab(results.in_args, results.query, false);
+ const ret_returned = addTab(results.returned, results.query, false);
// Navigate to the relevant tab if the current tab is empty, like in case users search
// for "-> String". If they had selected another tab previously, they have to click on
// it again.
- var currentTab = searchState.currentTab;
+ let currentTab = searchState.currentTab;
if ((currentTab === 0 && ret_others[1] === 0) ||
(currentTab === 1 && ret_in_args[1] === 0) ||
(currentTab === 2 && ret_returned[1] === 0)) {
let crates = "";
if (window.ALL_CRATES.length > 1) {
crates = ` in <select id="crate-search"><option value="All crates">All crates</option>`;
- for (let c of window.ALL_CRATES) {
+ for (const c of window.ALL_CRATES) {
crates += `<option value="${c}" ${c == filterCrates && "selected"}>${c}</option>`;
}
crates += `</select>`;
}
- var typeFilter = "";
+ let typeFilter = "";
if (results.query.typeFilter !== NO_TYPE_FILTER) {
typeFilter = " (type: " + escape(itemTypes[results.query.typeFilter]) + ")";
}
- var output = `<div id="search-settings">` +
+ let output = `<div id="search-settings">` +
`<h1 class="search-results-title">Results for ${escape(results.query.userQuery)}` +
`${typeFilter}</h1> in ${crates} </div>`;
if (results.query.error !== null) {
makeTabHeader(2, "In Return Types", ret_returned[1]) +
"</div>";
- var resultsElem = document.createElement("div");
+ const resultsElem = document.createElement("div");
resultsElem.id = "results";
resultsElem.appendChild(ret_others[0]);
resultsElem.appendChild(ret_in_args[0]);
resultsElem.appendChild(ret_returned[0]);
search.innerHTML = output;
- let crateSearch = document.getElementById("crate-search");
+ const crateSearch = document.getElementById("crate-search");
if (crateSearch) {
crateSearch.addEventListener("input", updateCrate);
}
// Reset focused elements.
searchState.focusedByTab = [null, null, null];
searchState.showResults(search);
- var elems = document.getElementById("titles").childNodes;
+ const elems = document.getElementById("titles").childNodes;
elems[0].onclick = function() { printTab(0); };
elems[1].onclick = function() { printTab(1); };
elems[2].onclick = function() { printTab(2); };
* @param {boolean} [forced]
*/
function search(e, forced) {
- var params = searchState.getQueryStringParams();
- var query = parseQuery(searchState.input.value.trim());
+ const params = searchState.getQueryStringParams();
+ const query = parseQuery(searchState.input.value.trim());
if (e) {
e.preventDefault();
return;
}
- var filterCrates = getFilterCrates();
+ let filterCrates = getFilterCrates();
// In case we have no information about the saved crate and there is a URL query parameter,
// we override it with the URL query parameter.
// Because searching is incremental by character, only the most
// recent search query is added to the browser history.
- if (searchState.browserSupportsHistoryApi()) {
- var newURL = buildUrl(query.original, filterCrates);
+ if (browserSupportsHistoryApi()) {
+ const newURL = buildUrl(query.original, filterCrates);
+
if (!history.state && !params.search) {
history.pushState(null, "", newURL);
} else {
/**
* @type {Array<string>}
*/
- var searchWords = [];
- var i, word;
- var currentIndex = 0;
- var id = 0;
+ const searchWords = [];
+ let i, word;
+ let currentIndex = 0;
+ let id = 0;
- for (var crate in rawSearchIndex) {
+ for (const crate in rawSearchIndex) {
if (!hasOwnPropertyRustdoc(rawSearchIndex, crate)) {
continue;
}
- var crateSize = 0;
+ let crateSize = 0;
/**
* The raw search data for a given crate. `n`, `t`, `d`, and `q`, `i`, and `f`
* p: Array<Object>,
* }}
*/
- var crateCorpus = rawSearchIndex[crate];
+ const crateCorpus = rawSearchIndex[crate];
searchWords.push(crate);
// This object should have exactly the same set of fields as the "row"
// object defined below. Your JavaScript runtime will thank you.
// https://mathiasbynens.be/notes/shapes-ics
- var crateRow = {
+ const crateRow = {
crate: crate,
ty: 1, // == ExternCrate
name: crate,
currentIndex += 1;
// an array of (Number) item types
- var itemTypes = crateCorpus.t;
+ const itemTypes = crateCorpus.t;
// an array of (String) item names
- var itemNames = crateCorpus.n;
+ const itemNames = crateCorpus.n;
// an array of (String) full paths (or empty string for previous path)
- var itemPaths = crateCorpus.q;
+ const itemPaths = crateCorpus.q;
// an array of (String) descriptions
- var itemDescs = crateCorpus.d;
+ const itemDescs = crateCorpus.d;
// an array of (Number) the parent path index + 1 to `paths`, or 0 if none
- var itemParentIdxs = crateCorpus.i;
+ const itemParentIdxs = crateCorpus.i;
// an array of (Object | null) the type of the function, if any
- var itemFunctionSearchTypes = crateCorpus.f;
+ const itemFunctionSearchTypes = crateCorpus.f;
// an array of [(Number) item type,
// (String) name]
- var paths = crateCorpus.p;
+ const paths = crateCorpus.p;
// an array of [(String) alias name
// [Number] index to items]
- var aliases = crateCorpus.a;
+ const aliases = crateCorpus.a;
// convert `rawPaths` entries into object form
- var len = paths.length;
+ let len = paths.length;
for (i = 0; i < len; ++i) {
paths[i] = {ty: paths[i][0], name: paths[i][1]};
}
// all other search operations have access to this cached data for
// faster analysis operations
len = itemTypes.length;
- var lastPath = "";
+ let lastPath = "";
for (i = 0; i < len; ++i) {
// This object should have exactly the same set of fields as the "crateRow"
// object defined above.
word = "";
searchWords.push("");
}
- var row = {
+ const row = {
crate: crate,
ty: itemTypes[i],
name: itemNames[i],
if (aliases) {
ALIASES[crate] = {};
- var j, local_aliases;
- for (var alias_name in aliases) {
+ for (const alias_name in aliases) {
if (!hasOwnPropertyRustdoc(aliases, alias_name)) {
continue;
}
if (!hasOwnPropertyRustdoc(ALIASES[crate], alias_name)) {
ALIASES[crate][alias_name] = [];
}
- local_aliases = aliases[alias_name];
- for (j = 0, len = local_aliases.length; j < len; ++j) {
- ALIASES[crate][alias_name].push(local_aliases[j] + currentIndex);
+ for (const local_alias of aliases[alias_name]) {
+ ALIASES[crate][alias_name].push(local_alias + currentIndex);
}
}
}
}
function putBackSearch() {
- var search_input = searchState.input;
+ const search_input = searchState.input;
if (!searchState.input) {
return;
}
- var search = searchState.outputElement();
- if (search_input.value !== "" && hasClass(search, "hidden")) {
- searchState.showResults(search);
- if (searchState.browserSupportsHistoryApi()) {
+ if (search_input.value !== "" && !searchState.isDisplayed()) {
+ searchState.showResults();
+ if (browserSupportsHistoryApi()) {
history.replaceState(null, "",
buildUrl(search_input.value, getFilterCrates()));
}
}
function registerSearchEvents() {
- var searchAfter500ms = function() {
+ const searchAfter500ms = function() {
searchState.clearInputTimeout();
if (searchState.input.value.length === 0) {
- if (searchState.browserSupportsHistoryApi()) {
+ if (browserSupportsHistoryApi()) {
history.replaceState(null, window.currentCrate + " - Rust",
getNakedUrl() + window.location.hash);
}
// up and down arrow select next/previous search result, or the
// search box if we're already at the top.
if (e.which === 38) { // up
- var previous = document.activeElement.previousElementSibling;
+ const previous = document.activeElement.previousElementSibling;
if (previous) {
previous.focus();
} else {
}
e.preventDefault();
} else if (e.which === 40) { // down
- var next = document.activeElement.nextElementSibling;
+ const next = document.activeElement.nextElementSibling;
if (next) {
next.focus();
}
- var rect = document.activeElement.getBoundingClientRect();
+ const rect = document.activeElement.getBoundingClientRect();
if (window.innerHeight - rect.bottom < rect.height) {
window.scrollBy(0, rect.height);
}
// Push and pop states are used to add search results to the browser
// history.
- if (searchState.browserSupportsHistoryApi()) {
+ if (browserSupportsHistoryApi()) {
// Store the previous <title> so we can revert back to it later.
- var previousTitle = document.title;
+ const previousTitle = document.title;
window.addEventListener("popstate", function(e) {
- var params = searchState.getQueryStringParams();
+ const params = searchState.getQueryStringParams();
// Revert to the previous title manually since the History
// API ignores the title parameter.
document.title = previousTitle;
// that try to sync state between the URL and the search input. To work around it,
// do a small amount of re-init on page show.
window.onpageshow = function(){
- var qSearch = searchState.getQueryStringParams().search;
+ const qSearch = searchState.getQueryStringParams().search;
if (searchState.input.value === "" && qSearch) {
searchState.input.value = qSearch;
}
function updateCrate(ev) {
if (ev.target.value === "All crates") {
// If we don't remove it from the URL, it'll be picked up again by the search.
- var params = searchState.getQueryStringParams();
- var query = searchState.input.value.trim();
+ const params = searchState.getQueryStringParams();
+ const query = searchState.input.value.trim();
if (!history.state && !params.search) {
history.pushState(null, "", buildUrl(query, null));
} else {
search(undefined, true);
}
- searchWords = buildIndex(rawSearchIndex);
+ /**
+ * @type {Array<string>}
+ */
+ const searchWords = buildIndex(rawSearchIndex);
registerSearchEvents();
function runSearchIfNeeded() {
+/* eslint-env es6 */
+/* eslint no-var: "error" */
+/* eslint prefer-const: "error" */
// Local js definitions:
-/* global getSettingValue, getVirtualKey, onEachLazy, updateLocalStorage, updateSystemTheme */
-/* global addClass, removeClass */
+/* global getSettingValue, getVirtualKey, updateLocalStorage, updateSystemTheme, loadCss */
+/* global addClass, removeClass, onEach, onEachLazy, NOT_DISPLAYED_ID */
+/* global MAIN_ID, getVar, getSettingsButton, switchDisplayedElement, getNotDisplayedElem */
(function () {
+ const isSettingsPage = window.location.pathname.endsWith("/settings.html");
+
function changeSetting(settingName, value) {
updateLocalStorage(settingName, value);
}
}
- function setEvents() {
+ function setEvents(settingsElement) {
updateLightAndDark();
- onEachLazy(document.getElementsByClassName("slider"), function(elem) {
- var toggle = elem.previousElementSibling;
- var settingId = toggle.id;
- var settingValue = getSettingValue(settingId);
+ onEachLazy(settingsElement.getElementsByClassName("slider"), function(elem) {
+ const toggle = elem.previousElementSibling;
+ const settingId = toggle.id;
+ const settingValue = getSettingValue(settingId);
if (settingValue !== null) {
toggle.checked = settingValue === "true";
}
toggle.onkeyup = handleKey;
toggle.onkeyrelease = handleKey;
});
- onEachLazy(document.getElementsByClassName("select-wrapper"), function(elem) {
- var select = elem.getElementsByTagName("select")[0];
- var settingId = select.id;
- var settingValue = getSettingValue(settingId);
+ onEachLazy(settingsElement.getElementsByClassName("select-wrapper"), function(elem) {
+ const select = elem.getElementsByTagName("select")[0];
+ const settingId = select.id;
+ const settingValue = getSettingValue(settingId);
if (settingValue !== null) {
select.value = settingValue;
}
changeSetting(this.id, this.value);
};
});
- onEachLazy(document.querySelectorAll("input[type=\"radio\"]"), function(elem) {
+ onEachLazy(settingsElement.querySelectorAll("input[type=\"radio\"]"), function(elem) {
const settingId = elem.name;
const settingValue = getSettingValue(settingId);
if (settingValue !== null && settingValue !== "null") {
changeSetting(ev.target.name, ev.target.value);
});
});
- document.getElementById("back").addEventListener("click", function() {
- history.back();
- });
}
- window.addEventListener("DOMContentLoaded", setEvents);
+ /**
+ * This function builds the sections inside the "settings page". It takes a `settings` list
+ * as argument which describes each setting and how to render it. It returns a string
+ * representing the raw HTML.
+ *
+ * @param {Array<Object>} settings
+ *
+ * @return {string}
+ */
+ function buildSettingsPageSections(settings) {
+ let output = "";
+
+ for (const setting of settings) {
+ output += `<div class="setting-line">`;
+ const js_data_name = setting["js_name"];
+ const setting_name = setting["name"];
+
+ if (setting["options"] !== undefined) {
+ // This is a select setting.
+ output += `<div class="radio-line" id="${js_data_name}">\
+ <span class="setting-name">${setting_name}</span>\
+ <div class="choices">`;
+ onEach(setting["options"], function(option) {
+ const checked = option === setting["default"] ? " checked" : "";
+
+ output += `<label for="${js_data_name}-${option}" class="choice">\
+ <input type="radio" name="${js_data_name}" \
+ id="${js_data_name}-${option}" value="${option}"${checked}>\
+ ${option}\
+ </label>`;
+ });
+ output += "</div></div>";
+ } else {
+ // This is a toggle.
+ const checked = setting["default"] === true ? " checked" : "";
+ output += `
+ <label class="toggle">
+ <input type="checkbox" id="${js_data_name}"${checked}>
+ <span class="slider"></span>
+ </label>
+ <div>${setting_name}</div>`;
+ }
+ output += "</div>";
+ }
+ return output;
+ }
+
+ /**
+ * This function builds the "settings page" and returns the generated HTML element.
+ *
+ * @return {HTMLElement}
+ */
+ function buildSettingsPage() {
+ const themes = getVar("themes").split(",");
+ const settings = [
+ {
+ "name": "Use system theme",
+ "js_name": "use-system-theme",
+ "default": true,
+ },
+ {
+ "name": "Theme",
+ "js_name": "theme",
+ "default": "light",
+ "options": themes,
+ },
+ {
+ "name": "Preferred light theme",
+ "js_name": "preferred-light-theme",
+ "default": "light",
+ "options": themes,
+ },
+ {
+ "name": "Preferred dark theme",
+ "js_name": "preferred-dark-theme",
+ "default": "dark",
+ "options": themes,
+ },
+ {
+ "name": "Auto-hide item contents for large items",
+ "js_name": "auto-hide-large-items",
+ "default": true,
+ },
+ {
+ "name": "Auto-hide item methods' documentation",
+ "js_name": "auto-hide-method-docs",
+ "default": false,
+ },
+ {
+ "name": "Auto-hide trait implementation documentation",
+ "js_name": "auto-hide-trait-implementations",
+ "default": false,
+ },
+ {
+ "name": "Directly go to item in search if there is only one result",
+ "js_name": "go-to-only-result",
+ "default": false,
+ },
+ {
+ "name": "Show line numbers on code examples",
+ "js_name": "line-numbers",
+ "default": false,
+ },
+ {
+ "name": "Disable keyboard shortcuts",
+ "js_name": "disable-shortcuts",
+ "default": false,
+ },
+ ];
+
+ // First, we add the settings.css file.
+ loadCss("settings");
+
+ // Then we build the DOM.
+ const el = document.createElement("section");
+ el.id = "settings";
+ let innerHTML = `
+ <div class="main-heading">
+ <h1 class="fqn">
+ <span class="in-band">Rustdoc settings</span>
+ </h1>
+ <span class="out-of-band">`;
+
+ if (isSettingsPage) {
+ innerHTML +=
+ `<a id="back" href="javascript:void(0)" onclick="history.back();">Back</a>`;
+ } else {
+ innerHTML +=
+ `<a id="back" href="javascript:void(0)" onclick="switchDisplayedElement(null);">\
+ Back</a>`;
+ }
+ innerHTML += `</span>
+ </div>
+ <div class="settings">${buildSettingsPageSections(settings)}</div>`;
+
+ el.innerHTML = innerHTML;
+
+ if (isSettingsPage) {
+ document.getElementById(MAIN_ID).appendChild(el);
+ } else {
+ getNotDisplayedElem().appendChild(el);
+ }
+ return el;
+ }
+
+ const settingsMenu = buildSettingsPage();
+
+ if (isSettingsPage) {
+ // We replace the existing "onclick" callback to do nothing if clicked.
+ getSettingsButton().onclick = function(event) {
+ event.preventDefault();
+ };
+ } else {
+ // We replace the existing "onclick" callback.
+ const settingsButton = getSettingsButton();
+ settingsButton.onclick = function(event) {
+ event.preventDefault();
+ if (settingsMenu.parentElement.id === NOT_DISPLAYED_ID) {
+ switchDisplayedElement(settingsMenu);
+ } else {
+ window.hideSettings();
+ }
+ };
+ window.hideSettings = function() {
+ switchDisplayedElement(null);
+ };
+ }
+
+ // We now wait a bit for the web browser to end re-computing the DOM...
+ setTimeout(function() {
+ setEvents(settingsMenu);
+ // The setting menu is already displayed if we're on the settings page.
+ if (!isSettingsPage) {
+ switchDisplayedElement(settingsMenu);
+ }
+ }, 0);
})();
+/* eslint-env es6 */
+/* eslint no-var: "error" */
+/* eslint prefer-const: "error" */
+
// From rust:
/* global search, sourcesIndex */
// Local js definitions:
-/* global addClass, getCurrentValue, hasClass, onEachLazy, removeClass, searchState */
+/* global addClass, getCurrentValue, hasClass, onEachLazy, removeClass, browserSupportsHistoryApi */
/* global updateLocalStorage */
(function() {
function getCurrentFilePath() {
- var parts = window.location.pathname.split("/");
- var rootPathParts = window.rootPath.split("/");
+ const parts = window.location.pathname.split("/");
+ const rootPathParts = window.rootPath.split("/");
- for (var i = 0, len = rootPathParts.length; i < len; ++i) {
- if (rootPathParts[i] === "..") {
+ for (const rootPathPart of rootPathParts) {
+ if (rootPathPart === "..") {
parts.pop();
}
}
- var file = window.location.pathname.substring(parts.join("/").length);
+ let file = window.location.pathname.substring(parts.join("/").length);
if (file.startsWith("/")) {
file = file.substring(1);
}
}
function createDirEntry(elem, parent, fullPath, currentFile, hasFoundFile) {
- var name = document.createElement("div");
+ const name = document.createElement("div");
name.className = "name";
fullPath += elem["name"] + "/";
};
name.innerText = elem["name"];
- var i, len;
-
- var children = document.createElement("div");
+ const children = document.createElement("div");
children.className = "children";
- var folders = document.createElement("div");
+ const folders = document.createElement("div");
folders.className = "folders";
if (elem.dirs) {
- for (i = 0, len = elem.dirs.length; i < len; ++i) {
- if (createDirEntry(elem.dirs[i], folders, fullPath, currentFile,
- hasFoundFile)) {
+ for (const dir of elem.dirs) {
+ if (createDirEntry(dir, folders, fullPath, currentFile, hasFoundFile)) {
addClass(name, "expand");
hasFoundFile = true;
}
}
children.appendChild(folders);
- var files = document.createElement("div");
+ const files = document.createElement("div");
files.className = "files";
if (elem.files) {
- for (i = 0, len = elem.files.length; i < len; ++i) {
- var file = document.createElement("a");
- file.innerText = elem.files[i];
- file.href = window.rootPath + "src/" + fullPath + elem.files[i] + ".html";
- if (!hasFoundFile && currentFile === fullPath + elem.files[i]) {
+ for (const file_text of elem.files) {
+ const file = document.createElement("a");
+ file.innerText = file_text;
+ file.href = window.rootPath + "src/" + fullPath + file_text + ".html";
+ if (!hasFoundFile && currentFile === fullPath + file_text) {
file.className = "selected";
addClass(name, "expand");
hasFoundFile = true;
}
function toggleSidebar() {
- var sidebar = document.querySelector("nav.sidebar");
- var child = this.children[0];
+ const sidebar = document.querySelector("nav.sidebar");
+ const child = this.children[0];
if (child.innerText === ">") {
sidebar.classList.add("expanded");
child.innerText = "<";
}
function createSidebarToggle() {
- var sidebarToggle = document.createElement("div");
+ const sidebarToggle = document.createElement("div");
sidebarToggle.id = "sidebar-toggle";
sidebarToggle.onclick = toggleSidebar;
- var inner = document.createElement("div");
+ const inner = document.createElement("div");
if (getCurrentValue("source-sidebar-show") === "true") {
inner.innerText = "<";
if (!window.rootPath.endsWith("/")) {
window.rootPath += "/";
}
- var container = document.querySelector("nav.sidebar");
+ const container = document.querySelector("nav.sidebar");
- var sidebarToggle = createSidebarToggle();
+ const sidebarToggle = createSidebarToggle();
container.insertBefore(sidebarToggle, container.firstChild);
- var sidebar = document.createElement("div");
+ const sidebar = document.createElement("div");
sidebar.id = "source-sidebar";
if (getCurrentValue("source-sidebar-show") !== "true") {
container.classList.remove("expanded");
container.classList.add("expanded");
}
- var currentFile = getCurrentFilePath();
- var hasFoundFile = false;
+ const currentFile = getCurrentFilePath();
+ let hasFoundFile = false;
- var title = document.createElement("div");
+ const title = document.createElement("div");
title.className = "title";
title.innerText = "Files";
sidebar.appendChild(title);
container.appendChild(sidebar);
// Focus on the current file in the source files sidebar.
- var selected_elem = sidebar.getElementsByClassName("selected")[0];
+ const selected_elem = sidebar.getElementsByClassName("selected")[0];
if (typeof selected_elem !== "undefined") {
selected_elem.focus();
}
}
-var lineNumbersRegex = /^#?(\d+)(?:-(\d+))?$/;
+const lineNumbersRegex = /^#?(\d+)(?:-(\d+))?$/;
function highlightSourceLines(match) {
if (typeof match === "undefined") {
if (!match) {
return;
}
- var from = parseInt(match[1], 10);
- var to = from;
+ let from = parseInt(match[1], 10);
+ let to = from;
if (typeof match[2] !== "undefined") {
to = parseInt(match[2], 10);
}
if (to < from) {
- var tmp = to;
+ const tmp = to;
to = from;
from = tmp;
}
- var elem = document.getElementById(from);
+ let elem = document.getElementById(from);
if (!elem) {
return;
}
- var x = document.getElementById(from);
+ const x = document.getElementById(from);
if (x) {
x.scrollIntoView();
}
removeClass(i_e, "line-highlighted");
});
});
- for (var i = from; i <= to; ++i) {
+ for (let i = from; i <= to; ++i) {
elem = document.getElementById(i);
if (!elem) {
break;
}
}
-var handleSourceHighlight = (function() {
- var prev_line_id = 0;
+const handleSourceHighlight = (function() {
+ let prev_line_id = 0;
- var set_fragment = function(name) {
- var x = window.scrollX,
+ const set_fragment = function(name) {
+ const x = window.scrollX,
y = window.scrollY;
- if (searchState.browserSupportsHistoryApi()) {
+ if (browserSupportsHistoryApi()) {
history.replaceState(null, null, "#" + name);
highlightSourceLines();
} else {
};
return function(ev) {
- var cur_line_id = parseInt(ev.target.id, 10);
+ let cur_line_id = parseInt(ev.target.id, 10);
ev.preventDefault();
if (ev.shiftKey && prev_line_id) {
// Swap selection if needed
if (prev_line_id > cur_line_id) {
- var tmp = prev_line_id;
+ const tmp = prev_line_id;
prev_line_id = cur_line_id;
cur_line_id = tmp;
}
}());
window.addEventListener("hashchange", function() {
- var match = window.location.hash.match(lineNumbersRegex);
+ const match = window.location.hash.match(lineNumbersRegex);
if (match) {
return highlightSourceLines(match);
}
-var darkThemes = ["dark", "ayu"];
+/* eslint-env es6 */
+/* eslint no-var: "error" */
+/* eslint prefer-const: "error" */
+
+const darkThemes = ["dark", "ayu"];
window.currentTheme = document.getElementById("themeStyle");
window.mainTheme = document.getElementById("mainThemeStyle");
-var settingsDataset = (function () {
- var settingsElement = document.getElementById("default-settings");
+const settingsDataset = (function () {
+ const settingsElement = document.getElementById("default-settings");
if (settingsElement === null) {
return null;
}
- var dataset = settingsElement.dataset;
+ const dataset = settingsElement.dataset;
if (dataset === undefined) {
return null;
}
})();
function getSettingValue(settingName) {
- var current = getCurrentValue(settingName);
+ const current = getCurrentValue(settingName);
if (current !== null) {
return current;
}
if (settingsDataset !== null) {
// See the comment for `default_settings.into_iter()` etc. in
// `Options::from_matches` in `librustdoc/config.rs`.
- var def = settingsDataset[settingName.replace(/-/g,'_')];
+ const def = settingsDataset[settingName.replace(/-/g,'_')];
if (def !== undefined) {
return def;
}
return null;
}
-var localStoredTheme = getSettingValue("theme");
+const localStoredTheme = getSettingValue("theme");
-var savedHref = [];
+const savedHref = [];
// eslint-disable-next-line no-unused-vars
function hasClass(elem, className) {
*/
function onEach(arr, func, reversed) {
if (arr && arr.length > 0 && func) {
- var length = arr.length;
- var i;
if (reversed) {
- for (i = length - 1; i >= 0; --i) {
+ const length = arr.length;
+ for (let i = length - 1; i >= 0; --i) {
if (func(arr[i])) {
return true;
}
}
} else {
- for (i = 0; i < length; ++i) {
- if (func(arr[i])) {
+ for (const elem of arr) {
+ if (func(elem)) {
return true;
}
}
}
function switchTheme(styleElem, mainStyleElem, newTheme, saveTheme) {
- var newHref = mainStyleElem.href.replace(
+ const newHref = mainStyleElem.href.replace(
/\/rustdoc([^/]*)\.css/, "/" + newTheme + "$1" + ".css");
// If this new value comes from a system setting or from the previously
return;
}
- var found = false;
+ let found = false;
if (savedHref.length === 0) {
onEachLazy(document.getElementsByTagName("link"), function(el) {
savedHref.push(el.href);
updateLocalStorage("use-system-theme", value);
// update the toggle if we're on the settings page
- var toggle = document.getElementById("use-system-theme");
+ const toggle = document.getElementById("use-system-theme");
if (toggle && toggle instanceof HTMLInputElement) {
toggle.checked = value;
}
}
-var updateSystemTheme = (function() {
+const updateSystemTheme = (function() {
if (!window.matchMedia) {
// fallback to the CSS computed value
return function() {
- var cssTheme = getComputedStyle(document.documentElement)
+ const cssTheme = getComputedStyle(document.documentElement)
.getPropertyValue('content');
switchTheme(
}
// only listen to (prefers-color-scheme: dark) because light is the default
- var mql = window.matchMedia("(prefers-color-scheme: dark)");
+ const mql = window.matchMedia("(prefers-color-scheme: dark)");
function handlePreferenceChange(mql) {
- let use = function(theme) {
+ const use = function(theme) {
switchTheme(window.currentTheme, window.mainTheme, theme, true);
};
// maybe the user has disabled the setting in the meantime!
if (getSettingValue("use-system-theme") !== "false") {
- var lightTheme = getSettingValue("preferred-light-theme") || "light";
- var darkTheme = getSettingValue("preferred-dark-theme") || "dark";
+ const lightTheme = getSettingValue("preferred-light-theme") || "light";
+ const darkTheme = getSettingValue("preferred-dark-theme") || "dark";
if (mql.matches) {
use(darkTheme);
/// Files related to the Fira Sans font.
crate mod fira_sans {
- /// The file `FiraSans-Regular.woff`, the Regular variant of the Fira Sans font.
- crate static REGULAR: &[u8] = include_bytes!("static/fonts/FiraSans-Regular.woff");
-
/// The file `FiraSans-Regular.woff2`, the Regular variant of the Fira Sans font in woff2.
- crate static REGULAR2: &[u8] = include_bytes!("static/fonts/FiraSans-Regular.woff2");
-
- /// The file `FiraSans-Medium.woff`, the Medium variant of the Fira Sans font.
- crate static MEDIUM: &[u8] = include_bytes!("static/fonts/FiraSans-Medium.woff");
+ crate static REGULAR: &[u8] = include_bytes!("static/fonts/FiraSans-Regular.woff2");
/// The file `FiraSans-Medium.woff2`, the Medium variant of the Fira Sans font in woff2.
- crate static MEDIUM2: &[u8] = include_bytes!("static/fonts/FiraSans-Medium.woff2");
+ crate static MEDIUM: &[u8] = include_bytes!("static/fonts/FiraSans-Medium.woff2");
/// The file `FiraSans-LICENSE.txt`, the license text for the Fira Sans font.
crate static LICENSE: &[u8] = include_bytes!("static/fonts/FiraSans-LICENSE.txt");
/// Files related to the Source Serif 4 font.
crate mod source_serif_4 {
- /// The file `SourceSerif4-Regular.ttf.woff`, the Regular variant of the Source Serif 4 font.
- crate static REGULAR: &[u8] = include_bytes!("static/fonts/SourceSerif4-Regular.ttf.woff");
-
/// The file `SourceSerif4-Regular.ttf.woff2`, the Regular variant of the Source Serif 4 font in
/// woff2.
- crate static REGULAR2: &[u8] = include_bytes!("static/fonts/SourceSerif4-Regular.ttf.woff2");
-
- /// The file `SourceSerif4-Bold.ttf.woff`, the Bold variant of the Source Serif 4 font.
- crate static BOLD: &[u8] = include_bytes!("static/fonts/SourceSerif4-Bold.ttf.woff");
+ crate static REGULAR: &[u8] = include_bytes!("static/fonts/SourceSerif4-Regular.ttf.woff2");
/// The file `SourceSerif4-Bold.ttf.woff2`, the Bold variant of the Source Serif 4 font in
/// woff2.
- crate static BOLD2: &[u8] = include_bytes!("static/fonts/SourceSerif4-Bold.ttf.woff2");
-
- /// The file `SourceSerif4-It.ttf.woff`, the Italic variant of the Source Serif 4 font.
- crate static ITALIC: &[u8] = include_bytes!("static/fonts/SourceSerif4-It.ttf.woff");
+ crate static BOLD: &[u8] = include_bytes!("static/fonts/SourceSerif4-Bold.ttf.woff2");
/// The file `SourceSerif4-It.ttf.woff2`, the Italic variant of the Source Serif 4 font in
/// woff2.
- crate static ITALIC2: &[u8] = include_bytes!("static/fonts/SourceSerif4-It.ttf.woff2");
+ crate static ITALIC: &[u8] = include_bytes!("static/fonts/SourceSerif4-It.ttf.woff2");
/// The file `SourceSerif4-LICENSE.txt`, the license text for the Source Serif 4 font.
crate static LICENSE: &[u8] = include_bytes!("static/fonts/SourceSerif4-LICENSE.md");
/// Files related to the Source Code Pro font.
crate mod source_code_pro {
- /// The file `SourceCodePro-Regular.ttf.woff`, the Regular variant of the Source Code Pro font.
- crate static REGULAR: &[u8] = include_bytes!("static/fonts/SourceCodePro-Regular.ttf.woff");
-
/// The file `SourceCodePro-Regular.ttf.woff2`, the Regular variant of the Source Code Pro font
/// in woff2.
- crate static REGULAR2: &[u8] = include_bytes!("static/fonts/SourceCodePro-Regular.ttf.woff2");
-
- /// The file `SourceCodePro-Semibold.ttf.woff`, the Semibold variant of the Source Code Pro
- /// font.
- crate static SEMIBOLD: &[u8] = include_bytes!("static/fonts/SourceCodePro-Semibold.ttf.woff");
+ crate static REGULAR: &[u8] = include_bytes!("static/fonts/SourceCodePro-Regular.ttf.woff2");
/// The file `SourceCodePro-Semibold.ttf.woff2`, the Semibold variant of the Source Code Pro
/// font in woff2.
- crate static SEMIBOLD2: &[u8] = include_bytes!("static/fonts/SourceCodePro-Semibold.ttf.woff2");
-
- /// The file `SourceCodePro-It.ttf.woff`, the Italic variant of the Source Code Pro font.
- crate static ITALIC: &[u8] = include_bytes!("static/fonts/SourceCodePro-It.ttf.woff");
+ crate static SEMIBOLD: &[u8] = include_bytes!("static/fonts/SourceCodePro-Semibold.ttf.woff2");
/// The file `SourceCodePro-It.ttf.woff2`, the Italic variant of the Source Code Pro font in
/// woff2.
- crate static ITALIC2: &[u8] = include_bytes!("static/fonts/SourceCodePro-It.ttf.woff2");
+ crate static ITALIC: &[u8] = include_bytes!("static/fonts/SourceCodePro-It.ttf.woff2");
/// The file `SourceCodePro-LICENSE.txt`, the license text of the Source Code Pro font.
crate static LICENSE: &[u8] = include_bytes!("static/fonts/SourceCodePro-LICENSE.txt");
/// ```sh
/// pyftsubset NanumBarunGothic.ttf \
/// --unicodes=U+AC00-D7AF,U+1100-11FF,U+3130-318F,U+A960-A97F,U+D7B0-D7FF \
-/// --output-file=NanumBarunGothic.ttf.woff --flavor=woff
-/// ```
-/// ```sh
-/// pyftsubset NanumBarunGothic.ttf \
-/// --unicodes=U+AC00-D7AF,U+1100-11FF,U+3130-318F,U+A960-A97F,U+D7B0-D7FF \
/// --output-file=NanumBarunGothic.ttf.woff2 --flavor=woff2
/// ```
crate mod nanum_barun_gothic {
- /// The file `NanumBarunGothic.ttf.woff`, the Regular variant of the Nanum Barun Gothic font.
- crate static REGULAR: &[u8] = include_bytes!("static/fonts/NanumBarunGothic.ttf.woff");
-
/// The file `NanumBarunGothic.ttf.woff2`, the Regular variant of the Nanum Barun Gothic font.
- crate static REGULAR2: &[u8] = include_bytes!("static/fonts/NanumBarunGothic.ttf.woff2");
+ crate static REGULAR: &[u8] = include_bytes!("static/fonts/NanumBarunGothic.ttf.woff2");
/// The file `NanumBarunGothic-LICENSE.txt`, the license text of the Nanum Barun Gothic font.
crate static LICENSE: &[u8] = include_bytes!("static/fonts/NanumBarunGothic-LICENSE.txt");
</nav> {#- -#}
</div> {#- -#}
<section id="main-content" class="content">{{- content|safe -}}</section> {#- -#}
- <section id="search" class="content hidden"></section> {#- -#}
</div> {#- -#}
</main> {#- -#}
{{- layout.external_html.after_content|safe -}}
let resolver_caches = resolver.borrow_mut().access(|resolver| {
collect_intra_doc_links::early_resolve_intra_doc_links(
resolver,
+ sess,
krate,
externs,
document_private,
let should_be_ignored = i
.item_id
.as_def_id()
- .and_then(|def_id| self.ctx.tcx.parent(def_id))
+ .and_then(|def_id| self.ctx.tcx.opt_parent(def_id))
.and_then(|def_id| self.ctx.tcx.hir().get_if_local(def_id))
.map(|node| {
matches!(
use rustc_hir::Mutability;
use rustc_middle::ty::{DefIdTree, Ty, TyCtxt};
use rustc_middle::{bug, span_bug, ty};
+use rustc_resolve::ParentScope;
use rustc_session::lint::Lint;
use rustc_span::hygiene::MacroKind;
use rustc_span::symbol::{sym, Ident, Symbol};
FragmentKind::StructField => write!(s, "structfield.{}", name),
FragmentKind::Variant => write!(s, "variant.{}", name),
FragmentKind::VariantField => {
- let variant = tcx.item_name(tcx.parent(def_id).unwrap());
+ let variant = tcx.item_name(tcx.parent(def_id));
write!(s, "variant.{}.field.{}", variant, name)
}
}
| DefKind::AssocTy
| DefKind::Variant
| DefKind::Field) => {
- let parent_def_id = tcx.parent(def_id).expect("nested item has no parent");
+ let parent_def_id = tcx.parent(def_id);
if def_kind == DefKind::Field && tcx.def_kind(parent_def_id) == DefKind::Variant
{
- tcx.parent(parent_def_id).expect("variant has no parent")
+ tcx.parent(parent_def_id)
} else {
parent_def_id
}
.copied()
.unwrap_or_else(|| {
self.cx.enter_resolver(|resolver| {
- resolver.resolve_rustdoc_path(path_str, ns, module_id)
+ let parent_scope =
+ ParentScope::module(resolver.expect_module(module_id), resolver);
+ resolver.resolve_rustdoc_path(path_str, ns, parent_scope)
})
})
.and_then(|res| res.try_into().ok())
cx: &DocContext<'_>,
res: Res,
) -> Result<(Res, Option<ItemFragment>), ErrorKind<'static>> {
- cx.tcx
- .parent(res.def_id(cx.tcx))
- .map(|parent| {
- let parent_def = Res::Def(DefKind::Enum, parent);
- let variant = cx.tcx.expect_variant_res(res.as_hir_res().unwrap());
- (parent_def, Some(ItemFragment(FragmentKind::Variant, variant.def_id)))
- })
- .ok_or_else(|| ResolutionFailure::NoParentItem.into())
+ let parent = cx.tcx.parent(res.def_id(cx.tcx));
+ let parent_def = Res::Def(DefKind::Enum, parent);
+ let variant = cx.tcx.expect_variant_res(res.as_hir_res().unwrap());
+ Ok((parent_def, Some(ItemFragment(FragmentKind::Variant, variant.def_id))))
}
/// Resolve a primitive type or value.
use crate::clean::Attributes;
use crate::core::ResolverCaches;
use crate::passes::collect_intra_doc_links::preprocessed_markdown_links;
-use crate::passes::collect_intra_doc_links::PreprocessedMarkdownLink;
+use crate::passes::collect_intra_doc_links::{Disambiguator, PreprocessedMarkdownLink};
use rustc_ast::visit::{self, AssocCtxt, Visitor};
use rustc_ast::{self as ast, ItemKind};
use rustc_data_structures::fx::FxHashMap;
use rustc_hir::def::Namespace::*;
use rustc_hir::def::{DefKind, Namespace, Res};
-use rustc_hir::def_id::{DefId, DefIdMap, DefIdSet, LocalDefId, CRATE_DEF_ID};
+use rustc_hir::def_id::{DefId, DefIdMap, DefIdSet, CRATE_DEF_ID};
use rustc_hir::TraitCandidate;
use rustc_middle::ty::{DefIdTree, Visibility};
use rustc_resolve::{ParentScope, Resolver};
use rustc_session::config::Externs;
+use rustc_session::Session;
+use rustc_span::symbol::sym;
use rustc_span::{Symbol, SyntaxContext};
use std::collections::hash_map::Entry;
crate fn early_resolve_intra_doc_links(
resolver: &mut Resolver<'_>,
+ sess: &Session,
krate: &ast::Crate,
externs: Externs,
document_private_items: bool,
) -> ResolverCaches {
+ let parent_scope =
+ ParentScope::module(resolver.expect_module(CRATE_DEF_ID.to_def_id()), resolver);
let mut link_resolver = EarlyDocLinkResolver {
resolver,
- current_mod: CRATE_DEF_ID,
+ sess,
+ parent_scope,
visited_mods: Default::default(),
markdown_links: Default::default(),
doc_link_resolutions: Default::default(),
// DO NOT REMOVE THIS without first testing on the reproducer in
// https://github.com/jyn514/objr/commit/edcee7b8124abf0e4c63873e8422ff81beb11ebb
for (extern_name, _) in externs.iter().filter(|(_, entry)| entry.add_prelude) {
- link_resolver.resolver.resolve_rustdoc_path(extern_name, TypeNS, CRATE_DEF_ID.to_def_id());
+ link_resolver.resolver.resolve_rustdoc_path(extern_name, TypeNS, parent_scope);
}
ResolverCaches {
}
fn doc_attrs<'a>(attrs: impl Iterator<Item = &'a ast::Attribute>) -> Attributes {
- let mut attrs = Attributes::from_ast_iter(attrs.map(|attr| (attr, None)), true);
- attrs.unindent_doc_comments();
- attrs
+ Attributes::from_ast_iter(attrs.map(|attr| (attr, None)), true)
}
struct EarlyDocLinkResolver<'r, 'ra> {
resolver: &'r mut Resolver<'ra>,
- current_mod: LocalDefId,
+ sess: &'r Session,
+ parent_scope: ParentScope<'ra>,
visited_mods: DefIdSet,
markdown_links: FxHashMap<String, Vec<PreprocessedMarkdownLink>>,
doc_link_resolutions: FxHashMap<(Symbol, Namespace, DefId), Option<Res<ast::NodeId>>>,
document_private_items: bool,
}
-impl EarlyDocLinkResolver<'_, '_> {
+impl<'ra> EarlyDocLinkResolver<'_, 'ra> {
fn add_traits_in_scope(&mut self, def_id: DefId) {
// Calls to `traits_in_scope` are expensive, so try to avoid them if only possible.
// Keys in the `traits_in_scope` cache are always module IDs.
}
}
- fn resolve_doc_links_extern_impl(&mut self, def_id: DefId, _is_inherent: bool) {
- // FIXME: Resolve links in associated items in addition to traits themselves,
- // `force` is used to provide traits in scope for the associated items.
- self.resolve_doc_links_extern_outer(def_id, def_id, true);
+ fn resolve_doc_links_extern_impl(&mut self, def_id: DefId, is_inherent: bool) {
+ self.resolve_doc_links_extern_outer(def_id, def_id);
+ let assoc_item_def_ids = Vec::from_iter(
+ self.resolver.cstore().associated_item_def_ids_untracked(def_id, self.sess),
+ );
+ for assoc_def_id in assoc_item_def_ids {
+ if !is_inherent
+ || self.resolver.cstore().visibility_untracked(assoc_def_id) == Visibility::Public
+ {
+ self.resolve_doc_links_extern_outer(assoc_def_id, def_id);
+ }
+ }
}
- fn resolve_doc_links_extern_outer(&mut self, def_id: DefId, scope_id: DefId, force: bool) {
- if !force && !self.resolver.cstore().may_have_doc_links_untracked(def_id) {
+ fn resolve_doc_links_extern_outer(&mut self, def_id: DefId, scope_id: DefId) {
+ if !self.resolver.cstore().may_have_doc_links_untracked(def_id) {
return;
}
// FIXME: actually resolve links, not just add traits in scope.
- if let Some(parent_id) = self.resolver.parent(scope_id) {
+ if let Some(parent_id) = self.resolver.opt_parent(scope_id) {
self.add_traits_in_scope(parent_id);
}
}
if !attrs.iter().any(|attr| attr.may_have_doc_links()) {
return;
}
- let module_id = self.current_mod.to_def_id();
- self.resolve_doc_links(doc_attrs(attrs.iter()), module_id);
+ self.resolve_doc_links(doc_attrs(attrs.iter()), self.parent_scope);
}
- fn resolve_doc_links(&mut self, attrs: Attributes, module_id: DefId) {
+ fn resolve_and_cache(
+ &mut self,
+ path_str: &str,
+ ns: Namespace,
+ parent_scope: &ParentScope<'ra>,
+ ) -> bool {
+ // FIXME: This caching may be incorrect in case of multiple `macro_rules`
+ // items with the same name in the same module.
+ self.doc_link_resolutions
+ .entry((Symbol::intern(path_str), ns, parent_scope.module.def_id()))
+ .or_insert_with_key(|(path, ns, _)| {
+ self.resolver.resolve_rustdoc_path(path.as_str(), *ns, *parent_scope)
+ })
+ .is_some()
+ }
+
+ fn resolve_doc_links(&mut self, attrs: Attributes, parent_scope: ParentScope<'ra>) {
let mut need_traits_in_scope = false;
for (doc_module, doc) in attrs.prepare_to_doc_link_resolution() {
assert_eq!(doc_module, None);
- let links = self
- .markdown_links
- .entry(doc)
- .or_insert_with_key(|doc| preprocessed_markdown_links(doc));
+ let mut tmp_links = mem::take(&mut self.markdown_links);
+ let links =
+ tmp_links.entry(doc).or_insert_with_key(|doc| preprocessed_markdown_links(doc));
for PreprocessedMarkdownLink(pp_link, _) in links {
if let Ok(pinfo) = pp_link {
- // FIXME: Resolve the path in all namespaces and resolve its prefixes too.
- let ns = TypeNS;
- self.doc_link_resolutions
- .entry((Symbol::intern(&pinfo.path_str), ns, module_id))
- .or_insert_with_key(|(path, ns, module_id)| {
- self.resolver.resolve_rustdoc_path(path.as_str(), *ns, *module_id)
- });
- need_traits_in_scope = true;
+ // The logic here is a conservative approximation for path resolution in
+ // `resolve_with_disambiguator`.
+ if let Some(ns) = pinfo.disambiguator.map(Disambiguator::ns) {
+ if self.resolve_and_cache(&pinfo.path_str, ns, &parent_scope) {
+ continue;
+ }
+ }
+
+ // Resolve all namespaces due to no disambiguator or for diagnostics.
+ let mut any_resolved = false;
+ let mut need_assoc = false;
+ for ns in [TypeNS, ValueNS, MacroNS] {
+ if self.resolve_and_cache(&pinfo.path_str, ns, &parent_scope) {
+ any_resolved = true;
+ } else if ns != MacroNS {
+ need_assoc = true;
+ }
+ }
+
+ // FIXME: Resolve all prefixes for type-relative resolution or for diagnostics.
+ if (need_assoc || !any_resolved) && pinfo.path_str.contains("::") {
+ need_traits_in_scope = true;
+ }
}
}
+ self.markdown_links = tmp_links;
}
if need_traits_in_scope {
- self.add_traits_in_scope(module_id);
+ self.add_traits_in_scope(parent_scope.module.def_id());
}
}
{
if let Some(def_id) = child.res.opt_def_id() && !def_id.is_local() {
let scope_id = match child.res {
- Res::Def(DefKind::Variant, ..) => self.resolver.parent(def_id).unwrap(),
+ Res::Def(DefKind::Variant, ..) => self.resolver.parent(def_id),
_ => def_id,
};
- self.resolve_doc_links_extern_outer(def_id, scope_id, false); // Outer attribute scope
+ self.resolve_doc_links_extern_outer(def_id, scope_id); // Outer attribute scope
if let Res::Def(DefKind::Mod, ..) = child.res {
self.resolve_doc_links_extern_inner(def_id); // Inner attribute scope
}
fn visit_item(&mut self, item: &ast::Item) {
self.resolve_doc_links_local(&item.attrs); // Outer attribute scope
if let ItemKind::Mod(..) = item.kind {
- let old_mod = mem::replace(&mut self.current_mod, self.resolver.local_def_id(item.id));
+ let module_def_id = self.resolver.local_def_id(item.id).to_def_id();
+ let module = self.resolver.expect_module(module_def_id);
+ let old_module = mem::replace(&mut self.parent_scope.module, module);
+ let old_macro_rules = self.parent_scope.macro_rules;
self.resolve_doc_links_local(&item.attrs); // Inner attribute scope
- self.process_module_children_or_reexports(self.current_mod.to_def_id());
+ self.process_module_children_or_reexports(module_def_id);
visit::walk_item(self, item);
- self.current_mod = old_mod;
+ if item
+ .attrs
+ .iter()
+ .all(|attr| !attr.has_name(sym::macro_use) && !attr.has_name(sym::macro_escape))
+ {
+ self.parent_scope.macro_rules = old_macro_rules;
+ }
+ self.parent_scope.module = old_module;
} else {
- match item.kind {
+ match &item.kind {
ItemKind::Trait(..) => {
self.all_traits.push(self.resolver.local_def_id(item.id).to_def_id());
}
ItemKind::Impl(box ast::Impl { of_trait: Some(..), .. }) => {
self.all_trait_impls.push(self.resolver.local_def_id(item.id).to_def_id());
}
+ ItemKind::MacroDef(macro_def) if macro_def.macro_rules => {
+ self.parent_scope.macro_rules =
+ self.resolver.macro_rules_scope(self.resolver.local_def_id(item.id));
+ }
_ => {}
}
visit::walk_item(self, item);
visit::walk_field_def(self, field)
}
+ fn visit_block(&mut self, block: &ast::Block) {
+ let old_macro_rules = self.parent_scope.macro_rules;
+ visit::walk_block(self, block);
+ self.parent_scope.macro_rules = old_macro_rules;
+ }
+
// NOTE: if doc-comments are ever allowed on other nodes (e.g. function parameters),
// then this will have to implement other visitor methods too.
}
coll.items
};
- let mut new_items = Vec::new();
+ let mut new_items_external = Vec::new();
+ let mut new_items_local = Vec::new();
// External trait impls.
cx.with_all_trait_impls(|cx, all_trait_impls| {
let _prof_timer = cx.tcx.sess.prof.generic_activity("build_extern_trait_impls");
for &impl_def_id in all_trait_impls.iter().skip_while(|def_id| def_id.is_local()) {
- inline::build_impl(cx, None, impl_def_id, None, &mut new_items);
+ inline::build_impl(cx, None, impl_def_id, None, &mut new_items_external);
+ }
+ });
+
+ // Local trait impls.
+ cx.with_all_trait_impls(|cx, all_trait_impls| {
+ let _prof_timer = cx.tcx.sess.prof.generic_activity("build_local_trait_impls");
+ let mut attr_buf = Vec::new();
+ for &impl_def_id in all_trait_impls.iter().take_while(|def_id| def_id.is_local()) {
+ let mut parent = Some(cx.tcx.parent(impl_def_id));
+ while let Some(did) = parent {
+ attr_buf.extend(
+ cx.tcx
+ .get_attrs(did)
+ .iter()
+ .filter(|attr| attr.has_name(sym::doc))
+ .filter(|attr| {
+ if let Some([attr]) = attr.meta_item_list().as_deref() {
+ attr.has_name(sym::cfg)
+ } else {
+ false
+ }
+ })
+ .cloned(),
+ );
+ parent = cx.tcx.opt_parent(did);
+ }
+ inline::build_impl(cx, None, impl_def_id, Some(&attr_buf), &mut new_items_local);
+ attr_buf.clear();
}
});
- // Also try to inline primitive impls from other crates.
cx.tcx.sess.prof.generic_activity("build_primitive_trait_impls").run(|| {
for def_id in PrimitiveType::all_impls(cx.tcx) {
+ // Try to inline primitive impls from other crates.
if !def_id.is_local() {
- inline::build_impl(cx, None, def_id, None, &mut new_items);
-
- // FIXME(eddyb) is this `doc(hidden)` check needed?
- if !cx.tcx.is_doc_hidden(def_id) {
+ inline::build_impl(cx, None, def_id, None, &mut new_items_external);
+ }
+ }
+ for (prim, did) in PrimitiveType::primitive_locations(cx.tcx) {
+ // Do not calculate blanket impl list for docs that are not going to be rendered.
+ // While the `impl` blocks themselves are only in `libcore`, the module with `doc`
+ // attached is directly included in `libstd` as well.
+ if did.is_local() {
+ for def_id in prim.impls(cx.tcx) {
let impls = get_auto_trait_and_blanket_impls(cx, def_id);
- new_items.extend(impls.filter(|i| cx.inlined.insert(i.item_id)));
+ new_items_external.extend(impls.filter(|i| cx.inlined.insert(i.item_id)));
}
}
}
cx: &DocContext<'_>,
map: &FxHashMap<DefId, &Type>,
cleaner: &mut BadImplStripper<'_>,
+ targets: &mut FxHashSet<DefId>,
type_did: DefId,
) {
if let Some(target) = map.get(&type_did) {
cleaner.prims.insert(target_prim);
} else if let Some(target_did) = target.def_id(&cx.cache) {
// `impl Deref<Target = S> for S`
- if target_did == type_did {
+ if !targets.insert(target_did) {
// Avoid infinite cycles
return;
}
cleaner.items.insert(target_did.into());
- add_deref_target(cx, map, cleaner, target_did);
+ add_deref_target(cx, map, cleaner, targets, target_did);
}
}
}
// scan through included items ahead of time to splice in Deref targets to the "valid" sets
- for it in &new_items {
+ for it in new_items_external.iter().chain(new_items_local.iter()) {
if let ImplItem(Impl { ref for_, ref trait_, ref items, .. }) = *it.kind {
if trait_.as_ref().map(|t| t.def_id()) == cx.tcx.lang_items().deref_trait()
&& cleaner.keep_impl(for_, true)
// `Deref` target type and the impl for type positions, this map of types is keyed by
// `DefId` and for convenience uses a special cleaner that accepts `DefId`s directly.
if cleaner.keep_impl_with_def_id(for_did.into()) {
- add_deref_target(cx, &type_did_to_deref_target, &mut cleaner, for_did);
+ let mut targets = FxHashSet::default();
+ targets.insert(for_did);
+ add_deref_target(
+ cx,
+ &type_did_to_deref_target,
+ &mut cleaner,
+ &mut targets,
+ for_did,
+ );
}
}
}
}
}
- new_items.retain(|it| {
+ // Filter out external items that are not needed
+ new_items_external.retain(|it| {
if let ImplItem(Impl { ref for_, ref trait_, ref kind, .. }) = *it.kind {
cleaner.keep_impl(
for_,
}
});
- // Local trait impls.
- cx.with_all_trait_impls(|cx, all_trait_impls| {
- let _prof_timer = cx.tcx.sess.prof.generic_activity("build_local_trait_impls");
- let mut attr_buf = Vec::new();
- for &impl_def_id in all_trait_impls.iter().take_while(|def_id| def_id.is_local()) {
- let mut parent = cx.tcx.parent(impl_def_id);
- while let Some(did) = parent {
- attr_buf.extend(
- cx.tcx
- .get_attrs(did)
- .iter()
- .filter(|attr| attr.has_name(sym::doc))
- .filter(|attr| {
- if let Some([attr]) = attr.meta_item_list().as_deref() {
- attr.has_name(sym::cfg)
- } else {
- false
- }
- })
- .cloned(),
- );
- parent = cx.tcx.parent(did);
- }
- inline::build_impl(cx, None, impl_def_id, Some(&attr_buf), &mut new_items);
- attr_buf.clear();
- }
- });
-
if let ModuleItem(Module { items, .. }) = &mut *krate.module.kind {
items.extend(synth_impls);
- items.extend(new_items);
+ items.extend(new_items_external);
+ items.extend(new_items_local);
} else {
panic!("collect-trait-impls can't run");
};
mod strip_priv_imports;
crate use self::strip_priv_imports::STRIP_PRIV_IMPORTS;
-mod unindent_comments;
-crate use self::unindent_comments::UNINDENT_COMMENTS;
-
mod propagate_doc_cfg;
crate use self::propagate_doc_cfg::PROPAGATE_DOC_CFG;
crate const PASSES: &[Pass] = &[
CHECK_DOC_TEST_VISIBILITY,
STRIP_HIDDEN,
- UNINDENT_COMMENTS,
STRIP_PRIVATE,
STRIP_PRIV_IMPORTS,
PROPAGATE_DOC_CFG,
/// The list of passes run by default.
crate const DEFAULT_PASSES: &[ConditionalPass] = &[
ConditionalPass::always(COLLECT_TRAIT_IMPLS),
- ConditionalPass::always(UNINDENT_COMMENTS),
ConditionalPass::always(CHECK_DOC_TEST_VISIBILITY),
ConditionalPass::new(STRIP_HIDDEN, WhenNotDocumentHidden),
ConditionalPass::new(STRIP_PRIVATE, WhenNotDocumentPrivate),
+++ /dev/null
-//! Removes excess indentation on comments in order for the Markdown
-//! to be parsed correctly. This is necessary because the convention for
-//! writing documentation is to provide a space between the /// or //! marker
-//! and the doc text, but Markdown is whitespace-sensitive. For example,
-//! a block of text with four-space indentation is parsed as a code block,
-//! so if we didn't unindent comments, these list items
-//!
-//! /// A list:
-//! ///
-//! /// - Foo
-//! /// - Bar
-//!
-//! would be parsed as if they were in a code block, which is likely not what the user intended.
-use std::cmp;
-
-use rustc_span::symbol::kw;
-
-use crate::clean::{self, DocFragment, DocFragmentKind, Item};
-use crate::core::DocContext;
-use crate::fold::{self, DocFolder};
-use crate::passes::Pass;
-
-#[cfg(test)]
-mod tests;
-
-crate const UNINDENT_COMMENTS: Pass = Pass {
- name: "unindent-comments",
- run: unindent_comments,
- description: "removes excess indentation on comments in order for markdown to like it",
-};
-
-crate fn unindent_comments(krate: clean::Crate, _: &mut DocContext<'_>) -> clean::Crate {
- CommentCleaner.fold_crate(krate)
-}
-
-struct CommentCleaner;
-
-impl fold::DocFolder for CommentCleaner {
- fn fold_item(&mut self, mut i: Item) -> Option<Item> {
- i.attrs.unindent_doc_comments();
- Some(self.fold_item_recur(i))
- }
-}
-
-impl clean::Attributes {
- crate fn unindent_doc_comments(&mut self) {
- unindent_fragments(&mut self.doc_strings);
- }
-}
-
-fn unindent_fragments(docs: &mut Vec<DocFragment>) {
- // `add` is used in case the most common sugared doc syntax is used ("/// "). The other
- // fragments kind's lines are never starting with a whitespace unless they are using some
- // markdown formatting requiring it. Therefore, if the doc block have a mix between the two,
- // we need to take into account the fact that the minimum indent minus one (to take this
- // whitespace into account).
- //
- // For example:
- //
- // /// hello!
- // #[doc = "another"]
- //
- // In this case, you want "hello! another" and not "hello! another".
- let add = if docs.windows(2).any(|arr| arr[0].kind != arr[1].kind)
- && docs.iter().any(|d| d.kind == DocFragmentKind::SugaredDoc)
- {
- // In case we have a mix of sugared doc comments and "raw" ones, we want the sugared one to
- // "decide" how much the minimum indent will be.
- 1
- } else {
- 0
- };
-
- // `min_indent` is used to know how much whitespaces from the start of each lines must be
- // removed. Example:
- //
- // /// hello!
- // #[doc = "another"]
- //
- // In here, the `min_indent` is 1 (because non-sugared fragment are always counted with minimum
- // 1 whitespace), meaning that "hello!" will be considered a codeblock because it starts with 4
- // (5 - 1) whitespaces.
- let Some(min_indent) = docs
- .iter()
- .map(|fragment| {
- fragment.doc.as_str().lines().fold(usize::MAX, |min_indent, line| {
- if line.chars().all(|c| c.is_whitespace()) {
- min_indent
- } else {
- // Compare against either space or tab, ignoring whether they are
- // mixed or not.
- let whitespace = line.chars().take_while(|c| *c == ' ' || *c == '\t').count();
- cmp::min(min_indent, whitespace)
- + if fragment.kind == DocFragmentKind::SugaredDoc { 0 } else { add }
- }
- })
- })
- .min()
- else {
- return;
- };
-
- for fragment in docs {
- if fragment.doc == kw::Empty {
- continue;
- }
-
- let min_indent = if fragment.kind != DocFragmentKind::SugaredDoc && min_indent > 0 {
- min_indent - add
- } else {
- min_indent
- };
-
- fragment.indent = min_indent;
- }
-}
+++ /dev/null
-use super::*;
-
-use crate::clean::collapse_doc_fragments;
-
-use rustc_span::create_default_session_globals_then;
-use rustc_span::source_map::DUMMY_SP;
-use rustc_span::symbol::Symbol;
-
-fn create_doc_fragment(s: &str) -> Vec<DocFragment> {
- vec![DocFragment {
- span: DUMMY_SP,
- parent_module: None,
- doc: Symbol::intern(s),
- kind: DocFragmentKind::SugaredDoc,
- indent: 0,
- }]
-}
-
-#[track_caller]
-fn run_test(input: &str, expected: &str) {
- create_default_session_globals_then(|| {
- let mut s = create_doc_fragment(input);
- unindent_fragments(&mut s);
- assert_eq!(collapse_doc_fragments(&s), expected);
- });
-}
-
-#[test]
-fn should_unindent() {
- run_test(" line1\n line2", "line1\nline2");
-}
-
-#[test]
-fn should_unindent_multiple_paragraphs() {
- run_test(" line1\n\n line2", "line1\n\nline2");
-}
-
-#[test]
-fn should_leave_multiple_indent_levels() {
- // Line 2 is indented another level beyond the
- // base indentation and should be preserved
- run_test(" line1\n\n line2", "line1\n\n line2");
-}
-
-#[test]
-fn should_ignore_first_line_indent() {
- run_test("line1\n line2", "line1\n line2");
-}
-
-#[test]
-fn should_not_ignore_first_line_indent_in_a_single_line_para() {
- run_test("line1\n\n line2", "line1\n\n line2");
-}
-
-#[test]
-fn should_unindent_tabs() {
- run_test("\tline1\n\tline2", "line1\nline2");
-}
-
-#[test]
-fn should_trim_mixed_indentation() {
- run_test("\t line1\n\t line2", "line1\nline2");
- run_test(" \tline1\n \tline2", "line1\nline2");
-}
-
-#[test]
-fn should_not_trim() {
- run_test("\t line1 \n\t line2", "line1 \nline2");
- run_test(" \tline1 \n \tline2", "line1 \nline2");
-}
-Subproject commit fd336816c3a6d228dcef22171c43140f6fa7656f
+Subproject commit fc10370ef7d91babf512c10505f8f2176bc8519d
--- /dev/null
+// assembly-output: ptx-linker
+// compile-flags: --crate-type cdylib -C target-cpu=sm_86
+// only-nvptx64
+// ignore-nvptx64
+
+// The following ABI tests are made with nvcc 11.6 does.
+//
+// The PTX ABI stability is tied to major versions of the PTX ISA
+// These tests assume major version 7
+//
+//
+// The following correspondence between types are assumed:
+// u<N> - uint<N>_t
+// i<N> - int<N>_t
+// [T, N] - std::array<T, N>
+// &T - T const*
+// &mut T - T*
+
+// CHECK: .version 7
+
+#![feature(abi_ptx, lang_items, no_core)]
+#![no_core]
+
+#[lang = "sized"]
+trait Sized {}
+#[lang = "copy"]
+trait Copy {}
+
+#[repr(C)]
+pub struct SingleU8 {
+ f: u8,
+}
+
+#[repr(C)]
+pub struct DoubleU8 {
+ f: u8,
+ g: u8,
+}
+
+#[repr(C)]
+pub struct TripleU8 {
+ f: u8,
+ g: u8,
+ h: u8,
+}
+
+#[repr(C)]
+pub struct TripleU16 {
+ f: u16,
+ g: u16,
+ h: u16,
+}
+#[repr(C)]
+pub struct TripleU32 {
+ f: u32,
+ g: u32,
+ h: u32,
+}
+#[repr(C)]
+pub struct TripleU64 {
+ f: u64,
+ g: u64,
+ h: u64,
+}
+
+#[repr(C)]
+pub struct DoubleFloat {
+ f: f32,
+ g: f32,
+}
+
+#[repr(C)]
+pub struct TripleFloat {
+ f: f32,
+ g: f32,
+ h: f32,
+}
+
+#[repr(C)]
+pub struct TripleDouble {
+ f: f64,
+ g: f64,
+ h: f64,
+}
+
+#[repr(C)]
+pub struct ManyIntegers {
+ f: u8,
+ g: u16,
+ h: u32,
+ i: u64,
+}
+
+#[repr(C)]
+pub struct ManyNumerics {
+ f: u8,
+ g: u16,
+ h: u32,
+ i: u64,
+ j: f32,
+ k: f64,
+}
+
+// CHECK: .visible .entry f_u8_arg(
+// CHECK: .param .u8 f_u8_arg_param_0
+#[no_mangle]
+pub unsafe extern "ptx-kernel" fn f_u8_arg(_a: u8) {}
+
+// CHECK: .visible .entry f_u16_arg(
+// CHECK: .param .u16 f_u16_arg_param_0
+#[no_mangle]
+pub unsafe extern "ptx-kernel" fn f_u16_arg(_a: u16) {}
+
+// CHECK: .visible .entry f_u32_arg(
+// CHECK: .param .u32 f_u32_arg_param_0
+#[no_mangle]
+pub unsafe extern "ptx-kernel" fn f_u32_arg(_a: u32) {}
+
+// CHECK: .visible .entry f_u64_arg(
+// CHECK: .param .u64 f_u64_arg_param_0
+#[no_mangle]
+pub unsafe extern "ptx-kernel" fn f_u64_arg(_a: u64) {}
+
+// CHECK: .visible .entry f_u128_arg(
+// CHECK: .param .align 16 .b8 f_u128_arg_param_0[16]
+#[no_mangle]
+pub unsafe extern "ptx-kernel" fn f_u128_arg(_a: u128) {}
+
+// CHECK: .visible .entry f_i8_arg(
+// CHECK: .param .u8 f_i8_arg_param_0
+#[no_mangle]
+pub unsafe extern "ptx-kernel" fn f_i8_arg(_a: i8) {}
+
+// CHECK: .visible .entry f_i16_arg(
+// CHECK: .param .u16 f_i16_arg_param_0
+#[no_mangle]
+pub unsafe extern "ptx-kernel" fn f_i16_arg(_a: i16) {}
+
+// CHECK: .visible .entry f_i32_arg(
+// CHECK: .param .u32 f_i32_arg_param_0
+#[no_mangle]
+pub unsafe extern "ptx-kernel" fn f_i32_arg(_a: i32) {}
+
+// CHECK: .visible .entry f_i64_arg(
+// CHECK: .param .u64 f_i64_arg_param_0
+#[no_mangle]
+pub unsafe extern "ptx-kernel" fn f_i64_arg(_a: i64) {}
+
+// CHECK: .visible .entry f_i128_arg(
+// CHECK: .param .align 16 .b8 f_i128_arg_param_0[16]
+#[no_mangle]
+pub unsafe extern "ptx-kernel" fn f_i128_arg(_a: i128) {}
+
+// CHECK: .visible .entry f_f32_arg(
+// CHECK: .param .f32 f_f32_arg_param_0
+#[no_mangle]
+pub unsafe extern "ptx-kernel" fn f_f32_arg(_a: f32) {}
+
+// CHECK: .visible .entry f_f64_arg(
+// CHECK: .param .f64 f_f64_arg_param_0
+#[no_mangle]
+pub unsafe extern "ptx-kernel" fn f_f64_arg(_a: f64) {}
+
+// CHECK: .visible .entry f_single_u8_arg(
+// CHECK: .param .align 1 .b8 f_single_u8_arg_param_0[1]
+#[no_mangle]
+pub unsafe extern "ptx-kernel" fn f_single_u8_arg(_a: SingleU8) {}
+
+// CHECK: .visible .entry f_double_u8_arg(
+// CHECK: .param .align 1 .b8 f_double_u8_arg_param_0[2]
+#[no_mangle]
+pub unsafe extern "ptx-kernel" fn f_double_u8_arg(_a: DoubleU8) {}
+
+// CHECK: .visible .entry f_triple_u8_arg(
+// CHECK: .param .align 1 .b8 f_triple_u8_arg_param_0[3]
+#[no_mangle]
+pub unsafe extern "ptx-kernel" fn f_triple_u8_arg(_a: TripleU8) {}
+
+// CHECK: .visible .entry f_triple_u16_arg(
+// CHECK: .param .align 2 .b8 f_triple_u16_arg_param_0[6]
+#[no_mangle]
+pub unsafe extern "ptx-kernel" fn f_triple_u16_arg(_a: TripleU16) {}
+
+// CHECK: .visible .entry f_triple_u32_arg(
+// CHECK: .param .align 4 .b8 f_triple_u32_arg_param_0[12]
+#[no_mangle]
+pub unsafe extern "ptx-kernel" fn f_triple_u32_arg(_a: TripleU32) {}
+
+// CHECK: .visible .entry f_triple_u64_arg(
+// CHECK: .param .align 8 .b8 f_triple_u64_arg_param_0[24]
+#[no_mangle]
+pub unsafe extern "ptx-kernel" fn f_triple_u64_arg(_a: TripleU64) {}
+
+// CHECK: .visible .entry f_many_integers_arg(
+// CHECK: .param .align 8 .b8 f_many_integers_arg_param_0[16]
+#[no_mangle]
+pub unsafe extern "ptx-kernel" fn f_many_integers_arg(_a: ManyIntegers) {}
+
+// CHECK: .visible .entry f_double_float_arg(
+// CHECK: .param .align 4 .b8 f_double_float_arg_param_0[8]
+#[no_mangle]
+pub unsafe extern "ptx-kernel" fn f_double_float_arg(_a: DoubleFloat) {}
+
+// CHECK: .visible .entry f_triple_float_arg(
+// CHECK: .param .align 4 .b8 f_triple_float_arg_param_0[12]
+#[no_mangle]
+pub unsafe extern "ptx-kernel" fn f_triple_float_arg(_a: TripleFloat) {}
+
+// CHECK: .visible .entry f_triple_double_arg(
+// CHECK: .param .align 8 .b8 f_triple_double_arg_param_0[24]
+#[no_mangle]
+pub unsafe extern "ptx-kernel" fn f_triple_double_arg(_a: TripleDouble) {}
+
+// CHECK: .visible .entry f_many_numerics_arg(
+// CHECK: .param .align 8 .b8 f_many_numerics_arg_param_0[32]
+#[no_mangle]
+pub unsafe extern "ptx-kernel" fn f_many_numerics_arg(_a: ManyNumerics) {}
+
+// CHECK: .visible .entry f_byte_array_arg(
+// CHECK: .param .align 1 .b8 f_byte_array_arg_param_0[5]
+#[no_mangle]
+pub unsafe extern "ptx-kernel" fn f_byte_array_arg(_a: [u8; 5]) {}
+
+// CHECK: .visible .entry f_float_array_arg(
+// CHECK: .param .align 4 .b8 f_float_array_arg_param_0[20]
+#[no_mangle]
+pub unsafe extern "ptx-kernel" fn f_float_array_arg(_a: [f32; 5]) {}
+
+// CHECK: .visible .entry f_u128_array_arg(
+// CHECK: .param .align 16 .b8 f_u128_array_arg_param_0[80]
+#[no_mangle]
+pub unsafe extern "ptx-kernel" fn f_u128_array_arg(_a: [u128; 5]) {}
+
+// CHECK: .visible .entry f_u32_slice_arg(
+// CHECK: .param .u64 f_u32_slice_arg_param_0
+// CHECK: .param .u64 f_u32_slice_arg_param_1
+#[no_mangle]
+pub unsafe extern "ptx-kernel" fn f_u32_slice_arg(_a: &[u32]) {}
+
+// CHECK: .visible .entry f_tuple_u8_u8_arg(
+// CHECK: .param .align 1 .b8 f_tuple_u8_u8_arg_param_0[2]
+#[no_mangle]
+pub unsafe extern "ptx-kernel" fn f_tuple_u8_u8_arg(_a: (u8, u8)) {}
+
+// CHECK: .visible .entry f_tuple_u32_u32_arg(
+// CHECK: .param .align 4 .b8 f_tuple_u32_u32_arg_param_0[8]
+#[no_mangle]
+pub unsafe extern "ptx-kernel" fn f_tuple_u32_u32_arg(_a: (u32, u32)) {}
+
+
+// CHECK: .visible .entry f_tuple_u8_u8_u32_arg(
+// CHECK: .param .align 4 .b8 f_tuple_u8_u8_u32_arg_param_0[8]
+#[no_mangle]
+pub unsafe extern "ptx-kernel" fn f_tuple_u8_u8_u32_arg(_a: (u8, u8, u32)) {}
-// compile-flags: -Z panic-in-drop=abort -O -Z new-llvm-pass-manager=no
+// compile-flags: -Z panic-in-drop=abort -O
+// ignore-msvc
// Ensure that unwinding code paths are eliminated from the output after
// optimization.
-// This test uses -Z new-llvm-pass-manager=no, because the expected optimization does not happen
-// on targets using SEH exceptions (i.e. MSVC) anymore. The core issue is that Rust promises that
-// the drop_in_place() function can't unwind, but implements it in a way that *can*, because we
-// currently go out of our way to allow longjmps, which also use the unwinding mechanism on MSVC
-// targets. We should either forbid longjmps, or not assume nounwind, making this optimization
-// incompatible with the current behavior of running cleanuppads on longjmp unwinding.
+// This test uses ignore-msvc, because the expected optimization does not happen on targets using
+// SEH exceptions with the new LLVM pass manager anymore, see
+// https://github.com/llvm/llvm-project/issues/51311.
// CHECK-NOT: {{(call|invoke).*}}should_not_appear_in_output
}
// Here we check that local debuginfo is mapped correctly.
-// CHECK: !DIFile(filename: "/the/src/remap_path_prefix/main.rs", directory: "/the/cwd/"
+// CHECK: !DIFile(filename: "/the/src/remap_path_prefix/main.rs", directory: "/the/cwd"
// And here that debuginfo from other crates are expanded to absolute paths.
// CHECK: !DIFile(filename: "/the/aux-src/remap_path_prefix_aux.rs", directory: ""
--- /dev/null
+// compile-flags: -O
+// only-x86_64
+// ignore-debug
+
+#![crate_type = "lib"]
+
+// CHECK-LABEL: @vec_zero_scalar
+#[no_mangle]
+pub fn vec_zero_scalar(n: usize) -> Vec<i32> {
+ // CHECK-NOT: __rust_alloc(
+ // CHECK: __rust_alloc_zeroed(
+ // CHECK-NOT: __rust_alloc(
+ vec![0; n]
+}
+
+// CHECK-LABEL: @vec_zero_rgb48
+#[no_mangle]
+pub fn vec_zero_rgb48(n: usize) -> Vec<[u16; 3]> {
+ // CHECK-NOT: __rust_alloc(
+ // CHECK: __rust_alloc_zeroed(
+ // CHECK-NOT: __rust_alloc(
+ vec![[0, 0, 0]; n]
+}
+
+// CHECK-LABEL: @vec_zero_array_32
+#[no_mangle]
+pub fn vec_zero_array_32(n: usize) -> Vec<[i64; 32]> {
+ // CHECK-NOT: __rust_alloc(
+ // CHECK: __rust_alloc_zeroed(
+ // CHECK-NOT: __rust_alloc(
+ vec![[0_i64; 32]; n]
+}
//
// cdb-command:dx t,d
// cdb-check:t,d : [...] [Type: std::thread::Thread *]
-// cdb-check: [...] inner : {...} [Type: alloc::sync::Arc<std::thread::Inner>]
+// cdb-check:[...] inner [...][Type: core::pin::Pin<alloc::sync::Arc<std::thread::Inner> >]
use std::thread;
--- /dev/null
+// compile-flags:-g
+
+// We only test Rust-aware versions of GDB:
+// min-gdb-version: 8.2
+
+// === GDB TESTS ===================================================================================
+
+// gdb-command: run
+
+// gdb-command: print _ref
+// gdb-check: $1 = (*mut ()) 0x[...]
+
+// gdb-command: print _ptr
+// gdb-check: $2 = (*mut ()) 0x[...]
+
+// gdb-command: print _local
+// gdb-check: $3 = ()
+
+// gdb-command: print _field
+// gdb-check: $4 = unit_type::_TypeContainingUnitField {_a: 123, _unit: (), _b: 456}
+
+// Check that we can cast "void pointers" to their actual type in the debugger
+// gdb-command: print /x *(_ptr as *const u64)
+// gdb-check: $5 = 0x1122334455667788
+
+// === CDB TESTS ===================================================================================
+
+// cdb-command: g
+// cdb-check: Breakpoint 0 hit
+
+// cdb-command: dx _ref
+// cdb-check: _ref : 0x[...] : () [Type: tuple$<> *]
+
+// cdb-command: dx _ptr
+// cdb-check: _ptr : 0x[...] : () [Type: tuple$<> *]
+
+// cdb-command: dx _local
+// cdb-check: _local : () [Type: tuple$<>]
+
+// cdb-command: dx _field,d
+// cdb-check: _field,d [Type: unit_type::_TypeContainingUnitField]
+// cdb-check: [+0x[...]] _a : 123 [Type: unsigned int]
+// cdb-check: [+0x[...]] _unit : () [Type: tuple$<>]
+// cdb-check: [+0x[...]] _b : 456 [Type: unsigned __int64]
+
+// Check that we can cast "void pointers" to their actual type in the debugger
+// cdb-command: dx ((__int64 *)_ptr),x
+// cdb-check: ((__int64 *)_ptr),x : 0x[...] : 0x1122334455667788 [Type: __int64 *]
+// cdb-check: 0x1122334455667788 [Type: __int64]
+
+struct _TypeContainingUnitField {
+ _a: u32,
+ _unit: (),
+ _b: u64,
+}
+
+fn foo(_ref: &(), _ptr: *const ()) {
+ let _local = ();
+ let _field = _TypeContainingUnitField { _a: 123, _unit: (), _b: 456 };
+
+ zzz(); // #break
+}
+
+fn main() {
+ let pointee = 0x1122_3344_5566_7788i64;
+
+ foo(&(), &pointee as *const i64 as *const ());
+}
+
+#[inline(never)]
+fn zzz() {}
// Change enum visibility -----------------------------------------------------
#[cfg(any(cfail1,cfail4))]
-enum EnumVisibility { A }
+enum EnumVisibility { A }
#[cfg(not(any(cfail1,cfail4)))]
-#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_nodes")]
+#[rustc_clean(cfg="cfail2")]
#[rustc_clean(cfg="cfail3")]
#[rustc_clean(cfg="cfail5", except="hir_owner,hir_owner_nodes")]
#[rustc_clean(cfg="cfail6")]
-pub enum EnumVisibility {
- A
-}
+pub enum EnumVisibility { A }
// Change Method Privacy -------------------------------------------------------
#[cfg(any(cfail1,cfail4))]
impl Foo {
- //------------------------------------------------------------------------------
+ //----------------------------------------------------
//--------------------------
//------------------------------------------------------------------------------
//--------------------------
#[rustc_clean(cfg="cfail5")]
#[rustc_clean(cfg="cfail6")]
impl Foo {
- #[rustc_clean(cfg="cfail2", except="associated_item,hir_owner,hir_owner_nodes")]
+ #[rustc_clean(cfg="cfail2", except="associated_item")]
#[rustc_clean(cfg="cfail3")]
- #[rustc_clean(cfg="cfail5", except="associated_item,hir_owner,hir_owner_nodes")]
+ #[rustc_clean(cfg="cfail5", except="hir_owner,hir_owner_nodes,associated_item")]
#[rustc_clean(cfg="cfail6")]
fn method_privacy() { }
}
// Change static visibility
#[cfg(any(cfail1,cfail4))]
-static STATIC_VISIBILITY: u8 = 0;
+static STATIC_VISIBILITY: u8 = 0;
#[cfg(not(any(cfail1,cfail4)))]
-#[rustc_clean(cfg="cfail2", except="hir_owner,hir_owner_nodes")]
+#[rustc_clean(cfg="cfail2")]
#[rustc_clean(cfg="cfail3")]
#[rustc_clean(cfg="cfail5", except="hir_owner,hir_owner_nodes")]
#[rustc_clean(cfg="cfail6")]
// Tuple Struct Field Visibility -----------------------------------------------
#[cfg(any(cfail1,cfail4))]
-struct TupleStructFieldVisibility(char);
+struct TupleStructFieldVisibility( char);
#[cfg(not(any(cfail1,cfail4)))]
-#[rustc_clean(except="hir_owner,hir_owner_nodes,type_of", cfg="cfail2")]
+#[rustc_clean(cfg="cfail2", except="type_of")]
#[rustc_clean(cfg="cfail3")]
-#[rustc_clean(except="hir_owner,hir_owner_nodes,type_of", cfg="cfail5")]
+#[rustc_clean(cfg="cfail5", except="hir_owner,hir_owner_nodes,type_of")]
#[rustc_clean(cfg="cfail6")]
struct TupleStructFieldVisibility(pub char);
// Record Struct Field Visibility ----------------------------------------------
#[cfg(any(cfail1,cfail4))]
-struct RecordStructFieldVisibility { x: f32 }
+struct RecordStructFieldVisibility { x: f32 }
#[cfg(not(any(cfail1,cfail4)))]
-#[rustc_clean(except="hir_owner,hir_owner_nodes,type_of", cfg="cfail2")]
+#[rustc_clean(cfg="cfail2", except="type_of")]
#[rustc_clean(cfg="cfail3")]
-#[rustc_clean(except="hir_owner,hir_owner_nodes,type_of", cfg="cfail5")]
+#[rustc_clean(cfg="cfail5", except="hir_owner,hir_owner_nodes,type_of")]
#[rustc_clean(cfg="cfail6")]
-struct RecordStructFieldVisibility {
- pub x: f32
-}
+struct RecordStructFieldVisibility { pub x: f32 }
// Add Lifetime Parameter ------------------------------------------------------
// Visibility ------------------------------------------------------------------
#[cfg(any(cfail1,cfail4))]
-struct Visibility;
+struct Visibility;
#[cfg(not(any(cfail1,cfail4)))]
-#[rustc_clean(except="hir_owner,hir_owner_nodes", cfg="cfail2")]
+#[rustc_clean(cfg="cfail2")]
#[rustc_clean(cfg="cfail3")]
-#[rustc_clean(except="hir_owner,hir_owner_nodes", cfg="cfail5")]
+#[rustc_clean(cfg="cfail5", except="hir_owner,hir_owner_nodes")]
#[rustc_clean(cfg="cfail6")]
pub struct Visibility;
trait TraitVisibility { }
#[cfg(not(any(cfail1,cfail4)))]
-#[rustc_clean(except="hir_owner,hir_owner_nodes", cfg="cfail2")]
+#[rustc_clean(cfg="cfail2")]
#[rustc_clean(cfg="cfail3")]
-#[rustc_clean(except="hir_owner,hir_owner_nodes", cfg="cfail5")]
+#[rustc_clean(cfg="cfail5", except="hir_owner,hir_owner_nodes")]
#[rustc_clean(cfg="cfail6")]
pub trait TraitVisibility { }
-// compile-flags: -C opt-level=0 -Z inline_mir=no
+// unit-test: InstCombine
// ignore-wasm32 compiled with panic=abort by default
// EMIT_MIR combine_clone_of_primitives.{impl#0}-clone.InstCombine.diff
}
bb0: {
+ StorageLive(_2); // scope 0 at $DIR/combine_clone_of_primitives.rs:8:5: 8:9
_2 = &((*_1).0: T); // scope 0 at $DIR/combine_clone_of_primitives.rs:8:5: 8:9
+ StorageLive(_3); // scope 0 at $DIR/combine_clone_of_primitives.rs:9:5: 9:11
_3 = &((*_1).1: u64); // scope 0 at $DIR/combine_clone_of_primitives.rs:9:5: 9:11
+ StorageLive(_4); // scope 0 at $DIR/combine_clone_of_primitives.rs:10:5: 10:16
_4 = &((*_1).2: [f32; 3]); // scope 0 at $DIR/combine_clone_of_primitives.rs:10:5: 10:16
+ StorageLive(_5); // scope 1 at $DIR/combine_clone_of_primitives.rs:8:5: 8:9
+ StorageLive(_6); // scope 1 at $DIR/combine_clone_of_primitives.rs:8:5: 8:9
+ StorageLive(_7); // scope 1 at $DIR/combine_clone_of_primitives.rs:8:5: 8:9
- _7 = &(*_2); // scope 1 at $DIR/combine_clone_of_primitives.rs:8:5: 8:9
- _6 = &(*_7); // scope 1 at $DIR/combine_clone_of_primitives.rs:8:5: 8:9
+ _7 = _2; // scope 1 at $DIR/combine_clone_of_primitives.rs:8:5: 8:9
}
bb1: {
+ StorageDead(_6); // scope 1 at $DIR/combine_clone_of_primitives.rs:8:8: 8:9
+ StorageLive(_8); // scope 1 at $DIR/combine_clone_of_primitives.rs:9:5: 9:11
+ StorageLive(_9); // scope 1 at $DIR/combine_clone_of_primitives.rs:9:5: 9:11
+ StorageLive(_10); // scope 1 at $DIR/combine_clone_of_primitives.rs:9:5: 9:11
- _10 = &(*_3); // scope 1 at $DIR/combine_clone_of_primitives.rs:9:5: 9:11
- _9 = &(*_10); // scope 1 at $DIR/combine_clone_of_primitives.rs:9:5: 9:11
- _8 = <u64 as Clone>::clone(move _9) -> [return: bb2, unwind: bb4]; // scope 1 at $DIR/combine_clone_of_primitives.rs:9:5: 9:11
}
bb2: {
+ StorageDead(_9); // scope 1 at $DIR/combine_clone_of_primitives.rs:9:10: 9:11
+ StorageLive(_11); // scope 1 at $DIR/combine_clone_of_primitives.rs:10:5: 10:16
+ StorageLive(_12); // scope 1 at $DIR/combine_clone_of_primitives.rs:10:5: 10:16
+ StorageLive(_13); // scope 1 at $DIR/combine_clone_of_primitives.rs:10:5: 10:16
- _13 = &(*_4); // scope 1 at $DIR/combine_clone_of_primitives.rs:10:5: 10:16
- _12 = &(*_13); // scope 1 at $DIR/combine_clone_of_primitives.rs:10:5: 10:16
- _11 = <[f32; 3] as Clone>::clone(move _12) -> [return: bb3, unwind: bb4]; // scope 1 at $DIR/combine_clone_of_primitives.rs:10:5: 10:16
}
bb3: {
+ StorageDead(_12); // scope 1 at $DIR/combine_clone_of_primitives.rs:10:15: 10:16
Deinit(_0); // scope 1 at $DIR/combine_clone_of_primitives.rs:6:10: 6:15
(_0.0: T) = move _5; // scope 1 at $DIR/combine_clone_of_primitives.rs:6:10: 6:15
(_0.1: u64) = move _8; // scope 1 at $DIR/combine_clone_of_primitives.rs:6:10: 6:15
(_0.2: [f32; 3]) = move _11; // scope 1 at $DIR/combine_clone_of_primitives.rs:6:10: 6:15
+ StorageDead(_13); // scope 1 at $DIR/combine_clone_of_primitives.rs:6:14: 6:15
+ StorageDead(_11); // scope 1 at $DIR/combine_clone_of_primitives.rs:6:14: 6:15
+ StorageDead(_10); // scope 1 at $DIR/combine_clone_of_primitives.rs:6:14: 6:15
+ StorageDead(_8); // scope 1 at $DIR/combine_clone_of_primitives.rs:6:14: 6:15
+ StorageDead(_7); // scope 1 at $DIR/combine_clone_of_primitives.rs:6:14: 6:15
+ StorageDead(_5); // scope 1 at $DIR/combine_clone_of_primitives.rs:6:14: 6:15
+ StorageDead(_4); // scope 0 at $DIR/combine_clone_of_primitives.rs:6:14: 6:15
+ StorageDead(_3); // scope 0 at $DIR/combine_clone_of_primitives.rs:6:14: 6:15
+ StorageDead(_2); // scope 0 at $DIR/combine_clone_of_primitives.rs:6:14: 6:15
return; // scope 0 at $DIR/combine_clone_of_primitives.rs:6:15: 6:15
}
--- /dev/null
+- // MIR for `main` before Derefer
++ // MIR for `main` after Derefer
+
+ fn main() -> () {
+ let mut _0: (); // return place in scope 0 at $DIR/derefer_complex_case.rs:3:11: 3:11
+ let mut _1: std::slice::Iter<i32>; // in scope 0 at $DIR/derefer_complex_case.rs:4:17: 4:26
+ let mut _2: &[i32; 2]; // in scope 0 at $DIR/derefer_complex_case.rs:4:17: 4:26
+ let _3: [i32; 2]; // in scope 0 at $DIR/derefer_complex_case.rs:4:18: 4:26
+ let mut _4: std::slice::Iter<i32>; // in scope 0 at $DIR/derefer_complex_case.rs:4:17: 4:26
+ let mut _5: (); // in scope 0 at $DIR/derefer_complex_case.rs:3:1: 5:2
+ let _6: (); // in scope 0 at $DIR/derefer_complex_case.rs:4:17: 4:26
+ let mut _7: std::option::Option<&i32>; // in scope 0 at $DIR/derefer_complex_case.rs:4:17: 4:26
+ let mut _8: &mut std::slice::Iter<i32>; // in scope 0 at $DIR/derefer_complex_case.rs:4:17: 4:26
+ let mut _9: &mut std::slice::Iter<i32>; // in scope 0 at $DIR/derefer_complex_case.rs:4:17: 4:26
+ let mut _10: isize; // in scope 0 at $DIR/derefer_complex_case.rs:4:5: 4:40
+ let mut _11: !; // in scope 0 at $DIR/derefer_complex_case.rs:4:5: 4:40
+ let mut _13: i32; // in scope 0 at $DIR/derefer_complex_case.rs:4:34: 4:37
+ let mut _14: &[i32; 2]; // in scope 0 at $DIR/derefer_complex_case.rs:4:17: 4:26
++ let mut _15: &i32; // in scope 0 at $DIR/derefer_complex_case.rs:4:17: 4:26
+ scope 1 {
+ debug iter => _4; // in scope 1 at $DIR/derefer_complex_case.rs:4:17: 4:26
+ let _12: i32; // in scope 1 at $DIR/derefer_complex_case.rs:4:10: 4:13
+ scope 2 {
+ debug foo => _12; // in scope 2 at $DIR/derefer_complex_case.rs:4:10: 4:13
+ }
+ }
+
+ bb0: {
+ StorageLive(_1); // scope 0 at $DIR/derefer_complex_case.rs:4:17: 4:26
+ StorageLive(_2); // scope 0 at $DIR/derefer_complex_case.rs:4:17: 4:26
+ _14 = const main::promoted[0]; // scope 0 at $DIR/derefer_complex_case.rs:4:17: 4:26
+ // mir::Constant
+ // + span: $DIR/derefer_complex_case.rs:4:17: 4:26
+ // + literal: Const { ty: &[i32; 2], val: Unevaluated(main, [], Some(promoted[0])) }
+ _2 = &(*_14); // scope 0 at $DIR/derefer_complex_case.rs:4:17: 4:26
+ _1 = <&[i32; 2] as IntoIterator>::into_iter(move _2) -> bb1; // scope 0 at $DIR/derefer_complex_case.rs:4:17: 4:26
+ // mir::Constant
+ // + span: $DIR/derefer_complex_case.rs:4:17: 4:26
+ // + literal: Const { ty: fn(&[i32; 2]) -> <&[i32; 2] as IntoIterator>::IntoIter {<&[i32; 2] as IntoIterator>::into_iter}, val: Value(Scalar(<ZST>)) }
+ }
+
+ bb1: {
+ StorageDead(_2); // scope 0 at $DIR/derefer_complex_case.rs:4:25: 4:26
+ StorageLive(_4); // scope 0 at $DIR/derefer_complex_case.rs:4:17: 4:26
+ _4 = move _1; // scope 0 at $DIR/derefer_complex_case.rs:4:17: 4:26
+ goto -> bb2; // scope 1 at $DIR/derefer_complex_case.rs:4:5: 4:40
+ }
+
+ bb2: {
+ StorageLive(_6); // scope 1 at $DIR/derefer_complex_case.rs:4:17: 4:26
+ StorageLive(_7); // scope 1 at $DIR/derefer_complex_case.rs:4:17: 4:26
+ StorageLive(_8); // scope 1 at $DIR/derefer_complex_case.rs:4:17: 4:26
+ StorageLive(_9); // scope 1 at $DIR/derefer_complex_case.rs:4:17: 4:26
+ _9 = &mut _4; // scope 1 at $DIR/derefer_complex_case.rs:4:17: 4:26
+ _8 = &mut (*_9); // scope 1 at $DIR/derefer_complex_case.rs:4:17: 4:26
+ _7 = <std::slice::Iter<i32> as Iterator>::next(move _8) -> bb3; // scope 1 at $DIR/derefer_complex_case.rs:4:17: 4:26
+ // mir::Constant
+ // + span: $DIR/derefer_complex_case.rs:4:17: 4:26
+ // + literal: Const { ty: for<'r> fn(&'r mut std::slice::Iter<i32>) -> Option<<std::slice::Iter<i32> as Iterator>::Item> {<std::slice::Iter<i32> as Iterator>::next}, val: Value(Scalar(<ZST>)) }
+ }
+
+ bb3: {
+ StorageDead(_8); // scope 1 at $DIR/derefer_complex_case.rs:4:25: 4:26
+ _10 = discriminant(_7); // scope 1 at $DIR/derefer_complex_case.rs:4:17: 4:26
+ switchInt(move _10) -> [0_isize: bb6, 1_isize: bb4, otherwise: bb5]; // scope 1 at $DIR/derefer_complex_case.rs:4:17: 4:26
+ }
+
+ bb4: {
+ StorageLive(_12); // scope 1 at $DIR/derefer_complex_case.rs:4:10: 4:13
+- _12 = (*((_7 as Some).0: &i32)); // scope 1 at $DIR/derefer_complex_case.rs:4:10: 4:13
++ StorageLive(_15); // scope 1 at $DIR/derefer_complex_case.rs:4:10: 4:13
++ _15 = move ((_7 as Some).0: &i32); // scope 1 at $DIR/derefer_complex_case.rs:4:10: 4:13
++ _12 = (*_15); // scope 1 at $DIR/derefer_complex_case.rs:4:10: 4:13
++ StorageDead(_15); // scope 2 at $DIR/derefer_complex_case.rs:4:34: 4:37
+ StorageLive(_13); // scope 2 at $DIR/derefer_complex_case.rs:4:34: 4:37
+ _13 = _12; // scope 2 at $DIR/derefer_complex_case.rs:4:34: 4:37
+ _6 = std::mem::drop::<i32>(move _13) -> bb7; // scope 2 at $DIR/derefer_complex_case.rs:4:29: 4:38
+ // mir::Constant
+ // + span: $DIR/derefer_complex_case.rs:4:29: 4:33
+ // + literal: Const { ty: fn(i32) {std::mem::drop::<i32>}, val: Value(Scalar(<ZST>)) }
+ }
+
+ bb5: {
+ unreachable; // scope 1 at $DIR/derefer_complex_case.rs:4:17: 4:26
+ }
+
+ bb6: {
+ _0 = const (); // scope 1 at $DIR/derefer_complex_case.rs:4:5: 4:40
+ StorageDead(_9); // scope 1 at $DIR/derefer_complex_case.rs:4:39: 4:40
+ StorageDead(_7); // scope 1 at $DIR/derefer_complex_case.rs:4:39: 4:40
+ StorageDead(_6); // scope 1 at $DIR/derefer_complex_case.rs:4:39: 4:40
+ StorageDead(_4); // scope 0 at $DIR/derefer_complex_case.rs:4:39: 4:40
+ StorageDead(_1); // scope 0 at $DIR/derefer_complex_case.rs:4:39: 4:40
+ return; // scope 0 at $DIR/derefer_complex_case.rs:5:2: 5:2
+ }
+
+ bb7: {
+ StorageDead(_13); // scope 2 at $DIR/derefer_complex_case.rs:4:37: 4:38
+ StorageDead(_12); // scope 1 at $DIR/derefer_complex_case.rs:4:39: 4:40
+ StorageDead(_9); // scope 1 at $DIR/derefer_complex_case.rs:4:39: 4:40
+ StorageDead(_7); // scope 1 at $DIR/derefer_complex_case.rs:4:39: 4:40
+ StorageDead(_6); // scope 1 at $DIR/derefer_complex_case.rs:4:39: 4:40
+ _5 = const (); // scope 1 at $DIR/derefer_complex_case.rs:4:5: 4:40
+ goto -> bb2; // scope 1 at $DIR/derefer_complex_case.rs:4:5: 4:40
+ }
+
+ bb8 (cleanup): {
+ resume; // scope 0 at $DIR/derefer_complex_case.rs:3:1: 5:2
+ }
+ }
+
--- /dev/null
+// EMIT_MIR derefer_complex_case.main.Derefer.diff
+
+fn main() {
+ for &foo in &[42, 43] { drop(foo) }
+}
--- /dev/null
+- // MIR for `main` before Derefer
++ // MIR for `main` after Derefer
+
+ fn main() -> () {
+ let mut _0: (); // return place in scope 0 at $DIR/derefer_terminator_test.rs:2:11: 2:11
+ let _1: bool; // in scope 0 at $DIR/derefer_terminator_test.rs:3:9: 3:10
+ let _3: (); // in scope 0 at $DIR/derefer_terminator_test.rs:5:5: 8:6
+ let mut _4: &&&&bool; // in scope 0 at $DIR/derefer_terminator_test.rs:5:15: 5:22
+ let _5: &&&bool; // in scope 0 at $DIR/derefer_terminator_test.rs:5:17: 5:21
+ let _6: &&bool; // in scope 0 at $DIR/derefer_terminator_test.rs:5:18: 5:21
+ let _7: &bool; // in scope 0 at $DIR/derefer_terminator_test.rs:5:19: 5:21
++ let mut _10: &&&bool; // in scope 0 at $DIR/derefer_terminator_test.rs:5:15: 5:22
++ let mut _11: &&bool; // in scope 0 at $DIR/derefer_terminator_test.rs:5:15: 5:22
++ let mut _12: &bool; // in scope 0 at $DIR/derefer_terminator_test.rs:5:15: 5:22
+ scope 1 {
+ debug b => _1; // in scope 1 at $DIR/derefer_terminator_test.rs:3:9: 3:10
+ let _2: bool; // in scope 1 at $DIR/derefer_terminator_test.rs:4:9: 4:10
+ scope 2 {
+ debug d => _2; // in scope 2 at $DIR/derefer_terminator_test.rs:4:9: 4:10
+ let _8: i32; // in scope 2 at $DIR/derefer_terminator_test.rs:6:22: 6:23
+ let _9: i32; // in scope 2 at $DIR/derefer_terminator_test.rs:9:9: 9:10
+ scope 3 {
+ debug x => _8; // in scope 3 at $DIR/derefer_terminator_test.rs:6:22: 6:23
+ }
+ scope 4 {
+ debug y => _9; // in scope 4 at $DIR/derefer_terminator_test.rs:9:9: 9:10
+ }
+ }
+ }
+
+ bb0: {
+ StorageLive(_1); // scope 0 at $DIR/derefer_terminator_test.rs:3:9: 3:10
+ _1 = foo() -> bb1; // scope 0 at $DIR/derefer_terminator_test.rs:3:13: 3:18
+ // mir::Constant
+ // + span: $DIR/derefer_terminator_test.rs:3:13: 3:16
+ // + literal: Const { ty: fn() -> bool {foo}, val: Value(Scalar(<ZST>)) }
+ }
+
+ bb1: {
+ StorageLive(_2); // scope 1 at $DIR/derefer_terminator_test.rs:4:9: 4:10
+ _2 = foo() -> bb2; // scope 1 at $DIR/derefer_terminator_test.rs:4:13: 4:18
+ // mir::Constant
+ // + span: $DIR/derefer_terminator_test.rs:4:13: 4:16
+ // + literal: Const { ty: fn() -> bool {foo}, val: Value(Scalar(<ZST>)) }
+ }
+
+ bb2: {
+ StorageLive(_3); // scope 2 at $DIR/derefer_terminator_test.rs:5:5: 8:6
+ StorageLive(_4); // scope 2 at $DIR/derefer_terminator_test.rs:5:15: 5:22
+ StorageLive(_5); // scope 2 at $DIR/derefer_terminator_test.rs:5:17: 5:21
+ StorageLive(_6); // scope 2 at $DIR/derefer_terminator_test.rs:5:18: 5:21
+ StorageLive(_7); // scope 2 at $DIR/derefer_terminator_test.rs:5:19: 5:21
+ _7 = &_1; // scope 2 at $DIR/derefer_terminator_test.rs:5:19: 5:21
+ _6 = &_7; // scope 2 at $DIR/derefer_terminator_test.rs:5:18: 5:21
+ _5 = &_6; // scope 2 at $DIR/derefer_terminator_test.rs:5:17: 5:21
+ _4 = &_5; // scope 2 at $DIR/derefer_terminator_test.rs:5:15: 5:22
+- switchInt((*(*(*(*_4))))) -> [false: bb3, otherwise: bb4]; // scope 2 at $DIR/derefer_terminator_test.rs:5:5: 5:22
++ StorageLive(_10); // scope 2 at $DIR/derefer_terminator_test.rs:5:5: 5:22
++ _10 = move (*_4); // scope 2 at $DIR/derefer_terminator_test.rs:5:5: 5:22
++ StorageLive(_11); // scope 2 at $DIR/derefer_terminator_test.rs:5:5: 5:22
++ _11 = move (*_10); // scope 2 at $DIR/derefer_terminator_test.rs:5:5: 5:22
++ StorageDead(_10); // scope 2 at $DIR/derefer_terminator_test.rs:5:5: 5:22
++ StorageLive(_12); // scope 2 at $DIR/derefer_terminator_test.rs:5:5: 5:22
++ _12 = move (*_11); // scope 2 at $DIR/derefer_terminator_test.rs:5:5: 5:22
++ StorageDead(_11); // scope 2 at $DIR/derefer_terminator_test.rs:5:5: 5:22
++ switchInt((*_12)) -> [false: bb3, otherwise: bb4]; // scope 2 at $DIR/derefer_terminator_test.rs:5:5: 5:22
+ }
+
+ bb3: {
++ StorageDead(_12); // scope 2 at $DIR/derefer_terminator_test.rs:5:5: 5:22
+ _3 = const (); // scope 2 at $DIR/derefer_terminator_test.rs:7:18: 7:20
+ goto -> bb5; // scope 2 at $DIR/derefer_terminator_test.rs:7:18: 7:20
+ }
+
+ bb4: {
++ StorageDead(_12); // scope 2 at $DIR/derefer_terminator_test.rs:5:5: 5:22
+ StorageLive(_8); // scope 2 at $DIR/derefer_terminator_test.rs:6:22: 6:23
+ _8 = const 5_i32; // scope 2 at $DIR/derefer_terminator_test.rs:6:26: 6:27
+ _3 = const (); // scope 2 at $DIR/derefer_terminator_test.rs:6:17: 6:29
+ StorageDead(_8); // scope 2 at $DIR/derefer_terminator_test.rs:6:28: 6:29
+ goto -> bb5; // scope 2 at $DIR/derefer_terminator_test.rs:6:28: 6:29
+ }
+
+ bb5: {
+ StorageDead(_7); // scope 2 at $DIR/derefer_terminator_test.rs:8:5: 8:6
+ StorageDead(_6); // scope 2 at $DIR/derefer_terminator_test.rs:8:5: 8:6
+ StorageDead(_5); // scope 2 at $DIR/derefer_terminator_test.rs:8:5: 8:6
+ StorageDead(_4); // scope 2 at $DIR/derefer_terminator_test.rs:8:5: 8:6
+ StorageDead(_3); // scope 2 at $DIR/derefer_terminator_test.rs:8:5: 8:6
+ StorageLive(_9); // scope 2 at $DIR/derefer_terminator_test.rs:9:9: 9:10
+ _9 = const 42_i32; // scope 2 at $DIR/derefer_terminator_test.rs:9:13: 9:15
+ _0 = const (); // scope 0 at $DIR/derefer_terminator_test.rs:2:11: 10:2
+ StorageDead(_9); // scope 2 at $DIR/derefer_terminator_test.rs:10:1: 10:2
+ StorageDead(_2); // scope 1 at $DIR/derefer_terminator_test.rs:10:1: 10:2
+ StorageDead(_1); // scope 0 at $DIR/derefer_terminator_test.rs:10:1: 10:2
+ return; // scope 0 at $DIR/derefer_terminator_test.rs:10:2: 10:2
+ }
+
+ bb6 (cleanup): {
+ resume; // scope 0 at $DIR/derefer_terminator_test.rs:2:1: 10:2
+ }
+ }
+
--- /dev/null
+// EMIT_MIR derefer_terminator_test.main.Derefer.diff
+fn main() {
+ let b = foo();
+ let d = foo();
+ match ****(&&&&b) {
+ true => {let x = 5;},
+ false => {}
+ }
+ let y = 42;
+}
+
+fn foo() -> bool {
+ true
+}
let mut _0: (); // return place in scope 0 at $DIR/derefer_test.rs:2:11: 2:11
let mut _1: (i32, i32); // in scope 0 at $DIR/derefer_test.rs:3:9: 3:14
let mut _3: &mut (i32, i32); // in scope 0 at $DIR/derefer_test.rs:4:22: 4:28
-+ let mut _6: &mut (i32, i32); // in scope 0 at $DIR/derefer_test.rs:5:13: 5:26
-+ let mut _7: &mut (i32, i32); // in scope 0 at $DIR/derefer_test.rs:6:13: 6:26
++ let mut _6: &mut (i32, i32); // in scope 0 at $DIR/derefer_test.rs:4:9: 4:14
++ let mut _7: &mut (i32, i32); // in scope 0 at $DIR/derefer_test.rs:4:9: 4:14
scope 1 {
debug a => _1; // in scope 1 at $DIR/derefer_test.rs:3:9: 3:14
let mut _2: (i32, &mut (i32, i32)); // in scope 1 at $DIR/derefer_test.rs:4:9: 4:14
bb0: {
StorageLive(_1); // scope 0 at $DIR/derefer_test.rs:3:9: 3:14
- Deinit(_1); // scope 0 at $DIR/derefer_test.rs:3:17: 3:24
- (_1.0: i32) = const 42_i32; // scope 0 at $DIR/derefer_test.rs:3:17: 3:24
- (_1.1: i32) = const 43_i32; // scope 0 at $DIR/derefer_test.rs:3:17: 3:24
+ _1 = (const 42_i32, const 43_i32); // scope 0 at $DIR/derefer_test.rs:3:17: 3:24
StorageLive(_2); // scope 1 at $DIR/derefer_test.rs:4:9: 4:14
StorageLive(_3); // scope 1 at $DIR/derefer_test.rs:4:22: 4:28
_3 = &mut _1; // scope 1 at $DIR/derefer_test.rs:4:22: 4:28
- Deinit(_2); // scope 1 at $DIR/derefer_test.rs:4:17: 4:29
- (_2.0: i32) = const 99_i32; // scope 1 at $DIR/derefer_test.rs:4:17: 4:29
- (_2.1: &mut (i32, i32)) = move _3; // scope 1 at $DIR/derefer_test.rs:4:17: 4:29
+ _2 = (const 99_i32, move _3); // scope 1 at $DIR/derefer_test.rs:4:17: 4:29
StorageDead(_3); // scope 1 at $DIR/derefer_test.rs:4:28: 4:29
StorageLive(_4); // scope 2 at $DIR/derefer_test.rs:5:9: 5:10
- _4 = &mut ((*(_2.1: &mut (i32, i32))).0: i32); // scope 2 at $DIR/derefer_test.rs:5:13: 5:26
StorageDead(_2); // scope 1 at $DIR/derefer_test.rs:7:1: 7:2
StorageDead(_1); // scope 0 at $DIR/derefer_test.rs:7:1: 7:2
return; // scope 0 at $DIR/derefer_test.rs:7:2: 7:2
-+ }
-+
-+ bb1 (cleanup): {
-+ resume; // scope 0 at $DIR/derefer_test.rs:2:1: 7:2
+ }
+
+ bb1 (cleanup): {
+ resume; // scope 0 at $DIR/derefer_test.rs:2:1: 7:2
}
}
let mut _3: &mut (i32, i32); // in scope 0 at $DIR/derefer_test_multiple.rs:4:22: 4:28
let mut _5: &mut (i32, &mut (i32, i32)); // in scope 0 at $DIR/derefer_test_multiple.rs:5:22: 5:28
let mut _7: &mut (i32, &mut (i32, &mut (i32, i32))); // in scope 0 at $DIR/derefer_test_multiple.rs:6:22: 6:28
-+ let mut _10: &mut (i32, &mut (i32, &mut (i32, i32))); // in scope 0 at $DIR/derefer_test_multiple.rs:7:13: 7:30
-+ let mut _11: &mut (i32, &mut (i32, i32)); // in scope 0 at $DIR/derefer_test_multiple.rs:7:13: 7:30
-+ let mut _12: &mut (i32, i32); // in scope 0 at $DIR/derefer_test_multiple.rs:7:13: 7:30
-+ let mut _13: &mut (i32, &mut (i32, &mut (i32, i32))); // in scope 0 at $DIR/derefer_test_multiple.rs:8:13: 8:30
-+ let mut _14: &mut (i32, &mut (i32, i32)); // in scope 0 at $DIR/derefer_test_multiple.rs:8:13: 8:30
-+ let mut _15: &mut (i32, i32); // in scope 0 at $DIR/derefer_test_multiple.rs:8:13: 8:30
++ let mut _10: &mut (i32, &mut (i32, &mut (i32, i32))); // in scope 0 at $DIR/derefer_test_multiple.rs:6:9: 6:14
++ let mut _11: &mut (i32, &mut (i32, i32)); // in scope 0 at $DIR/derefer_test_multiple.rs:6:9: 6:14
++ let mut _12: &mut (i32, i32); // in scope 0 at $DIR/derefer_test_multiple.rs:6:9: 6:14
++ let mut _13: &mut (i32, &mut (i32, &mut (i32, i32))); // in scope 0 at $DIR/derefer_test_multiple.rs:6:9: 6:14
++ let mut _14: &mut (i32, &mut (i32, i32)); // in scope 0 at $DIR/derefer_test_multiple.rs:6:9: 6:14
++ let mut _15: &mut (i32, i32); // in scope 0 at $DIR/derefer_test_multiple.rs:6:9: 6:14
scope 1 {
debug a => _1; // in scope 1 at $DIR/derefer_test_multiple.rs:3:9: 3:14
let mut _2: (i32, &mut (i32, i32)); // in scope 1 at $DIR/derefer_test_multiple.rs:4:9: 4:14
bb0: {
StorageLive(_1); // scope 0 at $DIR/derefer_test_multiple.rs:3:9: 3:14
- Deinit(_1); // scope 0 at $DIR/derefer_test_multiple.rs:3:17: 3:25
- (_1.0: i32) = const 42_i32; // scope 0 at $DIR/derefer_test_multiple.rs:3:17: 3:25
- (_1.1: i32) = const 43_i32; // scope 0 at $DIR/derefer_test_multiple.rs:3:17: 3:25
+ _1 = (const 42_i32, const 43_i32); // scope 0 at $DIR/derefer_test_multiple.rs:3:17: 3:25
StorageLive(_2); // scope 1 at $DIR/derefer_test_multiple.rs:4:9: 4:14
StorageLive(_3); // scope 1 at $DIR/derefer_test_multiple.rs:4:22: 4:28
_3 = &mut _1; // scope 1 at $DIR/derefer_test_multiple.rs:4:22: 4:28
- Deinit(_2); // scope 1 at $DIR/derefer_test_multiple.rs:4:17: 4:29
- (_2.0: i32) = const 99_i32; // scope 1 at $DIR/derefer_test_multiple.rs:4:17: 4:29
- (_2.1: &mut (i32, i32)) = move _3; // scope 1 at $DIR/derefer_test_multiple.rs:4:17: 4:29
+ _2 = (const 99_i32, move _3); // scope 1 at $DIR/derefer_test_multiple.rs:4:17: 4:29
StorageDead(_3); // scope 1 at $DIR/derefer_test_multiple.rs:4:28: 4:29
StorageLive(_4); // scope 2 at $DIR/derefer_test_multiple.rs:5:9: 5:14
StorageLive(_5); // scope 2 at $DIR/derefer_test_multiple.rs:5:22: 5:28
_5 = &mut _2; // scope 2 at $DIR/derefer_test_multiple.rs:5:22: 5:28
- Deinit(_4); // scope 2 at $DIR/derefer_test_multiple.rs:5:17: 5:29
- (_4.0: i32) = const 11_i32; // scope 2 at $DIR/derefer_test_multiple.rs:5:17: 5:29
- (_4.1: &mut (i32, &mut (i32, i32))) = move _5; // scope 2 at $DIR/derefer_test_multiple.rs:5:17: 5:29
+ _4 = (const 11_i32, move _5); // scope 2 at $DIR/derefer_test_multiple.rs:5:17: 5:29
StorageDead(_5); // scope 2 at $DIR/derefer_test_multiple.rs:5:28: 5:29
StorageLive(_6); // scope 3 at $DIR/derefer_test_multiple.rs:6:9: 6:14
StorageLive(_7); // scope 3 at $DIR/derefer_test_multiple.rs:6:22: 6:28
_7 = &mut _4; // scope 3 at $DIR/derefer_test_multiple.rs:6:22: 6:28
- Deinit(_6); // scope 3 at $DIR/derefer_test_multiple.rs:6:17: 6:29
- (_6.0: i32) = const 13_i32; // scope 3 at $DIR/derefer_test_multiple.rs:6:17: 6:29
- (_6.1: &mut (i32, &mut (i32, &mut (i32, i32)))) = move _7; // scope 3 at $DIR/derefer_test_multiple.rs:6:17: 6:29
+ _6 = (const 13_i32, move _7); // scope 3 at $DIR/derefer_test_multiple.rs:6:17: 6:29
StorageDead(_7); // scope 3 at $DIR/derefer_test_multiple.rs:6:28: 6:29
StorageLive(_8); // scope 4 at $DIR/derefer_test_multiple.rs:7:9: 7:10
- _8 = &mut ((*((*((*(_6.1: &mut (i32, &mut (i32, &mut (i32, i32))))).1: &mut (i32, &mut (i32, i32)))).1: &mut (i32, i32))).1: i32); // scope 4 at $DIR/derefer_test_multiple.rs:7:13: 7:30
+ _10 = move (_6.1: &mut (i32, &mut (i32, &mut (i32, i32)))); // scope 4 at $DIR/derefer_test_multiple.rs:7:13: 7:30
+ StorageLive(_11); // scope 4 at $DIR/derefer_test_multiple.rs:7:13: 7:30
+ _11 = move ((*_10).1: &mut (i32, &mut (i32, i32))); // scope 4 at $DIR/derefer_test_multiple.rs:7:13: 7:30
++ StorageDead(_10); // scope 4 at $DIR/derefer_test_multiple.rs:7:13: 7:30
+ StorageLive(_12); // scope 4 at $DIR/derefer_test_multiple.rs:7:13: 7:30
+ _12 = move ((*_11).1: &mut (i32, i32)); // scope 4 at $DIR/derefer_test_multiple.rs:7:13: 7:30
++ StorageDead(_11); // scope 4 at $DIR/derefer_test_multiple.rs:7:13: 7:30
+ _8 = &mut ((*_12).1: i32); // scope 4 at $DIR/derefer_test_multiple.rs:7:13: 7:30
-+ StorageDead(_10); // scope 5 at $DIR/derefer_test_multiple.rs:8:9: 8:10
-+ StorageDead(_11); // scope 5 at $DIR/derefer_test_multiple.rs:8:9: 8:10
+ StorageDead(_12); // scope 5 at $DIR/derefer_test_multiple.rs:8:9: 8:10
StorageLive(_9); // scope 5 at $DIR/derefer_test_multiple.rs:8:9: 8:10
- _9 = &mut ((*((*((*(_6.1: &mut (i32, &mut (i32, &mut (i32, i32))))).1: &mut (i32, &mut (i32, i32)))).1: &mut (i32, i32))).1: i32); // scope 5 at $DIR/derefer_test_multiple.rs:8:13: 8:30
+ _13 = move (_6.1: &mut (i32, &mut (i32, &mut (i32, i32)))); // scope 5 at $DIR/derefer_test_multiple.rs:8:13: 8:30
+ StorageLive(_14); // scope 5 at $DIR/derefer_test_multiple.rs:8:13: 8:30
+ _14 = move ((*_13).1: &mut (i32, &mut (i32, i32))); // scope 5 at $DIR/derefer_test_multiple.rs:8:13: 8:30
++ StorageDead(_13); // scope 5 at $DIR/derefer_test_multiple.rs:8:13: 8:30
+ StorageLive(_15); // scope 5 at $DIR/derefer_test_multiple.rs:8:13: 8:30
+ _15 = move ((*_14).1: &mut (i32, i32)); // scope 5 at $DIR/derefer_test_multiple.rs:8:13: 8:30
++ StorageDead(_14); // scope 5 at $DIR/derefer_test_multiple.rs:8:13: 8:30
+ _9 = &mut ((*_15).1: i32); // scope 5 at $DIR/derefer_test_multiple.rs:8:13: 8:30
-+ StorageDead(_13); // scope 0 at $DIR/derefer_test_multiple.rs:2:12: 9:2
-+ StorageDead(_14); // scope 0 at $DIR/derefer_test_multiple.rs:2:12: 9:2
+ StorageDead(_15); // scope 0 at $DIR/derefer_test_multiple.rs:2:12: 9:2
_0 = const (); // scope 0 at $DIR/derefer_test_multiple.rs:2:12: 9:2
StorageDead(_9); // scope 5 at $DIR/derefer_test_multiple.rs:9:1: 9:2
StorageDead(_2); // scope 1 at $DIR/derefer_test_multiple.rs:9:1: 9:2
StorageDead(_1); // scope 0 at $DIR/derefer_test_multiple.rs:9:1: 9:2
return; // scope 0 at $DIR/derefer_test_multiple.rs:9:2: 9:2
-+ }
-+
-+ bb1 (cleanup): {
-+ resume; // scope 0 at $DIR/derefer_test_multiple.rs:2:1: 9:2
+ }
+
+ bb1 (cleanup): {
+ resume; // scope 0 at $DIR/derefer_test_multiple.rs:2:1: 9:2
}
}
StorageDead(_5); // scope 0 at $DIR/early_otherwise_branch.rs:12:16: 12:17
StorageDead(_4); // scope 0 at $DIR/early_otherwise_branch.rs:12:16: 12:17
_8 = discriminant((_3.0: std::option::Option<u32>)); // scope 0 at $DIR/early_otherwise_branch.rs:12:11: 12:17
-- switchInt(move _8) -> [0_isize: bb1, 1_isize: bb3, otherwise: bb7]; // scope 0 at $DIR/early_otherwise_branch.rs:12:5: 12:17
+- switchInt(move _8) -> [0_isize: bb1, 1_isize: bb3, otherwise: bb2]; // scope 0 at $DIR/early_otherwise_branch.rs:12:5: 12:17
+ StorageLive(_11); // scope 0 at $DIR/early_otherwise_branch.rs:12:5: 12:17
+ _11 = discriminant((_3.1: std::option::Option<u32>)); // scope 0 at $DIR/early_otherwise_branch.rs:12:5: 12:17
+ StorageLive(_12); // scope 0 at $DIR/early_otherwise_branch.rs:12:5: 12:17
+ bb4: {
StorageDead(_3); // scope 0 at $DIR/early_otherwise_branch.rs:17:1: 17:2
return; // scope 0 at $DIR/early_otherwise_branch.rs:17:2: 17:2
- }
-
-- bb7: {
-- unreachable; // scope 0 at $DIR/early_otherwise_branch.rs:15:14: 15:15
++ }
++
+ bb5: {
+ StorageDead(_12); // scope 0 at $DIR/early_otherwise_branch.rs:12:5: 12:17
+ switchInt(_8) -> [0_isize: bb3, 1_isize: bb2, otherwise: bb1]; // scope 0 at $DIR/early_otherwise_branch.rs:12:5: 12:17
-// compile-flags: -Z mir-opt-level=4 -Z unsound-mir-opts
+// unit-test: EarlyOtherwiseBranch
// EMIT_MIR early_otherwise_branch.opt1.EarlyOtherwiseBranch.diff
fn opt1(x: Option<u32>, y: Option<u32>) -> u32 {
match (x, y) {
-// compile-flags: -Z mir-opt-level=4 -Z unsound-mir-opts
+// unit-test: EarlyOtherwiseBranch
// EMIT_MIR early_otherwise_branch_3_element_tuple.opt1.EarlyOtherwiseBranch.diff
fn opt1(x: Option<u32>, y: Option<u32>, z: Option<u32>) -> u32 {
let mut _31: f32; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:25:50: 25:55
let mut _32: !; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:26:14: 26:28
let mut _33: (); // in scope 0 at $DIR/early_otherwise_branch_68867.rs:26:25: 26:27
-+ let mut _34: isize; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
-+ let mut _35: bool; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
+ let mut _34: &ViewportPercentageLength; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+ let mut _35: &ViewportPercentageLength; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+ let mut _36: &ViewportPercentageLength; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+ let mut _37: &ViewportPercentageLength; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+ let mut _38: &ViewportPercentageLength; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+ let mut _39: &ViewportPercentageLength; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+ let mut _40: &ViewportPercentageLength; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+ let mut _41: &ViewportPercentageLength; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+ let mut _42: &ViewportPercentageLength; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+ let mut _43: &ViewportPercentageLength; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+ let mut _44: &ViewportPercentageLength; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+ let mut _45: &ViewportPercentageLength; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+ let mut _46: &ViewportPercentageLength; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
scope 1 {
- debug one => _12; // in scope 1 at $DIR/early_otherwise_branch_68867.rs:22:14: 22:17
- debug other => _13; // in scope 1 at $DIR/early_otherwise_branch_68867.rs:22:24: 22:29
StorageDead(_6); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:23: 21:24
- StorageDead(_5); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:23: 21:24
+ nop; // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:23: 21:24
- _11 = discriminant((*(_4.0: &ViewportPercentageLength))); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
-- switchInt(move _11) -> [0_isize: bb1, 1_isize: bb3, 2_isize: bb4, 3_isize: bb5, otherwise: bb11]; // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
-+ StorageLive(_34); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
-+ _34 = discriminant((*(_4.1: &ViewportPercentageLength))); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
-+ StorageLive(_35); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
-+ _35 = Ne(_11, move _34); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
-+ StorageDead(_34); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
-+ switchInt(move _35) -> [false: bb7, otherwise: bb1]; // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
+ StorageLive(_34); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+ _34 = move (_4.0: &ViewportPercentageLength); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+ _11 = discriminant((*_34)); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+ StorageDead(_34); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
+ switchInt(move _11) -> [0_isize: bb1, 1_isize: bb3, 2_isize: bb4, 3_isize: bb5, otherwise: bb2]; // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
}
bb1: {
-- _7 = discriminant((*(_4.1: &ViewportPercentageLength))); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
-- switchInt(move _7) -> [0_isize: bb6, otherwise: bb2]; // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
-- }
--
-- bb2: {
-+ StorageDead(_35); // scope 0 at $DIR/early_otherwise_branch_68867.rs:26:25: 26:27
+ StorageLive(_35); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+ _35 = move (_4.1: &ViewportPercentageLength); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+ _7 = discriminant((*_35)); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+ StorageDead(_35); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
+ switchInt(move _7) -> [0_isize: bb6, otherwise: bb2]; // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
+ }
+
+ bb2: {
StorageLive(_33); // scope 0 at $DIR/early_otherwise_branch_68867.rs:26:25: 26:27
-- nop; // scope 0 at $DIR/early_otherwise_branch_68867.rs:26:25: 26:27
+ nop; // scope 0 at $DIR/early_otherwise_branch_68867.rs:26:25: 26:27
Deinit(_0); // scope 0 at $DIR/early_otherwise_branch_68867.rs:26:21: 26:28
-- nop; // scope 0 at $DIR/early_otherwise_branch_68867.rs:26:21: 26:28
+ nop; // scope 0 at $DIR/early_otherwise_branch_68867.rs:26:21: 26:28
discriminant(_0) = 1; // scope 0 at $DIR/early_otherwise_branch_68867.rs:26:21: 26:28
StorageDead(_33); // scope 0 at $DIR/early_otherwise_branch_68867.rs:26:27: 26:28
- StorageDead(_3); // scope 0 at $DIR/early_otherwise_branch_68867.rs:27:6: 27:7
return; // scope 0 at $DIR/early_otherwise_branch_68867.rs:28:2: 28:2
}
-+ bb2: {
-+ nop; // scope 0 at $DIR/early_otherwise_branch_68867.rs:22:14: 22:17
-+ _15 = (((*(_4.0: &ViewportPercentageLength)) as Vw).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:22:14: 22:17
-+ nop; // scope 0 at $DIR/early_otherwise_branch_68867.rs:22:24: 22:29
-+ _16 = (((*(_4.1: &ViewportPercentageLength)) as Vw).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:22:24: 22:29
-+ nop; // scope 1 at $DIR/early_otherwise_branch_68867.rs:22:38: 22:49
-+ nop; // scope 1 at $DIR/early_otherwise_branch_68867.rs:22:38: 22:41
-+ nop; // scope 1 at $DIR/early_otherwise_branch_68867.rs:22:38: 22:41
-+ nop; // scope 1 at $DIR/early_otherwise_branch_68867.rs:22:44: 22:49
-+ nop; // scope 1 at $DIR/early_otherwise_branch_68867.rs:22:44: 22:49
-+ ((((_0 as Ok).0: ViewportPercentageLength) as Vw).0: f32) = Add(move _15, move _16); // scope 1 at $DIR/early_otherwise_branch_68867.rs:22:38: 22:49
-+ nop; // scope 1 at $DIR/early_otherwise_branch_68867.rs:22:48: 22:49
-+ nop; // scope 1 at $DIR/early_otherwise_branch_68867.rs:22:48: 22:49
-+ Deinit(((_0 as Ok).0: ViewportPercentageLength)); // scope 1 at $DIR/early_otherwise_branch_68867.rs:22:35: 22:50
-+ nop; // scope 1 at $DIR/early_otherwise_branch_68867.rs:22:35: 22:50
-+ discriminant(((_0 as Ok).0: ViewportPercentageLength)) = 0; // scope 1 at $DIR/early_otherwise_branch_68867.rs:22:35: 22:50
-+ nop; // scope 1 at $DIR/early_otherwise_branch_68867.rs:22:49: 22:50
-+ nop; // scope 0 at $DIR/early_otherwise_branch_68867.rs:22:49: 22:50
-+ nop; // scope 0 at $DIR/early_otherwise_branch_68867.rs:22:49: 22:50
-+ goto -> bb6; // scope 0 at $DIR/early_otherwise_branch_68867.rs:22:49: 22:50
-+ }
-+
bb3: {
-- _8 = discriminant((*(_4.1: &ViewportPercentageLength))); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
-- switchInt(move _8) -> [1_isize: bb7, otherwise: bb2]; // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
-+ nop; // scope 0 at $DIR/early_otherwise_branch_68867.rs:23:14: 23:17
-+ _20 = (((*(_4.0: &ViewportPercentageLength)) as Vh).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:23:14: 23:17
-+ nop; // scope 0 at $DIR/early_otherwise_branch_68867.rs:23:24: 23:29
-+ _21 = (((*(_4.1: &ViewportPercentageLength)) as Vh).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:23:24: 23:29
-+ nop; // scope 2 at $DIR/early_otherwise_branch_68867.rs:23:38: 23:49
-+ nop; // scope 2 at $DIR/early_otherwise_branch_68867.rs:23:38: 23:41
-+ nop; // scope 2 at $DIR/early_otherwise_branch_68867.rs:23:38: 23:41
-+ nop; // scope 2 at $DIR/early_otherwise_branch_68867.rs:23:44: 23:49
-+ nop; // scope 2 at $DIR/early_otherwise_branch_68867.rs:23:44: 23:49
-+ ((((_0 as Ok).0: ViewportPercentageLength) as Vh).0: f32) = Add(move _20, move _21); // scope 2 at $DIR/early_otherwise_branch_68867.rs:23:38: 23:49
-+ nop; // scope 2 at $DIR/early_otherwise_branch_68867.rs:23:48: 23:49
-+ nop; // scope 2 at $DIR/early_otherwise_branch_68867.rs:23:48: 23:49
-+ Deinit(((_0 as Ok).0: ViewportPercentageLength)); // scope 2 at $DIR/early_otherwise_branch_68867.rs:23:35: 23:50
-+ nop; // scope 2 at $DIR/early_otherwise_branch_68867.rs:23:35: 23:50
-+ discriminant(((_0 as Ok).0: ViewportPercentageLength)) = 1; // scope 2 at $DIR/early_otherwise_branch_68867.rs:23:35: 23:50
-+ nop; // scope 2 at $DIR/early_otherwise_branch_68867.rs:23:49: 23:50
-+ nop; // scope 0 at $DIR/early_otherwise_branch_68867.rs:23:49: 23:50
-+ nop; // scope 0 at $DIR/early_otherwise_branch_68867.rs:23:49: 23:50
-+ goto -> bb6; // scope 0 at $DIR/early_otherwise_branch_68867.rs:23:49: 23:50
+ StorageLive(_36); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+ _36 = move (_4.1: &ViewportPercentageLength); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+ _8 = discriminant((*_36)); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+ StorageDead(_36); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
+ switchInt(move _8) -> [1_isize: bb7, otherwise: bb2]; // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
}
bb4: {
-- _9 = discriminant((*(_4.1: &ViewportPercentageLength))); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
-- switchInt(move _9) -> [2_isize: bb8, otherwise: bb2]; // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
-+ nop; // scope 0 at $DIR/early_otherwise_branch_68867.rs:24:16: 24:19
-+ _25 = (((*(_4.0: &ViewportPercentageLength)) as Vmin).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:24:16: 24:19
-+ nop; // scope 0 at $DIR/early_otherwise_branch_68867.rs:24:28: 24:33
-+ _26 = (((*(_4.1: &ViewportPercentageLength)) as Vmin).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:24:28: 24:33
-+ nop; // scope 3 at $DIR/early_otherwise_branch_68867.rs:24:44: 24:55
-+ nop; // scope 3 at $DIR/early_otherwise_branch_68867.rs:24:44: 24:47
-+ nop; // scope 3 at $DIR/early_otherwise_branch_68867.rs:24:44: 24:47
-+ nop; // scope 3 at $DIR/early_otherwise_branch_68867.rs:24:50: 24:55
-+ nop; // scope 3 at $DIR/early_otherwise_branch_68867.rs:24:50: 24:55
-+ ((((_0 as Ok).0: ViewportPercentageLength) as Vmin).0: f32) = Add(move _25, move _26); // scope 3 at $DIR/early_otherwise_branch_68867.rs:24:44: 24:55
-+ nop; // scope 3 at $DIR/early_otherwise_branch_68867.rs:24:54: 24:55
-+ nop; // scope 3 at $DIR/early_otherwise_branch_68867.rs:24:54: 24:55
-+ Deinit(((_0 as Ok).0: ViewportPercentageLength)); // scope 3 at $DIR/early_otherwise_branch_68867.rs:24:39: 24:56
-+ nop; // scope 3 at $DIR/early_otherwise_branch_68867.rs:24:39: 24:56
-+ discriminant(((_0 as Ok).0: ViewportPercentageLength)) = 2; // scope 3 at $DIR/early_otherwise_branch_68867.rs:24:39: 24:56
-+ nop; // scope 3 at $DIR/early_otherwise_branch_68867.rs:24:55: 24:56
-+ nop; // scope 0 at $DIR/early_otherwise_branch_68867.rs:24:55: 24:56
-+ nop; // scope 0 at $DIR/early_otherwise_branch_68867.rs:24:55: 24:56
-+ goto -> bb6; // scope 0 at $DIR/early_otherwise_branch_68867.rs:24:55: 24:56
+ StorageLive(_37); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+ _37 = move (_4.1: &ViewportPercentageLength); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+ _9 = discriminant((*_37)); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+ StorageDead(_37); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
+ switchInt(move _9) -> [2_isize: bb8, otherwise: bb2]; // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
}
bb5: {
-- _10 = discriminant((*(_4.1: &ViewportPercentageLength))); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
-- switchInt(move _10) -> [3_isize: bb9, otherwise: bb2]; // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
-+ nop; // scope 0 at $DIR/early_otherwise_branch_68867.rs:25:16: 25:19
-+ _30 = (((*(_4.0: &ViewportPercentageLength)) as Vmax).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:25:16: 25:19
-+ nop; // scope 0 at $DIR/early_otherwise_branch_68867.rs:25:28: 25:33
-+ _31 = (((*(_4.1: &ViewportPercentageLength)) as Vmax).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:25:28: 25:33
-+ nop; // scope 4 at $DIR/early_otherwise_branch_68867.rs:25:44: 25:55
-+ nop; // scope 4 at $DIR/early_otherwise_branch_68867.rs:25:44: 25:47
-+ nop; // scope 4 at $DIR/early_otherwise_branch_68867.rs:25:44: 25:47
-+ nop; // scope 4 at $DIR/early_otherwise_branch_68867.rs:25:50: 25:55
-+ nop; // scope 4 at $DIR/early_otherwise_branch_68867.rs:25:50: 25:55
-+ ((((_0 as Ok).0: ViewportPercentageLength) as Vmax).0: f32) = Add(move _30, move _31); // scope 4 at $DIR/early_otherwise_branch_68867.rs:25:44: 25:55
-+ nop; // scope 4 at $DIR/early_otherwise_branch_68867.rs:25:54: 25:55
-+ nop; // scope 4 at $DIR/early_otherwise_branch_68867.rs:25:54: 25:55
-+ Deinit(((_0 as Ok).0: ViewportPercentageLength)); // scope 4 at $DIR/early_otherwise_branch_68867.rs:25:39: 25:56
-+ nop; // scope 4 at $DIR/early_otherwise_branch_68867.rs:25:39: 25:56
-+ discriminant(((_0 as Ok).0: ViewportPercentageLength)) = 3; // scope 4 at $DIR/early_otherwise_branch_68867.rs:25:39: 25:56
-+ nop; // scope 4 at $DIR/early_otherwise_branch_68867.rs:25:55: 25:56
-+ nop; // scope 0 at $DIR/early_otherwise_branch_68867.rs:25:55: 25:56
-+ nop; // scope 0 at $DIR/early_otherwise_branch_68867.rs:25:55: 25:56
-+ goto -> bb6; // scope 0 at $DIR/early_otherwise_branch_68867.rs:25:55: 25:56
+ StorageLive(_38); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+ _38 = move (_4.1: &ViewportPercentageLength); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+ _10 = discriminant((*_38)); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+ StorageDead(_38); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
+ switchInt(move _10) -> [3_isize: bb9, otherwise: bb2]; // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
}
bb6: {
- StorageLive(_12); // scope 0 at $DIR/early_otherwise_branch_68867.rs:22:14: 22:17
-- _12 = (((*(_4.0: &ViewportPercentageLength)) as Vw).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:22:14: 22:17
++ nop; // scope 0 at $DIR/early_otherwise_branch_68867.rs:22:14: 22:17
+ StorageLive(_39); // scope 0 at $DIR/early_otherwise_branch_68867.rs:22:14: 22:17
+ _39 = move (_4.0: &ViewportPercentageLength); // scope 0 at $DIR/early_otherwise_branch_68867.rs:22:14: 22:17
+- _12 = (((*_39) as Vw).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:22:14: 22:17
++ _15 = (((*_39) as Vw).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:22:14: 22:17
+ StorageDead(_39); // scope 0 at $DIR/early_otherwise_branch_68867.rs:22:24: 22:29
- StorageLive(_13); // scope 0 at $DIR/early_otherwise_branch_68867.rs:22:24: 22:29
-- _13 = (((*(_4.1: &ViewportPercentageLength)) as Vw).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:22:24: 22:29
++ nop; // scope 0 at $DIR/early_otherwise_branch_68867.rs:22:24: 22:29
+ StorageLive(_40); // scope 0 at $DIR/early_otherwise_branch_68867.rs:22:24: 22:29
+ _40 = move (_4.1: &ViewportPercentageLength); // scope 0 at $DIR/early_otherwise_branch_68867.rs:22:24: 22:29
+- _13 = (((*_40) as Vw).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:22:24: 22:29
++ _16 = (((*_40) as Vw).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:22:24: 22:29
+ StorageDead(_40); // scope 1 at $DIR/early_otherwise_branch_68867.rs:22:38: 22:49
- StorageLive(_14); // scope 1 at $DIR/early_otherwise_branch_68867.rs:22:38: 22:49
- StorageLive(_15); // scope 1 at $DIR/early_otherwise_branch_68867.rs:22:38: 22:41
- _15 = _12; // scope 1 at $DIR/early_otherwise_branch_68867.rs:22:38: 22:41
- StorageDead(_14); // scope 1 at $DIR/early_otherwise_branch_68867.rs:22:49: 22:50
- StorageDead(_13); // scope 0 at $DIR/early_otherwise_branch_68867.rs:22:49: 22:50
- StorageDead(_12); // scope 0 at $DIR/early_otherwise_branch_68867.rs:22:49: 22:50
-- goto -> bb10; // scope 0 at $DIR/early_otherwise_branch_68867.rs:22:49: 22:50
-- }
--
-- bb7: {
++ nop; // scope 1 at $DIR/early_otherwise_branch_68867.rs:22:38: 22:49
++ nop; // scope 1 at $DIR/early_otherwise_branch_68867.rs:22:38: 22:41
++ nop; // scope 1 at $DIR/early_otherwise_branch_68867.rs:22:38: 22:41
++ nop; // scope 1 at $DIR/early_otherwise_branch_68867.rs:22:44: 22:49
++ nop; // scope 1 at $DIR/early_otherwise_branch_68867.rs:22:44: 22:49
++ ((((_0 as Ok).0: ViewportPercentageLength) as Vw).0: f32) = Add(move _15, move _16); // scope 1 at $DIR/early_otherwise_branch_68867.rs:22:38: 22:49
++ nop; // scope 1 at $DIR/early_otherwise_branch_68867.rs:22:48: 22:49
++ nop; // scope 1 at $DIR/early_otherwise_branch_68867.rs:22:48: 22:49
++ Deinit(((_0 as Ok).0: ViewportPercentageLength)); // scope 1 at $DIR/early_otherwise_branch_68867.rs:22:35: 22:50
++ nop; // scope 1 at $DIR/early_otherwise_branch_68867.rs:22:35: 22:50
++ discriminant(((_0 as Ok).0: ViewportPercentageLength)) = 0; // scope 1 at $DIR/early_otherwise_branch_68867.rs:22:35: 22:50
++ nop; // scope 1 at $DIR/early_otherwise_branch_68867.rs:22:49: 22:50
++ nop; // scope 0 at $DIR/early_otherwise_branch_68867.rs:22:49: 22:50
++ nop; // scope 0 at $DIR/early_otherwise_branch_68867.rs:22:49: 22:50
+ goto -> bb10; // scope 0 at $DIR/early_otherwise_branch_68867.rs:22:49: 22:50
+ }
+
+ bb7: {
- StorageLive(_17); // scope 0 at $DIR/early_otherwise_branch_68867.rs:23:14: 23:17
-- _17 = (((*(_4.0: &ViewportPercentageLength)) as Vh).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:23:14: 23:17
++ nop; // scope 0 at $DIR/early_otherwise_branch_68867.rs:23:14: 23:17
+ StorageLive(_41); // scope 0 at $DIR/early_otherwise_branch_68867.rs:23:14: 23:17
+ _41 = move (_4.0: &ViewportPercentageLength); // scope 0 at $DIR/early_otherwise_branch_68867.rs:23:14: 23:17
+- _17 = (((*_41) as Vh).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:23:14: 23:17
++ _20 = (((*_41) as Vh).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:23:14: 23:17
+ StorageDead(_41); // scope 0 at $DIR/early_otherwise_branch_68867.rs:23:24: 23:29
- StorageLive(_18); // scope 0 at $DIR/early_otherwise_branch_68867.rs:23:24: 23:29
-- _18 = (((*(_4.1: &ViewportPercentageLength)) as Vh).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:23:24: 23:29
++ nop; // scope 0 at $DIR/early_otherwise_branch_68867.rs:23:24: 23:29
+ StorageLive(_42); // scope 0 at $DIR/early_otherwise_branch_68867.rs:23:24: 23:29
+ _42 = move (_4.1: &ViewportPercentageLength); // scope 0 at $DIR/early_otherwise_branch_68867.rs:23:24: 23:29
+- _18 = (((*_42) as Vh).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:23:24: 23:29
++ _21 = (((*_42) as Vh).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:23:24: 23:29
+ StorageDead(_42); // scope 2 at $DIR/early_otherwise_branch_68867.rs:23:38: 23:49
- StorageLive(_19); // scope 2 at $DIR/early_otherwise_branch_68867.rs:23:38: 23:49
- StorageLive(_20); // scope 2 at $DIR/early_otherwise_branch_68867.rs:23:38: 23:41
- _20 = _17; // scope 2 at $DIR/early_otherwise_branch_68867.rs:23:38: 23:41
- StorageDead(_19); // scope 2 at $DIR/early_otherwise_branch_68867.rs:23:49: 23:50
- StorageDead(_18); // scope 0 at $DIR/early_otherwise_branch_68867.rs:23:49: 23:50
- StorageDead(_17); // scope 0 at $DIR/early_otherwise_branch_68867.rs:23:49: 23:50
-- goto -> bb10; // scope 0 at $DIR/early_otherwise_branch_68867.rs:23:49: 23:50
-- }
--
-- bb8: {
++ nop; // scope 2 at $DIR/early_otherwise_branch_68867.rs:23:38: 23:49
++ nop; // scope 2 at $DIR/early_otherwise_branch_68867.rs:23:38: 23:41
++ nop; // scope 2 at $DIR/early_otherwise_branch_68867.rs:23:38: 23:41
++ nop; // scope 2 at $DIR/early_otherwise_branch_68867.rs:23:44: 23:49
++ nop; // scope 2 at $DIR/early_otherwise_branch_68867.rs:23:44: 23:49
++ ((((_0 as Ok).0: ViewportPercentageLength) as Vh).0: f32) = Add(move _20, move _21); // scope 2 at $DIR/early_otherwise_branch_68867.rs:23:38: 23:49
++ nop; // scope 2 at $DIR/early_otherwise_branch_68867.rs:23:48: 23:49
++ nop; // scope 2 at $DIR/early_otherwise_branch_68867.rs:23:48: 23:49
++ Deinit(((_0 as Ok).0: ViewportPercentageLength)); // scope 2 at $DIR/early_otherwise_branch_68867.rs:23:35: 23:50
++ nop; // scope 2 at $DIR/early_otherwise_branch_68867.rs:23:35: 23:50
++ discriminant(((_0 as Ok).0: ViewportPercentageLength)) = 1; // scope 2 at $DIR/early_otherwise_branch_68867.rs:23:35: 23:50
++ nop; // scope 2 at $DIR/early_otherwise_branch_68867.rs:23:49: 23:50
++ nop; // scope 0 at $DIR/early_otherwise_branch_68867.rs:23:49: 23:50
++ nop; // scope 0 at $DIR/early_otherwise_branch_68867.rs:23:49: 23:50
+ goto -> bb10; // scope 0 at $DIR/early_otherwise_branch_68867.rs:23:49: 23:50
+ }
+
+ bb8: {
- StorageLive(_22); // scope 0 at $DIR/early_otherwise_branch_68867.rs:24:16: 24:19
-- _22 = (((*(_4.0: &ViewportPercentageLength)) as Vmin).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:24:16: 24:19
++ nop; // scope 0 at $DIR/early_otherwise_branch_68867.rs:24:16: 24:19
+ StorageLive(_43); // scope 0 at $DIR/early_otherwise_branch_68867.rs:24:16: 24:19
+ _43 = move (_4.0: &ViewportPercentageLength); // scope 0 at $DIR/early_otherwise_branch_68867.rs:24:16: 24:19
+- _22 = (((*_43) as Vmin).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:24:16: 24:19
++ _25 = (((*_43) as Vmin).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:24:16: 24:19
+ StorageDead(_43); // scope 0 at $DIR/early_otherwise_branch_68867.rs:24:28: 24:33
- StorageLive(_23); // scope 0 at $DIR/early_otherwise_branch_68867.rs:24:28: 24:33
-- _23 = (((*(_4.1: &ViewportPercentageLength)) as Vmin).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:24:28: 24:33
++ nop; // scope 0 at $DIR/early_otherwise_branch_68867.rs:24:28: 24:33
+ StorageLive(_44); // scope 0 at $DIR/early_otherwise_branch_68867.rs:24:28: 24:33
+ _44 = move (_4.1: &ViewportPercentageLength); // scope 0 at $DIR/early_otherwise_branch_68867.rs:24:28: 24:33
+- _23 = (((*_44) as Vmin).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:24:28: 24:33
++ _26 = (((*_44) as Vmin).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:24:28: 24:33
+ StorageDead(_44); // scope 3 at $DIR/early_otherwise_branch_68867.rs:24:44: 24:55
- StorageLive(_24); // scope 3 at $DIR/early_otherwise_branch_68867.rs:24:44: 24:55
- StorageLive(_25); // scope 3 at $DIR/early_otherwise_branch_68867.rs:24:44: 24:47
- _25 = _22; // scope 3 at $DIR/early_otherwise_branch_68867.rs:24:44: 24:47
- StorageDead(_24); // scope 3 at $DIR/early_otherwise_branch_68867.rs:24:55: 24:56
- StorageDead(_23); // scope 0 at $DIR/early_otherwise_branch_68867.rs:24:55: 24:56
- StorageDead(_22); // scope 0 at $DIR/early_otherwise_branch_68867.rs:24:55: 24:56
-- goto -> bb10; // scope 0 at $DIR/early_otherwise_branch_68867.rs:24:55: 24:56
-- }
--
-- bb9: {
++ nop; // scope 3 at $DIR/early_otherwise_branch_68867.rs:24:44: 24:55
++ nop; // scope 3 at $DIR/early_otherwise_branch_68867.rs:24:44: 24:47
++ nop; // scope 3 at $DIR/early_otherwise_branch_68867.rs:24:44: 24:47
++ nop; // scope 3 at $DIR/early_otherwise_branch_68867.rs:24:50: 24:55
++ nop; // scope 3 at $DIR/early_otherwise_branch_68867.rs:24:50: 24:55
++ ((((_0 as Ok).0: ViewportPercentageLength) as Vmin).0: f32) = Add(move _25, move _26); // scope 3 at $DIR/early_otherwise_branch_68867.rs:24:44: 24:55
++ nop; // scope 3 at $DIR/early_otherwise_branch_68867.rs:24:54: 24:55
++ nop; // scope 3 at $DIR/early_otherwise_branch_68867.rs:24:54: 24:55
++ Deinit(((_0 as Ok).0: ViewportPercentageLength)); // scope 3 at $DIR/early_otherwise_branch_68867.rs:24:39: 24:56
++ nop; // scope 3 at $DIR/early_otherwise_branch_68867.rs:24:39: 24:56
++ discriminant(((_0 as Ok).0: ViewportPercentageLength)) = 2; // scope 3 at $DIR/early_otherwise_branch_68867.rs:24:39: 24:56
++ nop; // scope 3 at $DIR/early_otherwise_branch_68867.rs:24:55: 24:56
++ nop; // scope 0 at $DIR/early_otherwise_branch_68867.rs:24:55: 24:56
++ nop; // scope 0 at $DIR/early_otherwise_branch_68867.rs:24:55: 24:56
+ goto -> bb10; // scope 0 at $DIR/early_otherwise_branch_68867.rs:24:55: 24:56
+ }
+
+ bb9: {
- StorageLive(_27); // scope 0 at $DIR/early_otherwise_branch_68867.rs:25:16: 25:19
-- _27 = (((*(_4.0: &ViewportPercentageLength)) as Vmax).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:25:16: 25:19
++ nop; // scope 0 at $DIR/early_otherwise_branch_68867.rs:25:16: 25:19
+ StorageLive(_45); // scope 0 at $DIR/early_otherwise_branch_68867.rs:25:16: 25:19
+ _45 = move (_4.0: &ViewportPercentageLength); // scope 0 at $DIR/early_otherwise_branch_68867.rs:25:16: 25:19
+- _27 = (((*_45) as Vmax).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:25:16: 25:19
++ _30 = (((*_45) as Vmax).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:25:16: 25:19
+ StorageDead(_45); // scope 0 at $DIR/early_otherwise_branch_68867.rs:25:28: 25:33
- StorageLive(_28); // scope 0 at $DIR/early_otherwise_branch_68867.rs:25:28: 25:33
-- _28 = (((*(_4.1: &ViewportPercentageLength)) as Vmax).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:25:28: 25:33
++ nop; // scope 0 at $DIR/early_otherwise_branch_68867.rs:25:28: 25:33
+ StorageLive(_46); // scope 0 at $DIR/early_otherwise_branch_68867.rs:25:28: 25:33
+ _46 = move (_4.1: &ViewportPercentageLength); // scope 0 at $DIR/early_otherwise_branch_68867.rs:25:28: 25:33
+- _28 = (((*_46) as Vmax).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:25:28: 25:33
++ _31 = (((*_46) as Vmax).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:25:28: 25:33
+ StorageDead(_46); // scope 4 at $DIR/early_otherwise_branch_68867.rs:25:44: 25:55
- StorageLive(_29); // scope 4 at $DIR/early_otherwise_branch_68867.rs:25:44: 25:55
- StorageLive(_30); // scope 4 at $DIR/early_otherwise_branch_68867.rs:25:44: 25:47
- _30 = _27; // scope 4 at $DIR/early_otherwise_branch_68867.rs:25:44: 25:47
- StorageDead(_29); // scope 4 at $DIR/early_otherwise_branch_68867.rs:25:55: 25:56
- StorageDead(_28); // scope 0 at $DIR/early_otherwise_branch_68867.rs:25:55: 25:56
- StorageDead(_27); // scope 0 at $DIR/early_otherwise_branch_68867.rs:25:55: 25:56
-- goto -> bb10; // scope 0 at $DIR/early_otherwise_branch_68867.rs:25:55: 25:56
-- }
--
-- bb10: {
++ nop; // scope 4 at $DIR/early_otherwise_branch_68867.rs:25:44: 25:55
++ nop; // scope 4 at $DIR/early_otherwise_branch_68867.rs:25:44: 25:47
++ nop; // scope 4 at $DIR/early_otherwise_branch_68867.rs:25:44: 25:47
++ nop; // scope 4 at $DIR/early_otherwise_branch_68867.rs:25:50: 25:55
++ nop; // scope 4 at $DIR/early_otherwise_branch_68867.rs:25:50: 25:55
++ ((((_0 as Ok).0: ViewportPercentageLength) as Vmax).0: f32) = Add(move _30, move _31); // scope 4 at $DIR/early_otherwise_branch_68867.rs:25:44: 25:55
++ nop; // scope 4 at $DIR/early_otherwise_branch_68867.rs:25:54: 25:55
++ nop; // scope 4 at $DIR/early_otherwise_branch_68867.rs:25:54: 25:55
++ Deinit(((_0 as Ok).0: ViewportPercentageLength)); // scope 4 at $DIR/early_otherwise_branch_68867.rs:25:39: 25:56
++ nop; // scope 4 at $DIR/early_otherwise_branch_68867.rs:25:39: 25:56
++ discriminant(((_0 as Ok).0: ViewportPercentageLength)) = 3; // scope 4 at $DIR/early_otherwise_branch_68867.rs:25:39: 25:56
++ nop; // scope 4 at $DIR/early_otherwise_branch_68867.rs:25:55: 25:56
++ nop; // scope 0 at $DIR/early_otherwise_branch_68867.rs:25:55: 25:56
++ nop; // scope 0 at $DIR/early_otherwise_branch_68867.rs:25:55: 25:56
+ goto -> bb10; // scope 0 at $DIR/early_otherwise_branch_68867.rs:25:55: 25:56
+ }
+
+ bb10: {
Deinit(_0); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:5: 27:7
- ((_0 as Ok).0: ViewportPercentageLength) = move _3; // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:5: 27:7
+ nop; // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:5: 27:7
+ nop; // scope 0 at $DIR/early_otherwise_branch_68867.rs:28:1: 28:2
return; // scope 0 at $DIR/early_otherwise_branch_68867.rs:28:2: 28:2
}
-
-- bb11: {
-- unreachable; // scope 0 at $DIR/early_otherwise_branch_68867.rs:28:2: 28:2
-+ bb7: {
-+ StorageDead(_35); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
-+ switchInt(_11) -> [0_isize: bb2, 1_isize: bb3, 2_isize: bb4, 3_isize: bb5, otherwise: bb1]; // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
- }
}
let mut _31: f32; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:25:50: 25:55
let mut _32: !; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:26:14: 26:28
let mut _33: (); // in scope 0 at $DIR/early_otherwise_branch_68867.rs:26:25: 26:27
-+ let mut _34: isize; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
-+ let mut _35: bool; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
+ let mut _34: &ViewportPercentageLength; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+ let mut _35: &ViewportPercentageLength; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+ let mut _36: &ViewportPercentageLength; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+ let mut _37: &ViewportPercentageLength; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+ let mut _38: &ViewportPercentageLength; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+ let mut _39: &ViewportPercentageLength; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+ let mut _40: &ViewportPercentageLength; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+ let mut _41: &ViewportPercentageLength; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+ let mut _42: &ViewportPercentageLength; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+ let mut _43: &ViewportPercentageLength; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+ let mut _44: &ViewportPercentageLength; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+ let mut _45: &ViewportPercentageLength; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+ let mut _46: &ViewportPercentageLength; // in scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
scope 1 {
debug one => _12; // in scope 1 at $DIR/early_otherwise_branch_68867.rs:22:14: 22:17
debug other => _13; // in scope 1 at $DIR/early_otherwise_branch_68867.rs:22:24: 22:29
(_4.1: &ViewportPercentageLength) = move _6; // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
StorageDead(_6); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:23: 21:24
StorageDead(_5); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:23: 21:24
- _11 = discriminant((*(_4.0: &ViewportPercentageLength))); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
-- switchInt(move _11) -> [0_isize: bb1, 1_isize: bb3, 2_isize: bb4, 3_isize: bb5, otherwise: bb11]; // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
-+ StorageLive(_34); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
-+ _34 = discriminant((*(_4.1: &ViewportPercentageLength))); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
-+ StorageLive(_35); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
-+ _35 = Ne(_11, move _34); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
-+ StorageDead(_34); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
-+ switchInt(move _35) -> [false: bb7, otherwise: bb1]; // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
+ StorageLive(_34); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+ _34 = move (_4.0: &ViewportPercentageLength); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+ _11 = discriminant((*_34)); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+ StorageDead(_34); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
+ switchInt(move _11) -> [0_isize: bb1, 1_isize: bb3, 2_isize: bb4, 3_isize: bb5, otherwise: bb2]; // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
}
bb1: {
-- _7 = discriminant((*(_4.1: &ViewportPercentageLength))); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
-- switchInt(move _7) -> [0_isize: bb6, otherwise: bb2]; // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
-- }
--
-- bb2: {
-+ StorageDead(_35); // scope 0 at $DIR/early_otherwise_branch_68867.rs:26:25: 26:27
+ StorageLive(_35); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+ _35 = move (_4.1: &ViewportPercentageLength); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+ _7 = discriminant((*_35)); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+ StorageDead(_35); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
+ switchInt(move _7) -> [0_isize: bb6, otherwise: bb2]; // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
+ }
+
+ bb2: {
StorageLive(_33); // scope 0 at $DIR/early_otherwise_branch_68867.rs:26:25: 26:27
-- nop; // scope 0 at $DIR/early_otherwise_branch_68867.rs:26:25: 26:27
+ nop; // scope 0 at $DIR/early_otherwise_branch_68867.rs:26:25: 26:27
Deinit(_0); // scope 0 at $DIR/early_otherwise_branch_68867.rs:26:21: 26:28
-- nop; // scope 0 at $DIR/early_otherwise_branch_68867.rs:26:21: 26:28
+ nop; // scope 0 at $DIR/early_otherwise_branch_68867.rs:26:21: 26:28
discriminant(_0) = 1; // scope 0 at $DIR/early_otherwise_branch_68867.rs:26:21: 26:28
StorageDead(_33); // scope 0 at $DIR/early_otherwise_branch_68867.rs:26:27: 26:28
StorageDead(_3); // scope 0 at $DIR/early_otherwise_branch_68867.rs:27:6: 27:7
return; // scope 0 at $DIR/early_otherwise_branch_68867.rs:28:2: 28:2
}
-- bb3: {
-- _8 = discriminant((*(_4.1: &ViewportPercentageLength))); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
-- switchInt(move _8) -> [1_isize: bb7, otherwise: bb2]; // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
-- }
--
-- bb4: {
-- _9 = discriminant((*(_4.1: &ViewportPercentageLength))); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
-- switchInt(move _9) -> [2_isize: bb8, otherwise: bb2]; // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
-- }
--
-- bb5: {
-- _10 = discriminant((*(_4.1: &ViewportPercentageLength))); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
-- switchInt(move _10) -> [3_isize: bb9, otherwise: bb2]; // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
-- }
--
-- bb6: {
-+ bb2: {
+ bb3: {
+ StorageLive(_36); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+ _36 = move (_4.1: &ViewportPercentageLength); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+ _8 = discriminant((*_36)); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+ StorageDead(_36); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
+ switchInt(move _8) -> [1_isize: bb7, otherwise: bb2]; // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
+ }
+
+ bb4: {
+ StorageLive(_37); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+ _37 = move (_4.1: &ViewportPercentageLength); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+ _9 = discriminant((*_37)); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+ StorageDead(_37); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
+ switchInt(move _9) -> [2_isize: bb8, otherwise: bb2]; // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
+ }
+
+ bb5: {
+ StorageLive(_38); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+ _38 = move (_4.1: &ViewportPercentageLength); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+ _10 = discriminant((*_38)); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:14: 21:24
+ StorageDead(_38); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
+ switchInt(move _10) -> [3_isize: bb9, otherwise: bb2]; // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
+ }
+
+ bb6: {
StorageLive(_12); // scope 0 at $DIR/early_otherwise_branch_68867.rs:22:14: 22:17
- _12 = (((*(_4.0: &ViewportPercentageLength)) as Vw).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:22:14: 22:17
+ StorageLive(_39); // scope 0 at $DIR/early_otherwise_branch_68867.rs:22:14: 22:17
+ _39 = move (_4.0: &ViewportPercentageLength); // scope 0 at $DIR/early_otherwise_branch_68867.rs:22:14: 22:17
+ _12 = (((*_39) as Vw).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:22:14: 22:17
+ StorageDead(_39); // scope 0 at $DIR/early_otherwise_branch_68867.rs:22:24: 22:29
StorageLive(_13); // scope 0 at $DIR/early_otherwise_branch_68867.rs:22:24: 22:29
- _13 = (((*(_4.1: &ViewportPercentageLength)) as Vw).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:22:24: 22:29
+ StorageLive(_40); // scope 0 at $DIR/early_otherwise_branch_68867.rs:22:24: 22:29
+ _40 = move (_4.1: &ViewportPercentageLength); // scope 0 at $DIR/early_otherwise_branch_68867.rs:22:24: 22:29
+ _13 = (((*_40) as Vw).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:22:24: 22:29
+ StorageDead(_40); // scope 1 at $DIR/early_otherwise_branch_68867.rs:22:38: 22:49
StorageLive(_14); // scope 1 at $DIR/early_otherwise_branch_68867.rs:22:38: 22:49
StorageLive(_15); // scope 1 at $DIR/early_otherwise_branch_68867.rs:22:38: 22:41
_15 = _12; // scope 1 at $DIR/early_otherwise_branch_68867.rs:22:38: 22:41
StorageDead(_14); // scope 1 at $DIR/early_otherwise_branch_68867.rs:22:49: 22:50
StorageDead(_13); // scope 0 at $DIR/early_otherwise_branch_68867.rs:22:49: 22:50
StorageDead(_12); // scope 0 at $DIR/early_otherwise_branch_68867.rs:22:49: 22:50
-- goto -> bb10; // scope 0 at $DIR/early_otherwise_branch_68867.rs:22:49: 22:50
-+ goto -> bb6; // scope 0 at $DIR/early_otherwise_branch_68867.rs:22:49: 22:50
+ goto -> bb10; // scope 0 at $DIR/early_otherwise_branch_68867.rs:22:49: 22:50
}
-- bb7: {
-+ bb3: {
+ bb7: {
StorageLive(_17); // scope 0 at $DIR/early_otherwise_branch_68867.rs:23:14: 23:17
- _17 = (((*(_4.0: &ViewportPercentageLength)) as Vh).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:23:14: 23:17
+ StorageLive(_41); // scope 0 at $DIR/early_otherwise_branch_68867.rs:23:14: 23:17
+ _41 = move (_4.0: &ViewportPercentageLength); // scope 0 at $DIR/early_otherwise_branch_68867.rs:23:14: 23:17
+ _17 = (((*_41) as Vh).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:23:14: 23:17
+ StorageDead(_41); // scope 0 at $DIR/early_otherwise_branch_68867.rs:23:24: 23:29
StorageLive(_18); // scope 0 at $DIR/early_otherwise_branch_68867.rs:23:24: 23:29
- _18 = (((*(_4.1: &ViewportPercentageLength)) as Vh).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:23:24: 23:29
+ StorageLive(_42); // scope 0 at $DIR/early_otherwise_branch_68867.rs:23:24: 23:29
+ _42 = move (_4.1: &ViewportPercentageLength); // scope 0 at $DIR/early_otherwise_branch_68867.rs:23:24: 23:29
+ _18 = (((*_42) as Vh).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:23:24: 23:29
+ StorageDead(_42); // scope 2 at $DIR/early_otherwise_branch_68867.rs:23:38: 23:49
StorageLive(_19); // scope 2 at $DIR/early_otherwise_branch_68867.rs:23:38: 23:49
StorageLive(_20); // scope 2 at $DIR/early_otherwise_branch_68867.rs:23:38: 23:41
_20 = _17; // scope 2 at $DIR/early_otherwise_branch_68867.rs:23:38: 23:41
StorageDead(_19); // scope 2 at $DIR/early_otherwise_branch_68867.rs:23:49: 23:50
StorageDead(_18); // scope 0 at $DIR/early_otherwise_branch_68867.rs:23:49: 23:50
StorageDead(_17); // scope 0 at $DIR/early_otherwise_branch_68867.rs:23:49: 23:50
-- goto -> bb10; // scope 0 at $DIR/early_otherwise_branch_68867.rs:23:49: 23:50
-+ goto -> bb6; // scope 0 at $DIR/early_otherwise_branch_68867.rs:23:49: 23:50
+ goto -> bb10; // scope 0 at $DIR/early_otherwise_branch_68867.rs:23:49: 23:50
}
-- bb8: {
-+ bb4: {
+ bb8: {
StorageLive(_22); // scope 0 at $DIR/early_otherwise_branch_68867.rs:24:16: 24:19
- _22 = (((*(_4.0: &ViewportPercentageLength)) as Vmin).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:24:16: 24:19
+ StorageLive(_43); // scope 0 at $DIR/early_otherwise_branch_68867.rs:24:16: 24:19
+ _43 = move (_4.0: &ViewportPercentageLength); // scope 0 at $DIR/early_otherwise_branch_68867.rs:24:16: 24:19
+ _22 = (((*_43) as Vmin).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:24:16: 24:19
+ StorageDead(_43); // scope 0 at $DIR/early_otherwise_branch_68867.rs:24:28: 24:33
StorageLive(_23); // scope 0 at $DIR/early_otherwise_branch_68867.rs:24:28: 24:33
- _23 = (((*(_4.1: &ViewportPercentageLength)) as Vmin).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:24:28: 24:33
+ StorageLive(_44); // scope 0 at $DIR/early_otherwise_branch_68867.rs:24:28: 24:33
+ _44 = move (_4.1: &ViewportPercentageLength); // scope 0 at $DIR/early_otherwise_branch_68867.rs:24:28: 24:33
+ _23 = (((*_44) as Vmin).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:24:28: 24:33
+ StorageDead(_44); // scope 3 at $DIR/early_otherwise_branch_68867.rs:24:44: 24:55
StorageLive(_24); // scope 3 at $DIR/early_otherwise_branch_68867.rs:24:44: 24:55
StorageLive(_25); // scope 3 at $DIR/early_otherwise_branch_68867.rs:24:44: 24:47
_25 = _22; // scope 3 at $DIR/early_otherwise_branch_68867.rs:24:44: 24:47
StorageDead(_24); // scope 3 at $DIR/early_otherwise_branch_68867.rs:24:55: 24:56
StorageDead(_23); // scope 0 at $DIR/early_otherwise_branch_68867.rs:24:55: 24:56
StorageDead(_22); // scope 0 at $DIR/early_otherwise_branch_68867.rs:24:55: 24:56
-- goto -> bb10; // scope 0 at $DIR/early_otherwise_branch_68867.rs:24:55: 24:56
-+ goto -> bb6; // scope 0 at $DIR/early_otherwise_branch_68867.rs:24:55: 24:56
+ goto -> bb10; // scope 0 at $DIR/early_otherwise_branch_68867.rs:24:55: 24:56
}
-- bb9: {
-+ bb5: {
+ bb9: {
StorageLive(_27); // scope 0 at $DIR/early_otherwise_branch_68867.rs:25:16: 25:19
- _27 = (((*(_4.0: &ViewportPercentageLength)) as Vmax).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:25:16: 25:19
+ StorageLive(_45); // scope 0 at $DIR/early_otherwise_branch_68867.rs:25:16: 25:19
+ _45 = move (_4.0: &ViewportPercentageLength); // scope 0 at $DIR/early_otherwise_branch_68867.rs:25:16: 25:19
+ _27 = (((*_45) as Vmax).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:25:16: 25:19
+ StorageDead(_45); // scope 0 at $DIR/early_otherwise_branch_68867.rs:25:28: 25:33
StorageLive(_28); // scope 0 at $DIR/early_otherwise_branch_68867.rs:25:28: 25:33
- _28 = (((*(_4.1: &ViewportPercentageLength)) as Vmax).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:25:28: 25:33
+ StorageLive(_46); // scope 0 at $DIR/early_otherwise_branch_68867.rs:25:28: 25:33
+ _46 = move (_4.1: &ViewportPercentageLength); // scope 0 at $DIR/early_otherwise_branch_68867.rs:25:28: 25:33
+ _28 = (((*_46) as Vmax).0: f32); // scope 0 at $DIR/early_otherwise_branch_68867.rs:25:28: 25:33
+ StorageDead(_46); // scope 4 at $DIR/early_otherwise_branch_68867.rs:25:44: 25:55
StorageLive(_29); // scope 4 at $DIR/early_otherwise_branch_68867.rs:25:44: 25:55
StorageLive(_30); // scope 4 at $DIR/early_otherwise_branch_68867.rs:25:44: 25:47
_30 = _27; // scope 4 at $DIR/early_otherwise_branch_68867.rs:25:44: 25:47
StorageDead(_29); // scope 4 at $DIR/early_otherwise_branch_68867.rs:25:55: 25:56
StorageDead(_28); // scope 0 at $DIR/early_otherwise_branch_68867.rs:25:55: 25:56
StorageDead(_27); // scope 0 at $DIR/early_otherwise_branch_68867.rs:25:55: 25:56
-- goto -> bb10; // scope 0 at $DIR/early_otherwise_branch_68867.rs:25:55: 25:56
-+ goto -> bb6; // scope 0 at $DIR/early_otherwise_branch_68867.rs:25:55: 25:56
+ goto -> bb10; // scope 0 at $DIR/early_otherwise_branch_68867.rs:25:55: 25:56
}
-- bb10: {
-+ bb6: {
+ bb10: {
Deinit(_0); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:5: 27:7
((_0 as Ok).0: ViewportPercentageLength) = move _3; // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:5: 27:7
discriminant(_0) = 0; // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:5: 27:7
StorageDead(_4); // scope 0 at $DIR/early_otherwise_branch_68867.rs:28:1: 28:2
return; // scope 0 at $DIR/early_otherwise_branch_68867.rs:28:2: 28:2
}
-
-- bb11: {
-- unreachable; // scope 0 at $DIR/early_otherwise_branch_68867.rs:28:2: 28:2
-+ bb7: {
-+ StorageDead(_35); // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
-+ switchInt(_11) -> [0_isize: bb2, 1_isize: bb3, 2_isize: bb4, 3_isize: bb5, otherwise: bb1]; // scope 0 at $DIR/early_otherwise_branch_68867.rs:21:8: 21:24
- }
}
StorageDead(_5); // scope 0 at $DIR/early_otherwise_branch_noopt.rs:8:16: 8:17
StorageDead(_4); // scope 0 at $DIR/early_otherwise_branch_noopt.rs:8:16: 8:17
_8 = discriminant((_3.0: std::option::Option<u32>)); // scope 0 at $DIR/early_otherwise_branch_noopt.rs:8:11: 8:17
- switchInt(move _8) -> [0_isize: bb1, otherwise: bb3]; // scope 0 at $DIR/early_otherwise_branch_noopt.rs:8:5: 8:17
+ switchInt(move _8) -> [0_isize: bb1, 1_isize: bb4, otherwise: bb3]; // scope 0 at $DIR/early_otherwise_branch_noopt.rs:8:5: 8:17
}
bb1: {
_6 = discriminant((_3.1: std::option::Option<u32>)); // scope 0 at $DIR/early_otherwise_branch_noopt.rs:8:11: 8:17
- switchInt(move _6) -> [0_isize: bb2, otherwise: bb6]; // scope 0 at $DIR/early_otherwise_branch_noopt.rs:8:5: 8:17
+ switchInt(move _6) -> [0_isize: bb2, 1_isize: bb7, otherwise: bb3]; // scope 0 at $DIR/early_otherwise_branch_noopt.rs:8:5: 8:17
}
bb2: {
_0 = const 3_u32; // scope 0 at $DIR/early_otherwise_branch_noopt.rs:12:25: 12:26
- goto -> bb7; // scope 0 at $DIR/early_otherwise_branch_noopt.rs:12:25: 12:26
+ goto -> bb8; // scope 0 at $DIR/early_otherwise_branch_noopt.rs:12:25: 12:26
}
bb3: {
- _7 = discriminant((_3.1: std::option::Option<u32>)); // scope 0 at $DIR/early_otherwise_branch_noopt.rs:8:11: 8:17
- switchInt(move _7) -> [0_isize: bb5, otherwise: bb4]; // scope 0 at $DIR/early_otherwise_branch_noopt.rs:8:5: 8:17
+ unreachable; // scope 0 at $DIR/early_otherwise_branch_noopt.rs:8:11: 8:17
}
bb4: {
+ _7 = discriminant((_3.1: std::option::Option<u32>)); // scope 0 at $DIR/early_otherwise_branch_noopt.rs:8:11: 8:17
+ switchInt(move _7) -> [0_isize: bb6, 1_isize: bb5, otherwise: bb3]; // scope 0 at $DIR/early_otherwise_branch_noopt.rs:8:5: 8:17
+ }
+
+ bb5: {
StorageLive(_9); // scope 0 at $DIR/early_otherwise_branch_noopt.rs:9:15: 9:16
_9 = (((_3.0: std::option::Option<u32>) as Some).0: u32); // scope 0 at $DIR/early_otherwise_branch_noopt.rs:9:15: 9:16
StorageLive(_10); // scope 0 at $DIR/early_otherwise_branch_noopt.rs:9:24: 9:25
_0 = const 0_u32; // scope 1 at $DIR/early_otherwise_branch_noopt.rs:9:31: 9:32
StorageDead(_10); // scope 0 at $DIR/early_otherwise_branch_noopt.rs:9:31: 9:32
StorageDead(_9); // scope 0 at $DIR/early_otherwise_branch_noopt.rs:9:31: 9:32
- goto -> bb7; // scope 0 at $DIR/early_otherwise_branch_noopt.rs:9:31: 9:32
+ goto -> bb8; // scope 0 at $DIR/early_otherwise_branch_noopt.rs:9:31: 9:32
}
- bb5: {
+ bb6: {
StorageLive(_11); // scope 0 at $DIR/early_otherwise_branch_noopt.rs:10:15: 10:16
_11 = (((_3.0: std::option::Option<u32>) as Some).0: u32); // scope 0 at $DIR/early_otherwise_branch_noopt.rs:10:15: 10:16
_0 = const 1_u32; // scope 2 at $DIR/early_otherwise_branch_noopt.rs:10:28: 10:29
StorageDead(_11); // scope 0 at $DIR/early_otherwise_branch_noopt.rs:10:28: 10:29
- goto -> bb7; // scope 0 at $DIR/early_otherwise_branch_noopt.rs:10:28: 10:29
+ goto -> bb8; // scope 0 at $DIR/early_otherwise_branch_noopt.rs:10:28: 10:29
}
- bb6: {
+ bb7: {
StorageLive(_12); // scope 0 at $DIR/early_otherwise_branch_noopt.rs:11:21: 11:22
_12 = (((_3.1: std::option::Option<u32>) as Some).0: u32); // scope 0 at $DIR/early_otherwise_branch_noopt.rs:11:21: 11:22
_0 = const 2_u32; // scope 3 at $DIR/early_otherwise_branch_noopt.rs:11:28: 11:29
StorageDead(_12); // scope 0 at $DIR/early_otherwise_branch_noopt.rs:11:28: 11:29
- goto -> bb7; // scope 0 at $DIR/early_otherwise_branch_noopt.rs:11:28: 11:29
+ goto -> bb8; // scope 0 at $DIR/early_otherwise_branch_noopt.rs:11:28: 11:29
}
- bb7: {
+ bb8: {
StorageDead(_3); // scope 0 at $DIR/early_otherwise_branch_noopt.rs:14:1: 14:2
return; // scope 0 at $DIR/early_otherwise_branch_noopt.rs:14:2: 14:2
}
-// compile-flags: -Z mir-opt-level=4 -Zunsound-mir-opts
+// unit-test: EarlyOtherwiseBranch
// must not optimize as it does not follow the pattern of
// left and right hand side being the same variant
bb1: {
_0 = const 0_i32; // scope 0 at $DIR/early_otherwise_branch_soundness.rs:25:14: 25:15
- return; // scope 0 at $DIR/early_otherwise_branch_soundness.rs:25:14: 25:15
+ goto -> bb5; // scope 0 at $DIR/early_otherwise_branch_soundness.rs:25:14: 25:15
}
bb2: {
bb3: {
_0 = const 0_i32; // scope 0 at $DIR/early_otherwise_branch_soundness.rs:23:18: 23:19
- return; // scope 0 at $DIR/early_otherwise_branch_soundness.rs:23:18: 23:19
+ goto -> bb5; // scope 0 at $DIR/early_otherwise_branch_soundness.rs:23:18: 23:19
}
bb4: {
_5 = (((*_2) as Some).0: i32); // scope 0 at $DIR/early_otherwise_branch_soundness.rs:22:18: 22:19
_0 = _5; // scope 1 at $DIR/early_otherwise_branch_soundness.rs:22:24: 22:25
StorageDead(_5); // scope 0 at $DIR/early_otherwise_branch_soundness.rs:22:24: 22:25
- return; // scope 0 at $DIR/early_otherwise_branch_soundness.rs:22:24: 22:25
+ goto -> bb5; // scope 0 at $DIR/early_otherwise_branch_soundness.rs:22:24: 22:25
+ }
+
+ bb5: {
+ return; // scope 0 at $DIR/early_otherwise_branch_soundness.rs:27:2: 27:2
}
}
let mut _0: u32; // return place in scope 0 at $DIR/early_otherwise_branch_soundness.rs:12:26: 12:29
let mut _2: isize; // in scope 0 at $DIR/early_otherwise_branch_soundness.rs:13:20: 13:30
let mut _3: isize; // in scope 0 at $DIR/early_otherwise_branch_soundness.rs:13:12: 13:31
+ let mut _4: &E; // in scope 0 at $DIR/early_otherwise_branch_soundness.rs:12:16: 12:17
bb0: {
_3 = discriminant((*_1)); // scope 0 at $DIR/early_otherwise_branch_soundness.rs:13:12: 13:31
}
bb1: {
- _2 = discriminant((*(((*_1) as Some).0: &E))); // scope 0 at $DIR/early_otherwise_branch_soundness.rs:13:12: 13:31
+ StorageLive(_4); // scope 0 at $DIR/early_otherwise_branch_soundness.rs:13:12: 13:31
+ _4 = move (((*_1) as Some).0: &E); // scope 0 at $DIR/early_otherwise_branch_soundness.rs:13:12: 13:31
+ _2 = discriminant((*_4)); // scope 0 at $DIR/early_otherwise_branch_soundness.rs:13:12: 13:31
+ StorageDead(_4); // scope 0 at $DIR/early_otherwise_branch_soundness.rs:13:12: 13:31
switchInt(move _2) -> [1_isize: bb2, otherwise: bb3]; // scope 0 at $DIR/early_otherwise_branch_soundness.rs:13:12: 13:31
}
bb2: {
_0 = const 1_u32; // scope 0 at $DIR/early_otherwise_branch_soundness.rs:13:38: 13:39
- return; // scope 0 at $DIR/early_otherwise_branch_soundness.rs:13:5: 13:52
+ goto -> bb4; // scope 0 at $DIR/early_otherwise_branch_soundness.rs:13:5: 13:52
}
bb3: {
_0 = const 2_u32; // scope 0 at $DIR/early_otherwise_branch_soundness.rs:13:49: 13:50
- return; // scope 0 at $DIR/early_otherwise_branch_soundness.rs:13:5: 13:52
+ goto -> bb4; // scope 0 at $DIR/early_otherwise_branch_soundness.rs:13:5: 13:52
+ }
+
+ bb4: {
+ return; // scope 0 at $DIR/early_otherwise_branch_soundness.rs:14:2: 14:2
}
}
-// compile-flags: -Z mir-opt-level=4 -Zunsound-mir-opts
+// unit-test: EarlyOtherwiseBranch
// Tests various cases that the `early_otherwise_branch` opt should *not* optimize
-// compile-flags: -O
+// unit-test: SimplifyComparisonIntegral
// EMIT_MIR if_condition_int.opt_u32.SimplifyComparisonIntegral.diff
// EMIT_MIR if_condition_int.opt_negative.SimplifyComparisonIntegral.diff
// EMIT_MIR if_condition_int.opt_char.SimplifyComparisonIntegral.diff
debug t => (*((*_6).1: &T)); // in scope 2 at $DIR/inline-closure-captures.rs:10:17: 10:18
let mut _10: i32; // in scope 2 at $DIR/inline-closure-captures.rs:11:19: 11:20
let mut _11: T; // in scope 2 at $DIR/inline-closure-captures.rs:11:22: 11:23
+ let mut _12: &i32; // in scope 2 at $DIR/inline-closure-captures.rs:11:13: 11:24
+ let mut _13: &T; // in scope 2 at $DIR/inline-closure-captures.rs:11:13: 11:24
}
}
StorageLive(_9); // scope 1 at $DIR/inline-closure-captures.rs:12:5: 12:9
_9 = move (_7.0: i32); // scope 1 at $DIR/inline-closure-captures.rs:12:5: 12:9
StorageLive(_10); // scope 2 at $DIR/inline-closure-captures.rs:11:19: 11:20
- _10 = (*((*_6).0: &i32)); // scope 2 at $DIR/inline-closure-captures.rs:11:19: 11:20
+ StorageLive(_12); // scope 2 at $DIR/inline-closure-captures.rs:11:19: 11:20
+ _12 = move ((*_6).0: &i32); // scope 2 at $DIR/inline-closure-captures.rs:11:19: 11:20
+ _10 = (*_12); // scope 2 at $DIR/inline-closure-captures.rs:11:19: 11:20
+ StorageDead(_12); // scope 2 at $DIR/inline-closure-captures.rs:11:22: 11:23
StorageLive(_11); // scope 2 at $DIR/inline-closure-captures.rs:11:22: 11:23
- _11 = (*((*_6).1: &T)); // scope 2 at $DIR/inline-closure-captures.rs:11:22: 11:23
+ StorageLive(_13); // scope 2 at $DIR/inline-closure-captures.rs:11:22: 11:23
+ _13 = move ((*_6).1: &T); // scope 2 at $DIR/inline-closure-captures.rs:11:22: 11:23
+ _11 = (*_13); // scope 2 at $DIR/inline-closure-captures.rs:11:22: 11:23
+ StorageDead(_13); // scope 2 at $DIR/inline-closure-captures.rs:11:18: 11:24
Deinit(_0); // scope 2 at $DIR/inline-closure-captures.rs:11:18: 11:24
(_0.0: i32) = move _10; // scope 2 at $DIR/inline-closure-captures.rs:11:18: 11:24
(_0.1: T) = move _11; // scope 2 at $DIR/inline-closure-captures.rs:11:18: 11:24
StorageDead(_3); // scope 0 at $DIR/lower_array_len.rs:11:5: 11:6
return; // scope 0 at $DIR/lower_array_len.rs:12:2: 12:2
}
-
- bb6 (cleanup): {
- resume; // scope 0 at $DIR/lower_array_len.rs:6:1: 12:2
- }
}
StorageDead(_3); // scope 0 at $DIR/lower_array_len.rs:24:5: 24:6
return; // scope 0 at $DIR/lower_array_len.rs:25:2: 25:2
}
-
- bb7 (cleanup): {
- resume; // scope 0 at $DIR/lower_array_len.rs:17:1: 25:2
- }
}
StorageDead(_2); // scope 0 at $DIR/lower_array_len.rs:31:13: 31:14
return; // scope 0 at $DIR/lower_array_len.rs:32:2: 32:2
}
-
- bb2 (cleanup): {
- resume; // scope 0 at $DIR/lower_array_len.rs:30:1: 32:2
- }
}
StorageDead(_2); // scope 0 at $DIR/lower_array_len.rs:38:13: 38:14
return; // scope 0 at $DIR/lower_array_len.rs:39:2: 39:2
}
-
- bb2 (cleanup): {
- resume; // scope 0 at $DIR/lower_array_len.rs:37:1: 39:2
- }
}
StorageDead(_3); // scope 0 at $DIR/lower_slice_len.rs:9:5: 9:6
return; // scope 0 at $DIR/lower_slice_len.rs:10:2: 10:2
}
-
- bb6 (cleanup): {
- resume; // scope 0 at $DIR/lower_slice_len.rs:4:1: 10:2
- }
}
-// compile-flags: -Z mir-opt-level=3
+// unit-test: LowerSliceLenCalls
// EMIT_MIR lower_slice_len.bound.LowerSliceLenCalls.diff
pub fn bound(index: usize, slice: &[u8]) -> u8 {
-// compile-flags: -Zmir-opt-level=1
+// unit-test: RenameReturnPlace
// EMIT_MIR nrvo_simple.nrvo.RenameReturnPlace.diff
fn nrvo(init: fn(&mut [u8; 1024])) -> [u8; 1024] {
bb0: {
_2 = discriminant(_1); // scope 0 at $DIR/uninhabited_fallthrough_elimination.rs:22:11: 22:12
- switchInt(move _2) -> [1_isize: bb3, 2_isize: bb2, otherwise: bb1]; // scope 0 at $DIR/uninhabited_fallthrough_elimination.rs:22:5: 22:12
-+ switchInt(move _2) -> [1_isize: bb3, 2_isize: bb2, otherwise: bb6]; // scope 0 at $DIR/uninhabited_fallthrough_elimination.rs:22:5: 22:12
++ switchInt(move _2) -> [1_isize: bb3, 2_isize: bb2, otherwise: bb5]; // scope 0 at $DIR/uninhabited_fallthrough_elimination.rs:22:5: 22:12
}
bb1: {
bb4: {
return; // scope 0 at $DIR/uninhabited_fallthrough_elimination.rs:27:2: 27:2
- }
-
- bb5 (cleanup): {
- resume; // scope 0 at $DIR/uninhabited_fallthrough_elimination.rs:21:1: 27:2
+ }
+
-+ bb6: {
++ bb5: {
+ unreachable; // scope 0 at $DIR/uninhabited_fallthrough_elimination.rs:25:14: 25:15
}
}
bb4: {
return; // scope 0 at $DIR/uninhabited_fallthrough_elimination.rs:18:2: 18:2
}
-
- bb5 (cleanup): {
- resume; // scope 0 at $DIR/uninhabited_fallthrough_elimination.rs:12:1: 18:2
- }
}
_0 = const (); // scope 0 at $DIR/unreachable.rs:19:6: 19:6
StorageDead(_1); // scope 0 at $DIR/unreachable.rs:20:1: 20:2
return; // scope 0 at $DIR/unreachable.rs:20:2: 20:2
-- }
--
-- bb7 (cleanup): {
-- resume; // scope 0 at $DIR/unreachable.rs:8:1: 20:2
}
}
StorageDead(_1); // scope 0 at $DIR/unreachable_diverging.rs:20:1: 20:2
StorageDead(_2); // scope 0 at $DIR/unreachable_diverging.rs:20:1: 20:2
return; // scope 0 at $DIR/unreachable_diverging.rs:20:2: 20:2
-- }
--
-- bb7 (cleanup): {
-- resume; // scope 0 at $DIR/unreachable_diverging.rs:12:1: 20:2
}
}
// pp-exact:dollar-crate.pp
fn main() {
- ::std::io::_print(::core::fmt::Arguments::new_v1(&["rust\n"], &[]));
+ { ::std::io::_print(::core::fmt::Arguments::new_v1(&["rust\n"], &[])); };
}
// pretty-mode:hir
// pp-exact:hir-pretty-loop.pp
-pub fn foo() { loop { break; } }
+fn foo() { loop { break; } }
// #4264 fixed-length vector types
-pub fn foo(_: [i32; (3 as usize)]) ({ } as ())
+fn foo(_: [i32; (3 as usize)]) ({ } as ())
-pub fn bar() ({
+fn bar() ({
const FOO: usize = ((5 as usize) - (4 as usize) as usize);
let _: [(); (FOO as usize)] = ([(() as ())] as [(); 1]);
(res as String)
} as String);
} as ())
-pub type Foo = [i32; (3 as usize)];
-pub struct Bar {
- pub x: [i32; (3 as usize)],
+type Foo = [i32; (3 as usize)];
+struct Bar {
+ x: [i32; (3 as usize)],
}
-pub struct TupleBar([i32; (4 as usize)]);
-pub enum Baz { BazVariant([i32; (5 as usize)]), }
-pub fn id<T>(x: T) -> T ({ (x as T) } as T)
-pub fn use_id() ({
+struct TupleBar([i32; (4 as usize)]);
+enum Baz { BazVariant([i32; (5 as usize)]), }
+fn id<T>(x: T) -> T ({ (x as T) } as T)
+fn use_id() ({
let _ =
((id::<[i32; (3 as usize)]> as
fn([i32; 3]) -> [i32; 3] {id::<[i32; 3]>})(([(1 as i32),
--- /dev/null
+// pp-exact
+#![feature(yeet_expr)]
+
+fn yeet_no_expr() -> Option<String> { do yeet }
+
+fn yeet_no_expr_with_semicolon() -> Option<String> { do yeet; }
+
+fn yeet_with_expr() -> Result<String, i32> { do yeet 1 + 2 }
+
+fn yeet_with_expr_with_semicolon() -> Result<String, i32> { do yeet 1 + 2; }
+
+fn main() {}
116| 1|
117| 1| let
118| 1| _unused_closure
- 119| 1| =
- 120| 1| |
+ 119| | =
+ 120| | |
121| | mut countdown
122| | |
123| 0| {
169| | ;
170| |
171| 1| let short_used_not_covered_closure_line_break_no_block_embedded_branch =
- 172| 1| | _unused_arg: u8 |
+ 172| | | _unused_arg: u8 |
173| 0| println!(
174| 0| "not called: {}",
175| 0| if is_true { "check" } else { "me" }
187| | ;
188| |
189| 1| let short_used_covered_closure_line_break_no_block_embedded_branch =
- 190| | | _unused_arg: u8 |
+ 190| 1| | _unused_arg: u8 |
191| 1| println!(
192| 1| "not called: {}",
193| 1| if is_true { "check" } else { "me" }
let mut contents = Vec::new();
File::open(path).unwrap().read_to_end(&mut contents).unwrap();
+ // This file is produced during linking in a temporary directory.
+ let arg = if arg.ends_with("/symbols.o") || arg.ends_with("\\symbols.o") {
+ "symbols.o"
+ } else {
+ &*arg
+ };
out.push_str(&format!("{}: {}\n", arg, hash(&contents)));
}
# Check that a Rust dylib does not export generics if -Zshare-generics=no
[ "$$($(NM) $(TMPDIR)/$(RDYLIB_NAME) | grep -v __imp_ | grep -c public_generic_function_from_rlib)" -eq "0" ]
+# FIXME(nbdd0121): This is broken in MinGW, see https://github.com/rust-lang/rust/pull/95604#issuecomment-1101564032
+ifndef IS_WINDOWS
# Check that an executable does not export any dynamic symbols
[ "$$($(NM) $(TMPDIR)/$(EXE_NAME) | grep -v __imp_ | grep -c public_c_function_from_rlib)" -eq "0" ]
[ "$$($(NM) $(TMPDIR)/$(EXE_NAME) | grep -v __imp_ | grep -c public_rust_function_from_exe)" -eq "0" ]
+endif
# Check the combined case, where we generate a cdylib and an rlib in the same
[ "$$($(NM) $(TMPDIR)/$(RDYLIB_NAME) | grep -v __imp_ | grep -c public_rust_function_from_rlib)" -eq "1" ]
[ "$$($(NM) $(TMPDIR)/$(RDYLIB_NAME) | grep -v __imp_ | grep -c public_generic_function_from_rlib)" -eq "1" ]
+ifndef IS_WINDOWS
# Check that an executable does not export any dynamic symbols
[ "$$($(NM) $(TMPDIR)/$(EXE_NAME) | grep -v __imp_ | grep -c public_c_function_from_rlib)" -eq "0" ]
[ "$$($(NM) $(TMPDIR)/$(EXE_NAME) | grep -v __imp_ | grep -c public_rust_function_from_exe)" -eq "0" ]
+endif
_0 = move (_1.0: i32); // scope 0 at main.rs:5:5: 5:10
return; // scope 0 at main.rs:6:2: 6:2
}
-
- bb2 (cleanup): {
- resume; // scope 0 at main.rs:4:1: 6:2
- }
}
fn main() -> () {
[ -e $(INVOCATION_ONLY)/x/index.html ]
[ -e $(INVOCATION_ONLY)/theme-xxx.css ] # generated from z.css
! [ -e $(INVOCATION_ONLY)/storage-xxx.js ]
- ! [ -e $(INVOCATION_ONLY)/SourceSerif4-It.ttf.woff ]
+ ! [ -e $(INVOCATION_ONLY)/SourceSerif4-It.ttf.woff2 ]
# FIXME: this probably shouldn't have a suffix
[ -e $(INVOCATION_ONLY)/y-xxx.css ]
toolchain-only:
$(RUSTDOC) -Z unstable-options --emit=toolchain-shared-resources --output $(TOOLCHAIN_ONLY) --resource-suffix=-xxx --extend-css z.css x.rs
[ -e $(TOOLCHAIN_ONLY)/storage-xxx.js ]
- ! [ -e $(TOOLCHAIN_ONLY)/SourceSerif4-It.ttf.woff ]
+ ! [ -e $(TOOLCHAIN_ONLY)/SourceSerif4-It.ttf.woff2 ]
! [ -e $(TOOLCHAIN_ONLY)/search-index-xxx.js ]
! [ -e $(TOOLCHAIN_ONLY)/x/index.html ]
! [ -e $(TOOLCHAIN_ONLY)/theme.css ]
all-shared:
$(RUSTDOC) -Z unstable-options --emit=toolchain-shared-resources,unversioned-shared-resources --output $(ALL_SHARED) --resource-suffix=-xxx --extend-css z.css x.rs
[ -e $(ALL_SHARED)/storage-xxx.js ]
- [ -e $(ALL_SHARED)/SourceSerif4-It.ttf.woff ]
+ [ -e $(ALL_SHARED)/SourceSerif4-It.ttf.woff2 ]
! [ -e $(ALL_SHARED)/search-index-xxx.js ]
! [ -e $(ALL_SHARED)/settings.html ]
! [ -e $(ALL_SHARED)/x ]
--- /dev/null
+-include ../../run-make-fulldeps/tools.mk
+
+# only-linux
+# ignore-cross-compile
+
+all: main.rs
+ $(RUSTC) --crate-type lib lib.rs
+ $(RUSTC) --crate-type cdylib -Clink-args="-Tlinker.ld" main.rs
+ # Ensure `#[used]` and `KEEP`-ed section is there
+ objdump -s -j".static" $(TMPDIR)/libmain.so
+ # Ensure `#[no_mangle]` symbol is there
+ nm $(TMPDIR)/libmain.so | $(CGREP) bar
--- /dev/null
+mod foo {
+ #[link_section = ".rodata.STATIC"]
+ #[used]
+ static STATIC: [u32; 10] = [1; 10];
+}
+
+mod bar {
+ #[no_mangle]
+ extern "C" fn bar() -> i32 {
+ 0
+ }
+}
--- /dev/null
+SECTIONS
+{
+ .static : ALIGN(4)
+ {
+ KEEP(*(.rodata.STATIC));
+ }
+}
--- /dev/null
+extern crate lib;
--- /dev/null
+# only-windows
+# needs-rust-lld
+
+-include ../../run-make-fulldeps/tools.mk
+
+# Ensure that LLD can link
+all:
+ $(RUSTC) -C linker=rust-lld foo.rs
--- /dev/null
+#![crate_type = "cdylib"]
+
+#[no_mangle]
+extern "C" fn foo() {}
// First, we check that the search results are hidden when the Escape key is pressed.
write: (".search-input", "test")
wait-for: "#search h1" // The search element is empty before the first search
-assert-attribute: ("#search", {"class": "content"})
+// Check that the currently displayed element is search.
+wait-for: "#alternative-display #search"
assert-attribute: ("#main-content", {"class": "content hidden"})
assert-document-property: ({"URL": "index.html?search=test"}, ENDS_WITH)
press-key: "Escape"
-assert-attribute: ("#search", {"class": "content hidden"})
+// Checks that search is no longer in the displayed content.
+wait-for: "#not-displayed #search"
+assert-false: "#alternative-display #search"
assert-attribute: ("#main-content", {"class": "content"})
assert-document-property: ({"URL": "index.html"}, [ENDS_WITH])
// Check that focusing the search input brings back the search results
focus: ".search-input"
-assert-attribute: ("#search", {"class": "content"})
+wait-for: "#alternative-display #search"
assert-attribute: ("#main-content", {"class": "content hidden"})
assert-document-property: ({"URL": "index.html?search=test"}, ENDS_WITH)
assert-document-property: ({"URL": "index.html?search=test"}, [ENDS_WITH])
assert-attribute: ("#help", {"class": ""})
press-key: "Escape"
+wait-for: "#alternative-display #search"
assert-attribute: ("#help", {"class": "hidden"})
-assert-attribute: ("#search", {"class": "content"})
assert-attribute: ("#main-content", {"class": "content hidden"})
assert-document-property: ({"URL": "index.html?search=test"}, [ENDS_WITH])
assert: "#results a:focus"
press-key: "Escape"
assert-attribute: ("#help", {"class": "hidden"})
-assert-attribute: ("#search", {"class": "content hidden"})
+wait-for: "#not-displayed #search"
+assert-false: "#alternative-display #search"
assert-attribute: ("#main-content", {"class": "content"})
--- /dev/null
+// This test ensures that the settings menu display is working as expected.
+goto: file://|DOC_PATH|/test_docs/index.html
+// First, we check that the settings page doesn't exist.
+assert-false: "#settings"
+// We now click on the settings button.
+click: "#settings-menu"
+wait-for: "#settings"
+assert: "#main-content.hidden"
+assert-css: ("#settings", {"display": "block"})
+// Let's close it by clicking on the same button.
+click: "#settings-menu"
+assert-false: "#alternative-display #settings"
+assert: "#not-displayed #settings"
+assert: "#main-content:not(.hidden)"
+
+// Let's open and then close it again with the "close settings" button.
+click: "#settings-menu"
+wait-for: "#alternative-display #settings"
+assert: "#main-content.hidden"
+click: "#back"
+wait-for: "#not-displayed #settings"
+assert: "#main-content:not(.hidden)"
+
+// Let's check that pressing "ESCAPE" is closing it.
+click: "#settings-menu"
+wait-for: "#alternative-display #settings"
+press-key: "Escape"
+wait-for: "#not-displayed #settings"
+assert: "#main-content:not(.hidden)"
+
+// Let's click on it when the search results are displayed.
+focus: ".search-input"
+write: "test"
+wait-for: "#alternative-display #search"
+click: "#settings-menu"
+wait-for: "#alternative-display #settings"
+assert: "#not-displayed #search"
+assert: "#main-content.hidden"
+
+// Now let's check the content of the settings menu.
+local-storage: {"rustdoc-theme": "dark", "rustdoc-use-system-theme": "false"}
+reload:
+click: "#settings-menu"
+wait-for: "#settings"
+
+// We check that the "Use system theme" is disabled.
+assert-property: ("#use-system-theme", {"checked": "false"})
+assert: "//*[@class='setting-line']/*[text()='Use system theme']"
+// Meaning that only the "theme" menu is showing up.
+assert: ".setting-line:not(.hidden) #theme"
+assert: ".setting-line.hidden #preferred-dark-theme"
+assert: ".setting-line.hidden #preferred-light-theme"
+
+// We check that the correct theme is selected.
+assert-property: ("#theme .choices #theme-dark", {"checked": "true"})
+
+// We now switch the display.
+click: "#use-system-theme"
+// Wait for the hidden element to show up.
+wait-for: ".setting-line:not(.hidden) #preferred-dark-theme"
+assert: ".setting-line:not(.hidden) #preferred-light-theme"
+// Check that the theme picking is hidden.
+assert: ".setting-line.hidden #theme"
+
+// We check their text as well.
+assert-text: ("#preferred-dark-theme .setting-name", "Preferred dark theme")
+assert-text: ("#preferred-light-theme .setting-name", "Preferred light theme")
wait-for-css: ("body", { "background-color": "rgb(255, 255, 255)" })
goto: file://|DOC_PATH|/settings.html
+wait-for: "#settings"
click: "#theme-light"
wait-for-css: ("body", { "background-color": "rgb(255, 255, 255)" })
assert-local-storage: { "rustdoc-theme": "light" }
// Ensures that the theme is working when going back in history.
goto: file://|DOC_PATH|/test_docs/index.html
// Set the theme to dark.
-local-storage: {"rustdoc-theme": "dark", "rustdoc-preferred-dark-theme": "dark", "rustdoc-use-system-theme": "false"}
+local-storage: {
+ "rustdoc-theme": "dark",
+ "rustdoc-preferred-dark-theme": "dark",
+ "rustdoc-use-system-theme": "false",
+}
// We reload the page so the local storage settings are being used.
reload:
assert-css: ("body", { "background-color": "rgb(53, 53, 53)" })
assert-local-storage: { "rustdoc-theme": "dark" }
// Now we go to the settings page.
-click: "#settings-menu"
-wait-for: ".settings"
+goto: file://|DOC_PATH|/settings.html
+wait-for: "#settings"
// We change the theme to "light".
click: "#theme-light"
wait-for-css: ("body", { "background-color": "rgb(255, 255, 255)" })
// We go back in history.
history-go-back:
// Confirm that we're not on the settings page.
-assert-false: ".settings"
+assert-false: "#settings"
// Check that the current theme is still "light".
assert-css: ("body", { "background-color": "rgb(255, 255, 255)" })
assert-local-storage: { "rustdoc-theme": "light" }
"a,:",
" a<> :",
"mod : :",
+ "a!a",
+ "a!!",
];
const PARSED = [
userQuery: "mod : :",
error: 'Unexpected `:`',
},
+ {
+ elems: [],
+ foundElems: 0,
+ original: "a!a",
+ returned: [],
+ typeFilter: -1,
+ userQuery: "a!a",
+ error: '`!` can only be at the end of an ident',
+ },
+ {
+ elems: [],
+ foundElems: 0,
+ original: "a!!",
+ returned: [],
+ typeFilter: -1,
+ userQuery: "a!!",
+ error: 'Cannot have more than one `!` in an ident',
+ },
];
--- /dev/null
+const QUERY = [
+ "R<!>",
+ "!",
+ "a!",
+ "a!::b",
+ "a!::b!",
+];
+
+const PARSED = [
+ {
+ elems: [{
+ name: "r",
+ fullPath: ["r"],
+ pathWithoutLast: [],
+ pathLast: "r",
+ generics: [
+ {
+ name: "!",
+ fullPath: ["!"],
+ pathWithoutLast: [],
+ pathLast: "!",
+ generics: [],
+ },
+ ],
+ }],
+ foundElems: 1,
+ original: "R<!>",
+ returned: [],
+ typeFilter: -1,
+ userQuery: "r<!>",
+ error: null,
+ },
+ {
+ elems: [{
+ name: "!",
+ fullPath: ["!"],
+ pathWithoutLast: [],
+ pathLast: "!",
+ generics: [],
+ }],
+ foundElems: 1,
+ original: "!",
+ returned: [],
+ typeFilter: -1,
+ userQuery: "!",
+ error: null,
+ },
+ {
+ elems: [{
+ name: "a!",
+ fullPath: ["a!"],
+ pathWithoutLast: [],
+ pathLast: "a!",
+ generics: [],
+ }],
+ foundElems: 1,
+ original: "a!",
+ returned: [],
+ typeFilter: -1,
+ userQuery: "a!",
+ error: null,
+ },
+ {
+ elems: [{
+ name: "a!::b",
+ fullPath: ["a!", "b"],
+ pathWithoutLast: ["a!"],
+ pathLast: "b",
+ generics: [],
+ }],
+ foundElems: 1,
+ original: "a!::b",
+ returned: [],
+ typeFilter: -1,
+ userQuery: "a!::b",
+ error: null,
+ },
+ {
+ elems: [{
+ name: "a!::b!",
+ fullPath: ["a!", "b!"],
+ pathWithoutLast: ["a!"],
+ pathLast: "b!",
+ generics: [],
+ }],
+ foundElems: 1,
+ original: "a!::b!",
+ returned: [],
+ typeFilter: -1,
+ userQuery: "a!::b!",
+ error: null,
+ },
+];
-const QUERY = ['-> F<P>', '-> P', '->,a', 'aaaaa->a'];
+const QUERY = [
+ "-> F<P>",
+ "-> P",
+ "->,a",
+ "aaaaa->a",
+ "-> !",
+];
const PARSED = [
{
userQuery: "aaaaa->a",
error: null,
},
+ {
+ elems: [],
+ foundElems: 1,
+ original: "-> !",
+ returned: [{
+ name: "!",
+ fullPath: ["!"],
+ pathWithoutLast: [],
+ pathLast: "!",
+ generics: [],
+ }],
+ typeFilter: -1,
+ userQuery: "-> !",
+ error: null,
+ },
];
--- /dev/null
+// `macro_rules` scopes are respected during doc link resolution.
+
+// compile-flags: --document-private-items
+
+#![deny(rustdoc::broken_intra_doc_links)]
+
+mod no_escape {
+ macro_rules! before_but_limited_to_module {
+ () => {};
+ }
+}
+
+/// [before_but_limited_to_module] FIXME: This error should be reported
+// ERROR unresolved link to `before_but_limited_to_module`
+/// [after] FIXME: This error should be reported
+// ERROR unresolved link to `after`
+/// [str] FIXME: This error shouldn not be reported
+//~^ ERROR `str` is both a builtin type and a macro
+fn check() {}
+
+macro_rules! after {
+ () => {};
+}
+
+macro_rules! str {
+ () => {};
+}
--- /dev/null
+error: `str` is both a builtin type and a macro
+ --> $DIR/macro-rules-error.rs:17:6
+ |
+LL | /// [str] FIXME: This error shouldn not be reported
+ | ^^^ ambiguous link
+ |
+note: the lint level is defined here
+ --> $DIR/macro-rules-error.rs:5:9
+ |
+LL | #![deny(rustdoc::broken_intra_doc_links)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+help: to link to the builtin type, prefix with `prim@`
+ |
+LL | /// [prim@str] FIXME: This error shouldn not be reported
+ | +++++
+help: to link to the macro, add an exclamation mark
+ |
+LL | /// [str!] FIXME: This error shouldn not be reported
+ | +
+
+error: aborting due to previous error
+
/// [foo!]
pub fn baz() {}
+
+#[macro_use]
+mod macros {
+ macro_rules! escaping {
+ () => {};
+ }
+}
+
+pub mod inner {
+ /// [foo!]
+ /// [escaping]
+ pub fn baz() {
+ foo!();
+ }
+}
Available passes for running rustdoc:
check_doc_test_visibility - run various visibility-related lints on doctests
strip-hidden - strips all `#[doc(hidden)]` items from the output
- unindent-comments - removes excess indentation on comments in order for markdown to like it
strip-private - strips all private items from a crate which cannot be seen externally, implies strip-priv-imports
strip-priv-imports - strips all private import statements (`use`, `extern crate`) from a crate
propagate-doc-cfg - propagates `#[doc(cfg(...))]` to child items
Default passes for rustdoc:
collect-trait-impls
- unindent-comments
check_doc_test_visibility
strip-hidden (when not --document-hidden-items)
strip-private (when not --document-private-items)
--- /dev/null
+// https://github.com/rust-lang/rust/issues/95325
+//
+// Show methods reachable from Deref of primitive.
+#![no_std]
+
+use core::ops::Deref;
+
+// @has 'deref_slice_core/struct.MyArray.html'
+// @has '-' '//*[@id="deref-methods-%5BT%5D"]' 'Methods from Deref<Target = [T]>'
+// @has '-' '//*[@class="impl-items"]//*[@id="method.len"]' 'pub fn len(&self)'
+
+pub struct MyArray<T> {
+ array: [T; 10],
+}
+
+impl<T> Deref for MyArray<T> {
+ type Target = [T];
+
+ fn deref(&self) -> &Self::Target {
+ &self.array
+ }
+}
--- /dev/null
+// @has issue_95873/index.html "//*[@class='item-left import-item']" "pub use ::std as x;"
+pub use ::std as x;
--- /dev/null
+// should-fail
+
+#![allow(unused)]
+
+trait Foo<T>: Sized {
+ fn bar(i: i32, t: T, s: &Self) -> (T, i32);
+}
+
+impl Foo<usize> for () {
+ fn bar(i: _, t: _, s: _) -> _ {
+ //~^ ERROR the placeholder `_` is not allowed within types on item signatures for functions
+ (1, 2)
+ }
+}
+
+fn main() {}
// ignore-cross-compile
use std::env;
+use std::ffi::OsStr;
use std::fs;
+use std::path::PathBuf;
use std::process;
use std::str;
-use std::path::PathBuf;
fn main() {
// If we're the child, make sure we were invoked correctly
// checking that it ends_with the executable name. This
// is needed because of Windows, which has a different behavior.
// See #15149 for more info.
- return assert!(args[0].ends_with(&format!("mytest{}",
- env::consts::EXE_SUFFIX)));
+ let my_path = env::current_exe().unwrap();
+ return assert_eq!(my_path.file_stem(), Some(OsStr::new("mytest")));
}
test();
fn test() {
// If we're the parent, copy our own binary to a new directory.
let my_path = env::current_exe().unwrap();
- let my_dir = my_path.parent().unwrap();
+ let my_dir = my_path.parent().unwrap();
let child_dir = PathBuf::from(env::var_os("RUST_TEST_TMPDIR").unwrap());
let child_dir = child_dir.join("issue-15140-child");
fs::create_dir_all(&child_dir).unwrap();
- let child_path = child_dir.join(&format!("mytest{}",
- env::consts::EXE_SUFFIX));
+ let child_path = child_dir.join(&format!("mytest{}", env::consts::EXE_SUFFIX));
fs::copy(&my_path, &child_path).unwrap();
// Append the new directory to our own PATH.
env::join_paths(paths).unwrap()
};
- let child_output = process::Command::new("mytest").env("PATH", &path)
- .arg("child")
- .output().unwrap();
+ let child_output =
+ process::Command::new("mytest").env("PATH", &path).arg("child").output().unwrap();
- assert!(child_output.status.success(),
- "child assertion failed\n child stdout:\n {}\n child stderr:\n {}",
- str::from_utf8(&child_output.stdout).unwrap(),
- str::from_utf8(&child_output.stderr).unwrap());
+ assert!(
+ child_output.status.success(),
+ "child assertion failed\n child stdout:\n {}\n child stderr:\n {}",
+ str::from_utf8(&child_output.stdout).unwrap(),
+ str::from_utf8(&child_output.stderr).unwrap()
+ );
}
+++ /dev/null
-// check-fail
-// Tests error conditions for specifying diagnostics using #[derive(SessionDiagnostic)]
-
-// The proc_macro2 crate handles spans differently when on beta/stable release rather than nightly,
-// changing the output of this test. Since SessionDiagnostic is strictly internal to the compiler
-// the test is just ignored on stable and beta:
-// ignore-beta
-// ignore-stable
-
-#![feature(rustc_private)]
-#![crate_type = "lib"]
-
-extern crate rustc_span;
-use rustc_span::symbol::Ident;
-use rustc_span::Span;
-
-extern crate rustc_macros;
-use rustc_macros::SessionDiagnostic;
-
-extern crate rustc_middle;
-use rustc_middle::ty::Ty;
-
-extern crate rustc_errors;
-use rustc_errors::Applicability;
-
-extern crate rustc_session;
-
-#[derive(SessionDiagnostic)]
-#[error(code = "E0123", slug = "hello-world")]
-struct Hello {}
-
-#[derive(SessionDiagnostic)]
-#[warning(code = "E0123", slug = "hello-world")]
-struct HelloWarn {}
-
-#[derive(SessionDiagnostic)]
-#[error(code = "E0123", slug = "foo")]
-//~^ ERROR `#[derive(SessionDiagnostic)]` can only be used on structs
-enum SessionDiagnosticOnEnum {
- Foo,
- Bar,
-}
-
-#[derive(SessionDiagnostic)]
-#[error(code = "E0123", slug = "foo")]
-#[error = "E0123"]
-//~^ ERROR `#[error = ...]` is not a valid `SessionDiagnostic` struct attribute
-struct WrongStructAttrStyle {}
-
-#[derive(SessionDiagnostic)]
-#[nonsense(code = "E0123", slug = "foo")]
-//~^ ERROR `#[nonsense(...)]` is not a valid `SessionDiagnostic` struct attribute
-//~^^ ERROR diagnostic kind not specified
-//~^^^ ERROR cannot find attribute `nonsense` in this scope
-struct InvalidStructAttr {}
-
-#[derive(SessionDiagnostic)]
-#[error("E0123")]
-//~^ ERROR `#[error("...")]` is not a valid `SessionDiagnostic` struct attribute
-//~^^ ERROR `slug` not specified
-struct InvalidLitNestedAttr {}
-
-#[derive(SessionDiagnostic)]
-#[error(nonsense, code = "E0123", slug = "foo")]
-//~^ ERROR `#[error(nonsense)]` is not a valid `SessionDiagnostic` struct attribute
-struct InvalidNestedStructAttr {}
-
-#[derive(SessionDiagnostic)]
-#[error(nonsense("foo"), code = "E0123", slug = "foo")]
-//~^ ERROR `#[error(nonsense(...))]` is not a valid `SessionDiagnostic` struct attribute
-struct InvalidNestedStructAttr1 {}
-
-#[derive(SessionDiagnostic)]
-#[error(nonsense = "...", code = "E0123", slug = "foo")]
-//~^ ERROR `#[error(nonsense = ...)]` is not a valid `SessionDiagnostic` struct attribute
-struct InvalidNestedStructAttr2 {}
-
-#[derive(SessionDiagnostic)]
-#[error(nonsense = 4, code = "E0123", slug = "foo")]
-//~^ ERROR `#[error(nonsense = ...)]` is not a valid `SessionDiagnostic` struct attribute
-struct InvalidNestedStructAttr3 {}
-
-#[derive(SessionDiagnostic)]
-#[error(code = "E0123", slug = "foo")]
-struct WrongPlaceField {
- #[suggestion = "bar"]
- //~^ ERROR `#[suggestion = ...]` is not a valid `SessionDiagnostic` field attribute
- sp: Span,
-}
-
-#[derive(SessionDiagnostic)]
-#[error(code = "E0123", slug = "foo")]
-#[error(code = "E0456", slug = "bar")] //~ ERROR `error` specified multiple times
-struct ErrorSpecifiedTwice {}
-
-#[derive(SessionDiagnostic)]
-#[error(code = "E0123", slug = "foo")]
-#[warning(code = "E0293", slug = "bar")]
-//~^ ERROR `warning` specified when `error` was already specified
-struct WarnSpecifiedAfterError {}
-
-#[derive(SessionDiagnostic)]
-#[error(code = "E0456", code = "E0457", slug = "bar")] //~ ERROR `code` specified multiple times
-struct CodeSpecifiedTwice {}
-
-#[derive(SessionDiagnostic)]
-#[error(code = "E0456", slug = "foo", slug = "bar")] //~ ERROR `slug` specified multiple times
-struct SlugSpecifiedTwice {}
-
-#[derive(SessionDiagnostic)]
-struct KindNotProvided {} //~ ERROR diagnostic kind not specified
-
-#[derive(SessionDiagnostic)]
-#[error(code = "E0456")] //~ ERROR `slug` not specified
-struct SlugNotProvided {}
-
-#[derive(SessionDiagnostic)]
-#[error(slug = "foo")]
-struct CodeNotProvided {}
-
-#[derive(SessionDiagnostic)]
-#[error(code = "E0123", slug = "foo")]
-struct MessageWrongType {
- #[primary_span]
- //~^ ERROR `#[primary_span]` attribute can only be applied to fields of type `Span`
- foo: String,
-}
-
-#[derive(SessionDiagnostic)]
-#[error(code = "E0123", slug = "foo")]
-struct InvalidPathFieldAttr {
- #[nonsense]
- //~^ ERROR `#[nonsense]` is not a valid `SessionDiagnostic` field attribute
- //~^^ ERROR cannot find attribute `nonsense` in this scope
- foo: String,
-}
-
-#[derive(SessionDiagnostic)]
-#[error(code = "E0123", slug = "foo")]
-struct ErrorWithField {
- name: String,
- #[label = "bar"]
- span: Span,
-}
-
-#[derive(SessionDiagnostic)]
-#[error(code = "E0123", slug = "foo")]
-struct ErrorWithMessageAppliedToField {
- #[label = "bar"]
- //~^ ERROR the `#[label = ...]` attribute can only be applied to fields of type `Span`
- name: String,
-}
-
-#[derive(SessionDiagnostic)]
-#[error(code = "E0123", slug = "foo")]
-struct ErrorWithNonexistentField {
- #[suggestion(message = "bar", code = "{name}")]
- //~^ ERROR `name` doesn't refer to a field on this type
- suggestion: (Span, Applicability),
-}
-
-#[derive(SessionDiagnostic)]
-//~^ ERROR invalid format string: expected `'}'`
-#[error(code = "E0123", slug = "foo")]
-struct ErrorMissingClosingBrace {
- #[suggestion(message = "bar", code = "{name")]
- suggestion: (Span, Applicability),
- name: String,
- val: usize,
-}
-
-#[derive(SessionDiagnostic)]
-//~^ ERROR invalid format string: unmatched `}`
-#[error(code = "E0123", slug = "foo")]
-struct ErrorMissingOpeningBrace {
- #[suggestion(message = "bar", code = "name}")]
- suggestion: (Span, Applicability),
- name: String,
- val: usize,
-}
-
-#[derive(SessionDiagnostic)]
-#[error(code = "E0123", slug = "foo")]
-struct LabelOnSpan {
- #[label = "bar"]
- sp: Span,
-}
-
-#[derive(SessionDiagnostic)]
-#[error(code = "E0123", slug = "foo")]
-struct LabelOnNonSpan {
- #[label = "bar"]
- //~^ ERROR the `#[label = ...]` attribute can only be applied to fields of type `Span`
- id: u32,
-}
-
-#[derive(SessionDiagnostic)]
-#[error(code = "E0123", slug = "foo")]
-struct Suggest {
- #[suggestion(message = "bar", code = "This is the suggested code")]
- #[suggestion_short(message = "qux", code = "This is the suggested code")]
- #[suggestion_hidden(message = "foobar", code = "This is the suggested code")]
- #[suggestion_verbose(message = "fooqux", code = "This is the suggested code")]
- suggestion: (Span, Applicability),
-}
-
-#[derive(SessionDiagnostic)]
-#[error(code = "E0123", slug = "foo")]
-struct SuggestWithoutCode {
- #[suggestion(message = "bar")]
- suggestion: (Span, Applicability),
-}
-
-#[derive(SessionDiagnostic)]
-#[error(code = "E0123", slug = "foo")]
-struct SuggestWithBadKey {
- #[suggestion(nonsense = "bar")]
- //~^ ERROR `#[suggestion(nonsense = ...)]` is not a valid `SessionDiagnostic` field attribute
- suggestion: (Span, Applicability),
-}
-
-#[derive(SessionDiagnostic)]
-#[error(code = "E0123", slug = "foo")]
-struct SuggestWithShorthandMsg {
- #[suggestion(msg = "bar")]
- //~^ ERROR `#[suggestion(msg = ...)]` is not a valid `SessionDiagnostic` field attribute
- suggestion: (Span, Applicability),
-}
-
-#[derive(SessionDiagnostic)]
-#[error(code = "E0123", slug = "foo")]
-struct SuggestWithoutMsg {
- #[suggestion(code = "bar")]
- suggestion: (Span, Applicability),
-}
-
-#[derive(SessionDiagnostic)]
-#[error(code = "E0123", slug = "foo")]
-struct SuggestWithTypesSwapped {
- #[suggestion(message = "bar", code = "This is suggested code")]
- suggestion: (Applicability, Span),
-}
-
-#[derive(SessionDiagnostic)]
-#[error(code = "E0123", slug = "foo")]
-struct SuggestWithWrongTypeApplicabilityOnly {
- #[suggestion(message = "bar", code = "This is suggested code")]
- //~^ ERROR wrong field type for suggestion
- suggestion: Applicability,
-}
-
-#[derive(SessionDiagnostic)]
-#[error(code = "E0123", slug = "foo")]
-struct SuggestWithSpanOnly {
- #[suggestion(message = "bar", code = "This is suggested code")]
- suggestion: Span,
-}
-
-#[derive(SessionDiagnostic)]
-#[error(code = "E0123", slug = "foo")]
-struct SuggestWithDuplicateSpanAndApplicability {
- #[suggestion(message = "bar", code = "This is suggested code")]
- //~^ ERROR type of field annotated with `#[suggestion(...)]` contains more than one `Span`
- suggestion: (Span, Span, Applicability),
-}
-
-#[derive(SessionDiagnostic)]
-#[error(code = "E0123", slug = "foo")]
-struct SuggestWithDuplicateApplicabilityAndSpan {
- #[suggestion(message = "bar", code = "This is suggested code")]
- //~^ ERROR type of field annotated with `#[suggestion(...)]` contains more than one
- suggestion: (Applicability, Applicability, Span),
-}
-
-#[derive(SessionDiagnostic)]
-#[error(code = "E0123", slug = "foo")]
-struct WrongKindOfAnnotation {
- #[label("bar")]
- //~^ ERROR `#[label(...)]` is not a valid `SessionDiagnostic` field attribute
- z: Span,
-}
-
-#[derive(SessionDiagnostic)]
-#[error(code = "E0123", slug = "foo")]
-struct OptionsInErrors {
- #[label = "bar"]
- label: Option<Span>,
- #[suggestion(message = "bar")]
- opt_sugg: Option<(Span, Applicability)>,
-}
-
-#[derive(SessionDiagnostic)]
-#[error(code = "E0456", slug = "foo")]
-struct MoveOutOfBorrowError<'tcx> {
- name: Ident,
- ty: Ty<'tcx>,
- #[primary_span]
- #[label = "bar"]
- span: Span,
- #[label = "qux"]
- other_span: Span,
- #[suggestion(message = "bar", code = "{name}.clone()")]
- opt_sugg: Option<(Span, Applicability)>,
-}
-
-#[derive(SessionDiagnostic)]
-#[error(code = "E0123", slug = "foo")]
-struct ErrorWithLifetime<'a> {
- #[label = "bar"]
- span: Span,
- name: &'a str,
-}
-
-#[derive(SessionDiagnostic)]
-#[error(code = "E0123", slug = "foo")]
-struct ErrorWithDefaultLabelAttr<'a> {
- #[label]
- span: Span,
- name: &'a str,
-}
-
-#[derive(SessionDiagnostic)]
-//~^ ERROR no method named `into_diagnostic_arg` found for struct `Hello` in the current scope
-#[error(code = "E0123", slug = "foo")]
-struct ArgFieldWithoutSkip {
- #[primary_span]
- span: Span,
- other: Hello,
-}
-
-#[derive(SessionDiagnostic)]
-#[error(code = "E0123", slug = "foo")]
-struct ArgFieldWithSkip {
- #[primary_span]
- span: Span,
- // `Hello` does not implement `IntoDiagnosticArg` so this would result in an error if
- // not for `#[skip_arg]`.
- #[skip_arg]
- other: Hello,
-}
-
-#[derive(SessionDiagnostic)]
-#[error(code = "E0123", slug = "foo")]
-struct ErrorWithSpannedNote {
- #[note]
- span: Span,
-}
-
-#[derive(SessionDiagnostic)]
-#[error(code = "E0123", slug = "foo")]
-struct ErrorWithSpannedNoteCustom {
- #[note = "bar"]
- span: Span,
-}
-
-#[derive(SessionDiagnostic)]
-#[error(code = "E0123", slug = "foo")]
-#[note]
-struct ErrorWithNote {
- val: String,
-}
-
-#[derive(SessionDiagnostic)]
-#[error(code = "E0123", slug = "foo")]
-#[note = "bar"]
-struct ErrorWithNoteCustom {
- val: String,
-}
-
-#[derive(SessionDiagnostic)]
-#[error(code = "E0123", slug = "foo")]
-struct ErrorWithSpannedHelp {
- #[help]
- span: Span,
-}
-
-#[derive(SessionDiagnostic)]
-#[error(code = "E0123", slug = "foo")]
-struct ErrorWithSpannedHelpCustom {
- #[help = "bar"]
- span: Span,
-}
-
-#[derive(SessionDiagnostic)]
-#[error(code = "E0123", slug = "foo")]
-#[help]
-struct ErrorWithHelp {
- val: String,
-}
-
-#[derive(SessionDiagnostic)]
-#[error(code = "E0123", slug = "foo")]
-#[help = "bar"]
-struct ErrorWithHelpCustom {
- val: String,
-}
-
-#[derive(SessionDiagnostic)]
-#[help]
-//~^ ERROR `#[help]` must come after `#[error(..)]` or `#[warn(..)]`
-#[error(code = "E0123", slug = "foo")]
-struct ErrorWithHelpWrongOrder {
- val: String,
-}
-
-#[derive(SessionDiagnostic)]
-#[help = "bar"]
-//~^ ERROR `#[help = ...]` must come after `#[error(..)]` or `#[warn(..)]`
-#[error(code = "E0123", slug = "foo")]
-struct ErrorWithHelpCustomWrongOrder {
- val: String,
-}
-
-#[derive(SessionDiagnostic)]
-#[note]
-//~^ ERROR `#[note]` must come after `#[error(..)]` or `#[warn(..)]`
-#[error(code = "E0123", slug = "foo")]
-struct ErrorWithNoteWrongOrder {
- val: String,
-}
-
-#[derive(SessionDiagnostic)]
-#[note = "bar"]
-//~^ ERROR `#[note = ...]` must come after `#[error(..)]` or `#[warn(..)]`
-#[error(code = "E0123", slug = "foo")]
-struct ErrorWithNoteCustomWrongOrder {
- val: String,
-}
+++ /dev/null
-error: `#[derive(SessionDiagnostic)]` can only be used on structs
- --> $DIR/session-derive-errors.rs:37:1
- |
-LL | / #[error(code = "E0123", slug = "foo")]
-LL | |
-LL | | enum SessionDiagnosticOnEnum {
-LL | | Foo,
-LL | | Bar,
-LL | | }
- | |_^
-
-error: `#[error = ...]` is not a valid `SessionDiagnostic` struct attribute
- --> $DIR/session-derive-errors.rs:46:1
- |
-LL | #[error = "E0123"]
- | ^^^^^^^^^^^^^^^^^^
-
-error: `#[nonsense(...)]` is not a valid `SessionDiagnostic` struct attribute
- --> $DIR/session-derive-errors.rs:51:1
- |
-LL | #[nonsense(code = "E0123", slug = "foo")]
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: diagnostic kind not specified
- --> $DIR/session-derive-errors.rs:51:1
- |
-LL | / #[nonsense(code = "E0123", slug = "foo")]
-LL | |
-LL | |
-LL | |
-LL | | struct InvalidStructAttr {}
- | |___________________________^
- |
- = help: use the `#[error(...)]` attribute to create an error
-
-error: `#[error("...")]` is not a valid `SessionDiagnostic` struct attribute
- --> $DIR/session-derive-errors.rs:58:9
- |
-LL | #[error("E0123")]
- | ^^^^^^^
-
-error: `slug` not specified
- --> $DIR/session-derive-errors.rs:58:1
- |
-LL | / #[error("E0123")]
-LL | |
-LL | |
-LL | | struct InvalidLitNestedAttr {}
- | |______________________________^
- |
- = help: use the `#[error(slug = "...")]` attribute to set this diagnostic's slug
-
-error: `#[error(nonsense)]` is not a valid `SessionDiagnostic` struct attribute
- --> $DIR/session-derive-errors.rs:64:9
- |
-LL | #[error(nonsense, code = "E0123", slug = "foo")]
- | ^^^^^^^^
-
-error: `#[error(nonsense(...))]` is not a valid `SessionDiagnostic` struct attribute
- --> $DIR/session-derive-errors.rs:69:9
- |
-LL | #[error(nonsense("foo"), code = "E0123", slug = "foo")]
- | ^^^^^^^^^^^^^^^
-
-error: `#[error(nonsense = ...)]` is not a valid `SessionDiagnostic` struct attribute
- --> $DIR/session-derive-errors.rs:74:9
- |
-LL | #[error(nonsense = "...", code = "E0123", slug = "foo")]
- | ^^^^^^^^^^^^^^^^
-
-error: `#[error(nonsense = ...)]` is not a valid `SessionDiagnostic` struct attribute
- --> $DIR/session-derive-errors.rs:79:9
- |
-LL | #[error(nonsense = 4, code = "E0123", slug = "foo")]
- | ^^^^^^^^^^^^
- |
- = help: value must be a string
-
-error: `#[suggestion = ...]` is not a valid `SessionDiagnostic` field attribute
- --> $DIR/session-derive-errors.rs:86:5
- |
-LL | #[suggestion = "bar"]
- | ^^^^^^^^^^^^^^^^^^^^^
-
-error: `error` specified multiple times
- --> $DIR/session-derive-errors.rs:93:1
- |
-LL | #[error(code = "E0456", slug = "bar")]
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- |
-note: previously specified here
- --> $DIR/session-derive-errors.rs:92:1
- |
-LL | #[error(code = "E0123", slug = "foo")]
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: `warning` specified when `error` was already specified
- --> $DIR/session-derive-errors.rs:98:1
- |
-LL | #[warning(code = "E0293", slug = "bar")]
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- |
-note: previously specified here
- --> $DIR/session-derive-errors.rs:97:1
- |
-LL | #[error(code = "E0123", slug = "foo")]
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: `code` specified multiple times
- --> $DIR/session-derive-errors.rs:103:32
- |
-LL | #[error(code = "E0456", code = "E0457", slug = "bar")]
- | ^^^^^^^
- |
-note: previously specified here
- --> $DIR/session-derive-errors.rs:103:16
- |
-LL | #[error(code = "E0456", code = "E0457", slug = "bar")]
- | ^^^^^^^
-
-error: `slug` specified multiple times
- --> $DIR/session-derive-errors.rs:107:46
- |
-LL | #[error(code = "E0456", slug = "foo", slug = "bar")]
- | ^^^^^
- |
-note: previously specified here
- --> $DIR/session-derive-errors.rs:107:32
- |
-LL | #[error(code = "E0456", slug = "foo", slug = "bar")]
- | ^^^^^
-
-error: diagnostic kind not specified
- --> $DIR/session-derive-errors.rs:111:1
- |
-LL | struct KindNotProvided {}
- | ^^^^^^^^^^^^^^^^^^^^^^^^^
- |
- = help: use the `#[error(...)]` attribute to create an error
-
-error: `slug` not specified
- --> $DIR/session-derive-errors.rs:114:1
- |
-LL | / #[error(code = "E0456")]
-LL | | struct SlugNotProvided {}
- | |_________________________^
- |
- = help: use the `#[error(slug = "...")]` attribute to set this diagnostic's slug
-
-error: the `#[primary_span]` attribute can only be applied to fields of type `Span`
- --> $DIR/session-derive-errors.rs:124:5
- |
-LL | #[primary_span]
- | ^^^^^^^^^^^^^^^
-
-error: `#[nonsense]` is not a valid `SessionDiagnostic` field attribute
- --> $DIR/session-derive-errors.rs:132:5
- |
-LL | #[nonsense]
- | ^^^^^^^^^^^
-
-error: the `#[label = ...]` attribute can only be applied to fields of type `Span`
- --> $DIR/session-derive-errors.rs:149:5
- |
-LL | #[label = "bar"]
- | ^^^^^^^^^^^^^^^^
-
-error: `name` doesn't refer to a field on this type
- --> $DIR/session-derive-errors.rs:157:42
- |
-LL | #[suggestion(message = "bar", code = "{name}")]
- | ^^^^^^^^
-
-error: invalid format string: expected `'}'` but string was terminated
- --> $DIR/session-derive-errors.rs:162:16
- |
-LL | #[derive(SessionDiagnostic)]
- | - ^ expected `'}'` in format string
- | |
- | because of this opening brace
- |
- = note: if you intended to print `{`, you can escape it using `{{`
- = note: this error originates in the derive macro `SessionDiagnostic` (in Nightly builds, run with -Z macro-backtrace for more info)
-
-error: invalid format string: unmatched `}` found
- --> $DIR/session-derive-errors.rs:172:15
- |
-LL | #[derive(SessionDiagnostic)]
- | ^ unmatched `}` in format string
- |
- = note: if you intended to print `}`, you can escape it using `}}`
- = note: this error originates in the derive macro `SessionDiagnostic` (in Nightly builds, run with -Z macro-backtrace for more info)
-
-error: the `#[label = ...]` attribute can only be applied to fields of type `Span`
- --> $DIR/session-derive-errors.rs:192:5
- |
-LL | #[label = "bar"]
- | ^^^^^^^^^^^^^^^^
-
-error: `#[suggestion(nonsense = ...)]` is not a valid `SessionDiagnostic` field attribute
- --> $DIR/session-derive-errors.rs:217:18
- |
-LL | #[suggestion(nonsense = "bar")]
- | ^^^^^^^^^^^^^^^^
-
-error: `#[suggestion(msg = ...)]` is not a valid `SessionDiagnostic` field attribute
- --> $DIR/session-derive-errors.rs:225:18
- |
-LL | #[suggestion(msg = "bar")]
- | ^^^^^^^^^^^
-
-error: wrong field type for suggestion
- --> $DIR/session-derive-errors.rs:247:5
- |
-LL | / #[suggestion(message = "bar", code = "This is suggested code")]
-LL | |
-LL | | suggestion: Applicability,
- | |_____________________________^
- |
- = help: `#[suggestion(...)]` should be applied to fields of type `Span` or `(Span, Applicability)`
-
-error: type of field annotated with `#[suggestion(...)]` contains more than one `Span`
- --> $DIR/session-derive-errors.rs:262:5
- |
-LL | / #[suggestion(message = "bar", code = "This is suggested code")]
-LL | |
-LL | | suggestion: (Span, Span, Applicability),
- | |___________________________________________^
-
-error: type of field annotated with `#[suggestion(...)]` contains more than one Applicability
- --> $DIR/session-derive-errors.rs:270:5
- |
-LL | / #[suggestion(message = "bar", code = "This is suggested code")]
-LL | |
-LL | | suggestion: (Applicability, Applicability, Span),
- | |____________________________________________________^
-
-error: `#[label(...)]` is not a valid `SessionDiagnostic` field attribute
- --> $DIR/session-derive-errors.rs:278:5
- |
-LL | #[label("bar")]
- | ^^^^^^^^^^^^^^^
-
-error: `#[help]` must come after `#[error(..)]` or `#[warn(..)]`
- --> $DIR/session-derive-errors.rs:399:1
- |
-LL | #[help]
- | ^^^^^^^
-
-error: `#[help = ...]` must come after `#[error(..)]` or `#[warn(..)]`
- --> $DIR/session-derive-errors.rs:407:1
- |
-LL | #[help = "bar"]
- | ^^^^^^^^^^^^^^^
-
-error: `#[note]` must come after `#[error(..)]` or `#[warn(..)]`
- --> $DIR/session-derive-errors.rs:415:1
- |
-LL | #[note]
- | ^^^^^^^
-
-error: `#[note = ...]` must come after `#[error(..)]` or `#[warn(..)]`
- --> $DIR/session-derive-errors.rs:423:1
- |
-LL | #[note = "bar"]
- | ^^^^^^^^^^^^^^^
-
-error: cannot find attribute `nonsense` in this scope
- --> $DIR/session-derive-errors.rs:51:3
- |
-LL | #[nonsense(code = "E0123", slug = "foo")]
- | ^^^^^^^^
-
-error: cannot find attribute `nonsense` in this scope
- --> $DIR/session-derive-errors.rs:132:7
- |
-LL | #[nonsense]
- | ^^^^^^^^
-
-error[E0599]: no method named `into_diagnostic_arg` found for struct `Hello` in the current scope
- --> $DIR/session-derive-errors.rs:322:10
- |
-LL | struct Hello {}
- | ------------ method `into_diagnostic_arg` not found for this
-...
-LL | #[derive(SessionDiagnostic)]
- | ^^^^^^^^^^^^^^^^^ method not found in `Hello`
- |
- = note: this error originates in the derive macro `SessionDiagnostic` (in Nightly builds, run with -Z macro-backtrace for more info)
-
-error: aborting due to 37 previous errors
-
-For more information about this error, try `rustc --explain E0599`.
--- /dev/null
+// check-fail
+// Tests error conditions for specifying diagnostics using #[derive(SessionDiagnostic)]
+
+// The proc_macro2 crate handles spans differently when on beta/stable release rather than nightly,
+// changing the output of this test. Since SessionDiagnostic is strictly internal to the compiler
+// the test is just ignored on stable and beta:
+// ignore-beta
+// ignore-stable
+
+#![feature(rustc_private)]
+#![crate_type = "lib"]
+
+extern crate rustc_span;
+use rustc_span::symbol::Ident;
+use rustc_span::Span;
+
+extern crate rustc_macros;
+use rustc_macros::{SessionDiagnostic, SessionSubdiagnostic};
+
+extern crate rustc_middle;
+use rustc_middle::ty::Ty;
+
+extern crate rustc_errors;
+use rustc_errors::Applicability;
+
+extern crate rustc_session;
+
+#[derive(SessionDiagnostic)]
+#[error(code = "E0123", slug = "hello-world")]
+struct Hello {}
+
+#[derive(SessionDiagnostic)]
+#[warning(code = "E0123", slug = "hello-world")]
+struct HelloWarn {}
+
+#[derive(SessionDiagnostic)]
+#[error(code = "E0123", slug = "foo")]
+//~^ ERROR `#[derive(SessionDiagnostic)]` can only be used on structs
+enum SessionDiagnosticOnEnum {
+ Foo,
+ Bar,
+}
+
+#[derive(SessionDiagnostic)]
+#[error(code = "E0123", slug = "foo")]
+#[error = "E0123"]
+//~^ ERROR `#[error = ...]` is not a valid attribute
+struct WrongStructAttrStyle {}
+
+#[derive(SessionDiagnostic)]
+#[nonsense(code = "E0123", slug = "foo")]
+//~^ ERROR `#[nonsense(...)]` is not a valid attribute
+//~^^ ERROR diagnostic kind not specified
+//~^^^ ERROR cannot find attribute `nonsense` in this scope
+struct InvalidStructAttr {}
+
+#[derive(SessionDiagnostic)]
+#[error("E0123")]
+//~^ ERROR `#[error("...")]` is not a valid attribute
+//~^^ ERROR `slug` not specified
+struct InvalidLitNestedAttr {}
+
+#[derive(SessionDiagnostic)]
+#[error(nonsense, code = "E0123", slug = "foo")]
+//~^ ERROR `#[error(nonsense)]` is not a valid attribute
+struct InvalidNestedStructAttr {}
+
+#[derive(SessionDiagnostic)]
+#[error(nonsense("foo"), code = "E0123", slug = "foo")]
+//~^ ERROR `#[error(nonsense(...))]` is not a valid attribute
+struct InvalidNestedStructAttr1 {}
+
+#[derive(SessionDiagnostic)]
+#[error(nonsense = "...", code = "E0123", slug = "foo")]
+//~^ ERROR `#[error(nonsense = ...)]` is not a valid attribute
+struct InvalidNestedStructAttr2 {}
+
+#[derive(SessionDiagnostic)]
+#[error(nonsense = 4, code = "E0123", slug = "foo")]
+//~^ ERROR `#[error(nonsense = ...)]` is not a valid attribute
+struct InvalidNestedStructAttr3 {}
+
+#[derive(SessionDiagnostic)]
+#[error(code = "E0123", slug = "foo")]
+struct WrongPlaceField {
+ #[suggestion = "bar"]
+ //~^ ERROR `#[suggestion = ...]` is not a valid attribute
+ sp: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[error(code = "E0123", slug = "foo")]
+#[error(code = "E0456", slug = "bar")]
+//~^ ERROR specified multiple times
+//~^^ ERROR specified multiple times
+//~^^^ ERROR specified multiple times
+struct ErrorSpecifiedTwice {}
+
+#[derive(SessionDiagnostic)]
+#[error(code = "E0123", slug = "foo")]
+#[warning(code = "E0293", slug = "bar")]
+//~^ ERROR specified multiple times
+//~^^ ERROR specified multiple times
+//~^^^ ERROR specified multiple times
+struct WarnSpecifiedAfterError {}
+
+#[derive(SessionDiagnostic)]
+#[error(code = "E0456", code = "E0457", slug = "bar")]
+//~^ ERROR specified multiple times
+struct CodeSpecifiedTwice {}
+
+#[derive(SessionDiagnostic)]
+#[error(code = "E0456", slug = "foo", slug = "bar")]
+//~^ ERROR specified multiple times
+struct SlugSpecifiedTwice {}
+
+#[derive(SessionDiagnostic)]
+struct KindNotProvided {} //~ ERROR diagnostic kind not specified
+
+#[derive(SessionDiagnostic)]
+#[error(code = "E0456")] //~ ERROR `slug` not specified
+struct SlugNotProvided {}
+
+#[derive(SessionDiagnostic)]
+#[error(slug = "foo")]
+struct CodeNotProvided {}
+
+#[derive(SessionDiagnostic)]
+#[error(code = "E0123", slug = "foo")]
+struct MessageWrongType {
+ #[primary_span]
+ //~^ ERROR `#[primary_span]` attribute can only be applied to fields of type `Span`
+ foo: String,
+}
+
+#[derive(SessionDiagnostic)]
+#[error(code = "E0123", slug = "foo")]
+struct InvalidPathFieldAttr {
+ #[nonsense]
+ //~^ ERROR `#[nonsense]` is not a valid attribute
+ //~^^ ERROR cannot find attribute `nonsense` in this scope
+ foo: String,
+}
+
+#[derive(SessionDiagnostic)]
+#[error(code = "E0123", slug = "foo")]
+struct ErrorWithField {
+ name: String,
+ #[label = "bar"]
+ span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[error(code = "E0123", slug = "foo")]
+struct ErrorWithMessageAppliedToField {
+ #[label = "bar"]
+ //~^ ERROR the `#[label = ...]` attribute can only be applied to fields of type `Span`
+ name: String,
+}
+
+#[derive(SessionDiagnostic)]
+#[error(code = "E0123", slug = "foo")]
+struct ErrorWithNonexistentField {
+ #[suggestion(message = "bar", code = "{name}")]
+ //~^ ERROR `name` doesn't refer to a field on this type
+ suggestion: (Span, Applicability),
+}
+
+#[derive(SessionDiagnostic)]
+//~^ ERROR invalid format string: expected `'}'`
+#[error(code = "E0123", slug = "foo")]
+struct ErrorMissingClosingBrace {
+ #[suggestion(message = "bar", code = "{name")]
+ suggestion: (Span, Applicability),
+ name: String,
+ val: usize,
+}
+
+#[derive(SessionDiagnostic)]
+//~^ ERROR invalid format string: unmatched `}`
+#[error(code = "E0123", slug = "foo")]
+struct ErrorMissingOpeningBrace {
+ #[suggestion(message = "bar", code = "name}")]
+ suggestion: (Span, Applicability),
+ name: String,
+ val: usize,
+}
+
+#[derive(SessionDiagnostic)]
+#[error(code = "E0123", slug = "foo")]
+struct LabelOnSpan {
+ #[label = "bar"]
+ sp: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[error(code = "E0123", slug = "foo")]
+struct LabelOnNonSpan {
+ #[label = "bar"]
+ //~^ ERROR the `#[label = ...]` attribute can only be applied to fields of type `Span`
+ id: u32,
+}
+
+#[derive(SessionDiagnostic)]
+#[error(code = "E0123", slug = "foo")]
+struct Suggest {
+ #[suggestion(message = "bar", code = "This is the suggested code")]
+ #[suggestion_short(message = "qux", code = "This is the suggested code")]
+ #[suggestion_hidden(message = "foobar", code = "This is the suggested code")]
+ #[suggestion_verbose(message = "fooqux", code = "This is the suggested code")]
+ suggestion: (Span, Applicability),
+}
+
+#[derive(SessionDiagnostic)]
+#[error(code = "E0123", slug = "foo")]
+struct SuggestWithoutCode {
+ #[suggestion(message = "bar")]
+ suggestion: (Span, Applicability),
+}
+
+#[derive(SessionDiagnostic)]
+#[error(code = "E0123", slug = "foo")]
+struct SuggestWithBadKey {
+ #[suggestion(nonsense = "bar")]
+ //~^ ERROR `#[suggestion(nonsense = ...)]` is not a valid attribute
+ suggestion: (Span, Applicability),
+}
+
+#[derive(SessionDiagnostic)]
+#[error(code = "E0123", slug = "foo")]
+struct SuggestWithShorthandMsg {
+ #[suggestion(msg = "bar")]
+ //~^ ERROR `#[suggestion(msg = ...)]` is not a valid attribute
+ suggestion: (Span, Applicability),
+}
+
+#[derive(SessionDiagnostic)]
+#[error(code = "E0123", slug = "foo")]
+struct SuggestWithoutMsg {
+ #[suggestion(code = "bar")]
+ suggestion: (Span, Applicability),
+}
+
+#[derive(SessionDiagnostic)]
+#[error(code = "E0123", slug = "foo")]
+struct SuggestWithTypesSwapped {
+ #[suggestion(message = "bar", code = "This is suggested code")]
+ suggestion: (Applicability, Span),
+}
+
+#[derive(SessionDiagnostic)]
+#[error(code = "E0123", slug = "foo")]
+struct SuggestWithWrongTypeApplicabilityOnly {
+ #[suggestion(message = "bar", code = "This is suggested code")]
+ //~^ ERROR wrong field type for suggestion
+ suggestion: Applicability,
+}
+
+#[derive(SessionDiagnostic)]
+#[error(code = "E0123", slug = "foo")]
+struct SuggestWithSpanOnly {
+ #[suggestion(message = "bar", code = "This is suggested code")]
+ suggestion: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[error(code = "E0123", slug = "foo")]
+struct SuggestWithDuplicateSpanAndApplicability {
+ #[suggestion(message = "bar", code = "This is suggested code")]
+ //~^ ERROR type of field annotated with `#[suggestion(...)]` contains more than one `Span`
+ suggestion: (Span, Span, Applicability),
+}
+
+#[derive(SessionDiagnostic)]
+#[error(code = "E0123", slug = "foo")]
+struct SuggestWithDuplicateApplicabilityAndSpan {
+ #[suggestion(message = "bar", code = "This is suggested code")]
+ //~^ ERROR type of field annotated with `#[suggestion(...)]` contains more than one
+ suggestion: (Applicability, Applicability, Span),
+}
+
+#[derive(SessionDiagnostic)]
+#[error(code = "E0123", slug = "foo")]
+struct WrongKindOfAnnotation {
+ #[label("bar")]
+ //~^ ERROR `#[label(...)]` is not a valid attribute
+ z: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[error(code = "E0123", slug = "foo")]
+struct OptionsInErrors {
+ #[label = "bar"]
+ label: Option<Span>,
+ #[suggestion(message = "bar")]
+ opt_sugg: Option<(Span, Applicability)>,
+}
+
+#[derive(SessionDiagnostic)]
+#[error(code = "E0456", slug = "foo")]
+struct MoveOutOfBorrowError<'tcx> {
+ name: Ident,
+ ty: Ty<'tcx>,
+ #[primary_span]
+ #[label = "bar"]
+ span: Span,
+ #[label = "qux"]
+ other_span: Span,
+ #[suggestion(message = "bar", code = "{name}.clone()")]
+ opt_sugg: Option<(Span, Applicability)>,
+}
+
+#[derive(SessionDiagnostic)]
+#[error(code = "E0123", slug = "foo")]
+struct ErrorWithLifetime<'a> {
+ #[label = "bar"]
+ span: Span,
+ name: &'a str,
+}
+
+#[derive(SessionDiagnostic)]
+#[error(code = "E0123", slug = "foo")]
+struct ErrorWithDefaultLabelAttr<'a> {
+ #[label]
+ span: Span,
+ name: &'a str,
+}
+
+#[derive(SessionDiagnostic)]
+//~^ ERROR no method named `into_diagnostic_arg` found for struct `Hello` in the current scope
+#[error(code = "E0123", slug = "foo")]
+struct ArgFieldWithoutSkip {
+ #[primary_span]
+ span: Span,
+ other: Hello,
+}
+
+#[derive(SessionDiagnostic)]
+#[error(code = "E0123", slug = "foo")]
+struct ArgFieldWithSkip {
+ #[primary_span]
+ span: Span,
+ // `Hello` does not implement `IntoDiagnosticArg` so this would result in an error if
+ // not for `#[skip_arg]`.
+ #[skip_arg]
+ other: Hello,
+}
+
+#[derive(SessionDiagnostic)]
+#[error(code = "E0123", slug = "foo")]
+struct ErrorWithSpannedNote {
+ #[note]
+ span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[error(code = "E0123", slug = "foo")]
+struct ErrorWithSpannedNoteCustom {
+ #[note = "bar"]
+ span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[error(code = "E0123", slug = "foo")]
+#[note]
+struct ErrorWithNote {
+ val: String,
+}
+
+#[derive(SessionDiagnostic)]
+#[error(code = "E0123", slug = "foo")]
+#[note = "bar"]
+struct ErrorWithNoteCustom {
+ val: String,
+}
+
+#[derive(SessionDiagnostic)]
+#[error(code = "E0123", slug = "foo")]
+struct ErrorWithSpannedHelp {
+ #[help]
+ span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[error(code = "E0123", slug = "foo")]
+struct ErrorWithSpannedHelpCustom {
+ #[help = "bar"]
+ span: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[error(code = "E0123", slug = "foo")]
+#[help]
+struct ErrorWithHelp {
+ val: String,
+}
+
+#[derive(SessionDiagnostic)]
+#[error(code = "E0123", slug = "foo")]
+#[help = "bar"]
+struct ErrorWithHelpCustom {
+ val: String,
+}
+
+#[derive(SessionDiagnostic)]
+#[help]
+//~^ ERROR `#[help]` must come after `#[error(..)]` or `#[warn(..)]`
+#[error(code = "E0123", slug = "foo")]
+struct ErrorWithHelpWrongOrder {
+ val: String,
+}
+
+#[derive(SessionDiagnostic)]
+#[help = "bar"]
+//~^ ERROR `#[help = ...]` must come after `#[error(..)]` or `#[warn(..)]`
+#[error(code = "E0123", slug = "foo")]
+struct ErrorWithHelpCustomWrongOrder {
+ val: String,
+}
+
+#[derive(SessionDiagnostic)]
+#[note]
+//~^ ERROR `#[note]` must come after `#[error(..)]` or `#[warn(..)]`
+#[error(code = "E0123", slug = "foo")]
+struct ErrorWithNoteWrongOrder {
+ val: String,
+}
+
+#[derive(SessionDiagnostic)]
+#[note = "bar"]
+//~^ ERROR `#[note = ...]` must come after `#[error(..)]` or `#[warn(..)]`
+#[error(code = "E0123", slug = "foo")]
+struct ErrorWithNoteCustomWrongOrder {
+ val: String,
+}
+
+#[derive(SessionDiagnostic)]
+#[error(code = "E0123", slug = "foo")]
+struct ApplicabilityInBoth {
+ #[suggestion(message = "bar", code = "...", applicability = "maybe-incorrect")]
+ //~^ ERROR applicability cannot be set in both the field and attribute
+ suggestion: (Span, Applicability),
+}
+
+#[derive(SessionDiagnostic)]
+#[error(code = "E0123", slug = "foo")]
+struct InvalidApplicability {
+ #[suggestion(message = "bar", code = "...", applicability = "batman")]
+ //~^ ERROR invalid applicability
+ suggestion: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[error(code = "E0123", slug = "foo")]
+struct ValidApplicability {
+ #[suggestion(message = "bar", code = "...", applicability = "maybe-incorrect")]
+ suggestion: Span,
+}
+
+#[derive(SessionDiagnostic)]
+#[error(code = "E0123", slug = "foo")]
+struct NoApplicability {
+ #[suggestion(message = "bar", code = "...")]
+ suggestion: Span,
+}
+
+#[derive(SessionSubdiagnostic)]
+#[note(slug = "note")]
+struct Note;
+
+#[derive(SessionDiagnostic)]
+#[error(slug = "subdiagnostic")]
+struct Subdiagnostic {
+ #[subdiagnostic]
+ note: Note,
+}
--- /dev/null
+error: `#[derive(SessionDiagnostic)]` can only be used on structs
+ --> $DIR/diagnostic-derive.rs:37:1
+ |
+LL | / #[error(code = "E0123", slug = "foo")]
+LL | |
+LL | | enum SessionDiagnosticOnEnum {
+LL | | Foo,
+LL | | Bar,
+LL | | }
+ | |_^
+
+error: `#[error = ...]` is not a valid attribute
+ --> $DIR/diagnostic-derive.rs:46:1
+ |
+LL | #[error = "E0123"]
+ | ^^^^^^^^^^^^^^^^^^
+
+error: `#[nonsense(...)]` is not a valid attribute
+ --> $DIR/diagnostic-derive.rs:51:1
+ |
+LL | #[nonsense(code = "E0123", slug = "foo")]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: only `error` and `warning` are valid attributes
+
+error: diagnostic kind not specified
+ --> $DIR/diagnostic-derive.rs:51:1
+ |
+LL | / #[nonsense(code = "E0123", slug = "foo")]
+LL | |
+LL | |
+LL | |
+LL | | struct InvalidStructAttr {}
+ | |___________________________^
+ |
+ = help: use the `#[error(...)]` attribute to create an error
+
+error: `#[error("...")]` is not a valid attribute
+ --> $DIR/diagnostic-derive.rs:58:9
+ |
+LL | #[error("E0123")]
+ | ^^^^^^^
+
+error: `slug` not specified
+ --> $DIR/diagnostic-derive.rs:58:1
+ |
+LL | / #[error("E0123")]
+LL | |
+LL | |
+LL | | struct InvalidLitNestedAttr {}
+ | |______________________________^
+ |
+ = help: use the `#[error(slug = "...")]` attribute to set this diagnostic's slug
+
+error: `#[error(nonsense)]` is not a valid attribute
+ --> $DIR/diagnostic-derive.rs:64:9
+ |
+LL | #[error(nonsense, code = "E0123", slug = "foo")]
+ | ^^^^^^^^
+
+error: `#[error(nonsense(...))]` is not a valid attribute
+ --> $DIR/diagnostic-derive.rs:69:9
+ |
+LL | #[error(nonsense("foo"), code = "E0123", slug = "foo")]
+ | ^^^^^^^^^^^^^^^
+
+error: `#[error(nonsense = ...)]` is not a valid attribute
+ --> $DIR/diagnostic-derive.rs:74:9
+ |
+LL | #[error(nonsense = "...", code = "E0123", slug = "foo")]
+ | ^^^^^^^^^^^^^^^^
+ |
+ = help: only `slug` and `code` are valid nested attributes
+
+error: `#[error(nonsense = ...)]` is not a valid attribute
+ --> $DIR/diagnostic-derive.rs:79:9
+ |
+LL | #[error(nonsense = 4, code = "E0123", slug = "foo")]
+ | ^^^^^^^^^^^^
+
+error: `#[suggestion = ...]` is not a valid attribute
+ --> $DIR/diagnostic-derive.rs:86:5
+ |
+LL | #[suggestion = "bar"]
+ | ^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: only `label`, `note` and `help` are valid field attributes
+
+error: specified multiple times
+ --> $DIR/diagnostic-derive.rs:93:1
+ |
+LL | #[error(code = "E0456", slug = "bar")]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+note: previously specified here
+ --> $DIR/diagnostic-derive.rs:92:1
+ |
+LL | #[error(code = "E0123", slug = "foo")]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: specified multiple times
+ --> $DIR/diagnostic-derive.rs:93:16
+ |
+LL | #[error(code = "E0456", slug = "bar")]
+ | ^^^^^^^
+ |
+note: previously specified here
+ --> $DIR/diagnostic-derive.rs:92:16
+ |
+LL | #[error(code = "E0123", slug = "foo")]
+ | ^^^^^^^
+
+error: specified multiple times
+ --> $DIR/diagnostic-derive.rs:93:32
+ |
+LL | #[error(code = "E0456", slug = "bar")]
+ | ^^^^^
+ |
+note: previously specified here
+ --> $DIR/diagnostic-derive.rs:92:32
+ |
+LL | #[error(code = "E0123", slug = "foo")]
+ | ^^^^^
+
+error: specified multiple times
+ --> $DIR/diagnostic-derive.rs:101:1
+ |
+LL | #[warning(code = "E0293", slug = "bar")]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+note: previously specified here
+ --> $DIR/diagnostic-derive.rs:100:1
+ |
+LL | #[error(code = "E0123", slug = "foo")]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: specified multiple times
+ --> $DIR/diagnostic-derive.rs:101:18
+ |
+LL | #[warning(code = "E0293", slug = "bar")]
+ | ^^^^^^^
+ |
+note: previously specified here
+ --> $DIR/diagnostic-derive.rs:100:16
+ |
+LL | #[error(code = "E0123", slug = "foo")]
+ | ^^^^^^^
+
+error: specified multiple times
+ --> $DIR/diagnostic-derive.rs:101:34
+ |
+LL | #[warning(code = "E0293", slug = "bar")]
+ | ^^^^^
+ |
+note: previously specified here
+ --> $DIR/diagnostic-derive.rs:100:32
+ |
+LL | #[error(code = "E0123", slug = "foo")]
+ | ^^^^^
+
+error: specified multiple times
+ --> $DIR/diagnostic-derive.rs:108:32
+ |
+LL | #[error(code = "E0456", code = "E0457", slug = "bar")]
+ | ^^^^^^^
+ |
+note: previously specified here
+ --> $DIR/diagnostic-derive.rs:108:16
+ |
+LL | #[error(code = "E0456", code = "E0457", slug = "bar")]
+ | ^^^^^^^
+
+error: specified multiple times
+ --> $DIR/diagnostic-derive.rs:113:46
+ |
+LL | #[error(code = "E0456", slug = "foo", slug = "bar")]
+ | ^^^^^
+ |
+note: previously specified here
+ --> $DIR/diagnostic-derive.rs:113:32
+ |
+LL | #[error(code = "E0456", slug = "foo", slug = "bar")]
+ | ^^^^^
+
+error: diagnostic kind not specified
+ --> $DIR/diagnostic-derive.rs:118:1
+ |
+LL | struct KindNotProvided {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: use the `#[error(...)]` attribute to create an error
+
+error: `slug` not specified
+ --> $DIR/diagnostic-derive.rs:121:1
+ |
+LL | / #[error(code = "E0456")]
+LL | | struct SlugNotProvided {}
+ | |_________________________^
+ |
+ = help: use the `#[error(slug = "...")]` attribute to set this diagnostic's slug
+
+error: the `#[primary_span]` attribute can only be applied to fields of type `Span`
+ --> $DIR/diagnostic-derive.rs:131:5
+ |
+LL | #[primary_span]
+ | ^^^^^^^^^^^^^^^
+
+error: `#[nonsense]` is not a valid attribute
+ --> $DIR/diagnostic-derive.rs:139:5
+ |
+LL | #[nonsense]
+ | ^^^^^^^^^^^
+ |
+ = help: only `skip_arg`, `primary_span`, `label`, `note`, `help` and `subdiagnostic` are valid field attributes
+
+error: the `#[label = ...]` attribute can only be applied to fields of type `Span`
+ --> $DIR/diagnostic-derive.rs:156:5
+ |
+LL | #[label = "bar"]
+ | ^^^^^^^^^^^^^^^^
+
+error: `name` doesn't refer to a field on this type
+ --> $DIR/diagnostic-derive.rs:164:42
+ |
+LL | #[suggestion(message = "bar", code = "{name}")]
+ | ^^^^^^^^
+
+error: invalid format string: expected `'}'` but string was terminated
+ --> $DIR/diagnostic-derive.rs:169:16
+ |
+LL | #[derive(SessionDiagnostic)]
+ | - ^ expected `'}'` in format string
+ | |
+ | because of this opening brace
+ |
+ = note: if you intended to print `{`, you can escape it using `{{`
+ = note: this error originates in the derive macro `SessionDiagnostic` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: invalid format string: unmatched `}` found
+ --> $DIR/diagnostic-derive.rs:179:15
+ |
+LL | #[derive(SessionDiagnostic)]
+ | ^ unmatched `}` in format string
+ |
+ = note: if you intended to print `}`, you can escape it using `}}`
+ = note: this error originates in the derive macro `SessionDiagnostic` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: the `#[label = ...]` attribute can only be applied to fields of type `Span`
+ --> $DIR/diagnostic-derive.rs:199:5
+ |
+LL | #[label = "bar"]
+ | ^^^^^^^^^^^^^^^^
+
+error: `#[suggestion(nonsense = ...)]` is not a valid attribute
+ --> $DIR/diagnostic-derive.rs:224:18
+ |
+LL | #[suggestion(nonsense = "bar")]
+ | ^^^^^^^^^^^^^^^^
+ |
+ = help: only `message`, `code` and `applicability` are valid field attributes
+
+error: `#[suggestion(msg = ...)]` is not a valid attribute
+ --> $DIR/diagnostic-derive.rs:232:18
+ |
+LL | #[suggestion(msg = "bar")]
+ | ^^^^^^^^^^^
+ |
+ = help: only `message`, `code` and `applicability` are valid field attributes
+
+error: wrong field type for suggestion
+ --> $DIR/diagnostic-derive.rs:254:5
+ |
+LL | / #[suggestion(message = "bar", code = "This is suggested code")]
+LL | |
+LL | | suggestion: Applicability,
+ | |_____________________________^
+ |
+ = help: `#[suggestion(...)]` should be applied to fields of type `Span` or `(Span, Applicability)`
+
+error: type of field annotated with `#[suggestion(...)]` contains more than one `Span`
+ --> $DIR/diagnostic-derive.rs:269:5
+ |
+LL | / #[suggestion(message = "bar", code = "This is suggested code")]
+LL | |
+LL | | suggestion: (Span, Span, Applicability),
+ | |___________________________________________^
+
+error: type of field annotated with `#[suggestion(...)]` contains more than one Applicability
+ --> $DIR/diagnostic-derive.rs:277:5
+ |
+LL | / #[suggestion(message = "bar", code = "This is suggested code")]
+LL | |
+LL | | suggestion: (Applicability, Applicability, Span),
+ | |____________________________________________________^
+
+error: `#[label(...)]` is not a valid attribute
+ --> $DIR/diagnostic-derive.rs:285:5
+ |
+LL | #[label("bar")]
+ | ^^^^^^^^^^^^^^^
+ |
+ = help: only `suggestion{,_short,_hidden,_verbose}` are valid field attributes
+
+error: `#[help]` must come after `#[error(..)]` or `#[warn(..)]`
+ --> $DIR/diagnostic-derive.rs:406:1
+ |
+LL | #[help]
+ | ^^^^^^^
+
+error: `#[help = ...]` must come after `#[error(..)]` or `#[warn(..)]`
+ --> $DIR/diagnostic-derive.rs:414:1
+ |
+LL | #[help = "bar"]
+ | ^^^^^^^^^^^^^^^
+
+error: `#[note]` must come after `#[error(..)]` or `#[warn(..)]`
+ --> $DIR/diagnostic-derive.rs:422:1
+ |
+LL | #[note]
+ | ^^^^^^^
+
+error: `#[note = ...]` must come after `#[error(..)]` or `#[warn(..)]`
+ --> $DIR/diagnostic-derive.rs:430:1
+ |
+LL | #[note = "bar"]
+ | ^^^^^^^^^^^^^^^
+
+error: applicability cannot be set in both the field and attribute
+ --> $DIR/diagnostic-derive.rs:440:49
+ |
+LL | #[suggestion(message = "bar", code = "...", applicability = "maybe-incorrect")]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: invalid applicability
+ --> $DIR/diagnostic-derive.rs:448:49
+ |
+LL | #[suggestion(message = "bar", code = "...", applicability = "batman")]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: cannot find attribute `nonsense` in this scope
+ --> $DIR/diagnostic-derive.rs:51:3
+ |
+LL | #[nonsense(code = "E0123", slug = "foo")]
+ | ^^^^^^^^
+
+error: cannot find attribute `nonsense` in this scope
+ --> $DIR/diagnostic-derive.rs:139:7
+ |
+LL | #[nonsense]
+ | ^^^^^^^^
+
+error[E0599]: no method named `into_diagnostic_arg` found for struct `Hello` in the current scope
+ --> $DIR/diagnostic-derive.rs:329:10
+ |
+LL | struct Hello {}
+ | ------------ method `into_diagnostic_arg` not found for this
+...
+LL | #[derive(SessionDiagnostic)]
+ | ^^^^^^^^^^^^^^^^^ method not found in `Hello`
+ |
+ = note: this error originates in the derive macro `SessionDiagnostic` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 43 previous errors
+
+For more information about this error, try `rustc --explain E0599`.
--- /dev/null
+// check-fail
+// Tests error conditions for specifying subdiagnostics using #[derive(SessionSubdiagnostic)]
+
+// The proc_macro2 crate handles spans differently when on beta/stable release rather than nightly,
+// changing the output of this test. Since SessionSubdiagnostic is strictly internal to the compiler
+// the test is just ignored on stable and beta:
+// ignore-beta
+// ignore-stable
+
+#![feature(rustc_private)]
+#![crate_type = "lib"]
+
+extern crate rustc_errors;
+extern crate rustc_session;
+extern crate rustc_span;
+extern crate rustc_macros;
+
+use rustc_errors::Applicability;
+use rustc_span::Span;
+use rustc_macros::SessionSubdiagnostic;
+
+#[derive(SessionSubdiagnostic)]
+#[label(slug = "label-a")]
+struct A {
+ #[primary_span]
+ span: Span,
+ var: String,
+}
+
+#[derive(SessionSubdiagnostic)]
+enum B {
+ #[label(slug = "label-b-a")]
+ A {
+ #[primary_span]
+ span: Span,
+ var: String,
+ },
+ #[label(slug = "label-b-b")]
+ B {
+ #[primary_span]
+ span: Span,
+ var: String,
+ }
+}
+
+#[derive(SessionSubdiagnostic)]
+#[label(slug = "label-c")]
+//~^ ERROR label without `#[primary_span]` field
+struct C {
+ var: String,
+}
+
+#[derive(SessionSubdiagnostic)]
+#[label]
+//~^ ERROR `#[label]` is not a valid attribute
+struct D {
+ #[primary_span]
+ span: Span,
+ var: String,
+}
+
+#[derive(SessionSubdiagnostic)]
+#[foo]
+//~^ ERROR `#[foo]` is not a valid attribute
+//~^^ ERROR cannot find attribute `foo` in this scope
+struct E {
+ #[primary_span]
+ span: Span,
+ var: String,
+}
+
+#[derive(SessionSubdiagnostic)]
+#[label = "..."]
+//~^ ERROR `#[label = ...]` is not a valid attribute
+struct F {
+ #[primary_span]
+ span: Span,
+ var: String,
+}
+
+#[derive(SessionSubdiagnostic)]
+#[label(bug = "...")]
+//~^ ERROR `#[label(bug = ...)]` is not a valid attribute
+struct G {
+ #[primary_span]
+ span: Span,
+ var: String,
+}
+
+#[derive(SessionSubdiagnostic)]
+#[label("...")]
+//~^ ERROR `#[label("...")]` is not a valid attribute
+struct H {
+ #[primary_span]
+ span: Span,
+ var: String,
+}
+
+#[derive(SessionSubdiagnostic)]
+#[label(slug = 4)]
+//~^ ERROR `#[label(slug = ...)]` is not a valid attribute
+struct J {
+ #[primary_span]
+ span: Span,
+ var: String,
+}
+
+#[derive(SessionSubdiagnostic)]
+#[label(slug("..."))]
+//~^ ERROR `#[label(slug(...))]` is not a valid attribute
+struct K {
+ #[primary_span]
+ span: Span,
+ var: String,
+}
+
+#[derive(SessionSubdiagnostic)]
+#[label(slug)]
+//~^ ERROR `#[label(slug)]` is not a valid attribute
+struct L {
+ #[primary_span]
+ span: Span,
+ var: String,
+}
+
+#[derive(SessionSubdiagnostic)]
+#[label()]
+//~^ ERROR `slug` must be set in a `#[label(...)]` attribute
+struct M {
+ #[primary_span]
+ span: Span,
+ var: String,
+}
+
+#[derive(SessionSubdiagnostic)]
+#[label(code = "...")]
+//~^ ERROR `code` is not a valid nested attribute of a `label` attribute
+struct N {
+ #[primary_span]
+ span: Span,
+ var: String,
+}
+
+#[derive(SessionSubdiagnostic)]
+#[foo]
+//~^ ERROR cannot find attribute `foo` in this scope
+//~^^ ERROR unsupported type attribute for subdiagnostic enum
+enum O {
+ #[label(slug = "...")]
+ A {
+ #[primary_span]
+ span: Span,
+ var: String,
+ }
+}
+
+#[derive(SessionSubdiagnostic)]
+enum P {
+ #[bar]
+//~^ ERROR `#[bar]` is not a valid attribute
+//~^^ ERROR cannot find attribute `bar` in this scope
+ A {
+ #[primary_span]
+ span: Span,
+ var: String,
+ }
+}
+
+#[derive(SessionSubdiagnostic)]
+enum Q {
+ #[bar = "..."]
+//~^ ERROR `#[bar = ...]` is not a valid attribute
+//~^^ ERROR cannot find attribute `bar` in this scope
+ A {
+ #[primary_span]
+ span: Span,
+ var: String,
+ }
+}
+
+#[derive(SessionSubdiagnostic)]
+enum R {
+ #[bar = 4]
+//~^ ERROR `#[bar = ...]` is not a valid attribute
+//~^^ ERROR cannot find attribute `bar` in this scope
+ A {
+ #[primary_span]
+ span: Span,
+ var: String,
+ }
+}
+
+#[derive(SessionSubdiagnostic)]
+enum S {
+ #[bar("...")]
+//~^ ERROR `#[bar("...")]` is not a valid attribute
+//~^^ ERROR cannot find attribute `bar` in this scope
+ A {
+ #[primary_span]
+ span: Span,
+ var: String,
+ }
+}
+
+#[derive(SessionSubdiagnostic)]
+enum T {
+ #[label(code = "...")]
+//~^ ERROR `code` is not a valid nested attribute of a `label`
+ A {
+ #[primary_span]
+ span: Span,
+ var: String,
+ }
+}
+
+#[derive(SessionSubdiagnostic)]
+enum U {
+ #[label(slug = "label-u")]
+ A {
+ #[primary_span]
+ span: Span,
+ var: String,
+ },
+ B {
+//~^ ERROR subdiagnostic kind not specified
+ #[primary_span]
+ span: Span,
+ var: String,
+ }
+}
+
+#[derive(SessionSubdiagnostic)]
+#[label(slug = "...")]
+//~^ ERROR label without `#[primary_span]` field
+struct V {
+ #[primary_span]
+ //~^ ERROR the `#[primary_span]` attribute can only be applied to fields of type `Span`
+ span: String,
+}
+
+#[derive(SessionSubdiagnostic)]
+#[label(slug = "...")]
+struct W {
+ #[primary_span]
+ span: Span,
+ #[applicability]
+ //~^ ERROR `#[applicability]` is only valid on suggestions
+ applicability: Applicability,
+}
+
+#[derive(SessionSubdiagnostic)]
+#[label(slug = "...")]
+struct X {
+ #[primary_span]
+ span: Span,
+ #[bar]
+ //~^ ERROR `#[bar]` is not a valid attribute
+ //~^^ ERROR cannot find attribute `bar` in this scope
+ bar: String,
+}
+
+#[derive(SessionSubdiagnostic)]
+#[label(slug = "...")]
+struct Y {
+ #[primary_span]
+ span: Span,
+ #[bar = "..."]
+ //~^ ERROR `#[bar = ...]` is not a valid attribute
+ //~^^ ERROR cannot find attribute `bar` in this scope
+ bar: String,
+}
+
+#[derive(SessionSubdiagnostic)]
+#[label(slug = "...")]
+struct Z {
+ #[primary_span]
+ span: Span,
+ #[bar("...")]
+ //~^ ERROR `#[bar(...)]` is not a valid attribute
+ //~^^ ERROR cannot find attribute `bar` in this scope
+ bar: String,
+}
+
+#[derive(SessionSubdiagnostic)]
+#[label(slug = "label-aa")]
+struct AA {
+ #[primary_span]
+ span: Span,
+ #[skip_arg]
+ z: Z
+}
+
+#[derive(SessionSubdiagnostic)]
+union AB {
+//~^ ERROR unexpected unsupported untagged union
+ span: u32,
+ b: u64
+}
+
+#[derive(SessionSubdiagnostic)]
+#[label(slug = "label-ac-1")]
+//~^ NOTE previously specified here
+//~^^ NOTE previously specified here
+#[label(slug = "label-ac-2")]
+//~^ ERROR specified multiple times
+//~^^ ERROR specified multiple times
+struct AC {
+ #[primary_span]
+ span: Span,
+}
+
+#[derive(SessionSubdiagnostic)]
+#[label(slug = "label-ad-1", slug = "label-ad-2")]
+//~^ ERROR specified multiple times
+//~^^ NOTE previously specified here
+struct AD {
+ #[primary_span]
+ span: Span,
+}
+
+#[derive(SessionSubdiagnostic)]
+#[label(slug = "label-ad-1")]
+struct AE {
+ #[primary_span]
+//~^ NOTE previously specified here
+ span_a: Span,
+ #[primary_span]
+//~^ ERROR specified multiple times
+ span_b: Span,
+}
+
+#[derive(SessionSubdiagnostic)]
+struct AF {
+//~^ ERROR subdiagnostic kind not specified
+ #[primary_span]
+ span: Span,
+}
+
+#[derive(SessionSubdiagnostic)]
+#[suggestion(slug = "suggestion-af", code = "...")]
+struct AG {
+ #[primary_span]
+ span: Span,
+ #[applicability]
+ applicability: Applicability,
+ var: String,
+}
+
+#[derive(SessionSubdiagnostic)]
+enum AH {
+ #[suggestion(slug = "suggestion-ag-a", code = "...")]
+ A {
+ #[primary_span]
+ span: Span,
+ #[applicability]
+ applicability: Applicability,
+ var: String,
+ },
+ #[suggestion(slug = "suggestion-ag-b", code = "...")]
+ B {
+ #[primary_span]
+ span: Span,
+ #[applicability]
+ applicability: Applicability,
+ var: String,
+ }
+}
+
+#[derive(SessionSubdiagnostic)]
+#[suggestion(slug = "...", code = "...", code = "...")]
+//~^ ERROR specified multiple times
+//~^^ NOTE previously specified here
+struct AI {
+ #[primary_span]
+ span: Span,
+ #[applicability]
+ applicability: Applicability,
+}
+
+#[derive(SessionSubdiagnostic)]
+#[suggestion(slug = "...", code = "...")]
+struct AJ {
+ #[primary_span]
+ span: Span,
+ #[applicability]
+//~^ NOTE previously specified here
+ applicability_a: Applicability,
+ #[applicability]
+//~^ ERROR specified multiple times
+ applicability_b: Applicability,
+}
+
+#[derive(SessionSubdiagnostic)]
+#[suggestion(slug = "...", code = "...")]
+//~^ ERROR suggestion without `applicability`
+struct AK {
+ #[primary_span]
+ span: Span,
+ #[applicability]
+//~^ ERROR the `#[applicability]` attribute can only be applied to fields of type `Applicability`
+ applicability: Span,
+}
+
+#[derive(SessionSubdiagnostic)]
+#[suggestion(slug = "...", code = "...")]
+//~^ ERROR suggestion without `applicability`
+struct AL {
+ #[primary_span]
+ span: Span,
+}
+
+#[derive(SessionSubdiagnostic)]
+#[suggestion(slug = "...")]
+//~^ ERROR suggestion without `code = "..."`
+struct AM {
+ #[primary_span]
+ span: Span,
+ #[applicability]
+ applicability: Applicability,
+}
+
+#[derive(SessionSubdiagnostic)]
+#[suggestion(slug = "...", code ="...", applicability = "foo")]
+//~^ ERROR invalid applicability
+struct AN {
+ #[primary_span]
+ span: Span,
+}
+
+#[derive(SessionSubdiagnostic)]
+#[help(slug = "label-am")]
+struct AO {
+ var: String
+}
+
+#[derive(SessionSubdiagnostic)]
+#[note(slug = "label-an")]
+struct AP;
+
+#[derive(SessionSubdiagnostic)]
+#[suggestion(slug = "...", code = "...")]
+//~^ ERROR suggestion without `applicability`
+//~^^ ERROR suggestion without `#[primary_span]` field
+struct AQ {
+ var: String,
+}
+
+#[derive(SessionSubdiagnostic)]
+#[suggestion(slug = "...", code ="...", applicability = "machine-applicable")]
+struct AR {
+ #[primary_span]
+ span: Span,
+}
+
+#[derive(SessionSubdiagnostic)]
+#[label]
+//~^ ERROR unsupported type attribute for subdiagnostic enum
+enum AS {
+ #[label(slug = "...")]
+ A {
+ #[primary_span]
+ span: Span,
+ var: String,
+ }
+}
+
+#[derive(SessionSubdiagnostic)]
+#[suggestion(slug = "...", code ="{var}", applicability = "machine-applicable")]
+struct AT {
+ #[primary_span]
+ span: Span,
+ var: String,
+}
+
+#[derive(SessionSubdiagnostic)]
+#[suggestion(slug = "...", code ="{var}", applicability = "machine-applicable")]
+//~^ ERROR `var` doesn't refer to a field on this type
+struct AU {
+ #[primary_span]
+ span: Span,
+}
+
+#[derive(SessionSubdiagnostic)]
+enum AV {
+ #[suggestion(slug = "...", code ="{var}", applicability = "machine-applicable")]
+ A {
+ #[primary_span]
+ span: Span,
+ var: String,
+ }
+}
+
+#[derive(SessionSubdiagnostic)]
+enum AW {
+ #[suggestion(slug = "...", code ="{var}", applicability = "machine-applicable")]
+//~^ ERROR `var` doesn't refer to a field on this type
+ A {
+ #[primary_span]
+ span: Span,
+ }
+}
--- /dev/null
+error: label without `#[primary_span]` field
+ --> $DIR/subdiagnostic-derive.rs:47:1
+ |
+LL | / #[label(slug = "label-c")]
+LL | |
+LL | | struct C {
+LL | | var: String,
+LL | | }
+ | |_^
+
+error: `#[label]` is not a valid attribute
+ --> $DIR/subdiagnostic-derive.rs:54:1
+ |
+LL | #[label]
+ | ^^^^^^^^
+
+error: `#[foo]` is not a valid attribute
+ --> $DIR/subdiagnostic-derive.rs:63:1
+ |
+LL | #[foo]
+ | ^^^^^^
+
+error: `#[label = ...]` is not a valid attribute
+ --> $DIR/subdiagnostic-derive.rs:73:1
+ |
+LL | #[label = "..."]
+ | ^^^^^^^^^^^^^^^^
+
+error: `#[label(bug = ...)]` is not a valid attribute
+ --> $DIR/subdiagnostic-derive.rs:82:9
+ |
+LL | #[label(bug = "...")]
+ | ^^^^^^^^^^^
+ |
+ = help: only `code`, `slug` and `applicability` are valid nested attributes
+
+error: `#[label("...")]` is not a valid attribute
+ --> $DIR/subdiagnostic-derive.rs:91:9
+ |
+LL | #[label("...")]
+ | ^^^^^
+
+error: `#[label(slug = ...)]` is not a valid attribute
+ --> $DIR/subdiagnostic-derive.rs:100:9
+ |
+LL | #[label(slug = 4)]
+ | ^^^^^^^^
+
+error: `#[label(slug(...))]` is not a valid attribute
+ --> $DIR/subdiagnostic-derive.rs:109:9
+ |
+LL | #[label(slug("..."))]
+ | ^^^^^^^^^^^
+
+error: `#[label(slug)]` is not a valid attribute
+ --> $DIR/subdiagnostic-derive.rs:118:9
+ |
+LL | #[label(slug)]
+ | ^^^^
+
+error: `slug` must be set in a `#[label(...)]` attribute
+ --> $DIR/subdiagnostic-derive.rs:127:1
+ |
+LL | #[label()]
+ | ^^^^^^^^^^
+
+error: `code` is not a valid nested attribute of a `label` attribute
+ --> $DIR/subdiagnostic-derive.rs:136:1
+ |
+LL | #[label(code = "...")]
+ | ^^^^^^^^^^^^^^^^^^^^^^
+
+error: unsupported type attribute for subdiagnostic enum
+ --> $DIR/subdiagnostic-derive.rs:145:1
+ |
+LL | #[foo]
+ | ^^^^^^
+
+error: `#[bar]` is not a valid attribute
+ --> $DIR/subdiagnostic-derive.rs:159:5
+ |
+LL | #[bar]
+ | ^^^^^^
+
+error: `#[bar = ...]` is not a valid attribute
+ --> $DIR/subdiagnostic-derive.rs:171:5
+ |
+LL | #[bar = "..."]
+ | ^^^^^^^^^^^^^^
+
+error: `#[bar = ...]` is not a valid attribute
+ --> $DIR/subdiagnostic-derive.rs:183:5
+ |
+LL | #[bar = 4]
+ | ^^^^^^^^^^
+
+error: `#[bar("...")]` is not a valid attribute
+ --> $DIR/subdiagnostic-derive.rs:195:11
+ |
+LL | #[bar("...")]
+ | ^^^^^
+
+error: `code` is not a valid nested attribute of a `label` attribute
+ --> $DIR/subdiagnostic-derive.rs:207:5
+ |
+LL | #[label(code = "...")]
+ | ^^^^^^^^^^^^^^^^^^^^^^
+
+error: subdiagnostic kind not specified
+ --> $DIR/subdiagnostic-derive.rs:224:5
+ |
+LL | B {
+ | ^
+
+error: the `#[primary_span]` attribute can only be applied to fields of type `Span`
+ --> $DIR/subdiagnostic-derive.rs:236:5
+ |
+LL | #[primary_span]
+ | ^^^^^^^^^^^^^^^
+
+error: label without `#[primary_span]` field
+ --> $DIR/subdiagnostic-derive.rs:233:1
+ |
+LL | / #[label(slug = "...")]
+LL | |
+LL | | struct V {
+LL | | #[primary_span]
+LL | |
+LL | | span: String,
+LL | | }
+ | |_^
+
+error: `#[applicability]` is only valid on suggestions
+ --> $DIR/subdiagnostic-derive.rs:246:5
+ |
+LL | #[applicability]
+ | ^^^^^^^^^^^^^^^^
+
+error: `#[bar]` is not a valid attribute
+ --> $DIR/subdiagnostic-derive.rs:256:5
+ |
+LL | #[bar]
+ | ^^^^^^
+ |
+ = help: only `primary_span`, `applicability` and `skip_arg` are valid field attributes
+
+error: `#[bar = ...]` is not a valid attribute
+ --> $DIR/subdiagnostic-derive.rs:267:5
+ |
+LL | #[bar = "..."]
+ | ^^^^^^^^^^^^^^
+
+error: `#[bar(...)]` is not a valid attribute
+ --> $DIR/subdiagnostic-derive.rs:278:5
+ |
+LL | #[bar("...")]
+ | ^^^^^^^^^^^^^
+
+error: unexpected unsupported untagged union
+ --> $DIR/subdiagnostic-derive.rs:294:1
+ |
+LL | / union AB {
+LL | |
+LL | | span: u32,
+LL | | b: u64
+LL | | }
+ | |_^
+
+error: specified multiple times
+ --> $DIR/subdiagnostic-derive.rs:304:9
+ |
+LL | #[label(slug = "label-ac-2")]
+ | ^^^^^^^^^^^^^^^^^^^
+ |
+note: previously specified here
+ --> $DIR/subdiagnostic-derive.rs:301:9
+ |
+LL | #[label(slug = "label-ac-1")]
+ | ^^^^^^^^^^^^^^^^^^^
+
+error: specified multiple times
+ --> $DIR/subdiagnostic-derive.rs:304:1
+ |
+LL | #[label(slug = "label-ac-2")]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+note: previously specified here
+ --> $DIR/subdiagnostic-derive.rs:301:1
+ |
+LL | #[label(slug = "label-ac-1")]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: specified multiple times
+ --> $DIR/subdiagnostic-derive.rs:313:30
+ |
+LL | #[label(slug = "label-ad-1", slug = "label-ad-2")]
+ | ^^^^^^^^^^^^^^^^^^^
+ |
+note: previously specified here
+ --> $DIR/subdiagnostic-derive.rs:313:9
+ |
+LL | #[label(slug = "label-ad-1", slug = "label-ad-2")]
+ | ^^^^^^^^^^^^^^^^^^^
+
+error: specified multiple times
+ --> $DIR/subdiagnostic-derive.rs:327:5
+ |
+LL | #[primary_span]
+ | ^^^^^^^^^^^^^^^
+ |
+note: previously specified here
+ --> $DIR/subdiagnostic-derive.rs:324:5
+ |
+LL | #[primary_span]
+ | ^^^^^^^^^^^^^^^
+
+error: subdiagnostic kind not specified
+ --> $DIR/subdiagnostic-derive.rs:333:8
+ |
+LL | struct AF {
+ | ^^
+
+error: specified multiple times
+ --> $DIR/subdiagnostic-derive.rs:370:42
+ |
+LL | #[suggestion(slug = "...", code = "...", code = "...")]
+ | ^^^^^^^^^^^^
+ |
+note: previously specified here
+ --> $DIR/subdiagnostic-derive.rs:370:28
+ |
+LL | #[suggestion(slug = "...", code = "...", code = "...")]
+ | ^^^^^^^^^^^^
+
+error: specified multiple times
+ --> $DIR/subdiagnostic-derive.rs:388:5
+ |
+LL | #[applicability]
+ | ^^^^^^^^^^^^^^^^
+ |
+note: previously specified here
+ --> $DIR/subdiagnostic-derive.rs:385:5
+ |
+LL | #[applicability]
+ | ^^^^^^^^^^^^^^^^
+
+error: the `#[applicability]` attribute can only be applied to fields of type `Applicability`
+ --> $DIR/subdiagnostic-derive.rs:399:5
+ |
+LL | #[applicability]
+ | ^^^^^^^^^^^^^^^^
+
+error: suggestion without `applicability`
+ --> $DIR/subdiagnostic-derive.rs:394:1
+ |
+LL | / #[suggestion(slug = "...", code = "...")]
+LL | |
+LL | | struct AK {
+LL | | #[primary_span]
+... |
+LL | | applicability: Span,
+LL | | }
+ | |_^
+
+error: suggestion without `applicability`
+ --> $DIR/subdiagnostic-derive.rs:405:1
+ |
+LL | / #[suggestion(slug = "...", code = "...")]
+LL | |
+LL | | struct AL {
+LL | | #[primary_span]
+LL | | span: Span,
+LL | | }
+ | |_^
+
+error: suggestion without `code = "..."`
+ --> $DIR/subdiagnostic-derive.rs:413:1
+ |
+LL | / #[suggestion(slug = "...")]
+LL | |
+LL | | struct AM {
+LL | | #[primary_span]
+... |
+LL | | applicability: Applicability,
+LL | | }
+ | |_^
+
+error: invalid applicability
+ --> $DIR/subdiagnostic-derive.rs:423:41
+ |
+LL | #[suggestion(slug = "...", code ="...", applicability = "foo")]
+ | ^^^^^^^^^^^^^^^^^^^^^
+
+error: suggestion without `applicability`
+ --> $DIR/subdiagnostic-derive.rs:441:1
+ |
+LL | / #[suggestion(slug = "...", code = "...")]
+LL | |
+LL | |
+LL | | struct AQ {
+LL | | var: String,
+LL | | }
+ | |_^
+
+error: suggestion without `#[primary_span]` field
+ --> $DIR/subdiagnostic-derive.rs:441:1
+ |
+LL | / #[suggestion(slug = "...", code = "...")]
+LL | |
+LL | |
+LL | | struct AQ {
+LL | | var: String,
+LL | | }
+ | |_^
+
+error: unsupported type attribute for subdiagnostic enum
+ --> $DIR/subdiagnostic-derive.rs:456:1
+ |
+LL | #[label]
+ | ^^^^^^^^
+
+error: `var` doesn't refer to a field on this type
+ --> $DIR/subdiagnostic-derive.rs:476:34
+ |
+LL | #[suggestion(slug = "...", code ="{var}", applicability = "machine-applicable")]
+ | ^^^^^^^
+
+error: `var` doesn't refer to a field on this type
+ --> $DIR/subdiagnostic-derive.rs:495:38
+ |
+LL | #[suggestion(slug = "...", code ="{var}", applicability = "machine-applicable")]
+ | ^^^^^^^
+
+error: cannot find attribute `foo` in this scope
+ --> $DIR/subdiagnostic-derive.rs:63:3
+ |
+LL | #[foo]
+ | ^^^
+
+error: cannot find attribute `foo` in this scope
+ --> $DIR/subdiagnostic-derive.rs:145:3
+ |
+LL | #[foo]
+ | ^^^
+
+error: cannot find attribute `bar` in this scope
+ --> $DIR/subdiagnostic-derive.rs:159:7
+ |
+LL | #[bar]
+ | ^^^
+
+error: cannot find attribute `bar` in this scope
+ --> $DIR/subdiagnostic-derive.rs:171:7
+ |
+LL | #[bar = "..."]
+ | ^^^
+
+error: cannot find attribute `bar` in this scope
+ --> $DIR/subdiagnostic-derive.rs:183:7
+ |
+LL | #[bar = 4]
+ | ^^^
+
+error: cannot find attribute `bar` in this scope
+ --> $DIR/subdiagnostic-derive.rs:195:7
+ |
+LL | #[bar("...")]
+ | ^^^
+
+error: cannot find attribute `bar` in this scope
+ --> $DIR/subdiagnostic-derive.rs:256:7
+ |
+LL | #[bar]
+ | ^^^
+
+error: cannot find attribute `bar` in this scope
+ --> $DIR/subdiagnostic-derive.rs:267:7
+ |
+LL | #[bar = "..."]
+ | ^^^
+
+error: cannot find attribute `bar` in this scope
+ --> $DIR/subdiagnostic-derive.rs:278:7
+ |
+LL | #[bar("...")]
+ | ^^^
+
+error: aborting due to 51 previous errors
+
--- /dev/null
+fn f(_: usize, _: &usize, _: usize) {}
+
+fn arg<T>() -> T { todo!() }
+
+fn main() {
+ let x = arg(); // `x` must be inferred
+ // The reference on `&x` is important to reproduce the ICE
+ f(&x, ""); //~ ERROR this function takes 3 arguments but 2 arguments were supplied
+}
--- /dev/null
+error[E0061]: this function takes 3 arguments but 2 arguments were supplied
+ --> $DIR/issue-96638.rs:8:5
+ |
+LL | f(&x, "");
+ | ^ -- an argument of type `usize` is missing
+ |
+note: function defined here
+ --> $DIR/issue-96638.rs:1:4
+ |
+LL | fn f(_: usize, _: &usize, _: usize) {}
+ | ^ -------- --------- --------
+help: provide the argument
+ |
+LL | f({usize}, &x, {usize});
+ | ~~~~~~~~~~~~~~~~~~~~~~~
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0061`.
error[E0277]: the trait bound `Header<'_>: Copy` is not satisfied
- --> $DIR/repeat_empty_ok.rs:8:19
+ --> $DIR/repeat_empty_ok.rs:8:20
|
LL | let headers = [Header{value: &[]}; 128];
- | ^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `Header<'_>`
+ | ^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `Header<'_>`
|
- = note: the `Copy` trait is required because the repeated element will be copied
+ = note: the `Copy` trait is required because this value will be copied for each element of the array
help: consider annotating `Header<'_>` with `#[derive(Copy)]`
|
LL | #[derive(Copy)]
|
error[E0277]: the trait bound `Header<'_>: Copy` is not satisfied
- --> $DIR/repeat_empty_ok.rs:13:19
+ --> $DIR/repeat_empty_ok.rs:13:20
|
LL | let headers = [Header{value: &[0]}; 128];
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `Header<'_>`
+ | ^^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `Header<'_>`
|
- = note: the `Copy` trait is required because the repeated element will be copied
+ = note: the `Copy` trait is required because this value will be copied for each element of the array
help: consider annotating `Header<'_>` with `#[derive(Copy)]`
|
LL | #[derive(Copy)]
fn baz() -> impl Bar<Item = i32> {
//~^ ERROR type mismatch resolving `<impl Bar as Foo>::Item == i32`
- //~| ERROR type mismatch resolving `<impl Bar as Foo>::Item == i32`
bar()
}
LL | fn bar() -> impl Bar<Item = i32> {
| ++++++++++++
-error[E0271]: type mismatch resolving `<impl Bar as Foo>::Item == i32`
- --> $DIR/impl-trait-return-missing-constraint.rs:25:34
- |
-LL | fn bar() -> impl Bar {
- | -------- the expected opaque type
-...
-LL | fn baz() -> impl Bar<Item = i32> {
- | __________________________________^
-LL | |
-LL | |
-LL | | bar()
-LL | | }
- | |_^ expected associated type, found `i32`
- |
- = note: expected associated type `<impl Bar as Foo>::Item`
- found type `i32`
- = help: consider constraining the associated type `<impl Bar as Foo>::Item` to `i32` or calling a method that returns `<impl Bar as Foo>::Item`
- = note: for more information, visit https://doc.rust-lang.org/book/ch19-03-advanced-traits.html
-help: consider constraining the associated type `<impl Bar as Foo>::Item` to `i32`
- |
-LL | fn bar() -> impl Bar<Item = i32> {
- | ++++++++++++
-
-error: aborting due to 2 previous errors
+error: aborting due to previous error
For more information about this error, try `rustc --explain E0271`.
--- /dev/null
+// check-pass
+pub trait Hasher {
+ type State;
+
+ fn hash<T: Hash<
+ <Self as Hasher>::State
+ >>(&self, value: &T) -> u64;
+}
+
+pub trait Hash<S> {
+ fn hash(&self, state: &mut S);
+}
+
+fn main() {}
--- /dev/null
+// run-pass
+#![allow(dead_code)]
+#![allow(unused_variables)]
+#![feature(associated_type_defaults)]
+
+use std::marker::PhantomData;
+
+pub trait Routing<I> {
+ type Output;
+ fn resolve(&self, input: I);
+}
+
+pub trait ToRouting {
+ type Input;
+ type Routing : ?Sized = dyn Routing<Self::Input, Output=()>;
+ fn to_routing(self) -> Self::Routing;
+}
+
+pub struct Mount<I, R: Routing<I>> {
+ action: R,
+ _marker: PhantomData<I>
+}
+
+impl<I, R: Routing<I>> Mount<I, R> {
+ pub fn create<T: ToRouting<Routing=R>>(mount: &str, input: T) {
+ input.to_routing();
+ }
+}
+
+fn main() {
+}
--- /dev/null
+// run-pass
+
+// Regression test for #55846, which once caused an ICE.
+
+use std::marker::PhantomData;
+
+struct Foo;
+
+struct Bar<A> {
+ a: PhantomData<A>,
+}
+
+impl Fooifier for Foo {
+ type Assoc = Foo;
+}
+
+trait Fooifier {
+ type Assoc;
+}
+
+trait Barifier<H> {
+ fn barify();
+}
+
+impl<H> Barifier<H> for Bar<H> {
+ fn barify() {
+ println!("All correct!");
+ }
+}
+
+impl Bar<<Foo as Fooifier>::Assoc> {
+ fn this_shouldnt_crash() {
+ <Self as Barifier<<Foo as Fooifier>::Assoc>>::barify();
+ }
+}
+
+fn main() {
+ Bar::<Foo>::this_shouldnt_crash();
+}
= note: consult the function's documentation for information on how to avoid undefined behavior
error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
- --> $DIR/async-unsafe-fn-call-in-safe.rs:15:5
+ --> $DIR/async-unsafe-fn-call-in-safe.rs:17:5
|
LL | f();
| ^^^ call to unsafe function
= note: consult the function's documentation for information on how to avoid undefined behavior
error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
- --> $DIR/async-unsafe-fn-call-in-safe.rs:19:5
+ --> $DIR/async-unsafe-fn-call-in-safe.rs:23:5
|
LL | S::f();
| ^^^^^^ call to unsafe function
= note: consult the function's documentation for information on how to avoid undefined behavior
error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
- --> $DIR/async-unsafe-fn-call-in-safe.rs:20:5
+ --> $DIR/async-unsafe-fn-call-in-safe.rs:24:5
|
LL | f();
| ^^^ call to unsafe function
async unsafe fn f() {}
async fn g() {
- S::f(); //~ ERROR call to unsafe function is unsafe
- f(); //~ ERROR call to unsafe function is unsafe
+ S::f();
+ //[mir]~^ ERROR call to unsafe function is unsafe
+ //[thir]~^^ ERROR call to unsafe function `S::f` is unsafe
+ f();
+ //[mir]~^ ERROR call to unsafe function is unsafe
+ //[thir]~^^ ERROR call to unsafe function `f` is unsafe
}
fn main() {
-error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
+error[E0133]: call to unsafe function `S::f` is unsafe and requires unsafe function or block
--> $DIR/async-unsafe-fn-call-in-safe.rs:14:5
|
LL | S::f();
|
= note: consult the function's documentation for information on how to avoid undefined behavior
-error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
- --> $DIR/async-unsafe-fn-call-in-safe.rs:15:5
+error[E0133]: call to unsafe function `f` is unsafe and requires unsafe function or block
+ --> $DIR/async-unsafe-fn-call-in-safe.rs:17:5
|
LL | f();
| ^^^ call to unsafe function
LL | let x = x;
| ^ has type `&T` which is not `Send`, because `T` is not `Sync`
= note: required for the cast to the object type `dyn Future<Output = ()> + Send`
-help: consider further restricting type parameter `T`
+help: consider further restricting this bound
|
-LL | where 'me:'async_trait, T: std::marker::Sync {
- | ++++++++++++++++++++++
+LL | fn bar<'me, 'async_trait, T: Send + std::marker::Sync>(x: &'me T)
+ | +++++++++++++++++++
error: aborting due to previous error
| | expected one of `extern`, `fn`, or `unsafe`
| help: `const` must come before `async`: `const async`
|
- = note: keyword order for functions declaration is `default`, `pub`, `const`, `async`, `unsafe`, `extern`
+ = note: keyword order for functions declaration is `pub`, `default`, `const`, `async`, `unsafe`, `extern`
error: aborting due to previous error
LL | }
| - the item list ends here
|
- = note: keyword order for functions declaration is `default`, `pub`, `const`, `async`, `unsafe`, `extern`
+ = note: keyword order for functions declaration is `pub`, `default`, `const`, `async`, `unsafe`, `extern`
error: expected one of `extern` or `fn`, found keyword `async`
--> $DIR/no-unsafe-async.rs:11:8
| | expected one of `extern` or `fn`
| help: `async` must come before `unsafe`: `async unsafe`
|
- = note: keyword order for functions declaration is `default`, `pub`, `const`, `async`, `unsafe`, `extern`
+ = note: keyword order for functions declaration is `pub`, `default`, `const`, `async`, `unsafe`, `extern`
error: aborting due to 2 previous errors
--- /dev/null
+struct Thing {
+ x: isize
+}
+
+impl Thing {
+ fn mul(&self, c: &isize) -> Thing {
+ Thing {x: self.x * *c}
+ }
+}
+
+fn main() {
+ let u = Thing {x: 2};
+ let _v = u.mul(&3); // This is ok
+ let w = u * 3; //~ ERROR cannot multiply `Thing` by `{integer}`
+}
--- /dev/null
+error[E0369]: cannot multiply `Thing` by `{integer}`
+ --> $DIR/issue-3820.rs:14:15
+ |
+LL | let w = u * 3;
+ | - ^ - {integer}
+ | |
+ | Thing
+ |
+note: an implementation of `Mul<_>` might be missing for `Thing`
+ --> $DIR/issue-3820.rs:1:1
+ |
+LL | struct Thing {
+ | ^^^^^^^^^^^^ must implement `Mul<_>`
+note: the following trait must be implemented
+ --> $SRC_DIR/core/src/ops/arith.rs:LL:COL
+ |
+LL | / pub trait Mul<Rhs = Self> {
+LL | | /// The resulting type after applying the `*` operator.
+LL | | #[stable(feature = "rust1", since = "1.0.0")]
+LL | | type Output;
+... |
+LL | | fn mul(self, rhs: Rhs) -> Self::Output;
+LL | | }
+ | |_^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0369`.
--- /dev/null
+// Regression test for #93927: suggested trait bound for T should be Eq, not PartialEq
+struct MyType<T>(T);
+
+impl<T> PartialEq for MyType<T>
+where
+ T: Eq,
+{
+ fn eq(&self, other: &Self) -> bool {
+ true
+ }
+}
+
+fn cond<T: PartialEq>(val: MyType<T>) -> bool {
+ val == val
+ //~^ ERROR binary operation `==` cannot be applied to type `MyType<T>`
+}
+
+fn main() {
+ cond(MyType(0));
+}
--- /dev/null
+error[E0369]: binary operation `==` cannot be applied to type `MyType<T>`
+ --> $DIR/issue-93927.rs:14:9
+ |
+LL | val == val
+ | --- ^^ --- MyType<T>
+ | |
+ | MyType<T>
+ |
+help: consider further restricting this bound
+ |
+LL | fn cond<T: PartialEq + std::cmp::Eq>(val: MyType<T>) -> bool {
+ | ++++++++++++++
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0369`.
--> $DIR/builtin-superkinds-self-type.rs:10:16
|
LL | impl <T: Sync> Foo for T { }
- | -- ^^^ ...so that the type `T` will meet its required lifetime bounds
- | |
- | help: consider adding an explicit lifetime bound...: `T: 'static +`
+ | ^^^ ...so that the type `T` will meet its required lifetime bounds
+ |
+help: consider adding an explicit lifetime bound...
+ |
+LL | impl <T: Sync + 'static> Foo for T { }
+ | +++++++++
error: aborting due to previous error
-error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
+error[E0133]: call to unsafe function `Pin::<P>::new_unchecked` is unsafe and requires unsafe function or block
--> $DIR/coerce-unsafe-closure-to-unsafe-fn-ptr.rs:5:31
|
LL | let _: unsafe fn() = || { ::std::pin::Pin::new_unchecked(&0_u8); };
--- /dev/null
+// run-pass
+fn main() {
+ let mut t = [1; 2];
+ t = [t[1] * 2, t[0] * 2];
+ assert_eq!(&t[..], &[2, 2]);
+}
--- /dev/null
+// run-pass
+struct A {
+ pub x: u32,
+ pub y: u32,
+}
+
+fn main() {
+ let mut a = A { x: 1, y: 1 };
+ a = A { x: a.y * 2, y: a.x * 2 };
+ assert_eq!(a.x, 2);
+ assert_eq!(a.y, 2);
+}
--- /dev/null
+// run-pass
+#![allow(unused_variables)]
+#![allow(unused_assignments)]
+#[derive(Debug)]
+enum Foo {
+ Bar(u32, u32),
+ Baz(&'static u32, &'static u32)
+}
+
+static NUM: u32 = 100;
+
+fn main () {
+ let mut b = Foo::Baz(&NUM, &NUM);
+ b = Foo::Bar(f(&b), g(&b));
+}
+
+static FNUM: u32 = 1;
+
+fn f (b: &Foo) -> u32 {
+ FNUM
+}
+
+static GNUM: u32 = 2;
+
+fn g (b: &Foo) -> u32 {
+ GNUM
+}
--- /dev/null
+error[E0277]: the trait bound `(): std::error::Error` is not satisfied
+ --> $DIR/coerce-issue-49593-box-never-windows.rs:18:53
+ |
+LL | /* *mut $0 is coerced to Box<dyn Error> here */ Box::<_ /* ! */>::new(x)
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::error::Error` is not implemented for `()`
+ |
+ = help: the following other types implement trait `std::error::Error`:
+ !
+ &'a T
+ AccessError
+ AddrParseError
+ Arc<T>
+ BorrowError
+ BorrowMutError
+ Box<T>
+ and 45 others
+ = note: required for the cast to the object type `dyn std::error::Error`
+
+error[E0277]: the trait bound `(): std::error::Error` is not satisfied
+ --> $DIR/coerce-issue-49593-box-never-windows.rs:23:49
+ |
+LL | /* *mut $0 is coerced to *mut Error here */ raw_ptr_box::<_ /* ! */>(x)
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::error::Error` is not implemented for `()`
+ |
+ = help: the following other types implement trait `std::error::Error`:
+ !
+ &'a T
+ AccessError
+ AddrParseError
+ Arc<T>
+ BorrowError
+ BorrowMutError
+ Box<T>
+ and 45 others
+ = note: required for the cast to the object type `(dyn std::error::Error + 'static)`
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0277`.
--- /dev/null
+// revisions: nofallback fallback
+// only-windows - the number of `Error` impls is platform-dependent
+//[fallback] check-pass
+//[nofallback] check-fail
+
+#![feature(never_type)]
+#![cfg_attr(fallback, feature(never_type_fallback))]
+#![allow(unreachable_code)]
+
+use std::error::Error;
+use std::mem;
+
+fn raw_ptr_box<T>(t: T) -> *mut T {
+ panic!()
+}
+
+fn foo(x: !) -> Box<dyn Error> {
+ /* *mut $0 is coerced to Box<dyn Error> here */ Box::<_ /* ! */>::new(x)
+ //[nofallback]~^ ERROR trait bound `(): std::error::Error` is not satisfied
+}
+
+fn foo_raw_ptr(x: !) -> *mut dyn Error {
+ /* *mut $0 is coerced to *mut Error here */ raw_ptr_box::<_ /* ! */>(x)
+ //[nofallback]~^ ERROR trait bound `(): std::error::Error` is not satisfied
+}
+
+fn no_coercion(d: *mut dyn Error) -> *mut dyn Error {
+ /* an unsize coercion won't compile here, and it is indeed not used
+ because there is nothing requiring the _ to be Sized */
+ d as *mut _
+}
+
+trait Xyz {}
+struct S;
+struct T;
+impl Xyz for S {}
+impl Xyz for T {}
+
+fn foo_no_never() {
+ let mut x /* : Option<S> */ = None;
+ let mut first_iter = false;
+ loop {
+ if !first_iter {
+ let y: Box<dyn Xyz>
+ = /* Box<$0> is coerced to Box<Xyz> here */ Box::new(x.unwrap());
+ }
+
+ x = Some(S);
+ first_iter = true;
+ }
+
+ let mut y : Option<S> = None;
+ // assert types are equal
+ mem::swap(&mut x, &mut y);
+}
+
+fn main() {
+}
error[E0277]: the trait bound `(): std::error::Error` is not satisfied
- --> $DIR/coerce-issue-49593-box-never.rs:17:53
+ --> $DIR/coerce-issue-49593-box-never.rs:18:53
|
LL | /* *mut $0 is coerced to Box<dyn Error> here */ Box::<_ /* ! */>::new(x)
| ^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::error::Error` is not implemented for `()`
= note: required for the cast to the object type `dyn std::error::Error`
error[E0277]: the trait bound `(): std::error::Error` is not satisfied
- --> $DIR/coerce-issue-49593-box-never.rs:22:49
+ --> $DIR/coerce-issue-49593-box-never.rs:23:49
|
LL | /* *mut $0 is coerced to *mut Error here */ raw_ptr_box::<_ /* ! */>(x)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `std::error::Error` is not implemented for `()`
// revisions: nofallback fallback
+// ignore-windows - the number of `Error` impls is platform-dependent
//[fallback] check-pass
//[nofallback] check-fail
-error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
+error[E0117]: only traits defined in the current crate can be implemented for types defined outside of the crate
--> $DIR/coherence-cow.rs:18:1
|
LL | impl<T> Remote for Pair<T,Cover<T>> { }
-error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
+error[E0117]: only traits defined in the current crate can be implemented for types defined outside of the crate
--> $DIR/coherence-cow.rs:22:1
|
LL | impl<T> Remote for Pair<Cover<T>,T> { }
-error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
+error[E0117]: only traits defined in the current crate can be implemented for types defined outside of the crate
--> $DIR/coherence-cow.rs:26:1
|
LL | impl<T,U> Remote for Pair<Cover<T>,U> { }
-error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
+error[E0117]: only traits defined in the current crate can be implemented for primitive types
--> $DIR/coherence-impls-copy.rs:5:1
|
LL | impl Copy for i32 {}
-error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
+error[E0117]: only traits defined in the current crate can be implemented for primitive types
--> $DIR/coherence-orphan.rs:10:1
|
LL | impl TheTrait<usize> for isize { }
|
= note: define and implement a trait or new type instead
-error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
+error[E0117]: only traits defined in the current crate can be implemented for types defined outside of the crate
--> $DIR/coherence-orphan.rs:17:1
|
LL | impl !Send for Vec<isize> { }
-error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
+error[E0117]: only traits defined in the current crate can be implemented for types defined outside of the crate
--> $DIR/coherence-overlapping-pairs.rs:8:1
|
LL | impl<T> Remote for lib::Pair<T,Foo> { }
-error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
+error[E0117]: only traits defined in the current crate can be implemented for primitive types
--> $DIR/coherence-pair-covered-uncovered-1.rs:12:1
|
LL | impl<T, U> Remote1<Pair<T, Local<U>>> for i32 { }
-error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
+error[E0117]: only traits defined in the current crate can be implemented for types defined outside of the crate
--> $DIR/coherence-pair-covered-uncovered.rs:8:1
|
LL | impl<T,U> Remote for Pair<T,Local<U>> { }
-error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
+error[E0117]: only traits defined in the current crate can be implemented for types defined outside of the crate
--> $DIR/coherence-vec-local-2.rs:11:1
|
LL | impl<T> Remote for Vec<Local<T>> { }
-error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
+error[E0117]: only traits defined in the current crate can be implemented for types defined outside of the crate
--> $DIR/coherence-vec-local.rs:11:1
|
LL | impl Remote for Vec<Local> { }
-error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
+error[E0117]: only traits defined in the current crate can be implemented for types defined outside of the crate
--> $DIR/coherence_local_err_struct.rs:14:1
|
LL | impl lib::MyCopy for lib::MyStruct<MyType> { }
-error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
+error[E0117]: only traits defined in the current crate can be implemented for primitive types
--> $DIR/impl-foreign-for-foreign.rs:10:1
|
LL | impl Remote for i32 {
-error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
+error[E0117]: only traits defined in the current crate can be implemented for primitive types
--> $DIR/impl-foreign-for-foreign[foreign].rs:10:1
|
LL | impl Remote1<Rc<i32>> for i32 {
|
= note: define and implement a trait or new type instead
-error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
+error[E0117]: only traits defined in the current crate can be implemented for primitive types
--> $DIR/impl-foreign-for-foreign[foreign].rs:14:1
|
LL | impl Remote1<Rc<Local>> for f64 {
|
= note: define and implement a trait or new type instead
-error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
+error[E0117]: only traits defined in the current crate can be implemented for primitive types
--> $DIR/impl-foreign-for-foreign[foreign].rs:18:1
|
LL | impl<T> Remote1<Rc<T>> for f32 {
-error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
+error[E0117]: only traits defined in the current crate can be implemented for types defined outside of the crate
--> $DIR/impl-foreign-for-fundamental[foreign].rs:10:1
|
LL | impl Remote for Box<i32> {
|
= note: define and implement a trait or new type instead
-error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
+error[E0117]: only traits defined in the current crate can be implemented for types defined outside of the crate
--> $DIR/impl-foreign-for-fundamental[foreign].rs:14:1
|
LL | impl<T> Remote for Box<Rc<T>> {
-error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
+error[E0117]: only traits defined in the current crate can be implemented for primitive types
--> $DIR/impl-foreign[foreign]-for-foreign.rs:10:1
|
LL | impl Remote1<u32> for f64 {
-error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
+error[E0117]: only traits defined in the current crate can be implemented for primitive types
--> $DIR/impl-foreign[fundemental[foreign]]-for-foreign.rs:11:1
|
LL | impl Remote1<Box<String>> for i32 {
|
= note: define and implement a trait or new type instead
-error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
+error[E0117]: only traits defined in the current crate can be implemented for primitive types
--> $DIR/impl-foreign[fundemental[foreign]]-for-foreign.rs:15:1
|
LL | impl Remote1<Box<Rc<i32>>> for f64 {
|
= note: define and implement a trait or new type instead
-error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
+error[E0117]: only traits defined in the current crate can be implemented for primitive types
--> $DIR/impl-foreign[fundemental[foreign]]-for-foreign.rs:19:1
|
LL | impl<T> Remote1<Box<Rc<T>>> for f32 {
-error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
+error[E0117]: only traits defined in the current crate can be implemented for types defined outside of the crate
--> $DIR/impl[t]-foreign-for-foreign[t].rs:11:1
|
LL | impl Remote for Rc<Local> {
|
= note: define and implement a trait or new type instead
-error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
+error[E0117]: only traits defined in the current crate can be implemented for types defined outside of the crate
--> $DIR/impl[t]-foreign-for-foreign[t].rs:16:1
|
LL | impl<T> Remote for Arc<T> {
fn will_ice(something: &u32) -> impl Iterator<Item = &u32> {
//~^ ERROR `()` is not an iterator
- //~| ERROR `()` is not an iterator
}
fn main() {}
|
= help: the trait `Iterator` is not implemented for `()`
-error[E0277]: `()` is not an iterator
- --> $DIR/conservative_impl_trait.rs:3:60
- |
-LL | fn will_ice(something: &u32) -> impl Iterator<Item = &u32> {
- | ____________________________________________________________^
-LL | |
-LL | |
-LL | | }
- | |_^ `()` is not an iterator
- |
- = help: the trait `Iterator` is not implemented for `()`
-
-error: aborting due to 2 previous errors
+error: aborting due to previous error
For more information about this error, try `rustc --explain E0277`.
= help: const parameters may only be used as standalone arguments, i.e. `N`
= help: use `#![feature(generic_const_exprs)]` to allow generic const expressions
-error: generic parameters may not be used in const operations
- --> $DIR/const-arg-in-const-arg.rs:24:23
- |
-LL | let _ = [0; bar::<N>()];
- | ^ cannot perform const operation using `N`
- |
- = help: const parameters may only be used as standalone arguments, i.e. `N`
- = help: use `#![feature(generic_const_exprs)]` to allow generic const expressions
-
-error: generic parameters may not be used in const operations
- --> $DIR/const-arg-in-const-arg.rs:29:24
- |
-LL | let _: Foo<{ foo::<T>() }>;
- | ^ cannot perform const operation using `T`
- |
- = note: type parameters may not be used in const expressions
- = help: use `#![feature(generic_const_exprs)]` to allow generic const expressions
-
-error: generic parameters may not be used in const operations
- --> $DIR/const-arg-in-const-arg.rs:30:24
- |
-LL | let _: Foo<{ bar::<N>() }>;
- | ^ cannot perform const operation using `N`
- |
- = help: const parameters may only be used as standalone arguments, i.e. `N`
- = help: use `#![feature(generic_const_exprs)]` to allow generic const expressions
-
-error: generic parameters may not be used in const operations
- --> $DIR/const-arg-in-const-arg.rs:35:27
- |
-LL | let _ = Foo::<{ foo::<T>() }>;
- | ^ cannot perform const operation using `T`
- |
- = note: type parameters may not be used in const expressions
- = help: use `#![feature(generic_const_exprs)]` to allow generic const expressions
-
-error: generic parameters may not be used in const operations
- --> $DIR/const-arg-in-const-arg.rs:36:27
- |
-LL | let _ = Foo::<{ bar::<N>() }>;
- | ^ cannot perform const operation using `N`
- |
- = help: const parameters may only be used as standalone arguments, i.e. `N`
- = help: use `#![feature(generic_const_exprs)]` to allow generic const expressions
-
error[E0658]: a non-static lifetime is not allowed in a `const`
- --> $DIR/const-arg-in-const-arg.rs:15:23
+ --> $DIR/const-arg-in-const-arg.rs:16:23
|
LL | let _: [u8; faz::<'a>(&())];
| ^^
= help: add `#![feature(generic_const_exprs)]` to the crate attributes to enable
error[E0658]: a non-static lifetime is not allowed in a `const`
- --> $DIR/const-arg-in-const-arg.rs:16:23
+ --> $DIR/const-arg-in-const-arg.rs:18:23
|
LL | let _: [u8; baz::<'a>(&())];
| ^^
= help: add `#![feature(generic_const_exprs)]` to the crate attributes to enable
error[E0658]: a non-static lifetime is not allowed in a `const`
- --> $DIR/const-arg-in-const-arg.rs:17:23
+ --> $DIR/const-arg-in-const-arg.rs:19:23
|
LL | let _: [u8; faz::<'b>(&())];
| ^^
= help: add `#![feature(generic_const_exprs)]` to the crate attributes to enable
error[E0658]: a non-static lifetime is not allowed in a `const`
- --> $DIR/const-arg-in-const-arg.rs:18:23
+ --> $DIR/const-arg-in-const-arg.rs:21:23
|
LL | let _: [u8; baz::<'b>(&())];
| ^^
= note: see issue #76560 <https://github.com/rust-lang/rust/issues/76560> for more information
= help: add `#![feature(generic_const_exprs)]` to the crate attributes to enable
+error: generic parameters may not be used in const operations
+ --> $DIR/const-arg-in-const-arg.rs:24:23
+ |
+LL | let _ = [0; bar::<N>()];
+ | ^ cannot perform const operation using `N`
+ |
+ = help: const parameters may only be used as standalone arguments, i.e. `N`
+ = help: use `#![feature(generic_const_exprs)]` to allow generic const expressions
+
error[E0658]: a non-static lifetime is not allowed in a `const`
- --> $DIR/const-arg-in-const-arg.rs:25:23
+ --> $DIR/const-arg-in-const-arg.rs:26:23
|
LL | let _ = [0; faz::<'a>(&())];
| ^^
= help: add `#![feature(generic_const_exprs)]` to the crate attributes to enable
error[E0658]: a non-static lifetime is not allowed in a `const`
- --> $DIR/const-arg-in-const-arg.rs:26:23
+ --> $DIR/const-arg-in-const-arg.rs:28:23
|
LL | let _ = [0; baz::<'a>(&())];
| ^^
= help: add `#![feature(generic_const_exprs)]` to the crate attributes to enable
error[E0658]: a non-static lifetime is not allowed in a `const`
- --> $DIR/const-arg-in-const-arg.rs:27:23
+ --> $DIR/const-arg-in-const-arg.rs:29:23
|
LL | let _ = [0; faz::<'b>(&())];
| ^^
= help: add `#![feature(generic_const_exprs)]` to the crate attributes to enable
error[E0658]: a non-static lifetime is not allowed in a `const`
- --> $DIR/const-arg-in-const-arg.rs:28:23
+ --> $DIR/const-arg-in-const-arg.rs:31:23
|
LL | let _ = [0; baz::<'b>(&())];
| ^^
= note: see issue #76560 <https://github.com/rust-lang/rust/issues/76560> for more information
= help: add `#![feature(generic_const_exprs)]` to the crate attributes to enable
+error: generic parameters may not be used in const operations
+ --> $DIR/const-arg-in-const-arg.rs:32:24
+ |
+LL | let _: Foo<{ foo::<T>() }>;
+ | ^ cannot perform const operation using `T`
+ |
+ = note: type parameters may not be used in const expressions
+ = help: use `#![feature(generic_const_exprs)]` to allow generic const expressions
+
+error: generic parameters may not be used in const operations
+ --> $DIR/const-arg-in-const-arg.rs:33:24
+ |
+LL | let _: Foo<{ bar::<N>() }>;
+ | ^ cannot perform const operation using `N`
+ |
+ = help: const parameters may only be used as standalone arguments, i.e. `N`
+ = help: use `#![feature(generic_const_exprs)]` to allow generic const expressions
+
error[E0658]: a non-static lifetime is not allowed in a `const`
- --> $DIR/const-arg-in-const-arg.rs:31:24
+ --> $DIR/const-arg-in-const-arg.rs:35:24
|
LL | let _: Foo<{ faz::<'a>(&()) }>;
| ^^
= help: add `#![feature(generic_const_exprs)]` to the crate attributes to enable
error[E0658]: a non-static lifetime is not allowed in a `const`
- --> $DIR/const-arg-in-const-arg.rs:32:24
+ --> $DIR/const-arg-in-const-arg.rs:37:24
|
LL | let _: Foo<{ baz::<'a>(&()) }>;
| ^^
= help: add `#![feature(generic_const_exprs)]` to the crate attributes to enable
error[E0658]: a non-static lifetime is not allowed in a `const`
- --> $DIR/const-arg-in-const-arg.rs:33:24
+ --> $DIR/const-arg-in-const-arg.rs:38:24
|
LL | let _: Foo<{ faz::<'b>(&()) }>;
| ^^
= help: add `#![feature(generic_const_exprs)]` to the crate attributes to enable
error[E0658]: a non-static lifetime is not allowed in a `const`
- --> $DIR/const-arg-in-const-arg.rs:34:24
+ --> $DIR/const-arg-in-const-arg.rs:40:24
|
LL | let _: Foo<{ baz::<'b>(&()) }>;
| ^^
= note: see issue #76560 <https://github.com/rust-lang/rust/issues/76560> for more information
= help: add `#![feature(generic_const_exprs)]` to the crate attributes to enable
+error: generic parameters may not be used in const operations
+ --> $DIR/const-arg-in-const-arg.rs:41:27
+ |
+LL | let _ = Foo::<{ foo::<T>() }>;
+ | ^ cannot perform const operation using `T`
+ |
+ = note: type parameters may not be used in const expressions
+ = help: use `#![feature(generic_const_exprs)]` to allow generic const expressions
+
+error: generic parameters may not be used in const operations
+ --> $DIR/const-arg-in-const-arg.rs:42:27
+ |
+LL | let _ = Foo::<{ bar::<N>() }>;
+ | ^ cannot perform const operation using `N`
+ |
+ = help: const parameters may only be used as standalone arguments, i.e. `N`
+ = help: use `#![feature(generic_const_exprs)]` to allow generic const expressions
+
error[E0658]: a non-static lifetime is not allowed in a `const`
- --> $DIR/const-arg-in-const-arg.rs:37:27
+ --> $DIR/const-arg-in-const-arg.rs:44:27
|
LL | let _ = Foo::<{ faz::<'a>(&()) }>;
| ^^
= help: add `#![feature(generic_const_exprs)]` to the crate attributes to enable
error[E0658]: a non-static lifetime is not allowed in a `const`
- --> $DIR/const-arg-in-const-arg.rs:38:27
+ --> $DIR/const-arg-in-const-arg.rs:46:27
|
LL | let _ = Foo::<{ baz::<'a>(&()) }>;
| ^^
= help: add `#![feature(generic_const_exprs)]` to the crate attributes to enable
error[E0658]: a non-static lifetime is not allowed in a `const`
- --> $DIR/const-arg-in-const-arg.rs:39:27
+ --> $DIR/const-arg-in-const-arg.rs:47:27
|
LL | let _ = Foo::<{ faz::<'b>(&()) }>;
| ^^
= help: add `#![feature(generic_const_exprs)]` to the crate attributes to enable
error[E0658]: a non-static lifetime is not allowed in a `const`
- --> $DIR/const-arg-in-const-arg.rs:40:27
+ --> $DIR/const-arg-in-const-arg.rs:49:27
|
LL | let _ = Foo::<{ baz::<'b>(&()) }>;
| ^^
= note: see issue #76560 <https://github.com/rust-lang/rust/issues/76560> for more information
= help: add `#![feature(generic_const_exprs)]` to the crate attributes to enable
-error: aborting due to 23 previous errors
+error[E0747]: unresolved item provided when a constant was expected
+ --> $DIR/const-arg-in-const-arg.rs:14:23
+ |
+LL | let _: [u8; bar::<N>()];
+ | ^
+ |
+help: if this generic argument was intended as a const parameter, surround it with braces
+ |
+LL | let _: [u8; bar::<{ N }>()];
+ | + +
+
+error: cannot specify lifetime arguments explicitly if late bound lifetime parameters are present
+ --> $DIR/const-arg-in-const-arg.rs:16:23
+ |
+LL | let _: [u8; faz::<'a>(&())];
+ | ^^
+ |
+note: the late bound lifetime parameter is introduced here
+ --> $DIR/const-arg-in-const-arg.rs:8:14
+ |
+LL | const fn faz<'a>(_: &'a ()) -> usize { 13 }
+ | ^^
+
+error: cannot specify lifetime arguments explicitly if late bound lifetime parameters are present
+ --> $DIR/const-arg-in-const-arg.rs:19:23
+ |
+LL | let _: [u8; faz::<'b>(&())];
+ | ^^
+ |
+note: the late bound lifetime parameter is introduced here
+ --> $DIR/const-arg-in-const-arg.rs:8:14
+ |
+LL | const fn faz<'a>(_: &'a ()) -> usize { 13 }
+ | ^^
+
+error[E0747]: unresolved item provided when a constant was expected
+ --> $DIR/const-arg-in-const-arg.rs:24:23
+ |
+LL | let _ = [0; bar::<N>()];
+ | ^
+ |
+help: if this generic argument was intended as a const parameter, surround it with braces
+ |
+LL | let _ = [0; bar::<{ N }>()];
+ | + +
+
+error: cannot specify lifetime arguments explicitly if late bound lifetime parameters are present
+ --> $DIR/const-arg-in-const-arg.rs:26:23
+ |
+LL | let _ = [0; faz::<'a>(&())];
+ | ^^
+ |
+note: the late bound lifetime parameter is introduced here
+ --> $DIR/const-arg-in-const-arg.rs:8:14
+ |
+LL | const fn faz<'a>(_: &'a ()) -> usize { 13 }
+ | ^^
+
+error: cannot specify lifetime arguments explicitly if late bound lifetime parameters are present
+ --> $DIR/const-arg-in-const-arg.rs:29:23
+ |
+LL | let _ = [0; faz::<'b>(&())];
+ | ^^
+ |
+note: the late bound lifetime parameter is introduced here
+ --> $DIR/const-arg-in-const-arg.rs:8:14
+ |
+LL | const fn faz<'a>(_: &'a ()) -> usize { 13 }
+ | ^^
+
+error[E0747]: unresolved item provided when a constant was expected
+ --> $DIR/const-arg-in-const-arg.rs:33:24
+ |
+LL | let _: Foo<{ bar::<N>() }>;
+ | ^
+ |
+help: if this generic argument was intended as a const parameter, surround it with braces
+ |
+LL | let _: Foo<{ bar::<{ N }>() }>;
+ | + +
+
+error: cannot specify lifetime arguments explicitly if late bound lifetime parameters are present
+ --> $DIR/const-arg-in-const-arg.rs:35:24
+ |
+LL | let _: Foo<{ faz::<'a>(&()) }>;
+ | ^^
+ |
+note: the late bound lifetime parameter is introduced here
+ --> $DIR/const-arg-in-const-arg.rs:8:14
+ |
+LL | const fn faz<'a>(_: &'a ()) -> usize { 13 }
+ | ^^
+
+error: cannot specify lifetime arguments explicitly if late bound lifetime parameters are present
+ --> $DIR/const-arg-in-const-arg.rs:38:24
+ |
+LL | let _: Foo<{ faz::<'b>(&()) }>;
+ | ^^
+ |
+note: the late bound lifetime parameter is introduced here
+ --> $DIR/const-arg-in-const-arg.rs:8:14
+ |
+LL | const fn faz<'a>(_: &'a ()) -> usize { 13 }
+ | ^^
+
+error: constant expression depends on a generic parameter
+ --> $DIR/const-arg-in-const-arg.rs:23:17
+ |
+LL | let _ = [0; foo::<T>()];
+ | ^^^^^^^^^^
+ |
+ = note: this may fail depending on what value the parameter takes
+
+error[E0747]: unresolved item provided when a constant was expected
+ --> $DIR/const-arg-in-const-arg.rs:42:27
+ |
+LL | let _ = Foo::<{ bar::<N>() }>;
+ | ^
+ |
+help: if this generic argument was intended as a const parameter, surround it with braces
+ |
+LL | let _ = Foo::<{ bar::<{ N }>() }>;
+ | + +
+
+error: cannot specify lifetime arguments explicitly if late bound lifetime parameters are present
+ --> $DIR/const-arg-in-const-arg.rs:44:27
+ |
+LL | let _ = Foo::<{ faz::<'a>(&()) }>;
+ | ^^
+ |
+note: the late bound lifetime parameter is introduced here
+ --> $DIR/const-arg-in-const-arg.rs:8:14
+ |
+LL | const fn faz<'a>(_: &'a ()) -> usize { 13 }
+ | ^^
+
+error: cannot specify lifetime arguments explicitly if late bound lifetime parameters are present
+ --> $DIR/const-arg-in-const-arg.rs:47:27
+ |
+LL | let _ = Foo::<{ faz::<'b>(&()) }>;
+ | ^^
+ |
+note: the late bound lifetime parameter is introduced here
+ --> $DIR/const-arg-in-const-arg.rs:8:14
+ |
+LL | const fn faz<'a>(_: &'a ()) -> usize { 13 }
+ | ^^
+
+error: aborting due to 36 previous errors
-For more information about this error, try `rustc --explain E0658`.
+Some errors have detailed explanations: E0658, E0747.
+For more information about an error, try `rustc --explain E0658`.
fn test<'a, 'b, T, const N: usize>() where &'b (): Sized {
let _: [u8; foo::<T>()]; //~ ERROR generic parameters may not
let _: [u8; bar::<N>()]; //~ ERROR generic parameters may not
+ //~^ ERROR unresolved item provided when a constant was expected
let _: [u8; faz::<'a>(&())]; //~ ERROR a non-static lifetime
+ //~^ ERROR cannot specify lifetime arguments
let _: [u8; baz::<'a>(&())]; //~ ERROR a non-static lifetime
let _: [u8; faz::<'b>(&())]; //~ ERROR a non-static lifetime
+ //~^ ERROR cannot specify lifetime arguments
let _: [u8; baz::<'b>(&())]; //~ ERROR a non-static lifetime
- // NOTE: This can be a future compat warning instead of an error,
- // so we stop compilation before emitting this error in this test.
- let _ = [0; foo::<T>()];
-
+ let _ = [0; foo::<T>()]; //~ ERROR constant expression depends on a generic parameter
let _ = [0; bar::<N>()]; //~ ERROR generic parameters may not
+ //~^ ERROR unresolved item provided when a constant was expected
let _ = [0; faz::<'a>(&())]; //~ ERROR a non-static lifetime
+ //~^ ERROR cannot specify lifetime arguments
let _ = [0; baz::<'a>(&())]; //~ ERROR a non-static lifetime
let _ = [0; faz::<'b>(&())]; //~ ERROR a non-static lifetime
+ //~^ ERROR cannot specify lifetime arguments
let _ = [0; baz::<'b>(&())]; //~ ERROR a non-static lifetime
let _: Foo<{ foo::<T>() }>; //~ ERROR generic parameters may not
let _: Foo<{ bar::<N>() }>; //~ ERROR generic parameters may not
+ //~^ ERROR unresolved item provided when a constant was expected
let _: Foo<{ faz::<'a>(&()) }>; //~ ERROR a non-static lifetime
+ //~^ ERROR cannot specify lifetime arguments
let _: Foo<{ baz::<'a>(&()) }>; //~ ERROR a non-static lifetime
let _: Foo<{ faz::<'b>(&()) }>; //~ ERROR a non-static lifetime
+ //~^ ERROR cannot specify lifetime arguments
let _: Foo<{ baz::<'b>(&()) }>; //~ ERROR a non-static lifetime
let _ = Foo::<{ foo::<T>() }>; //~ ERROR generic parameters may not
let _ = Foo::<{ bar::<N>() }>; //~ ERROR generic parameters may not
+ //~^ ERROR unresolved item provided when a constant was expected
let _ = Foo::<{ faz::<'a>(&()) }>; //~ ERROR a non-static lifetime
+ //~^ ERROR cannot specify lifetime arguments
let _ = Foo::<{ baz::<'a>(&()) }>; //~ ERROR a non-static lifetime
let _ = Foo::<{ faz::<'b>(&()) }>; //~ ERROR a non-static lifetime
+ //~^ ERROR cannot specify lifetime arguments
let _ = Foo::<{ baz::<'b>(&()) }>; //~ ERROR a non-static lifetime
}
fn rawr() -> impl Trait {
//~^ error: the trait bound `Uwu<10_u32, 12_u32>: Trait` is not satisfied
- //~| error: the trait bound `Uwu<10_u32, 12_u32>: Trait` is not satisfied
Uwu::<10, 12>
}
fn uwu<const N: u8>() -> impl Traitor<N> {
//~^ error: the trait bound `u32: Traitor<N, N>` is not satisfied
- //~| error: the trait bound `u32: Traitor<N, N>` is not satisfied
1_u32
}
fn owo() -> impl Traitor {
//~^ error: the trait bound `u64: Traitor<1_u8, 1_u8>` is not satisfied
- //~| error: the trait bound `u64: Traitor<1_u8, 1_u8>` is not satisfied
1_u64
}
|
= help: the trait `Trait` is implemented for `Uwu<N>`
-error[E0277]: the trait bound `Uwu<10_u32, 12_u32>: Trait` is not satisfied
- --> $DIR/rp_impl_trait_fail.rs:6:25
- |
-LL | fn rawr() -> impl Trait {
- | _________________________^
-LL | |
-LL | |
-LL | | Uwu::<10, 12>
-LL | | }
- | |_^ the trait `Trait` is not implemented for `Uwu<10_u32, 12_u32>`
- |
- = help: the trait `Trait` is implemented for `Uwu<N>`
-
error[E0277]: the trait bound `u32: Traitor<N, N>` is not satisfied
- --> $DIR/rp_impl_trait_fail.rs:18:26
+ --> $DIR/rp_impl_trait_fail.rs:17:26
|
LL | fn uwu<const N: u8>() -> impl Traitor<N> {
| ^^^^^^^^^^^^^^^ the trait `Traitor<N, N>` is not implemented for `u32`
<u32 as Traitor<N, 2_u8>>
<u64 as Traitor<1_u8, 2_u8>>
-error[E0277]: the trait bound `u32: Traitor<N, N>` is not satisfied
- --> $DIR/rp_impl_trait_fail.rs:18:42
- |
-LL | fn uwu<const N: u8>() -> impl Traitor<N> {
- | __________________________________________^
-LL | |
-LL | |
-LL | | 1_u32
-LL | | }
- | |_^ the trait `Traitor<N, N>` is not implemented for `u32`
- |
- = help: the following other types implement trait `Traitor<N, M>`:
- <u32 as Traitor<N, 2_u8>>
- <u64 as Traitor<1_u8, 2_u8>>
-
error[E0277]: the trait bound `u64: Traitor<1_u8, 1_u8>` is not satisfied
- --> $DIR/rp_impl_trait_fail.rs:24:13
+ --> $DIR/rp_impl_trait_fail.rs:22:13
|
LL | fn owo() -> impl Traitor {
| ^^^^^^^^^^^^ the trait `Traitor<1_u8, 1_u8>` is not implemented for `u64`
<u32 as Traitor<N, 2_u8>>
<u64 as Traitor<1_u8, 2_u8>>
-error[E0277]: the trait bound `u64: Traitor<1_u8, 1_u8>` is not satisfied
- --> $DIR/rp_impl_trait_fail.rs:24:26
- |
-LL | fn owo() -> impl Traitor {
- | __________________________^
-LL | |
-LL | |
-LL | | 1_u64
-LL | | }
- | |_^ the trait `Traitor<1_u8, 1_u8>` is not implemented for `u64`
- |
- = help: the following other types implement trait `Traitor<N, M>`:
- <u32 as Traitor<N, 2_u8>>
- <u64 as Traitor<1_u8, 2_u8>>
-
-error: aborting due to 6 previous errors
+error: aborting due to 3 previous errors
For more information about this error, try `rustc --explain E0277`.
|
= note: for more information, see issue #74052 <https://github.com/rust-lang/rust/issues/74052>
-error: aborting due to previous error
+error: `&'static str` is forbidden as the type of a const generic parameter
+ --> $DIR/issue-56445-1.rs:9:25
+ |
+LL | struct Bug<'a, const S: &'a str>(PhantomData<&'a ()>);
+ | ^^^^^^^
+ |
+ = note: the only supported types are integers, `bool` and `char`
+ = help: more complex types are supported with `#![feature(adt_const_params)]`
+
+error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0771`.
struct Bug<'a, const S: &'a str>(PhantomData<&'a ()>);
//~^ ERROR: use of non-static lifetime `'a` in const generic
+//[min]~| ERROR: `&'static str` is forbidden as the type of a const generic parameter
impl Bug<'_, ""> {}
error[E0277]: the trait bound `T: Copy` is not satisfied
- --> $DIR/issue-61336-2.rs:6:5
+ --> $DIR/issue-61336-2.rs:6:6
|
LL | [x; { N }]
- | ^^^^^^^^^^ the trait `Copy` is not implemented for `T`
+ | ^ the trait `Copy` is not implemented for `T`
|
- = note: the `Copy` trait is required because the repeated element will be copied
+ = note: the `Copy` trait is required because this value will be copied for each element of the array
help: consider restricting type parameter `T`
|
LL | fn g<T: std::marker::Copy, const N: usize>(x: T) -> [T; N] {
error[E0277]: the trait bound `T: Copy` is not satisfied
- --> $DIR/issue-61336.rs:6:5
+ --> $DIR/issue-61336.rs:6:6
|
LL | [x; N]
- | ^^^^^^ the trait `Copy` is not implemented for `T`
+ | ^ the trait `Copy` is not implemented for `T`
|
- = note: the `Copy` trait is required because the repeated element will be copied
+ = note: the `Copy` trait is required because this value will be copied for each element of the array
help: consider restricting type parameter `T`
|
LL | fn g<T: std::marker::Copy, const N: usize>(x: T) -> [T; N] {
fn main() {
let _: [u32; 2] = [copy(); 2];
let _: [Option<Bar>; 2] = [no_copy(); 2];
- //~^ ERROR the trait bound `Option<Bar>: Copy` is not satisfied
+ //~^ ERROR the trait bound `Bar: Copy` is not satisfied
}
-error[E0277]: the trait bound `Option<Bar>: Copy` is not satisfied
- --> $DIR/fn-call-in-non-const.rs:14:31
+error[E0277]: the trait bound `Bar: Copy` is not satisfied
+ --> $DIR/fn-call-in-non-const.rs:14:32
|
LL | let _: [Option<Bar>; 2] = [no_copy(); 2];
- | ^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `Option<Bar>`
+ | ^^^^^^^^^ the trait `Copy` is not implemented for `Bar`
|
- = help: the trait `Copy` is implemented for `Option<T>`
- = note: the `Copy` trait is required because the repeated element will be copied
+ = note: required because of the requirements on the impl of `Copy` for `Option<Bar>`
+ = note: the `Copy` trait is required because this value will be copied for each element of the array
= help: consider creating a new `const` item and initializing it with the result of the function call to be used in the repeat position, like `const VAL: Type = const_fn();` and `let x = [VAL; 42];`
= help: create an inline `const` block, see RFC #2920 <https://github.com/rust-lang/rfcs/pull/2920> for more information
+help: consider annotating `Bar` with `#[derive(Copy)]`
+ |
+LL | #[derive(Copy)]
+ |
error: aborting due to previous error
fn no_impl_copy_empty_value_multiple_elements() {
let x = None;
let arr: [Option<Bar>; 2] = [x; 2];
- //~^ ERROR the trait bound `Option<Bar>: Copy` is not satisfied [E0277]
+ //~^ ERROR the trait bound `Bar: Copy` is not satisfied [E0277]
}
fn no_impl_copy_value_multiple_elements() {
let x = Some(Bar);
let arr: [Option<Bar>; 2] = [x; 2];
- //~^ ERROR the trait bound `Option<Bar>: Copy` is not satisfied [E0277]
+ //~^ ERROR the trait bound `Bar: Copy` is not satisfied [E0277]
}
}
-error[E0277]: the trait bound `Option<Bar>: Copy` is not satisfied
- --> $DIR/migrate-fail.rs:13:37
+error[E0277]: the trait bound `Bar: Copy` is not satisfied
+ --> $DIR/migrate-fail.rs:13:38
|
LL | let arr: [Option<Bar>; 2] = [x; 2];
- | ^^^^^^ the trait `Copy` is not implemented for `Option<Bar>`
+ | ^ the trait `Copy` is not implemented for `Bar`
+ |
+ = note: required because of the requirements on the impl of `Copy` for `Option<Bar>`
+ = note: the `Copy` trait is required because this value will be copied for each element of the array
+help: consider annotating `Bar` with `#[derive(Copy)]`
+ |
+LL | #[derive(Copy)]
|
- = help: the trait `Copy` is implemented for `Option<T>`
- = note: the `Copy` trait is required because the repeated element will be copied
-error[E0277]: the trait bound `Option<Bar>: Copy` is not satisfied
- --> $DIR/migrate-fail.rs:19:37
+error[E0277]: the trait bound `Bar: Copy` is not satisfied
+ --> $DIR/migrate-fail.rs:19:38
|
LL | let arr: [Option<Bar>; 2] = [x; 2];
- | ^^^^^^ the trait `Copy` is not implemented for `Option<Bar>`
+ | ^ the trait `Copy` is not implemented for `Bar`
+ |
+ = note: required because of the requirements on the impl of `Copy` for `Option<Bar>`
+ = note: the `Copy` trait is required because this value will be copied for each element of the array
+help: consider annotating `Bar` with `#[derive(Copy)]`
+ |
+LL | #[derive(Copy)]
|
- = help: the trait `Copy` is implemented for `Option<T>`
- = note: the `Copy` trait is required because the repeated element will be copied
error: aborting due to 2 previous errors
fn no_impl_copy_empty_value_multiple_elements() {
let x = None;
let arr: [Option<Bar>; 2] = [x; 2];
- //~^ ERROR the trait bound `Option<Bar>: Copy` is not satisfied [E0277]
+ //~^ ERROR the trait bound `Bar: Copy` is not satisfied [E0277]
}
fn no_impl_copy_value_multiple_elements() {
let x = Some(Bar);
let arr: [Option<Bar>; 2] = [x; 2];
- //~^ ERROR the trait bound `Option<Bar>: Copy` is not satisfied [E0277]
+ //~^ ERROR the trait bound `Bar: Copy` is not satisfied [E0277]
}
}
-error[E0277]: the trait bound `Option<Bar>: Copy` is not satisfied
- --> $DIR/nll-fail.rs:12:37
+error[E0277]: the trait bound `Bar: Copy` is not satisfied
+ --> $DIR/nll-fail.rs:12:38
|
LL | let arr: [Option<Bar>; 2] = [x; 2];
- | ^^^^^^ the trait `Copy` is not implemented for `Option<Bar>`
+ | ^ the trait `Copy` is not implemented for `Bar`
+ |
+ = note: required because of the requirements on the impl of `Copy` for `Option<Bar>`
+ = note: the `Copy` trait is required because this value will be copied for each element of the array
+help: consider annotating `Bar` with `#[derive(Copy)]`
+ |
+LL | #[derive(Copy)]
|
- = help: the trait `Copy` is implemented for `Option<T>`
- = note: the `Copy` trait is required because the repeated element will be copied
-error[E0277]: the trait bound `Option<Bar>: Copy` is not satisfied
- --> $DIR/nll-fail.rs:18:37
+error[E0277]: the trait bound `Bar: Copy` is not satisfied
+ --> $DIR/nll-fail.rs:18:38
|
LL | let arr: [Option<Bar>; 2] = [x; 2];
- | ^^^^^^ the trait `Copy` is not implemented for `Option<Bar>`
+ | ^ the trait `Copy` is not implemented for `Bar`
+ |
+ = note: required because of the requirements on the impl of `Copy` for `Option<Bar>`
+ = note: the `Copy` trait is required because this value will be copied for each element of the array
+help: consider annotating `Bar` with `#[derive(Copy)]`
+ |
+LL | #[derive(Copy)]
|
- = help: the trait `Copy` is implemented for `Option<T>`
- = note: the `Copy` trait is required because the repeated element will be copied
error: aborting due to 2 previous errors
fn main() {
[Foo(String::new()); 4];
- //~^ ERROR the trait bound `Foo<String>: Copy` is not satisfied [E0277]
+ //~^ ERROR the trait bound `String: Copy` is not satisfied [E0277]
}
-error[E0277]: the trait bound `Foo<String>: Copy` is not satisfied
- --> $DIR/trait-error.rs:5:5
+error[E0277]: the trait bound `String: Copy` is not satisfied
+ --> $DIR/trait-error.rs:5:6
|
LL | [Foo(String::new()); 4];
- | ^^^^^^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `Foo<String>`
+ | ^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `String`
|
- = help: the trait `Copy` is implemented for `Foo<T>`
- = note: the `Copy` trait is required because the repeated element will be copied
+note: required because of the requirements on the impl of `Copy` for `Foo<String>`
+ --> $DIR/trait-error.rs:1:10
+ |
+LL | #[derive(Copy, Clone)]
+ | ^^^^
+ = note: the `Copy` trait is required because this value will be copied for each element of the array
+ = help: consider creating a new `const` item and initializing it with the result of the function call to be used in the repeat position, like `const VAL: Type = const_fn();` and `let x = [VAL; 42];`
+ = help: create an inline `const` block, see RFC #2920 <https://github.com/rust-lang/rfcs/pull/2920> for more information
+ = note: this error originates in the derive macro `Copy` (in Nightly builds, run with -Z macro-backtrace for more info)
error: aborting due to previous error
LL | &<A<T> as Foo<T>>::BAR
| ^^^^^^^^^^^^^^^^^^^^^ referenced constant has errors
+note: the above error was encountered while instantiating `fn foo::<()>`
+ --> $DIR/issue-50814-2.rs:31:22
+ |
+LL | println!("{:x}", foo::<()>() as *const usize as usize);
+ | ^^^^^^^^^^^
+
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0080`.
LL | &Sum::<U8,U8>::MAX
| ^^^^^^^^^^^^^^^^^ referenced constant has errors
+note: the above error was encountered while instantiating `fn foo::<i32>`
+ --> $DIR/issue-50814.rs:26:5
+ |
+LL | foo(0);
+ | ^^^^^^
+
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0080`.
| transmuting to uninhabited type
| inside `foo` at $DIR/validate_uninhabited_zsts.rs:4:14
...
-LL | const FOO: [Empty; 3] = [foo(); 3];
- | ----- inside `FOO` at $DIR/validate_uninhabited_zsts.rs:13:26
+LL | const FOO: [empty::Empty; 3] = [foo(); 3];
+ | ----- inside `FOO` at $DIR/validate_uninhabited_zsts.rs:20:33
-error[E0080]: evaluation of constant value failed
- --> $DIR/validate_uninhabited_zsts.rs:16:35
+error[E0080]: it is undefined behavior to use this value
+ --> $DIR/validate_uninhabited_zsts.rs:23:1
+ |
+LL | const BAR: [empty::Empty; 3] = [unsafe { std::mem::transmute(()) }; 3];
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed at [0].0: encountered a value of uninhabited type empty::Void
|
-LL | const BAR: [Empty; 3] = [unsafe { std::mem::transmute(()) }; 3];
- | ^^^^^^^^^^^^^^^^^^^^^^^ transmuting to uninhabited type
+ = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
+ = note: the raw bytes of the constant (size: 0, align: 1) {}
warning: the type `!` does not permit zero-initialization
--> $DIR/validate_uninhabited_zsts.rs:4:14
= note: `#[warn(invalid_value)]` on by default
= note: the `!` type has no valid value
-warning: the type `Empty` does not permit zero-initialization
- --> $DIR/validate_uninhabited_zsts.rs:16:35
+warning: the type `empty::Empty` does not permit zero-initialization
+ --> $DIR/validate_uninhabited_zsts.rs:23:42
+ |
+LL | const BAR: [empty::Empty; 3] = [unsafe { std::mem::transmute(()) }; 3];
+ | ^^^^^^^^^^^^^^^^^^^^^^^
+ | |
+ | this code causes undefined behavior when executed
+ | help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
|
-LL | const BAR: [Empty; 3] = [unsafe { std::mem::transmute(()) }; 3];
- | ^^^^^^^^^^^^^^^^^^^^^^^
- | |
- | this code causes undefined behavior when executed
- | help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
+note: enums with no variants have no valid value (in this struct field)
+ --> $DIR/validate_uninhabited_zsts.rs:16:22
|
- = note: enums with no variants have no valid value
+LL | pub struct Empty(Void);
+ | ^^^^
error: aborting due to 2 previous errors; 2 warnings emitted
| transmuting to uninhabited type
| inside `foo` at $DIR/validate_uninhabited_zsts.rs:4:14
...
-LL | const FOO: [Empty; 3] = [foo(); 3];
- | ----- inside `FOO` at $DIR/validate_uninhabited_zsts.rs:13:26
+LL | const FOO: [empty::Empty; 3] = [foo(); 3];
+ | ----- inside `FOO` at $DIR/validate_uninhabited_zsts.rs:20:33
-error[E0080]: evaluation of constant value failed
- --> $DIR/validate_uninhabited_zsts.rs:16:35
+error[E0080]: it is undefined behavior to use this value
+ --> $DIR/validate_uninhabited_zsts.rs:23:1
+ |
+LL | const BAR: [empty::Empty; 3] = [unsafe { std::mem::transmute(()) }; 3];
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type validation failed at [0].0: encountered a value of uninhabited type empty::Void
|
-LL | const BAR: [Empty; 3] = [unsafe { std::mem::transmute(()) }; 3];
- | ^^^^^^^^^^^^^^^^^^^^^^^ transmuting to uninhabited type
+ = note: The rules on what exactly is undefined behavior aren't clear, so this check might be overzealous. Please open an issue on the rustc repository if you believe it should not be considered undefined behavior.
+ = note: the raw bytes of the constant (size: 0, align: 1) {}
warning: the type `!` does not permit zero-initialization
--> $DIR/validate_uninhabited_zsts.rs:4:14
= note: `#[warn(invalid_value)]` on by default
= note: the `!` type has no valid value
-warning: the type `Empty` does not permit zero-initialization
- --> $DIR/validate_uninhabited_zsts.rs:16:35
+warning: the type `empty::Empty` does not permit zero-initialization
+ --> $DIR/validate_uninhabited_zsts.rs:23:42
+ |
+LL | const BAR: [empty::Empty; 3] = [unsafe { std::mem::transmute(()) }; 3];
+ | ^^^^^^^^^^^^^^^^^^^^^^^
+ | |
+ | this code causes undefined behavior when executed
+ | help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
|
-LL | const BAR: [Empty; 3] = [unsafe { std::mem::transmute(()) }; 3];
- | ^^^^^^^^^^^^^^^^^^^^^^^
- | |
- | this code causes undefined behavior when executed
- | help: use `MaybeUninit<T>` instead, and only call `assume_init` after initialization is done
+note: enums with no variants have no valid value (in this struct field)
+ --> $DIR/validate_uninhabited_zsts.rs:16:22
|
- = note: enums with no variants have no valid value
+LL | pub struct Empty(Void);
+ | ^^^^
error: aborting due to 2 previous errors; 2 warnings emitted
//~| WARN the type `!` does not permit zero-initialization [invalid_value]
}
-#[derive(Clone, Copy)]
-enum Empty { }
+// Type defined in a submodule, so that it is not "visibly"
+// uninhabited (which would change interpreter behavior).
+pub mod empty {
+ #[derive(Clone, Copy)]
+ enum Void {}
+
+ #[derive(Clone, Copy)]
+ pub struct Empty(Void);
+}
#[warn(const_err)]
-const FOO: [Empty; 3] = [foo(); 3];
+const FOO: [empty::Empty; 3] = [foo(); 3];
#[warn(const_err)]
-const BAR: [Empty; 3] = [unsafe { std::mem::transmute(()) }; 3];
-//~^ ERROR evaluation of constant value failed
-//~| WARN the type `Empty` does not permit zero-initialization
+const BAR: [empty::Empty; 3] = [unsafe { std::mem::transmute(()) }; 3];
+//~^ ERROR it is undefined behavior to use this value
+//~| WARN the type `empty::Empty` does not permit zero-initialization
fn main() {
FOO;
= note: consult the function's documentation for information on how to avoid undefined behavior
error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
- --> $DIR/const-extern-fn-requires-unsafe.rs:11:5
+ --> $DIR/const-extern-fn-requires-unsafe.rs:12:5
|
LL | foo();
| ^^^^^ call to unsafe function
fn main() {
let a: [u8; foo()];
- //~^ ERROR call to unsafe function is unsafe and requires unsafe function or block
+ //[mir]~^ call to unsafe function is unsafe and requires unsafe function or block
+ //[thir]~^^ call to unsafe function `foo` is unsafe and requires unsafe function or block
foo();
//[mir]~^ ERROR call to unsafe function is unsafe and requires unsafe function or block
}
-error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
+error[E0133]: call to unsafe function `foo` is unsafe and requires unsafe function or block
--> $DIR/const-extern-fn-requires-unsafe.rs:9:17
|
LL | let a: [u8; foo()];
const_assert!(f32::from_bits(0x44a72000), 1337.0);
const_assert!(f32::from_ne_bytes(0x44a72000u32.to_ne_bytes()), 1337.0);
const_assert!(f32::from_bits(0xc1640000), -14.25);
-
- // Check that NaNs roundtrip their bits regardless of signalingness
- // 0xA is 0b1010; 0x5 is 0b0101 -- so these two together clobbers all the mantissa bits
- const MASKED_NAN1: u32 = f32::NAN.to_bits() ^ 0x002A_AAAA;
- const MASKED_NAN2: u32 = f32::NAN.to_bits() ^ 0x0055_5555;
-
- const_assert!(f32::from_bits(MASKED_NAN1).is_nan());
- const_assert!(f32::from_bits(MASKED_NAN1).is_nan());
-
- // LLVM does not guarantee that loads and stores of NaNs preserve their exact bit pattern.
- // In practice, this seems to only cause a problem on x86, since the most widely used calling
- // convention mandates that floating point values are returned on the x87 FPU stack. See #73328.
- if !cfg!(target_arch = "x86") {
- const_assert!(f32::from_bits(MASKED_NAN1).to_bits(), MASKED_NAN1);
- const_assert!(f32::from_bits(MASKED_NAN2).to_bits(), MASKED_NAN2);
- }
}
fn f64() {
const_assert!(f64::from_bits(0x4094e40000000000), 1337.0);
const_assert!(f64::from_ne_bytes(0x4094e40000000000u64.to_ne_bytes()), 1337.0);
const_assert!(f64::from_bits(0xc02c800000000000), -14.25);
-
- // Check that NaNs roundtrip their bits regardless of signalingness
- // 0xA is 0b1010; 0x5 is 0b0101 -- so these two together clobbers all the mantissa bits
- const MASKED_NAN1: u64 = f64::NAN.to_bits() ^ 0x000A_AAAA_AAAA_AAAA;
- const MASKED_NAN2: u64 = f64::NAN.to_bits() ^ 0x0005_5555_5555_5555;
-
- const_assert!(f64::from_bits(MASKED_NAN1).is_nan());
- const_assert!(f64::from_bits(MASKED_NAN1).is_nan());
-
- // See comment above.
- if !cfg!(target_arch = "x86") {
- const_assert!(f64::from_bits(MASKED_NAN1).to_bits(), MASKED_NAN1);
- const_assert!(f64::from_bits(MASKED_NAN2).to_bits(), MASKED_NAN2);
- }
}
fn main() {
--- /dev/null
+// compile-flags: -Zmir-opt-level=0
+#![feature(const_float_bits_conv)]
+#![feature(const_float_classify)]
+
+// Don't promote
+const fn nop<T>(x: T) -> T { x }
+
+macro_rules! const_assert {
+ ($a:expr) => {
+ {
+ const _: () = assert!($a);
+ assert!(nop($a));
+ }
+ };
+ ($a:expr, $b:expr) => {
+ {
+ const _: () = assert!($a == $b);
+ assert_eq!(nop($a), nop($b));
+ }
+ };
+}
+
+fn f32() {
+ // Check that NaNs roundtrip their bits regardless of signalingness
+ // 0xA is 0b1010; 0x5 is 0b0101 -- so these two together clobbers all the mantissa bits
+ // ...actually, let's just check that these break. :D
+ const MASKED_NAN1: u32 = f32::NAN.to_bits() ^ 0x002A_AAAA;
+ const MASKED_NAN2: u32 = f32::NAN.to_bits() ^ 0x0055_5555;
+
+ const_assert!(f32::from_bits(MASKED_NAN1).is_nan());
+ const_assert!(f32::from_bits(MASKED_NAN1).is_nan());
+
+ // LLVM does not guarantee that loads and stores of NaNs preserve their exact bit pattern.
+ // In practice, this seems to only cause a problem on x86, since the most widely used calling
+ // convention mandates that floating point values are returned on the x87 FPU stack. See #73328.
+ if !cfg!(target_arch = "x86") {
+ const_assert!(f32::from_bits(MASKED_NAN1).to_bits(), MASKED_NAN1);
+ const_assert!(f32::from_bits(MASKED_NAN2).to_bits(), MASKED_NAN2);
+ }
+}
+
+fn f64() {
+ // Check that NaNs roundtrip their bits regardless of signalingness
+ // 0xA is 0b1010; 0x5 is 0b0101 -- so these two together clobbers all the mantissa bits
+ // ...actually, let's just check that these break. :D
+ const MASKED_NAN1: u64 = f64::NAN.to_bits() ^ 0x000A_AAAA_AAAA_AAAA;
+ const MASKED_NAN2: u64 = f64::NAN.to_bits() ^ 0x0005_5555_5555_5555;
+
+ const_assert!(f64::from_bits(MASKED_NAN1).is_nan());
+ const_assert!(f64::from_bits(MASKED_NAN1).is_nan());
+
+ // See comment above.
+ if !cfg!(target_arch = "x86") {
+ const_assert!(f64::from_bits(MASKED_NAN1).to_bits(), MASKED_NAN1);
+ const_assert!(f64::from_bits(MASKED_NAN2).to_bits(), MASKED_NAN2);
+ }
+}
+
+fn main() {
+ f32();
+ f64();
+}
--- /dev/null
+error[E0080]: evaluation of constant value failed
+ --> $SRC_DIR/core/src/num/f32.rs:LL:COL
+ |
+LL | panic!("const-eval error: cannot use f32::to_bits on a NaN")
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | |
+ | the evaluated program panicked at 'const-eval error: cannot use f32::to_bits on a NaN', $SRC_DIR/core/src/num/f32.rs:LL:COL
+ | inside `core::f32::<impl f32>::to_bits::ct_f32_to_u32` at $SRC_DIR/core/src/panic.rs:LL:COL
+...
+LL | unsafe { intrinsics::const_eval_select((self,), ct_f32_to_u32, rt_f32_to_u32) }
+ | -------------------------------------------------------------------- inside `core::f32::<impl f32>::to_bits` at $SRC_DIR/core/src/num/f32.rs:LL:COL
+ |
+ ::: $SRC_DIR/core/src/ops/function.rs:LL:COL
+ |
+LL | extern "rust-call" fn call_once(self, args: Args) -> Self::Output;
+ | ------------------------------------------------------------------ inside `<fn(f32) -> u32 {core::f32::<impl f32>::to_bits::ct_f32_to_u32} as FnOnce<(f32,)>>::call_once - shim(fn(f32) -> u32 {core::f32::<impl f32>::to_bits::ct_f32_to_u32})` at $SRC_DIR/core/src/ops/function.rs:LL:COL
+ |
+ ::: $SRC_DIR/core/src/intrinsics.rs:LL:COL
+ |
+LL | called_in_const.call_once(arg)
+ | ------------------------------ inside `const_eval_select::<(f32,), fn(f32) -> u32 {core::f32::<impl f32>::to_bits::ct_f32_to_u32}, [closure@core::f32::<impl f32>::to_bits::{closure#0}], u32>` at $SRC_DIR/core/src/intrinsics.rs:LL:COL
+ |
+ ::: $DIR/const-float-bits-reject-conv.rs:27:30
+ |
+LL | const MASKED_NAN1: u32 = f32::NAN.to_bits() ^ 0x002A_AAAA;
+ | ------------------ inside `f32::MASKED_NAN1` at $DIR/const-float-bits-reject-conv.rs:27:30
+ |
+ = note: this error originates in the macro `$crate::panic::panic_2021` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error[E0080]: evaluation of constant value failed
+ --> $SRC_DIR/core/src/num/f32.rs:LL:COL
+ |
+LL | panic!("const-eval error: cannot use f32::to_bits on a NaN")
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | |
+ | the evaluated program panicked at 'const-eval error: cannot use f32::to_bits on a NaN', $SRC_DIR/core/src/num/f32.rs:LL:COL
+ | inside `core::f32::<impl f32>::to_bits::ct_f32_to_u32` at $SRC_DIR/core/src/panic.rs:LL:COL
+...
+LL | unsafe { intrinsics::const_eval_select((self,), ct_f32_to_u32, rt_f32_to_u32) }
+ | -------------------------------------------------------------------- inside `core::f32::<impl f32>::to_bits` at $SRC_DIR/core/src/num/f32.rs:LL:COL
+ |
+ ::: $SRC_DIR/core/src/ops/function.rs:LL:COL
+ |
+LL | extern "rust-call" fn call_once(self, args: Args) -> Self::Output;
+ | ------------------------------------------------------------------ inside `<fn(f32) -> u32 {core::f32::<impl f32>::to_bits::ct_f32_to_u32} as FnOnce<(f32,)>>::call_once - shim(fn(f32) -> u32 {core::f32::<impl f32>::to_bits::ct_f32_to_u32})` at $SRC_DIR/core/src/ops/function.rs:LL:COL
+ |
+ ::: $SRC_DIR/core/src/intrinsics.rs:LL:COL
+ |
+LL | called_in_const.call_once(arg)
+ | ------------------------------ inside `const_eval_select::<(f32,), fn(f32) -> u32 {core::f32::<impl f32>::to_bits::ct_f32_to_u32}, [closure@core::f32::<impl f32>::to_bits::{closure#0}], u32>` at $SRC_DIR/core/src/intrinsics.rs:LL:COL
+ |
+ ::: $DIR/const-float-bits-reject-conv.rs:28:30
+ |
+LL | const MASKED_NAN2: u32 = f32::NAN.to_bits() ^ 0x0055_5555;
+ | ------------------ inside `f32::MASKED_NAN2` at $DIR/const-float-bits-reject-conv.rs:28:30
+ |
+ = note: this error originates in the macro `$crate::panic::panic_2021` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error[E0080]: evaluation of constant value failed
+ --> $SRC_DIR/core/src/num/f64.rs:LL:COL
+ |
+LL | panic!("const-eval error: cannot use f64::to_bits on a NaN")
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | |
+ | the evaluated program panicked at 'const-eval error: cannot use f64::to_bits on a NaN', $SRC_DIR/core/src/num/f64.rs:LL:COL
+ | inside `core::f64::<impl f64>::to_bits::ct_f64_to_u64` at $SRC_DIR/core/src/panic.rs:LL:COL
+...
+LL | unsafe { intrinsics::const_eval_select((self,), ct_f64_to_u64, rt_f64_to_u64) }
+ | -------------------------------------------------------------------- inside `core::f64::<impl f64>::to_bits` at $SRC_DIR/core/src/num/f64.rs:LL:COL
+ |
+ ::: $SRC_DIR/core/src/ops/function.rs:LL:COL
+ |
+LL | extern "rust-call" fn call_once(self, args: Args) -> Self::Output;
+ | ------------------------------------------------------------------ inside `<fn(f64) -> u64 {core::f64::<impl f64>::to_bits::ct_f64_to_u64} as FnOnce<(f64,)>>::call_once - shim(fn(f64) -> u64 {core::f64::<impl f64>::to_bits::ct_f64_to_u64})` at $SRC_DIR/core/src/ops/function.rs:LL:COL
+ |
+ ::: $SRC_DIR/core/src/intrinsics.rs:LL:COL
+ |
+LL | called_in_const.call_once(arg)
+ | ------------------------------ inside `const_eval_select::<(f64,), fn(f64) -> u64 {core::f64::<impl f64>::to_bits::ct_f64_to_u64}, [closure@core::f64::<impl f64>::to_bits::{closure#0}], u64>` at $SRC_DIR/core/src/intrinsics.rs:LL:COL
+ |
+ ::: $DIR/const-float-bits-reject-conv.rs:46:30
+ |
+LL | const MASKED_NAN1: u64 = f64::NAN.to_bits() ^ 0x000A_AAAA_AAAA_AAAA;
+ | ------------------ inside `f64::MASKED_NAN1` at $DIR/const-float-bits-reject-conv.rs:46:30
+ |
+ = note: this error originates in the macro `$crate::panic::panic_2021` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error[E0080]: evaluation of constant value failed
+ --> $SRC_DIR/core/src/num/f64.rs:LL:COL
+ |
+LL | panic!("const-eval error: cannot use f64::to_bits on a NaN")
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | |
+ | the evaluated program panicked at 'const-eval error: cannot use f64::to_bits on a NaN', $SRC_DIR/core/src/num/f64.rs:LL:COL
+ | inside `core::f64::<impl f64>::to_bits::ct_f64_to_u64` at $SRC_DIR/core/src/panic.rs:LL:COL
+...
+LL | unsafe { intrinsics::const_eval_select((self,), ct_f64_to_u64, rt_f64_to_u64) }
+ | -------------------------------------------------------------------- inside `core::f64::<impl f64>::to_bits` at $SRC_DIR/core/src/num/f64.rs:LL:COL
+ |
+ ::: $SRC_DIR/core/src/ops/function.rs:LL:COL
+ |
+LL | extern "rust-call" fn call_once(self, args: Args) -> Self::Output;
+ | ------------------------------------------------------------------ inside `<fn(f64) -> u64 {core::f64::<impl f64>::to_bits::ct_f64_to_u64} as FnOnce<(f64,)>>::call_once - shim(fn(f64) -> u64 {core::f64::<impl f64>::to_bits::ct_f64_to_u64})` at $SRC_DIR/core/src/ops/function.rs:LL:COL
+ |
+ ::: $SRC_DIR/core/src/intrinsics.rs:LL:COL
+ |
+LL | called_in_const.call_once(arg)
+ | ------------------------------ inside `const_eval_select::<(f64,), fn(f64) -> u64 {core::f64::<impl f64>::to_bits::ct_f64_to_u64}, [closure@core::f64::<impl f64>::to_bits::{closure#0}], u64>` at $SRC_DIR/core/src/intrinsics.rs:LL:COL
+ |
+ ::: $DIR/const-float-bits-reject-conv.rs:47:30
+ |
+LL | const MASKED_NAN2: u64 = f64::NAN.to_bits() ^ 0x0005_5555_5555_5555;
+ | ------------------ inside `f64::MASKED_NAN2` at $DIR/const-float-bits-reject-conv.rs:47:30
+ |
+ = note: this error originates in the macro `$crate::panic::panic_2021` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0080`.
note: impl defined here, but it is not `const`
--> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL
|
-LL | impl<I: Iterator> IntoIterator for I {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | impl<I: ~const Iterator> const IntoIterator for I {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: calls in constant functions are limited to constant functions, tuple structs and tuple variants
error[E0658]: mutable references are not allowed in constant functions
error[E0277]: the trait bound `String: Copy` is not satisfied
- --> $DIR/const-fn-in-vec.rs:4:32
+ --> $DIR/const-fn-in-vec.rs:4:33
|
LL | let strings: [String; 5] = [String::new(); 5];
- | ^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `String`
+ | ^^^^^^^^^^^^^ the trait `Copy` is not implemented for `String`
|
- = note: the `Copy` trait is required because the repeated element will be copied
+ = note: the `Copy` trait is required because this value will be copied for each element of the array
= help: consider creating a new `const` item and initializing it with the result of the function call to be used in the repeat position, like `const VAL: Type = const_fn();` and `let x = [VAL; 42];`
= help: create an inline `const` block, see RFC #2920 <https://github.com/rust-lang/rfcs/pull/2920> for more information
note: impl defined here, but it is not `const`
--> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL
|
-LL | impl<I: Iterator> IntoIterator for I {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | impl<I: ~const Iterator> const IntoIterator for I {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: calls in constants are limited to constant functions, tuple structs and tuple variants
error[E0015]: cannot call non-const fn `<std::ops::Range<i32> as Iterator>::next` in constants
--- /dev/null
+// run-pass
+
+struct MyStruct { field: usize }
+struct Nested { nested: MyStruct }
+struct Mix2 { nested: ((usize,),) }
+
+const STRUCT: MyStruct = MyStruct { field: 42 };
+const TUP: (usize,) = (43,);
+const NESTED_S: Nested = Nested { nested: MyStruct { field: 5 } };
+const NESTED_T: ((usize,),) = ((4,),);
+const MIX_1: ((Nested,),) = ((Nested { nested: MyStruct { field: 3 } },),);
+const MIX_2: Mix2 = Mix2 { nested: ((2,),) };
+const INSTANT_1: usize = (MyStruct { field: 1 }).field;
+const INSTANT_2: usize = (0,).0;
+
+fn main() {
+ let a = [0; STRUCT.field];
+ let b = [0; TUP.0];
+ let c = [0; NESTED_S.nested.field];
+ let d = [0; (NESTED_T.0).0];
+ let e = [0; (MIX_1.0).0.nested.field];
+ let f = [0; (MIX_2.nested.0).0];
+ let g = [0; INSTANT_1];
+ let h = [0; INSTANT_2];
+
+ assert_eq!(a.len(), 42);
+ assert_eq!(b.len(), 43);
+ assert_eq!(c.len(), 5);
+ assert_eq!(d.len(), 4);
+ assert_eq!(e.len(), 3);
+ assert_eq!(f.len(), 2);
+ assert_eq!(g.len(), 1);
+ assert_eq!(h.len(), 0);
+}
--- /dev/null
+// run-rustfix
+#![allow(unused)]
+
+trait Foo<T>: Sized {
+ fn bar(i: i32, t: T, s: &Self) -> (T, i32);
+}
+
+impl Foo<usize> for () {
+ fn bar(i: i32, t: usize, s: &()) -> (usize, i32) {
+ //~^ ERROR the placeholder `_` is not allowed within types on item signatures for functions
+ (1, 2)
+ }
+}
+
+fn main() {}
--- /dev/null
+// run-rustfix
+#![allow(unused)]
+
+trait Foo<T>: Sized {
+ fn bar(i: i32, t: T, s: &Self) -> (T, i32);
+}
+
+impl Foo<usize> for () {
+ fn bar(i: _, t: _, s: _) -> _ {
+ //~^ ERROR the placeholder `_` is not allowed within types on item signatures for functions
+ (1, 2)
+ }
+}
+
+fn main() {}
--- /dev/null
+error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions
+ --> $DIR/replace-impl-infer-ty-from-trait.rs:9:15
+ |
+LL | fn bar(i: _, t: _, s: _) -> _ {
+ | ^ ^ ^ ^ not allowed in type signatures
+ | | | |
+ | | | not allowed in type signatures
+ | | not allowed in type signatures
+ | not allowed in type signatures
+ |
+help: try replacing `_` with the types in the corresponding trait method signature
+ |
+LL | fn bar(i: i32, t: usize, s: &()) -> (usize, i32) {
+ | ~~~ ~~~~~ ~~~ ~~~~~~~~~~~~
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0121`.
-error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
+error[E0117]: only traits defined in the current crate can be implemented for primitive types
--> $DIR/E0117.rs:1:1
|
LL | impl Drop for u32 {}
-error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
+error[E0133]: call to unsafe function `f` is unsafe and requires unsafe function or block
--> $DIR/E0133.rs:7:5
|
LL | f();
| | |
| | cannot infer type for type parameter `T` declared on the trait `Into`
| this method call resolves to `T`
- | help: use the fully qualified path for the potential candidate: `<Impl as Into<u32>>::into(foo_impl)`
|
note: multiple `impl`s satisfying `Impl: Into<_>` found
--> $DIR/E0283.rs:17:1
= note: and another `impl` found in the `core` crate:
- impl<T, U> Into<U> for T
where U: From<T>;
+help: use the fully qualified path for the potential candidate
+ |
+LL | let bar = <Impl as Into<u32>>::into(foo_impl) * 1u32;
+ | ++++++++++++++++++++++++++ ~
error: aborting due to 2 previous errors
+error[E0771]: use of non-static lifetime `'a` in const generic
+ --> $DIR/E0771.rs:4:41
+ |
+LL | fn function_with_str<'a, const STRING: &'a str>() {}
+ | ^^
+ |
+ = note: for more information, see issue #74052 <https://github.com/rust-lang/rust/issues/74052>
+
warning: the feature `adt_const_params` is incomplete and may not be safe to use and/or cause compiler crashes
--> $DIR/E0771.rs:1:12
|
= note: `#[warn(incomplete_features)]` on by default
= note: see issue #95174 <https://github.com/rust-lang/rust/issues/95174> for more information
-error[E0771]: use of non-static lifetime `'a` in const generic
- --> $DIR/E0771.rs:4:41
- |
-LL | fn function_with_str<'a, const STRING: &'a str>() {}
- | ^^
- |
- = note: for more information, see issue #74052 <https://github.com/rust-lang/rust/issues/74052>
-
error: aborting due to previous error; 1 warning emitted
For more information about this error, try `rustc --explain E0771`.
--- /dev/null
+// aux-crate:somedep=somedep.rs
+// compile-flags: -Zunstable-options -Dunused-crate-dependencies
+// edition:2018
+
+fn main() { //~ ERROR external crate `somedep` unused in `no_nounused`
+}
--- /dev/null
+error: external crate `somedep` unused in `no_nounused`: remove the dependency or add `use somedep as _;`
+ --> $DIR/no-nounused.rs:5:1
+ |
+LL | fn main() {
+ | ^
+ |
+ = note: requested on the command line with `-D unused-crate-dependencies`
+
+error: aborting due to previous error
+
--- /dev/null
+// check-pass
+// aux-crate:nounused:somedep=somedep.rs
+// compile-flags: -Zunstable-options -Dunused-crate-dependencies
+// edition:2018
+
+fn main() {
+}
--- /dev/null
+// We previously mentioned other extern types in the error message here.
+//
+// Two extern types shouldn't really be considered similar just
+// because they are both extern types.
+
+#![feature(extern_types)]
+extern {
+ type ShouldNotBeMentioned;
+}
+
+extern {
+ type Foo;
+}
+
+unsafe impl Send for ShouldNotBeMentioned {}
+
+fn assert_send<T: Send + ?Sized>() {}
+
+fn main() {
+ assert_send::<Foo>()
+ //~^ ERROR `Foo` cannot be sent between threads safely
+}
--- /dev/null
+error[E0277]: `Foo` cannot be sent between threads safely
+ --> $DIR/extern-type-diag-not-similar.rs:20:19
+ |
+LL | assert_send::<Foo>()
+ | ^^^ `Foo` cannot be sent between threads safely
+ |
+ = help: the trait `Send` is not implemented for `Foo`
+note: required by a bound in `assert_send`
+ --> $DIR/extern-type-diag-not-similar.rs:17:19
+ |
+LL | fn assert_send<T: Send + ?Sized>() {}
+ | ^^^^ required by this bound in `assert_send`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
error[E0310]: the parameter type `U` may not live long enough
--> $DIR/feature-gate-infer_static_outlives_requirements.rs:5:10
|
-LL | struct Foo<U> {
- | - help: consider adding an explicit lifetime bound...: `U: 'static`
LL | bar: Bar<U>
| ^^^^^^ ...so that the type `U` will meet its required lifetime bounds...
|
|
LL | struct Bar<T: 'static> {
| ^^^^^^^
+help: consider adding an explicit lifetime bound...
+ |
+LL | struct Foo<U: 'static> {
+ | +++++++++
error: aborting due to previous error
--- /dev/null
+// compile-flags: --edition 2021
+
+pub fn demo() -> Option<i32> {
+ #[cfg(nope)]
+ {
+ do yeet //~ ERROR `do yeet` expression is experimental
+ }
+
+ Some(1)
+}
+
+#[cfg(nope)]
+pub fn alternative() -> Result<(), String> {
+ do yeet "hello"; //~ ERROR `do yeet` expression is experimental
+}
+
+fn main() {
+ demo();
+}
--- /dev/null
+error[E0658]: `do yeet` expression is experimental
+ --> $DIR/feature-gate-yeet_expr-in-cfg.rs:6:9
+ |
+LL | do yeet
+ | ^^^^^^^
+ |
+ = note: see issue #96373 <https://github.com/rust-lang/rust/issues/96373> for more information
+ = help: add `#![feature(yeet_expr)]` to the crate attributes to enable
+
+error[E0658]: `do yeet` expression is experimental
+ --> $DIR/feature-gate-yeet_expr-in-cfg.rs:14:5
+ |
+LL | do yeet "hello";
+ | ^^^^^^^^^^^^^^^
+ |
+ = note: see issue #96373 <https://github.com/rust-lang/rust/issues/96373> for more information
+ = help: add `#![feature(yeet_expr)]` to the crate attributes to enable
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0658`.
--- /dev/null
+// compile-flags: --edition 2018
+
+pub fn demo() -> Option<i32> {
+ do yeet //~ ERROR `do yeet` expression is experimental
+}
+
+pub fn main() -> Result<(), String> {
+ do yeet "hello"; //~ ERROR `do yeet` expression is experimental
+}
--- /dev/null
+error[E0658]: `do yeet` expression is experimental
+ --> $DIR/feature-gate-yeet_expr.rs:4:5
+ |
+LL | do yeet
+ | ^^^^^^^
+ |
+ = note: see issue #96373 <https://github.com/rust-lang/rust/issues/96373> for more information
+ = help: add `#![feature(yeet_expr)]` to the crate attributes to enable
+
+error[E0658]: `do yeet` expression is experimental
+ --> $DIR/feature-gate-yeet_expr.rs:8:5
+ |
+LL | do yeet "hello";
+ | ^^^^^^^^^^^^^^^
+ |
+ = note: see issue #96373 <https://github.com/rust-lang/rust/issues/96373> for more information
+ = help: add `#![feature(yeet_expr)]` to the crate attributes to enable
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0658`.
--- /dev/null
+// edition:2018
+
+default pub const async unsafe extern fn err() {} //~ ERROR `default` is not followed by an item
+//~^ ERROR expected item, found keyword `pub`
+
+pub default const async unsafe extern fn ok() {}
--- /dev/null
+error: `default` is not followed by an item
+ --> $DIR/keyword-order.rs:3:1
+ |
+LL | default pub const async unsafe extern fn err() {}
+ | ^^^^^^^ the `default` qualifier
+ |
+ = note: only `fn`, `const`, `type`, or `impl` items may be prefixed by `default`
+
+error: expected item, found keyword `pub`
+ --> $DIR/keyword-order.rs:3:9
+ |
+LL | default pub const async unsafe extern fn err() {}
+ | ^^^ expected item
+
+error: aborting due to 2 previous errors
+
fn main() {
test::free();
- //~^ ERROR call to unsafe function is unsafe
+ //[mir]~^ ERROR call to unsafe function is unsafe
+ //[thir]~^^ ERROR call to unsafe function `test::free` is unsafe
}
-error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
+error[E0133]: call to unsafe function `test::free` is unsafe and requires unsafe function or block
--> $DIR/foreign-unsafe-fn-called.rs:11:5
|
LL | test::free();
let arc = std::sync::Arc::new(oops);
//~^ ERROR cannot find value `oops` in this scope
//~| NOTE not found
- // The error "note: `arc` is a function, perhaps you wish to call it" MUST NOT appear.
+ // The error "note: this is a function, perhaps you wish to call it" MUST NOT appear.
arc.blablabla();
//~^ ERROR no method named `blablabla`
//~| NOTE method not found
let arc2 = std::sync::Arc::new(|| 1);
- // The error "note: `arc2` is a function, perhaps you wish to call it" SHOULD appear
+ // The error "note: this is a function, perhaps you wish to call it" SHOULD appear
arc2.blablabla();
//~^ ERROR no method named `blablabla`
//~| NOTE method not found
- //~| NOTE `arc2` is a function, perhaps you wish to call it
+ //~| NOTE this is a function, perhaps you wish to call it
}
--> $DIR/fn-help-with-err.rs:12:10
|
LL | arc2.blablabla();
- | ^^^^^^^^^ method not found in `Arc<[closure@$DIR/fn-help-with-err.rs:10:36: 10:40]>`
- |
- = note: `arc2` is a function, perhaps you wish to call it
+ | ---- ^^^^^^^^^ method not found in `Arc<[closure@$DIR/fn-help-with-err.rs:10:36: 10:40]>`
+ | |
+ | this is a function, perhaps you wish to call it
error: aborting due to 3 previous errors
fn foo(bar: bool) -> impl Generator<(bool,)> {
//~^ ERROR: type mismatch in generator arguments [E0631]
- //~| ERROR: type mismatch in generator arguments [E0631]
- //~| NOTE: expected signature of `fn((bool,)) -> _`
//~| NOTE: expected signature of `fn((bool,)) -> _`
//~| NOTE: in this expansion of desugaring of `impl Trait`
|bar| {
//~^ NOTE: found signature of `fn(bool) -> _`
- //~| NOTE: found signature of `fn(bool) -> _`
if bar {
yield bar;
}
LL | |bar| {
| ----- found signature of `fn(bool) -> _`
-error[E0631]: type mismatch in generator arguments
- --> $DIR/issue-88653.rs:8:46
- |
-LL | fn foo(bar: bool) -> impl Generator<(bool,)> {
- | ______________________________________________^
-LL | |
-LL | |
-LL | |
-... |
-LL | | |bar| {
- | | ----- found signature of `fn(bool) -> _`
-... |
-LL | | }
-LL | | }
- | |_^ expected signature of `fn((bool,)) -> _`
-
-error: aborting due to 2 previous errors
+error: aborting due to previous error
For more information about this error, try `rustc --explain E0631`.
fn foo() -> impl Generator<Return = i32> {
//~^ ERROR type mismatch
- //~| ERROR type mismatch
|| {
if false {
return Ok(6);
error[E0308]: mismatched types
- --> $DIR/type-mismatch-signature-deduction.rs:15:9
+ --> $DIR/type-mismatch-signature-deduction.rs:14:9
|
LL | 5
| ^ expected enum `Result`, found integer
= note: expected type `Result<{integer}, _>`
found type `{integer}`
note: return type inferred to be `Result<{integer}, _>` here
- --> $DIR/type-mismatch-signature-deduction.rs:10:20
+ --> $DIR/type-mismatch-signature-deduction.rs:9:20
|
LL | return Ok(6);
| ^^^^^
-error[E0271]: type mismatch resolving `<[generator@$DIR/type-mismatch-signature-deduction.rs:8:5: 16:6] as Generator>::Return == i32`
+error[E0271]: type mismatch resolving `<[generator@$DIR/type-mismatch-signature-deduction.rs:7:5: 15:6] as Generator>::Return == i32`
--> $DIR/type-mismatch-signature-deduction.rs:5:13
|
LL | fn foo() -> impl Generator<Return = i32> {
= note: expected enum `Result<{integer}, _>`
found type `i32`
-error[E0271]: type mismatch resolving `<[generator@$DIR/type-mismatch-signature-deduction.rs:8:5: 16:6] as Generator>::Return == i32`
- --> $DIR/type-mismatch-signature-deduction.rs:5:42
- |
-LL | fn foo() -> impl Generator<Return = i32> {
- | __________________________________________^
-LL | |
-LL | |
-LL | | || {
-... |
-LL | | }
-LL | | }
- | |_^ expected enum `Result`, found `i32`
- |
- = note: expected enum `Result<{integer}, _>`
- found type `i32`
-
-error: aborting due to 3 previous errors
+error: aborting due to 2 previous errors
Some errors have detailed explanations: E0271, E0308.
For more information about an error, try `rustc --explain E0271`.
fn create_doc() -> impl Document<Cursor<'_> = DocCursorImpl<'_>> {
//~^ ERROR: missing lifetime specifier
+ //~| ERROR: missing lifetime specifier
DocumentImpl {}
}
LL | fn create_doc() -> impl Document<Cursor<'static> = DocCursorImpl<'_>> {
| ~~~~~~~
-error: aborting due to previous error
+error[E0106]: missing lifetime specifier
+ --> $DIR/issue-70304.rs:47:61
+ |
+LL | fn create_doc() -> impl Document<Cursor<'_> = DocCursorImpl<'_>> {
+ | ^^ expected named lifetime parameter
+ |
+ = help: this function's return type contains a borrowed value, but there is no value for it to be borrowed from
+help: consider using the `'static` lifetime
+ |
+LL | fn create_doc() -> impl Document<Cursor<'_> = DocCursorImpl<'static>> {
+ | ~~~~~~~
+
+error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0106`.
error[E0311]: the parameter type `T` may not live long enough
--> $DIR/issue-86483.rs:5:1
|
-LL | pub trait IceIce<T>
- | ^ - help: consider adding an explicit lifetime bound...: `T: 'a`
- | _|
- | |
+LL | / pub trait IceIce<T>
LL | | where
LL | | for<'a> T: 'a,
LL | | {
|
LL | for<'a> T: 'a,
| ^^
+help: consider adding an explicit lifetime bound...
+ |
+LL | for<'a> T: 'a + 'a,
+ | ++++
error[E0311]: the parameter type `T` may not live long enough
--> $DIR/issue-86483.rs:9:5
|
-LL | pub trait IceIce<T>
- | - help: consider adding an explicit lifetime bound...: `T: 'a`
-...
LL | type Ice<'v>: IntoIterator<Item = &'v T>;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds...
|
|
LL | for<'a> T: 'a,
| ^^
+help: consider adding an explicit lifetime bound...
+ |
+LL | for<'a> T: 'a + 'a,
+ | ++++
error[E0309]: the parameter type `T` may not live long enough
--> $DIR/issue-86483.rs:9:32
error[E0311]: the parameter type `T` may not live long enough
--> $DIR/issue-91139.rs:27:12
|
-LL | fn foo<T>() {
- | - help: consider adding an explicit lifetime bound...: `T: 'a`
LL | let _: for<'a> fn(<() as Foo<T>>::Type<'a>, &'a T) = |_, _| ();
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds
+ |
+help: consider adding an explicit lifetime bound...
+ |
+LL | fn foo<T: 'a>() {
+ | ++++
error: aborting due to previous error
--> $DIR/issue-92096.rs:20:33
|
LL | fn call_connect<C>(c: &'_ C) -> impl '_ + Future + Send
- | - ^^^^^^^^^^^^^^^^^^^^^^^ ...so that the type `C` will meet its required lifetime bounds
- | |
- | help: consider adding an explicit lifetime bound...: `C: 'a`
+ | ^^^^^^^^^^^^^^^^^^^^^^^ ...so that the type `C` will meet its required lifetime bounds
+ |
+help: consider adding an explicit lifetime bound...
+ |
+LL | C: Client + Send + Sync + 'a,
+ | ++++
error[E0311]: the parameter type `C` may not live long enough
--> $DIR/issue-92096.rs:20:33
|
LL | fn call_connect<C>(c: &'_ C) -> impl '_ + Future + Send
- | - ^^^^^^^^^^^^^^^^^^^^^^^ ...so that the type `C` will meet its required lifetime bounds
- | |
- | help: consider adding an explicit lifetime bound...: `C: 'a`
+ | ^^^^^^^^^^^^^^^^^^^^^^^ ...so that the type `C` will meet its required lifetime bounds
+ |
+help: consider adding an explicit lifetime bound...
+ |
+LL | C: Client + Send + Sync + 'a,
+ | ++++
error: aborting due to 2 previous errors
--- /dev/null
+// It's not yet clear how '_ and GATs should interact.
+// Forbid it for now but proper support might be added
+// at some point in the future.
+
+#![feature(generic_associated_types)]
+
+trait Foo {
+ type Item<'a>;
+}
+
+fn foo(x: &impl Foo<Item<'_> = u32>) { }
+ //~^ ERROR missing lifetime specifier
+
+fn bar(x: &impl for<'a> Foo<Item<'a> = &'_ u32>) { }
+ //~^ ERROR missing lifetime specifier
+
+fn main() {}
--- /dev/null
+error[E0106]: missing lifetime specifier
+ --> $DIR/issue-95305.rs:11:26
+ |
+LL | fn foo(x: &impl Foo<Item<'_> = u32>) { }
+ | ^^ expected named lifetime parameter
+ |
+help: consider introducing a named lifetime parameter
+ |
+LL | fn foo<'a>(x: &impl Foo<Item<'a> = u32>) { }
+ | ++++ ~~
+
+error[E0106]: missing lifetime specifier
+ --> $DIR/issue-95305.rs:14:41
+ |
+LL | fn bar(x: &impl for<'a> Foo<Item<'a> = &'_ u32>) { }
+ | ^^ expected named lifetime parameter
+ |
+help: consider using the `'a` lifetime
+ |
+LL | fn bar(x: &impl for<'a> Foo<Item<'a> = &'a u32>) { }
+ | ~~
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0106`.
+++ /dev/null
-// run-rustfix
-
-use std::ops::Add;
-
-struct A<B>(B);
-
-impl<B> Add for A<B> where B: Add + Add<Output = B> {
- type Output = Self;
-
- fn add(self, rhs: Self) -> Self {
- A(self.0 + rhs.0) //~ ERROR mismatched types
- }
-}
-
-struct C<B>(B);
-
-impl<B: Add + Add<Output = B>> Add for C<B> {
- type Output = Self;
-
- fn add(self, rhs: Self) -> Self {
- Self(self.0 + rhs.0) //~ ERROR mismatched types
- }
-}
-
-struct D<B>(B);
-
-impl<B: std::ops::Add<Output = B>> Add for D<B> {
- type Output = Self;
-
- fn add(self, rhs: Self) -> Self {
- Self(self.0 + rhs.0) //~ ERROR cannot add `B` to `B`
- }
-}
-
-struct E<B>(B);
-
-impl<B: Add> Add for E<B> where B: Add<Output = B>, B: Add<Output = B> {
- //~^ ERROR equality constraints are not yet supported in `where` clauses
- type Output = Self;
-
- fn add(self, rhs: Self) -> Self {
- Self(self.0 + rhs.0) //~ ERROR mismatched types
- }
-}
-
-fn main() {}
-// run-rustfix
-
use std::ops::Add;
struct A<B>(B);
error: equality constraints are not yet supported in `where` clauses
- --> $DIR/missing-bounds.rs:37:33
+ --> $DIR/missing-bounds.rs:35:33
|
LL | impl<B: Add> Add for E<B> where <B as Add>::Output = B {
| ^^^^^^^^^^^^^^^^^^^^^^ not supported
| ~~~~~~~~~~~~~~~~~~
error[E0308]: mismatched types
- --> $DIR/missing-bounds.rs:11:11
+ --> $DIR/missing-bounds.rs:9:11
|
LL | impl<B> Add for A<B> where B: Add {
| - this type parameter
= note: expected type parameter `B`
found associated type `<B as Add>::Output`
note: tuple struct defined here
- --> $DIR/missing-bounds.rs:5:8
+ --> $DIR/missing-bounds.rs:3:8
|
LL | struct A<B>(B);
| ^
| +++++++++++++++++
error[E0308]: mismatched types
- --> $DIR/missing-bounds.rs:21:14
+ --> $DIR/missing-bounds.rs:19:14
|
LL | impl<B: Add> Add for C<B> {
| - this type parameter
= note: expected type parameter `B`
found associated type `<B as Add>::Output`
note: tuple struct defined here
- --> $DIR/missing-bounds.rs:15:8
+ --> $DIR/missing-bounds.rs:13:8
|
LL | struct C<B>(B);
| ^
| +++++++++++++++++
error[E0369]: cannot add `B` to `B`
- --> $DIR/missing-bounds.rs:31:21
+ --> $DIR/missing-bounds.rs:29:21
|
LL | Self(self.0 + rhs.0)
| ------ ^ ----- B
|
help: consider restricting type parameter `B`
|
-LL | impl<B: std::ops::Add<Output = B>> Add for D<B> {
- | +++++++++++++++++++++++++++
+LL | impl<B: std::ops::Add> Add for D<B> {
+ | +++++++++++++++
error[E0308]: mismatched types
- --> $DIR/missing-bounds.rs:42:14
+ --> $DIR/missing-bounds.rs:40:14
|
LL | impl<B: Add> Add for E<B> where <B as Add>::Output = B {
| - this type parameter
= note: expected type parameter `B`
found associated type `<B as Add>::Output`
note: tuple struct defined here
- --> $DIR/missing-bounds.rs:35:8
+ --> $DIR/missing-bounds.rs:33:8
|
LL | struct E<B>(B);
| ^
-help: consider further restricting type parameter `B`
+help: consider further restricting this bound
|
-LL | impl<B: Add> Add for E<B> where <B as Add>::Output = B, B: Add<Output = B> {
- | ++++++++++++++++++++
+LL | impl<B: Add + Add<Output = B>> Add for E<B> where <B as Add>::Output = B {
+ | +++++++++++++++++
error: aborting due to 5 previous errors
--> $DIR/unsatified-item-lifetime-bound.rs:4:12
|
LL | type Y<'a: 'static>;
- | ^^^^^^^^^^^
+ | ^^
|
= help: you can use the `'static` lifetime directly, in place of `'a`
--- /dev/null
+#![crate_type = "lib"]
+
+struct S<T = (), 'a>(&'a T);
+//~^ ERROR lifetime parameters must be declared prior to type parameters
--- /dev/null
+error: lifetime parameters must be declared prior to type parameters
+ --> $DIR/issue-80512-param-reordering-with-defaults.rs:3:18
+ |
+LL | struct S<T = (), 'a>(&'a T);
+ | ---------^^- help: reorder the parameters: lifetimes, then consts and types: `<'a, T = ()>`
+
+error: aborting due to previous error
+
--- /dev/null
+// build-fail
+
+fn assert_zst<T>() {
+ struct F<T>(T);
+ impl<T> F<T> {
+ const V: () = assert!(std::mem::size_of::<T>() == 0);
+ //~^ ERROR: evaluation of `assert_zst::F::<u32>::V` failed [E0080]
+ //~| NOTE: in this expansion of assert!
+ //~| NOTE: the evaluated program panicked
+ //~| ERROR: evaluation of `assert_zst::F::<i32>::V` failed [E0080]
+ //~| NOTE: in this expansion of assert!
+ //~| NOTE: the evaluated program panicked
+ }
+ let _ = F::<T>::V;
+}
+
+fn foo<U>() {
+ assert_zst::<U>()
+ //~^ NOTE: the above error was encountered while instantiating `fn assert_zst::<u32>`
+ //~| NOTE: the above error was encountered while instantiating `fn assert_zst::<i32>`
+}
+
+
+fn bar<V>() {
+ foo::<V>()
+}
+
+fn main() {
+ bar::<()>();
+ bar::<u32>();
+ bar::<u32>();
+ bar::<i32>();
+}
--- /dev/null
+error[E0080]: evaluation of `assert_zst::F::<u32>::V` failed
+ --> $DIR/post_monomorphization_error_backtrace.rs:6:23
+ |
+LL | const V: () = assert!(std::mem::size_of::<T>() == 0);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'assertion failed: std::mem::size_of::<T>() == 0', $DIR/post_monomorphization_error_backtrace.rs:6:23
+ |
+ = note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+note: the above error was encountered while instantiating `fn assert_zst::<u32>`
+ --> $DIR/post_monomorphization_error_backtrace.rs:18:5
+ |
+LL | assert_zst::<U>()
+ | ^^^^^^^^^^^^^^^^^
+
+error[E0080]: evaluation of `assert_zst::F::<i32>::V` failed
+ --> $DIR/post_monomorphization_error_backtrace.rs:6:23
+ |
+LL | const V: () = assert!(std::mem::size_of::<T>() == 0);
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the evaluated program panicked at 'assertion failed: std::mem::size_of::<T>() == 0', $DIR/post_monomorphization_error_backtrace.rs:6:23
+ |
+ = note: this error originates in the macro `assert` (in Nightly builds, run with -Z macro-backtrace for more info)
+
+note: the above error was encountered while instantiating `fn assert_zst::<i32>`
+ --> $DIR/post_monomorphization_error_backtrace.rs:18:5
+ |
+LL | assert_zst::<U>()
+ | ^^^^^^^^^^^^^^^^^
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0080`.
|
LL | impl<T> StreamExt for T where for<'a> &'a mut T: Stream {}
| --------- - ^^^^^^ unsatisfied trait bound introduced here
+help: one of the expressions' fields has a method of the same name
+ |
+LL | let filter = map.stream.filterx(|x: &_| true);
+ | +++++++
error[E0599]: the method `countx` exists for struct `Filter<Map<Repeat, for<'r> fn(&'r u64) -> &'r u64 {identity::<u64>}>, [closure@$DIR/issue-30786.rs:139:30: 139:42]>`, but its trait bounds were not satisfied
--> $DIR/issue-30786.rs:140:24
|
LL | impl<T> StreamExt for T where for<'a> &'a mut T: Stream {}
| --------- - ^^^^^^ unsatisfied trait bound introduced here
+help: one of the expressions' fields has a method of the same name
+ |
+LL | let count = filter.stream.countx();
+ | +++++++
error: aborting due to 2 previous errors
|
LL | impl<T> StreamExt for T where for<'a> &'a mut T: Stream {}
| --------- - ^^^^^^ unsatisfied trait bound introduced here
+help: one of the expressions' fields has a method of the same name
+ |
+LL | let filter = map.stream.filterx(|x: &_| true);
+ | +++++++
error[E0599]: the method `countx` exists for struct `Filter<Map<Repeat, for<'r> fn(&'r u64) -> &'r u64 {identity::<u64>}>, [closure@$DIR/issue-30786.rs:139:30: 139:42]>`, but its trait bounds were not satisfied
--> $DIR/issue-30786.rs:140:24
|
LL | impl<T> StreamExt for T where for<'a> &'a mut T: Stream {}
| --------- - ^^^^^^ unsatisfied trait bound introduced here
+help: one of the expressions' fields has a method of the same name
+ |
+LL | let count = filter.stream.countx();
+ | +++++++
error: aborting due to 2 previous errors
/// `T::Assoc` can't be normalized any further here.
fn foo_fail<T: Trait>() -> impl FooLike<Output = T::Assoc> {
//~^ ERROR: type mismatch
- //~| ERROR: type mismatch
Foo(())
}
}
fn foo2_fail<'a, T: Trait<'a>>() -> impl FooLike<Output = T::Assoc> {
//~^ ERROR `impl Trait` return type cannot contain a projection or `Self` that references lifetimes from a parent scope
//~| ERROR: type mismatch
- //~| ERROR: type mismatch
Foo(())
}
}
LL | fn foo_fail<T: Trait<Assoc = ()>>() -> impl FooLike<Output = T::Assoc> {
| ++++++++++++
-error[E0271]: type mismatch resolving `<Foo<()> as FooLike>::Output == <T as impl_trait::Trait>::Assoc`
- --> $DIR/bound-normalization-fail.rs:25:64
- |
-LL | fn foo_fail<T: Trait>() -> impl FooLike<Output = T::Assoc> {
- | ________________________________________________________________^
-LL | |
-LL | |
-LL | | Foo(())
-LL | | }
- | |_____^ type mismatch resolving `<Foo<()> as FooLike>::Output == <T as impl_trait::Trait>::Assoc`
- |
-note: expected this to be `()`
- --> $DIR/bound-normalization-fail.rs:14:19
- |
-LL | type Output = T;
- | ^
- = note: expected unit type `()`
- found associated type `<T as impl_trait::Trait>::Assoc`
-help: consider constraining the associated type `<T as impl_trait::Trait>::Assoc` to `()`
- |
-LL | fn foo_fail<T: Trait<Assoc = ()>>() -> impl FooLike<Output = T::Assoc> {
- | ++++++++++++
-
error[E0760]: `impl Trait` return type cannot contain a projection or `Self` that references lifetimes from a parent scope
- --> $DIR/bound-normalization-fail.rs:42:41
+ --> $DIR/bound-normalization-fail.rs:41:41
|
LL | fn foo2_fail<'a, T: Trait<'a>>() -> impl FooLike<Output = T::Assoc> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
error[E0271]: type mismatch resolving `<Foo<()> as FooLike>::Output == <T as lifetimes::Trait<'static>>::Assoc`
- --> $DIR/bound-normalization-fail.rs:42:41
+ --> $DIR/bound-normalization-fail.rs:41:41
|
LL | fn foo2_fail<'a, T: Trait<'a>>() -> impl FooLike<Output = T::Assoc> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ type mismatch resolving `<Foo<()> as FooLike>::Output == <T as lifetimes::Trait<'static>>::Assoc`
LL | fn foo2_fail<'a, T: Trait<'a, Assoc = ()>>() -> impl FooLike<Output = T::Assoc> {
| ++++++++++++
-error[E0271]: type mismatch resolving `<Foo<()> as FooLike>::Output == <T as lifetimes::Trait<'static>>::Assoc`
- --> $DIR/bound-normalization-fail.rs:42:73
- |
-LL | fn foo2_fail<'a, T: Trait<'a>>() -> impl FooLike<Output = T::Assoc> {
- | _________________________________________________________________________^
-LL | |
-LL | |
-LL | |
-LL | | Foo(())
-LL | | }
- | |_____^ type mismatch resolving `<Foo<()> as FooLike>::Output == <T as lifetimes::Trait<'static>>::Assoc`
- |
-note: expected this to be `()`
- --> $DIR/bound-normalization-fail.rs:14:19
- |
-LL | type Output = T;
- | ^
- = note: expected unit type `()`
- found associated type `<T as lifetimes::Trait<'static>>::Assoc`
-help: consider constraining the associated type `<T as lifetimes::Trait<'static>>::Assoc` to `()`
- |
-LL | fn foo2_fail<'a, T: Trait<'a, Assoc = ()>>() -> impl FooLike<Output = T::Assoc> {
- | ++++++++++++
-
-error: aborting due to 5 previous errors
+error: aborting due to 3 previous errors
Some errors have detailed explanations: E0271, E0760.
For more information about an error, try `rustc --explain E0271`.
}
fn muh() -> Result<(), impl std::fmt::Debug> {
- Err("whoops")?; //~ ERROR `?` couldn't convert the error to `impl Debug`
+ Err("whoops")?; //~^ ERROR type annotations needed
Ok(())
}
fn muh2() -> Result<(), impl std::fmt::Debug> {
- return Err(From::from("foo")); //~ ERROR the trait bound `impl Debug: From<&str>` is not satisfied
+ return Err(From::from("foo")); //~^ ERROR type annotations needed
Ok(())
}
fn muh3() -> Result<(), impl std::fmt::Debug> {
- Err(From::from("foo")) //~ ERROR the trait bound `impl Debug: From<&str>` is not satisfied
+ Err(From::from("foo")) //~^ ERROR type annotations needed
}
fn main() {}
-error[E0277]: `?` couldn't convert the error to `impl Debug`
- --> $DIR/cross-return-site-inference.rs:32:18
+error[E0282]: type annotations needed
+ --> $DIR/cross-return-site-inference.rs:31:24
|
LL | fn muh() -> Result<(), impl std::fmt::Debug> {
- | -------------------------------- expected `impl Debug` because of this
-LL | Err("whoops")?;
- | ^ the trait `From<&str>` is not implemented for `impl Debug`
- |
- = note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait
- = help: the trait `FromResidual<Result<Infallible, E>>` is implemented for `Result<T, F>`
- = note: required because of the requirements on the impl of `FromResidual<Result<Infallible, &str>>` for `Result<(), impl Debug>`
+ | ^^^^^^^^^^^^^^^^^^^^ cannot infer type
-error[E0277]: the trait bound `impl Debug: From<&str>` is not satisfied
- --> $DIR/cross-return-site-inference.rs:37:16
+error[E0282]: type annotations needed
+ --> $DIR/cross-return-site-inference.rs:36:25
|
-LL | return Err(From::from("foo"));
- | ^^^^^^^^^^ the trait `From<&str>` is not implemented for `impl Debug`
+LL | fn muh2() -> Result<(), impl std::fmt::Debug> {
+ | ^^^^^^^^^^^^^^^^^^^^ cannot infer type
-error[E0277]: the trait bound `impl Debug: From<&str>` is not satisfied
- --> $DIR/cross-return-site-inference.rs:42:9
+error[E0282]: type annotations needed
+ --> $DIR/cross-return-site-inference.rs:41:25
|
-LL | Err(From::from("foo"))
- | ^^^^^^^^^^ the trait `From<&str>` is not implemented for `impl Debug`
+LL | fn muh3() -> Result<(), impl std::fmt::Debug> {
+ | ^^^^^^^^^^^^^^^^^^^^ cannot infer type
error: aborting due to 3 previous errors
-For more information about this error, try `rustc --explain E0277`.
+For more information about this error, try `rustc --explain E0282`.
--> $DIR/equal-hidden-lifetimes.rs:7:25
|
LL | fn equal_regions_static<'a: 'static>(x: &'a i32) -> impl Sized {
- | ^^^^^^^^^^^
+ | ^^
|
= help: you can use the `'static` lifetime directly, in place of `'a`
--- /dev/null
+use std::marker::PhantomData;
+
+fn weird() -> PhantomData<impl Sized> {
+ PhantomData //~^ ERROR type annotations needed
+}
+
+fn main() {}
--- /dev/null
+error[E0282]: type annotations needed
+ --> $DIR/fallback_inference.rs:3:27
+ |
+LL | fn weird() -> PhantomData<impl Sized> {
+ | ^^^^^^^^^^ cannot infer type
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0282`.
impl Lint {}
pub fn gather_all() -> impl Iterator<Item = Lint> {
- //~^ ERROR `()` is not an iterator
+ //~^ ERROR type annotations needed
lint_files().flat_map(|f| gather_from_file(&f))
}
LL | fn lint_files() -> impl Iterator<Item = foo::MissingItem> {
| ^^^ use of undeclared crate or module `foo`
-error[E0277]: `()` is not an iterator
+error[E0282]: type annotations needed
--> $DIR/issue-72911.rs:7:24
|
LL | pub fn gather_all() -> impl Iterator<Item = Lint> {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^ `()` is not an iterator
- |
- = help: the trait `Iterator` is not implemented for `()`
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type
error: aborting due to 3 previous errors
-Some errors have detailed explanations: E0277, E0433.
-For more information about an error, try `rustc --explain E0277`.
+Some errors have detailed explanations: E0282, E0433.
+For more information about an error, try `rustc --explain E0282`.
error: implementation of `FnOnce` is not general enough
- --> $DIR/issue-67830.rs:21:66
+ --> $DIR/issue-67830.rs:21:14
|
-LL | fn test() -> impl for<'a> MyFn<&'a A, Output=impl Iterator + 'a> {
- | __________________________________________________________________^
-LL | |
-LL | | Wrap(|a| Some(a).into_iter())
-LL | | }
- | |_^ implementation of `FnOnce` is not general enough
+LL | fn test() -> impl for<'a> MyFn<&'a A, Output=impl Iterator + 'a> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `FnOnce` is not general enough
|
= note: closure with signature `fn(&'2 A) -> std::option::IntoIter<&A>` must implement `FnOnce<(&'1 A,)>`, for any lifetime `'1`...
= note: ...but it actually implements `FnOnce<(&'2 A,)>`, for some specific lifetime `'2`
fn execute_transaction_fut<'f, F, O>(
f: F,
-) -> impl FnOnce(&mut dyn Transaction) -> TransactionFuture<O>
+) -> impl FnOnce(&mut dyn Transaction) -> TransactionFuture<'_, O>
where
- F: FnOnce(&mut dyn Transaction) -> TransactionFuture<O> + 'f
+ F: FnOnce(&mut dyn Transaction) -> TransactionFuture<'_, O> + 'f
{
f
}
impl Context {
async fn do_transaction<O>(
- &self, f: impl FnOnce(&mut dyn Transaction) -> TransactionFuture<O>
+ &self, f: impl FnOnce(&mut dyn Transaction) -> TransactionFuture<'_, O>
) -> TransactionResult<O>
{
let mut conn = Connection {};
LL | x
| ^ returning this value requires that `'b` must outlive `'static`
|
-help: to allow this `impl Trait` to capture borrowed data with lifetime `'b`, add `'b` as a bound
+help: to declare that the `impl Trait` captures data from argument `x`, you can add an explicit `'b` lifetime bound
|
LL | fn make_bad_impl<'b>(x: &'b ()) -> impl for<'a> Hrtb<'a, Assoc = impl Send + 'a> + 'b {
| ++++
+help: to declare that the `impl Trait` captures data from argument `x`, you can add an explicit `'b` lifetime bound
+ |
+LL | fn make_bad_impl<'b>(x: &'b ()) -> impl for<'a> Hrtb<'a, Assoc = impl Send + 'a + 'b> {
+ | ++++
error: implementation of `Hrtb` is not general enough
--> $DIR/issue-88236-2.rs:20:5
= note: ...but `Hrtb<'1>` is actually implemented for the type `&'1 ()`, for some specific lifetime `'1`
error: implementation of `Hrtb` is not general enough
- --> $DIR/issue-88236-2.rs:19:82
+ --> $DIR/issue-88236-2.rs:19:36
|
-LL | fn make_bad_impl<'b>(x: &'b ()) -> impl for<'a> Hrtb<'a, Assoc = impl Send + 'a> {
- | __________________________________________________________________________________^
-LL | | x
-LL | | }
- | |_^ implementation of `Hrtb` is not general enough
+LL | fn make_bad_impl<'b>(x: &'b ()) -> impl for<'a> Hrtb<'a, Assoc = impl Send + 'a> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `Hrtb` is not general enough
|
- = note: `&()` must implement `Hrtb<'0>`, for any lifetime `'0`...
+ = note: `Hrtb<'1>` would have to be implemented for the type `&()`, for any lifetime `'1`...
= note: ...but `Hrtb<'_>` is actually implemented for the type `&()`
error: aborting due to 2 previous errors
--- /dev/null
+// edition:2021
+
+use std::iter;
+
+fn f<T>(data: &[T]) -> impl Iterator<Item = Vec> {
+ //~^ ERROR: missing generics for struct `Vec` [E0107]
+ iter::empty() //~ ERROR: type annotations needed [E0282]
+}
+
+fn g<T>(data: &[T], target: T) -> impl Iterator<Item = Vec<T>> {
+ //~^ ERROR: type annotations needed [E0282]
+ f(data).filter(|x| x == target)
+}
+
+fn main() {}
--- /dev/null
+error[E0107]: missing generics for struct `Vec`
+ --> $DIR/issue-92305.rs:5:45
+ |
+LL | fn f<T>(data: &[T]) -> impl Iterator<Item = Vec> {
+ | ^^^ expected at least 1 generic argument
+ |
+note: struct defined here, with at least 1 generic parameter: `T`
+ --> $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
+ |
+LL | pub struct Vec<T, #[unstable(feature = "allocator_api", issue = "32838")] A: Allocator = Global> {
+ | ^^^ -
+help: add missing generic argument
+ |
+LL | fn f<T>(data: &[T]) -> impl Iterator<Item = Vec<T>> {
+ | ~~~~~~
+
+error[E0282]: type annotations needed
+ --> $DIR/issue-92305.rs:7:5
+ |
+LL | iter::empty()
+ | ^^^^^^^^^^^ cannot infer type for type parameter `T` declared on the function `empty`
+
+error[E0282]: type annotations needed
+ --> $DIR/issue-92305.rs:10:35
+ |
+LL | fn g<T>(data: &[T], target: T) -> impl Iterator<Item = Vec<T>> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ cannot infer type
+
+error: aborting due to 3 previous errors
+
+Some errors have detailed explanations: E0107, E0282.
+For more information about an error, try `rustc --explain E0107`.
| |
| let's call the lifetime of this reference `'1`
|
- = help: consider replacing `'1` with `'static`
+help: consider changing the `impl Trait`'s explicit `'static` bound to the lifetime of argument `x`
+ |
+LL | fn elided2(x: &i32) -> impl Copy + '_ { x }
+ | ~~
+help: alternatively, add an explicit `'static` bound to this reference
+ |
+LL | fn elided2(x: &'static i32) -> impl Copy + 'static { x }
+ | ~~~~~~~~~~~~
error: lifetime may not live long enough
--> $DIR/must_outlive_least_region_or_bound.rs:11:55
LL | fn explicit2<'a>(x: &'a i32) -> impl Copy + 'static { x }
| -- lifetime `'a` defined here ^ returning this value requires that `'a` must outlive `'static`
|
- = help: consider replacing `'a` with `'static`
+help: consider changing the `impl Trait`'s explicit `'static` bound to the lifetime of argument `x`
+ |
+LL | fn explicit2<'a>(x: &'a i32) -> impl Copy + 'a { x }
+ | ~~
+help: alternatively, add an explicit `'static` bound to this reference
+ |
+LL | fn explicit2<'a>(x: &'static i32) -> impl Copy + 'static { x }
+ | ~~~~~~~~~~~~
error[E0621]: explicit lifetime required in the type of `x`
--> $DIR/must_outlive_least_region_or_bound.rs:13:41
| - ^^^^^^^^^^^^^^^^ returning this value requires that `'1` must outlive `'static`
| |
| let's call the lifetime of this reference `'1`
+ |
+help: to declare that the trait object captures data from argument `x`, you can add an explicit `'_` lifetime bound
+ |
+LL | fn elided5(x: &i32) -> (Box<dyn Debug + '_>, impl Debug) { (Box::new(x), x) }
+ | ++++
+help: to declare that the `impl Trait` captures data from argument `x`, you can add an explicit `'_` lifetime bound
+ |
+LL | fn elided5(x: &i32) -> (Box<dyn Debug>, impl Debug + '_) { (Box::new(x), x) }
+ | ++++
error: lifetime may not live long enough
--> $DIR/must_outlive_least_region_or_bound.rs:29:69
LL | fn with_bound<'a>(x: &'a i32) -> impl LifetimeTrait<'a> + 'static { x }
| -- lifetime `'a` defined here ^ returning this value requires that `'a` must outlive `'static`
|
- = help: consider replacing `'a` with `'static`
+help: consider changing the `impl Trait`'s explicit `'static` bound to the lifetime of argument `x`
+ |
+LL | fn with_bound<'a>(x: &'a i32) -> impl LifetimeTrait<'a> + 'a { x }
+ | ~~
+help: alternatively, add an explicit `'static` bound to this reference
+ |
+LL | fn with_bound<'a>(x: &'static i32) -> impl LifetimeTrait<'a> + 'static { x }
+ | ~~~~~~~~~~~~
error[E0700]: hidden type for `impl Trait` captures lifetime that does not appear in bounds
--> $DIR/must_outlive_least_region_or_bound.rs:34:5
| ++++
error[E0310]: the parameter type `T` may not live long enough
- --> $DIR/must_outlive_least_region_or_bound.rs:41:5
+ --> $DIR/must_outlive_least_region_or_bound.rs:40:5
|
LL | x
- | ^
+ | ^ ...so that the type `T` will meet its required lifetime bounds
+ |
+help: consider adding an explicit lifetime bound...
|
- = help: consider adding an explicit lifetime bound `T: 'static`...
+LL | fn ty_param_wont_outlive_static<T:Debug + 'static>(x: T) -> impl Debug + 'static {
+ | +++++++++
error: aborting due to 9 previous errors
fn ty_param_wont_outlive_static<T:Debug>(x: T) -> impl Debug + 'static {
//~^ ERROR the parameter type `T` may not live long enough
- //~| ERROR the parameter type `T` may not live long enough
x
}
--> $DIR/must_outlive_least_region_or_bound.rs:38:51
|
LL | fn ty_param_wont_outlive_static<T:Debug>(x: T) -> impl Debug + 'static {
- | -- ^^^^^^^^^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds
- | |
- | help: consider adding an explicit lifetime bound...: `T: 'static +`
-
-error[E0310]: the parameter type `T` may not live long enough
- --> $DIR/must_outlive_least_region_or_bound.rs:38:72
- |
-LL | fn ty_param_wont_outlive_static<T:Debug>(x: T) -> impl Debug + 'static {
- | _________________________________--_____________________________________^
- | | |
- | | help: consider adding an explicit lifetime bound...: `T: 'static +`
-LL | |
-LL | |
-LL | | x
-LL | | }
- | |_^ ...so that the type `T` will meet its required lifetime bounds
+ | ^^^^^^^^^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds
+ |
+help: consider adding an explicit lifetime bound...
+ |
+LL | fn ty_param_wont_outlive_static<T:Debug + 'static>(x: T) -> impl Debug + 'static {
+ | +++++++++
error[E0759]: `x` has an anonymous lifetime `'_` but it needs to satisfy a `'static` lifetime requirement
--> $DIR/must_outlive_least_region_or_bound.rs:16:50
LL | fn explicit4<'a>(x: &'static i32) -> Box<dyn Debug + 'static> { Box::new(x) }
| ~~~~~~~~~~~~
-error: aborting due to 14 previous errors
+error: aborting due to 13 previous errors
Some errors have detailed explanations: E0310, E0621, E0700, E0759.
For more information about an error, try `rustc --explain E0310`.
// type does not implement `Duh`, even if its hidden type does. So we error out.
fn foo() -> impl Trait<Assoc = Sendable> {
//~^ ERROR `Sendable: Duh` is not satisfied
- //~| ERROR `Sendable: Duh` is not satisfied
|| 42
}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Duh` is not implemented for `Sendable`
|
= help: the trait `Duh` is implemented for `i32`
-note: required because of the requirements on the impl of `Trait` for `[closure@$DIR/nested-return-type2-tait.rs:28:5: 28:10]`
+note: required because of the requirements on the impl of `Trait` for `[closure@$DIR/nested-return-type2-tait.rs:27:5: 27:10]`
--> $DIR/nested-return-type2-tait.rs:14:31
|
LL | impl<R: Duh, F: FnMut() -> R> Trait for F {
| ^^^^^ ^
-error[E0277]: the trait bound `Sendable: Duh` is not satisfied
- --> $DIR/nested-return-type2-tait.rs:25:42
- |
-LL | fn foo() -> impl Trait<Assoc = Sendable> {
- | __________________________________________^
-LL | |
-LL | |
-LL | | || 42
-LL | | }
- | |_^ the trait `Duh` is not implemented for `Sendable`
- |
- = help: the trait `Duh` is implemented for `i32`
-note: required because of the requirements on the impl of `Trait` for `[closure@$DIR/nested-return-type2-tait.rs:28:5: 28:10]`
- --> $DIR/nested-return-type2-tait.rs:14:31
- |
-LL | impl<R: Duh, F: FnMut() -> R> Trait for F {
- | ^^^^^ ^
-
-error: aborting due to 2 previous errors
+error: aborting due to previous error
For more information about this error, try `rustc --explain E0277`.
error[E0310]: the parameter type `T` may not live long enough
- --> $DIR/type_parameters_captured.rs:10:5
+ --> $DIR/type_parameters_captured.rs:9:5
|
LL | x
- | ^
+ | ^ ...so that the type `T` will meet its required lifetime bounds
|
- = help: consider adding an explicit lifetime bound `T: 'static`...
+help: consider adding an explicit lifetime bound...
+ |
+LL | fn foo<T: 'static>(x: T) -> impl Any + 'static {
+ | +++++++++
error: aborting due to previous error
// Check that type parameters are captured and not considered 'static
fn foo<T>(x: T) -> impl Any + 'static {
//~^ ERROR the parameter type `T` may not live long enough
- //~| ERROR the parameter type `T` may not live long enough
x
}
--> $DIR/type_parameters_captured.rs:7:20
|
LL | fn foo<T>(x: T) -> impl Any + 'static {
- | - ^^^^^^^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds
- | |
- | help: consider adding an explicit lifetime bound...: `T: 'static`
-
-error[E0310]: the parameter type `T` may not live long enough
- --> $DIR/type_parameters_captured.rs:7:39
+ | ^^^^^^^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds
+ |
+help: consider adding an explicit lifetime bound...
|
-LL | fn foo<T>(x: T) -> impl Any + 'static {
- | ________-______________________________^
- | | |
- | | help: consider adding an explicit lifetime bound...: `T: 'static`
-LL | |
-LL | |
-LL | | x
-LL | | }
- | |_^ ...so that the type `T` will meet its required lifetime bounds
+LL | fn foo<T: 'static>(x: T) -> impl Any + 'static {
+ | +++++++++
-error: aborting due to 2 previous errors
+error: aborting due to previous error
For more information about this error, try `rustc --explain E0310`.
use std::fmt::Debug;
-// check-pass
-
fn in_adt_in_return() -> Vec<impl Debug> { panic!() }
+//~^ ERROR type annotations needed
fn main() {}
--- /dev/null
+error[E0282]: type annotations needed
+ --> $DIR/where-allowed-2.rs:3:30
+ |
+LL | fn in_adt_in_return() -> Vec<impl Debug> { panic!() }
+ | ^^^^^^^^^^ cannot infer type
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0282`.
-error[E0284]: type annotations needed
- --> $DIR/question-mark-type-infer.rs:10:21
+error[E0282]: type annotations needed
+ --> $DIR/question-mark-type-infer.rs:10:30
|
LL | l.iter().map(f).collect()?
- | ^^^^^^^ cannot infer type
- |
- = note: cannot satisfy `<_ as Try>::Residual == _`
-help: consider specifying the type argument in the method call
- |
-LL | l.iter().map(f).collect::<B>()?
- | +++++
+ | ^ cannot infer type
error: aborting due to previous error
-For more information about this error, try `rustc --explain E0284`.
+For more information about this error, try `rustc --explain E0282`.
-error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
+error[E0133]: call to unsafe function `unchecked_add` is unsafe and requires unsafe function or block
--> $DIR/unchecked_math_unsafe.rs:8:15
|
LL | let add = std::intrinsics::unchecked_add(x, y);
|
= note: consult the function's documentation for information on how to avoid undefined behavior
-error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
+error[E0133]: call to unsafe function `unchecked_sub` is unsafe and requires unsafe function or block
--> $DIR/unchecked_math_unsafe.rs:9:15
|
LL | let sub = std::intrinsics::unchecked_sub(x, y);
|
= note: consult the function's documentation for information on how to avoid undefined behavior
-error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
+error[E0133]: call to unsafe function `unchecked_mul` is unsafe and requires unsafe function or block
--> $DIR/unchecked_math_unsafe.rs:10:15
|
LL | let mul = std::intrinsics::unchecked_mul(x, y);
fn test_ref(x: &u32) -> impl std::future::Future<Output = u32> + '_ {
//~^ ERROR `u32` is not a future
- //~| ERROR `u32` is not a future
*x
}
error[E0425]: cannot find value `u` in this scope
- --> $DIR/issues-71798.rs:8:24
+ --> $DIR/issues-71798.rs:7:24
|
LL | let _ = test_ref & u;
| ^ not found in this scope
= help: the trait `Future` is not implemented for `u32`
= note: u32 must be a future or must implement `IntoFuture` to be awaited
-error[E0277]: `u32` is not a future
- --> $DIR/issues-71798.rs:1:69
- |
-LL | fn test_ref(x: &u32) -> impl std::future::Future<Output = u32> + '_ {
- | _____________________________________________________________________^
-LL | |
-LL | |
-LL | | *x
-LL | | }
- | |_^ `u32` is not a future
- |
- = help: the trait `Future` is not implemented for `u32`
- = note: u32 must be a future or must implement `IntoFuture` to be awaited
-
-error: aborting due to 3 previous errors
+error: aborting due to 2 previous errors
Some errors have detailed explanations: E0277, E0425.
For more information about an error, try `rustc --explain E0277`.
+++ /dev/null
-// run-pass
-fn main() {
- let mut t = [1; 2];
- t = [t[1] * 2, t[0] * 2];
- assert_eq!(&t[..], &[2, 2]);
-}
+++ /dev/null
-// run-pass
-struct A {
- pub x: u32,
- pub y: u32,
-}
-
-fn main() {
- let mut a = A { x: 1, y: 1 };
- a = A { x: a.y * 2, y: a.x * 2 };
- assert_eq!(a.x, 2);
- assert_eq!(a.y, 2);
-}
+++ /dev/null
-// run-pass
-#![allow(unused_variables)]
-#![allow(unused_assignments)]
-#[derive(Debug)]
-enum Foo {
- Bar(u32, u32),
- Baz(&'static u32, &'static u32)
-}
-
-static NUM: u32 = 100;
-
-fn main () {
- let mut b = Foo::Baz(&NUM, &NUM);
- b = Foo::Bar(f(&b), g(&b));
-}
-
-static FNUM: u32 = 1;
-
-fn f (b: &Foo) -> u32 {
- FNUM
-}
-
-static GNUM: u32 = 2;
-
-fn g (b: &Foo) -> u32 {
- GNUM
-}
| - let's call the lifetime of this reference `'1`
LL | Box::new(value) as Box<dyn Any>
| ^^^^^^^^^^^^^^^ cast requires that `'1` must outlive `'static`
+ |
+help: to declare that the trait object captures data from argument `value`, you can add an explicit `'_` lifetime bound
+ |
+LL | fn foo<T: Any>(value: &T) -> Box<dyn Any + '_> {
+ | ++++
error: aborting due to previous error
+++ /dev/null
-// run-pass
-
-const X1: &'static [u8] = &[b'1'];
-const X2: &'static [u8] = b"1";
-const X3: &'static [u8; 1] = &[b'1'];
-const X4: &'static [u8; 1] = b"1";
-
-static Y1: u8 = X1[0];
-static Y2: u8 = X2[0];
-static Y3: u8 = X3[0];
-static Y4: u8 = X4[0];
-
-fn main() {
- assert_eq!(Y1, Y2);
- assert_eq!(Y1, Y3);
- assert_eq!(Y1, Y4);
-}
+++ /dev/null
-// build-pass
-#![allow(dead_code)]
-#![allow(non_camel_case_types)]
-#![warn(clashing_extern_declarations)]
-
-// pretty-expanded FIXME #23616
-
-mod a {
- pub type rust_task = usize;
- pub mod rustrt {
- use super::rust_task;
- extern "C" {
- pub fn rust_task_is_unwinding(rt: *const rust_task) -> bool;
- }
- }
-}
-
-mod b {
- pub type rust_task = bool;
- pub mod rustrt {
- use super::rust_task;
- extern "C" {
- pub fn rust_task_is_unwinding(rt: *const rust_task) -> bool;
- //~^ WARN `rust_task_is_unwinding` redeclared with a different signature
- }
- }
-}
-
-pub fn main() {}
+++ /dev/null
-warning: `rust_task_is_unwinding` redeclared with a different signature
- --> $DIR/issue-1866.rs:23:13
- |
-LL | pub fn rust_task_is_unwinding(rt: *const rust_task) -> bool;
- | ------------------------------------------------------------ `rust_task_is_unwinding` previously declared here
-...
-LL | pub fn rust_task_is_unwinding(rt: *const rust_task) -> bool;
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this signature doesn't match the previous declaration
- |
-note: the lint level is defined here
- --> $DIR/issue-1866.rs:4:9
- |
-LL | #![warn(clashing_extern_declarations)]
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- = note: expected `unsafe extern "C" fn(*const usize) -> bool`
- found `unsafe extern "C" fn(*const bool) -> bool`
-
-warning: 1 warning emitted
-
+++ /dev/null
-// check-pass
-pub trait Hasher {
- type State;
-
- fn hash<T: Hash<
- <Self as Hasher>::State
- >>(&self, value: &T) -> u64;
-}
-
-pub trait Hash<S> {
- fn hash(&self, state: &mut S);
-}
-
-fn main() {}
+++ /dev/null
-// run-pass
-
-struct MyStruct { field: usize }
-struct Nested { nested: MyStruct }
-struct Mix2 { nested: ((usize,),) }
-
-const STRUCT: MyStruct = MyStruct { field: 42 };
-const TUP: (usize,) = (43,);
-const NESTED_S: Nested = Nested { nested: MyStruct { field: 5 } };
-const NESTED_T: ((usize,),) = ((4,),);
-const MIX_1: ((Nested,),) = ((Nested { nested: MyStruct { field: 3 } },),);
-const MIX_2: Mix2 = Mix2 { nested: ((2,),) };
-const INSTANT_1: usize = (MyStruct { field: 1 }).field;
-const INSTANT_2: usize = (0,).0;
-
-fn main() {
- let a = [0; STRUCT.field];
- let b = [0; TUP.0];
- let c = [0; NESTED_S.nested.field];
- let d = [0; (NESTED_T.0).0];
- let e = [0; (MIX_1.0).0.nested.field];
- let f = [0; (MIX_2.nested.0).0];
- let g = [0; INSTANT_1];
- let h = [0; INSTANT_2];
-
- assert_eq!(a.len(), 42);
- assert_eq!(b.len(), 43);
- assert_eq!(c.len(), 5);
- assert_eq!(d.len(), 4);
- assert_eq!(e.len(), 3);
- assert_eq!(f.len(), 2);
- assert_eq!(g.len(), 1);
- assert_eq!(h.len(), 0);
-}
+++ /dev/null
-trait Foo {
- fn foo<T>(&self, val: T);
-}
-
-trait Bar: Foo { }
-
-pub struct Thing;
-
-impl Foo for Thing {
- fn foo<T>(&self, val: T) { }
-}
-
-impl Bar for Thing { }
-
-fn main() {
- let mut thing = Thing;
- let test: &mut dyn Bar = &mut thing;
- //~^ ERROR E0038
- //~| ERROR E0038
-}
+++ /dev/null
-error[E0038]: the trait `Bar` cannot be made into an object
- --> $DIR/issue-19538.rs:17:15
- |
-LL | let test: &mut dyn Bar = &mut thing;
- | ^^^^^^^^^^^^ `Bar` cannot be made into an object
- |
-note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
- --> $DIR/issue-19538.rs:2:8
- |
-LL | fn foo<T>(&self, val: T);
- | ^^^ ...because method `foo` has generic type parameters
-...
-LL | trait Bar: Foo { }
- | --- this trait cannot be made into an object...
- = help: consider moving `foo` to another trait
-
-error[E0038]: the trait `Bar` cannot be made into an object
- --> $DIR/issue-19538.rs:17:30
- |
-LL | let test: &mut dyn Bar = &mut thing;
- | ^^^^^^^^^^ `Bar` cannot be made into an object
- |
-note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
- --> $DIR/issue-19538.rs:2:8
- |
-LL | fn foo<T>(&self, val: T);
- | ^^^ ...because method `foo` has generic type parameters
-...
-LL | trait Bar: Foo { }
- | --- this trait cannot be made into an object...
- = help: consider moving `foo` to another trait
- = note: required because of the requirements on the impl of `CoerceUnsized<&mut dyn Bar>` for `&mut Thing`
- = note: required by cast to type `&mut dyn Bar`
-
-error: aborting due to 2 previous errors
-
-For more information about this error, try `rustc --explain E0038`.
+++ /dev/null
-// run-pass
-#![allow(unused_variables)]
-// Regression test for Issue #20343.
-
-// pretty-expanded FIXME #23616
-
-#![deny(dead_code)]
-
-struct B { b: u32 }
-struct C;
-struct D;
-
-trait T<A> { fn dummy(&self, a: A) { } }
-impl<A> T<A> for () {}
-
-impl B {
- // test for unused code in arguments
- fn foo(B { b }: B) -> u32 { b }
-
- // test for unused code in return type
- fn bar() -> C { unsafe { ::std::mem::transmute(()) } }
-
- // test for unused code in generics
- fn baz<A: T<D>>() {}
-}
-
-pub fn main() {
- let b = B { b: 3 };
- B::foo(b);
- B::bar();
- B::baz::<()>();
-}
+++ /dev/null
-// run-pass
-#![allow(dead_code)]
-#![allow(unused_variables)]
-#![feature(associated_type_defaults)]
-
-use std::marker::PhantomData;
-
-pub trait Routing<I> {
- type Output;
- fn resolve(&self, input: I);
-}
-
-pub trait ToRouting {
- type Input;
- type Routing : ?Sized = dyn Routing<Self::Input, Output=()>;
- fn to_routing(self) -> Self::Routing;
-}
-
-pub struct Mount<I, R: Routing<I>> {
- action: R,
- _marker: PhantomData<I>
-}
-
-impl<I, R: Routing<I>> Mount<I, R> {
- pub fn create<T: ToRouting<Routing=R>>(mount: &str, input: T) {
- input.to_routing();
- }
-}
-
-fn main() {
-}
+++ /dev/null
-mod foo {
- pub struct B(pub ());
-}
-
-mod baz {
- fn foo() {
- B(());
- //~^ ERROR cannot find function, tuple struct or tuple variant `B` in this scope [E0425]
- }
-}
-
-fn main() {}
+++ /dev/null
-error[E0425]: cannot find function, tuple struct or tuple variant `B` in this scope
- --> $DIR/issue-26545.rs:7:9
- |
-LL | B(());
- | ^ not found in this scope
- |
-help: consider importing this tuple struct
- |
-LL | use foo::B;
- |
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0425`.
-error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
+error[E0133]: call to unsafe function `std::ptr::write` is unsafe and requires unsafe function or block
--> $DIR/issue-28776.rs:7:5
|
LL | (&ptr::write)(1 as *mut _, 42);
--> $DIR/issue-29124.rs:15:15
|
LL | Obj::func.x();
- | ^ method not found in `fn() -> Ret {Obj::func}`
- |
- = note: `Obj::func` is a function, perhaps you wish to call it
+ | --------- ^ method not found in `fn() -> Ret {Obj::func}`
+ | |
+ | this is a function, perhaps you wish to call it
error[E0599]: no method named `x` found for fn item `fn() -> Ret {func}` in the current scope
--> $DIR/issue-29124.rs:17:10
|
LL | func.x();
- | ^ method not found in `fn() -> Ret {func}`
- |
- = note: `func` is a function, perhaps you wish to call it
+ | ---- ^ method not found in `fn() -> Ret {func}`
+ | |
+ | this is a function, perhaps you wish to call it
error: aborting due to 2 previous errors
+++ /dev/null
-// run-pass
-// ignore-emscripten no threads support
-
-use std::thread;
-
-struct Foo;
-
-impl Drop for Foo {
- fn drop(&mut self) {
- println!("test2");
- }
-}
-
-thread_local!(static FOO: Foo = Foo);
-
-fn main() {
- // Off the main thread due to #28129, be sure to initialize FOO first before
- // calling `println!`
- thread::spawn(|| {
- FOO.with(|_| {});
- println!("test1");
- }).join().unwrap();
-}
struct Test<'a> { s: &'a str }
fn silly<'y, 'z>(_s: &'y Test<'z>) -> &'y <Test<'z> as Trait>::Out where 'z: 'static {
+ //~^ WARN unnecessary lifetime parameter `'z`
let x = Test { s: "this cannot last" };
&x
//~^ ERROR: cannot return reference to local variable `x`
+warning: unnecessary lifetime parameter `'z`
+ --> $DIR/issue-30438-c.rs:7:74
+ |
+LL | fn silly<'y, 'z>(_s: &'y Test<'z>) -> &'y <Test<'z> as Trait>::Out where 'z: 'static {
+ | ^^
+ |
+ = help: you can use the `'static` lifetime directly, in place of `'z`
+
error[E0515]: cannot return reference to local variable `x`
- --> $DIR/issue-30438-c.rs:9:5
+ --> $DIR/issue-30438-c.rs:10:5
|
LL | &x
| ^^ returns a reference to data owned by the current function
-error: aborting due to previous error
+error: aborting due to previous error; 1 warning emitted
For more information about this error, try `rustc --explain E0515`.
-error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
+error[E0133]: call to unsafe function `X::with` is unsafe and requires unsafe function or block
--> $DIR/issue-3080.rs:10:5
|
LL | X(()).with();
| ^ the trait `From<{integer}>` is not implemented for `()`
|
= note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait
- = help: the trait `FromResidual<Result<Infallible, E>>` is implemented for `Result<T, F>`
+ = help: the following other types implement trait `FromResidual<R>`:
+ <Result<T, F> as FromResidual<Result<Infallible, E>>>
+ <Result<T, F> as FromResidual<Yeet<E>>>
= note: required because of the requirements on the impl of `FromResidual<Result<Infallible, {integer}>>` for `Result<i32, ()>`
error: aborting due to previous error
+++ /dev/null
-use std::mem;
-
-trait Misc {}
-
-fn size_of_copy<T: Copy+?Sized>() -> usize { mem::size_of::<T>() }
-
-fn main() {
- size_of_copy::<dyn Misc + Copy>();
- //~^ ERROR only auto traits can be used as additional traits in a trait object
- //~| ERROR only auto traits can be used as additional traits in a trait object
- //~| ERROR the trait bound `dyn Misc: Copy` is not satisfied
-}
+++ /dev/null
-error[E0225]: only auto traits can be used as additional traits in a trait object
- --> $DIR/issue-32963.rs:8:31
- |
-LL | size_of_copy::<dyn Misc + Copy>();
- | ---- ^^^^ additional non-auto trait
- | |
- | first non-auto trait
- |
- = help: consider creating a new trait with all of these as supertraits and using that trait here instead: `trait NewTrait: Misc + Copy {}`
- = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit <https://doc.rust-lang.org/reference/special-types-and-traits.html#auto-traits>
-
-error[E0225]: only auto traits can be used as additional traits in a trait object
- --> $DIR/issue-32963.rs:8:31
- |
-LL | size_of_copy::<dyn Misc + Copy>();
- | ---- ^^^^ additional non-auto trait
- | |
- | first non-auto trait
- |
- = help: consider creating a new trait with all of these as supertraits and using that trait here instead: `trait NewTrait: Misc + Copy {}`
- = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit <https://doc.rust-lang.org/reference/special-types-and-traits.html#auto-traits>
-
-error[E0277]: the trait bound `dyn Misc: Copy` is not satisfied
- --> $DIR/issue-32963.rs:8:5
- |
-LL | size_of_copy::<dyn Misc + Copy>();
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `dyn Misc`
- |
-note: required by a bound in `size_of_copy`
- --> $DIR/issue-32963.rs:5:20
- |
-LL | fn size_of_copy<T: Copy+?Sized>() -> usize { mem::size_of::<T>() }
- | ^^^^ required by this bound in `size_of_copy`
-
-error: aborting due to 3 previous errors
-
-Some errors have detailed explanations: E0225, E0277.
-For more information about an error, try `rustc --explain E0225`.
+++ /dev/null
-macro_rules! m { ($($t:tt)*) => { $($t)* } }
-
-fn main() {
- m!($t); //~ ERROR expected expression
-}
+++ /dev/null
-error: expected expression, found `$`
- --> $DIR/issue-35450.rs:4:8
- |
-LL | m!($t);
- | ^ expected expression
-
-error: aborting due to previous error
-
| |
| &T
|
-help: consider restricting type parameter `T`
+help: consider introducing a `where` bound, but there might be an alternative better way to express this requirement
|
-LL | fn func<'a, T: std::ops::Mul<Output = &T>>(a: &'a [T]) -> impl Iterator<Item=&'a T> {
- | ++++++++++++++++++++++++++++
+LL | fn func<'a, T>(a: &'a [T]) -> impl Iterator<Item=&'a T> where &T: Mul<&T> {
+ | +++++++++++++++++
error: aborting due to previous error
+++ /dev/null
-// check-pass
-// compile-flags: -Zsave-analysis
-
-#![feature(rustc_attrs)]
-#![allow(warnings)]
-
-#[derive(Debug)]
-struct Point {
-}
-
-struct NestedA<'a, 'b> {
- x: &'a NestedB<'b>
-}
-
-struct NestedB<'a> {
- x: &'a i32,
-}
-
-fn main() {
-}
+++ /dev/null
-struct Thing {
- x: isize
-}
-
-impl Thing {
- fn mul(&self, c: &isize) -> Thing {
- Thing {x: self.x * *c}
- }
-}
-
-fn main() {
- let u = Thing {x: 2};
- let _v = u.mul(&3); // This is ok
- let w = u * 3; //~ ERROR cannot multiply `Thing` by `{integer}`
-}
+++ /dev/null
-error[E0369]: cannot multiply `Thing` by `{integer}`
- --> $DIR/issue-3820.rs:14:15
- |
-LL | let w = u * 3;
- | - ^ - {integer}
- | |
- | Thing
- |
-note: an implementation of `Mul<_>` might be missing for `Thing`
- --> $DIR/issue-3820.rs:1:1
- |
-LL | struct Thing {
- | ^^^^^^^^^^^^ must implement `Mul<_>`
-note: the following trait must be implemented
- --> $SRC_DIR/core/src/ops/arith.rs:LL:COL
- |
-LL | / pub trait Mul<Rhs = Self> {
-LL | | /// The resulting type after applying the `*` operator.
-LL | | #[stable(feature = "rust1", since = "1.0.0")]
-LL | | type Output;
-... |
-LL | | fn mul(self, rhs: Rhs) -> Self::Output;
-LL | | }
- | |_^
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0369`.
+++ /dev/null
-// run-pass
-#![allow(dead_code)]
-
-pub fn main() {
- let _id: &Mat2<f64> = &Matrix::identity(1.0);
-}
-
-pub trait Index<Index,Result> { fn get(&self, _: Index) -> Result { panic!() } }
-pub trait Dimensional<T>: Index<usize, T> { }
-
-pub struct Mat2<T> { x: T }
-pub struct Vec2<T> { x: T }
-
-impl<T> Dimensional<Vec2<T>> for Mat2<T> { }
-impl<T> Index<usize, Vec2<T>> for Mat2<T> { }
-
-impl<T> Dimensional<T> for Vec2<T> { }
-impl<T> Index<usize, T> for Vec2<T> { }
-
-pub trait Matrix<T,V>: Dimensional<V> {
- fn identity(t:T) -> Self;
-}
-
-impl<T> Matrix<T, Vec2<T>> for Mat2<T> {
- fn identity(t:T) -> Mat2<T> { Mat2{ x: t } }
-}
+++ /dev/null
-fn main() {
- include!(line!()); //~ ERROR argument must be a string literal
-}
+++ /dev/null
-error: argument must be a string literal
- --> $DIR/issue-41776.rs:2:14
- |
-LL | include!(line!());
- | ^^^^^^^
-
-error: aborting due to previous error
-
+++ /dev/null
-// run-pass
-#![allow(unused)]
-
-fn main() {
-}
-
-fn foo() {
- let b = mk::<
- Forward<(Box<dyn Future<Error = u32>>,)>,
- >();
- b.map_err(|_| ()).join();
-}
-
-fn mk<T>() -> T {
- loop {}
-}
-
-impl<I: Future<Error = E>, E> Future for (I,) {
- type Error = E;
-}
-
-struct Forward<T: Future> {
- _a: T,
-}
-
-impl<T: Future> Future for Forward<T>
-where
- T::Error: From<u32>,
-{
- type Error = T::Error;
-}
-
-trait Future {
- type Error;
-
- fn map_err<F, E>(self, _: F) -> (Self, F)
- where
- F: FnOnce(Self::Error) -> E,
- Self: Sized,
- {
- loop {}
- }
-
- fn join(self) -> (MaybeDone<Self>, ())
- where
- Self: Sized,
- {
- loop {}
- }
-}
-
-impl<S: ?Sized + Future> Future for Box<S> {
- type Error = S::Error;
-}
-
-enum MaybeDone<A: Future> {
- _Done(A::Error),
-}
-
-impl<U, A: Future, F> Future for (A, F)
-where
- F: FnOnce(A::Error) -> U,
-{
- type Error = U;
-}
+++ /dev/null
-// run-pass
-
-#![feature(decl_macro)]
-
-pub struct Foo {
- bar: u32,
-}
-pub macro pattern($a:pat) {
- Foo { bar: $a }
-}
-
-fn main() {
- match (Foo { bar: 3 }) {
- pattern!(3) => println!("Test OK"),
- _ => unreachable!(),
- }
-}
+++ /dev/null
-// rust-lang/rust#45696: This test checks the compiler won't infinite loop when
-// you declare a variable of type `struct A(Box<A>, ...);` (which is impossible
-// to construct but *is* possible to declare; see also issues #4287, #44933,
-// and #52852).
-//
-// We will explicitly test NLL, and migration modes; thus we will also skip the
-// automated compare-mode=nll.
-
-// run-pass
-
-// This test has structs and functions that are by definition unusable
-// all over the place, so just go ahead and allow dead_code
-#![allow(dead_code)]
-
-// direct regular recursion with indirect ownership via box
-struct C { field: Box<C> }
-
-// direct non-regular recursion with indirect ownership via box
-struct D { field: Box<(D, D)> }
-
-// indirect regular recursion with indirect ownership via box.
-struct E { field: F }
-struct F { field: Box<E> }
-
-// indirect non-regular recursion with indirect ownership via box.
-struct G { field: (H, H) }
-struct H { field: Box<G> }
-
-// These enums are cases that are not currently hit by the
-// `visit_terminator_drop` recursion down a type's structural
-// definition.
-//
-// But it seems prudent to include them in this test as variants on
-// the above, in that they are similarly non-constructable data types
-// with destructors that would diverge.
-enum I { One(Box<I>) }
-enum J { One(Box<J>), Two(Box<J>) }
-
-fn impossible_to_call_c(_c: C) { }
-fn impossible_to_call_d(_d: D) { }
-fn impossible_to_call_e(_e: E) { }
-fn impossible_to_call_f(_f: F) { }
-fn impossible_to_call_g(_g: G) { }
-fn impossible_to_call_h(_h: H) { }
-fn impossible_to_call_i(_i: I) { }
-fn impossible_to_call_j(_j: J) { }
-
-fn main() {
-
-}
+++ /dev/null
-trait T {
- fn f(&self, _: ()) {
- None::<()>.map(Self::f);
- }
- //~^^ ERROR function is expected to take a single 0-tuple as argument
-}
-
-fn main() {}
+++ /dev/null
-error[E0593]: function is expected to take a single 0-tuple as argument, but it takes 2 distinct arguments
- --> $DIR/issue-47706-trait.rs:3:24
- |
-LL | fn f(&self, _: ()) {
- | ------------------ takes 2 distinct arguments
-LL | None::<()>.map(Self::f);
- | --- ^^^^^^^ expected function that takes a single 0-tuple as argument
- | |
- | required by a bound introduced by this call
- |
-note: required by a bound in `Option::<T>::map`
- --> $SRC_DIR/core/src/option.rs:LL:COL
- |
-LL | F: ~const FnOnce(T) -> U,
- | ^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Option::<T>::map`
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0593`.
+++ /dev/null
-pub struct Foo {
- foo: Option<i32>,
-}
-
-impl Foo {
- pub fn new(foo: Option<i32>, _: ()) -> Foo {
- Foo { foo }
- }
-
- pub fn map(self) -> Option<Foo> {
- self.foo.map(Foo::new)
- }
- //~^^ ERROR function is expected to take 1 argument, but it takes 2 arguments [E0593]
-}
-
-enum Qux {
- Bar(i32),
-}
-
-fn foo<F>(f: F)
-where
- F: Fn(),
-{
-}
-
-fn main() {
- foo(Qux::Bar);
-}
-//~^^ ERROR function is expected to take 0 arguments, but it takes 1 argument [E0593]
+++ /dev/null
-error[E0593]: function is expected to take 1 argument, but it takes 2 arguments
- --> $DIR/issue-47706.rs:11:22
- |
-LL | pub fn new(foo: Option<i32>, _: ()) -> Foo {
- | ------------------------------------------ takes 2 arguments
-...
-LL | self.foo.map(Foo::new)
- | --- ^^^^^^^^ expected function that takes 1 argument
- | |
- | required by a bound introduced by this call
- |
-note: required by a bound in `Option::<T>::map`
- --> $SRC_DIR/core/src/option.rs:LL:COL
- |
-LL | F: ~const FnOnce(T) -> U,
- | ^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Option::<T>::map`
-
-error[E0593]: function is expected to take 0 arguments, but it takes 1 argument
- --> $DIR/issue-47706.rs:27:9
- |
-LL | Bar(i32),
- | -------- takes 1 argument
-...
-LL | foo(Qux::Bar);
- | --- ^^^^^^^^ expected function that takes 0 arguments
- | |
- | required by a bound introduced by this call
- |
-note: required by a bound in `foo`
- --> $DIR/issue-47706.rs:22:8
- |
-LL | fn foo<F>(f: F)
- | --- required by a bound in this
-LL | where
-LL | F: Fn(),
- | ^^^^ required by this bound in `foo`
-
-error: aborting due to 2 previous errors
-
-For more information about this error, try `rustc --explain E0593`.
+++ /dev/null
-error: lifetime may not live long enough
- --> $DIR/issue-52213.rs:3:20
- |
-LL | fn transmute_lifetime<'a, 'b, T>(t: &'a (T,)) -> &'b T {
- | -- -- lifetime `'b` defined here
- | |
- | lifetime `'a` defined here
-LL | match (&t,) {
-LL | ((u,),) => u,
- | ^ function was supposed to return data with lifetime `'b` but it is returning data with lifetime `'a`
- |
- = help: consider adding the following bound: `'a: 'b`
-
-error: aborting due to previous error
-
+++ /dev/null
-fn transmute_lifetime<'a, 'b, T>(t: &'a (T,)) -> &'b T {
- match (&t,) { //~ ERROR cannot infer an appropriate lifetime
- ((u,),) => u,
- }
-}
-
-fn main() {
- let x = {
- let y = Box::new((42,));
- transmute_lifetime(&y)
- };
-
- println!("{}", x);
-}
+++ /dev/null
-error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
- --> $DIR/issue-52213.rs:2:11
- |
-LL | match (&t,) {
- | ^^^^^
- |
-note: first, the lifetime cannot outlive the lifetime `'a` as defined here...
- --> $DIR/issue-52213.rs:1:23
- |
-LL | fn transmute_lifetime<'a, 'b, T>(t: &'a (T,)) -> &'b T {
- | ^^
-note: ...so that the types are compatible
- --> $DIR/issue-52213.rs:2:11
- |
-LL | match (&t,) {
- | ^^^^^
- = note: expected `(&&(T,),)`
- found `(&&'a (T,),)`
-note: but, the lifetime must be valid for the lifetime `'b` as defined here...
- --> $DIR/issue-52213.rs:1:27
- |
-LL | fn transmute_lifetime<'a, 'b, T>(t: &'a (T,)) -> &'b T {
- | ^^
-note: ...so that reference does not outlive borrowed content
- --> $DIR/issue-52213.rs:3:20
- |
-LL | ((u,),) => u,
- | ^
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0495`.
+++ /dev/null
-fn main() {
- let items = vec![1, 2, 3];
- let ref_items: &[i32] = &items;
- let items_clone: Vec<i32> = ref_items.clone();
-//~^ ERROR mismatched types
-
- // in that case no suggestion will be triggered
- let items_clone_2:Vec<i32> = items.clone();
-
- let s = "hi";
- let string: String = s.clone();
-//~^ ERROR mismatched types
-
- // in that case no suggestion will be triggered
- let s2 = "hi";
- let string_2: String = s2.to_string();
-}
+++ /dev/null
-error[E0308]: mismatched types
- --> $DIR/issue-53692.rs:4:37
- |
-LL | let items_clone: Vec<i32> = ref_items.clone();
- | -------- ^^^^^^^^^^^^^^^^^
- | | |
- | | expected struct `Vec`, found `&[i32]`
- | | help: try using a conversion method: `ref_items.to_vec()`
- | expected due to this
- |
- = note: expected struct `Vec<i32>`
- found reference `&[i32]`
-
-error[E0308]: mismatched types
- --> $DIR/issue-53692.rs:11:30
- |
-LL | let string: String = s.clone();
- | ------ ^^^^^^^^^
- | | |
- | | expected struct `String`, found `&str`
- | | help: try using a conversion method: `s.to_string()`
- | expected due to this
-
-error: aborting due to 2 previous errors
-
-For more information about this error, try `rustc --explain E0308`.
+++ /dev/null
-// run-pass
-
-// Regression test for #55846, which once caused an ICE.
-
-use std::marker::PhantomData;
-
-struct Foo;
-
-struct Bar<A> {
- a: PhantomData<A>,
-}
-
-impl Fooifier for Foo {
- type Assoc = Foo;
-}
-
-trait Fooifier {
- type Assoc;
-}
-
-trait Barifier<H> {
- fn barify();
-}
-
-impl<H> Barifier<H> for Bar<H> {
- fn barify() {
- println!("All correct!");
- }
-}
-
-impl Bar<<Foo as Fooifier>::Assoc> {
- fn this_shouldnt_crash() {
- <Self as Barifier<<Foo as Fooifier>::Assoc>>::barify();
- }
-}
-
-fn main() {
- Bar::<Foo>::this_shouldnt_crash();
-}
--> $DIR/issue-57362-1.rs:20:7
|
LL | a.f();
- | ^ method not found in `fn(&u8)`
+ | - ^ method not found in `fn(&u8)`
+ | |
+ | this is a function, perhaps you wish to call it
|
- = note: `a` is a function, perhaps you wish to call it
= help: items from traits can only be used if the trait is implemented and in scope
note: `Trait` defines an item `f`, perhaps you need to implement it
--> $DIR/issue-57362-1.rs:8:1
-error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
+error[E0133]: call to unsafe function `rand` is unsafe and requires unsafe function or block
--> $DIR/issue-5844.rs:8:5
|
LL | issue_5844_aux::rand();
+++ /dev/null
-fn main() {
- (if foobar) //~ ERROR expected `{`, found `)`
-}
+++ /dev/null
-error: expected `{`, found `)`
- --> $DIR/issue-61858.rs:2:15
- |
-LL | (if foobar)
- | -- ^ expected `{`
- | |
- | this `if` expression has a condition, but no block
-
-error: aborting due to previous error
-
+++ /dev/null
-#![feature(unsize, dispatch_from_dyn)]
-
-use std::marker::Unsize;
-use std::ops::DispatchFromDyn;
-
-#[allow(unused)]
-struct Foo<'a, T: ?Sized> {
- _inner: &'a &'a T,
-}
-
-impl<'a, T: ?Sized + Unsize<U>, U: ?Sized> DispatchFromDyn<Foo<'a, U>> for Foo<'a, T> {}
-//~^ ERROR the trait bound `&'a T: Unsize<&'a U>` is not satisfied
-//~| NOTE the trait `Unsize<&'a U>` is not implemented for `&'a T`
-//~| NOTE all implementations of `Unsize` are provided automatically by the compiler
-//~| NOTE required because of the requirements on the impl
-
-fn main() {}
+++ /dev/null
-error[E0277]: the trait bound `&'a T: Unsize<&'a U>` is not satisfied
- --> $DIR/issue-71036.rs:11:1
- |
-LL | impl<'a, T: ?Sized + Unsize<U>, U: ?Sized> DispatchFromDyn<Foo<'a, U>> for Foo<'a, T> {}
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Unsize<&'a U>` is not implemented for `&'a T`
- |
- = note: all implementations of `Unsize` are provided automatically by the compiler, see <https://doc.rust-lang.org/stable/std/marker/trait.Unsize.html> for more information
- = note: required because of the requirements on the impl of `DispatchFromDyn<&'a &'a U>` for `&'a &'a T`
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0277`.
+++ /dev/null
-fn main() {
- let x = (1, 2, 3);
- match x {
- (_a, _x @ ..) => {}
- _ => {}
- }
-}
-//~^^^^ ERROR `_x @` is not allowed in a tuple
-//~| ERROR: `..` patterns are not allowed here
-//~| ERROR: mismatched types
+++ /dev/null
-error: `_x @` is not allowed in a tuple
- --> $DIR/issue-72574-1.rs:4:14
- |
-LL | (_a, _x @ ..) => {}
- | ^^^^^^^ this is only allowed in slice patterns
- |
- = help: remove this and bind each tuple field independently
-help: if you don't need to use the contents of _x, discard the tuple's remaining fields
- |
-LL | (_a, ..) => {}
- | ~~
-
-error: `..` patterns are not allowed here
- --> $DIR/issue-72574-1.rs:4:19
- |
-LL | (_a, _x @ ..) => {}
- | ^^
- |
- = note: only allowed in tuple, tuple struct, and slice patterns
-
-error[E0308]: mismatched types
- --> $DIR/issue-72574-1.rs:4:9
- |
-LL | match x {
- | - this expression has type `({integer}, {integer}, {integer})`
-LL | (_a, _x @ ..) => {}
- | ^^^^^^^^^^^^^ expected a tuple with 3 elements, found one with 2 elements
- |
- = note: expected tuple `({integer}, {integer}, {integer})`
- found tuple `(_, _)`
-
-error: aborting due to 3 previous errors
-
-For more information about this error, try `rustc --explain E0308`.
+++ /dev/null
-enum A {
- StructWithFields { x: () },
- TupleWithFields(()),
- Struct {},
- Tuple(),
- Unit,
-}
-
-enum B {
- StructWithFields { x: () },
- TupleWithFields(()),
-}
-
-enum C {
- StructWithFields { x: () },
- TupleWithFields(()),
- Unit,
-}
-
-enum D {
- TupleWithFields(()),
- Unit,
-}
-
-fn main() {
- // Only variants without fields are suggested (and others mentioned in a note) where an enum
- // is used rather than a variant.
-
- A.foo();
- //~^ ERROR expected value, found enum `A`
- B.foo();
- //~^ ERROR expected value, found enum `B`
- C.foo();
- //~^ ERROR expected value, found enum `C`
- D.foo();
- //~^ ERROR expected value, found enum `D`
-
- // Only tuple variants are suggested in calls or tuple struct pattern matching.
-
- let x = A(3);
- //~^ ERROR expected function, tuple struct or tuple variant, found enum `A`
- if let A(3) = x { }
- //~^ ERROR expected tuple struct or tuple variant, found enum `A`
-}
+++ /dev/null
-error[E0423]: expected value, found enum `A`
- --> $DIR/issue-73427.rs:29:5
- |
-LL | A.foo();
- | ^
- |
-note: the enum is defined here
- --> $DIR/issue-73427.rs:1:1
- |
-LL | / enum A {
-LL | | StructWithFields { x: () },
-LL | | TupleWithFields(()),
-LL | | Struct {},
-LL | | Tuple(),
-LL | | Unit,
-LL | | }
- | |_^
-help: you might have meant to use one of the following enum variants
- |
-LL | (A::Struct {}).foo();
- | ~~~~~~~~~~~~~~
-LL | (A::Tuple()).foo();
- | ~~~~~~~~~~~~
-LL | A::Unit.foo();
- | ~~~~~~~
-help: the following enum variants are available
- |
-LL | (A::StructWithFields { /* fields */ }).foo();
- | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-LL | (A::TupleWithFields(/* fields */)).foo();
- | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-error[E0423]: expected value, found enum `B`
- --> $DIR/issue-73427.rs:31:5
- |
-LL | B.foo();
- | ^
- |
-note: the enum is defined here
- --> $DIR/issue-73427.rs:9:1
- |
-LL | / enum B {
-LL | | StructWithFields { x: () },
-LL | | TupleWithFields(()),
-LL | | }
- | |_^
-help: the following enum variants are available
- |
-LL | (B::StructWithFields { /* fields */ }).foo();
- | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-LL | (B::TupleWithFields(/* fields */)).foo();
- | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-error[E0423]: expected value, found enum `C`
- --> $DIR/issue-73427.rs:33:5
- |
-LL | C.foo();
- | ^
- |
-note: the enum is defined here
- --> $DIR/issue-73427.rs:14:1
- |
-LL | / enum C {
-LL | | StructWithFields { x: () },
-LL | | TupleWithFields(()),
-LL | | Unit,
-LL | | }
- | |_^
-help: you might have meant to use the following enum variant
- |
-LL | C::Unit.foo();
- | ~~~~~~~
-help: the following enum variants are available
- |
-LL | (C::StructWithFields { /* fields */ }).foo();
- | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-LL | (C::TupleWithFields(/* fields */)).foo();
- | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-error[E0423]: expected value, found enum `D`
- --> $DIR/issue-73427.rs:35:5
- |
-LL | D.foo();
- | ^
- |
-note: the enum is defined here
- --> $DIR/issue-73427.rs:20:1
- |
-LL | / enum D {
-LL | | TupleWithFields(()),
-LL | | Unit,
-LL | | }
- | |_^
-help: you might have meant to use the following enum variant
- |
-LL | D::Unit.foo();
- | ~~~~~~~
-help: the following enum variant is available
- |
-LL | (D::TupleWithFields(/* fields */)).foo();
- | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
-
-error[E0423]: expected function, tuple struct or tuple variant, found enum `A`
- --> $DIR/issue-73427.rs:40:13
- |
-LL | let x = A(3);
- | ^
- |
- = help: you might have meant to construct one of the enum's non-tuple variants
-note: the enum is defined here
- --> $DIR/issue-73427.rs:1:1
- |
-LL | / enum A {
-LL | | StructWithFields { x: () },
-LL | | TupleWithFields(()),
-LL | | Struct {},
-LL | | Tuple(),
-LL | | Unit,
-LL | | }
- | |_^
-help: try to construct one of the enum's variants
- |
-LL | let x = A::Tuple(3);
- | ~~~~~~~~
-LL | let x = A::TupleWithFields(3);
- | ~~~~~~~~~~~~~~~~~~
-
-error[E0532]: expected tuple struct or tuple variant, found enum `A`
- --> $DIR/issue-73427.rs:42:12
- |
-LL | if let A(3) = x { }
- | ^
- |
- = help: you might have meant to match against one of the enum's non-tuple variants
-note: the enum is defined here
- --> $DIR/issue-73427.rs:1:1
- |
-LL | / enum A {
-LL | | StructWithFields { x: () },
-LL | | TupleWithFields(()),
-LL | | Struct {},
-LL | | Tuple(),
-LL | | Unit,
-LL | | }
- | |_^
-help: try to match against one of the enum's variants
- |
-LL | if let A::Tuple(3) = x { }
- | ~~~~~~~~
-LL | if let A::TupleWithFields(3) = x { }
- | ~~~~~~~~~~~~~~~~~~
-
-error: aborting due to 6 previous errors
-
-Some errors have detailed explanations: E0423, E0532.
-For more information about an error, try `rustc --explain E0423`.
+++ /dev/null
-#![crate_type = "lib"]
-
-struct S<T = (), 'a>(&'a T);
-//~^ ERROR lifetime parameters must be declared prior to type parameters
+++ /dev/null
-error: lifetime parameters must be declared prior to type parameters
- --> $DIR/issue-80512-param-reordering-with-defaults.rs:3:18
- |
-LL | struct S<T = (), 'a>(&'a T);
- | ---------^^- help: reorder the parameters: lifetimes, then consts and types: `<'a, T = ()>`
-
-error: aborting due to previous error
-
--- /dev/null
+fn main() {
+ //~^ NOTE required by a bound in this
+ let whatever: [u32; 10] = (0..10).collect();
+ //~^ ERROR an array of type `[u32; 10]` cannot be built directly from an iterator
+ //~| NOTE try collecting into a `Vec<{integer}>`, then using `.try_into()`
+ //~| NOTE required by a bound in `collect`
+}
--- /dev/null
+error[E0277]: an array of type `[u32; 10]` cannot be built directly from an iterator
+ --> $DIR/collect-into-array.rs:3:39
+ |
+LL | let whatever: [u32; 10] = (0..10).collect();
+ | ^^^^^^^ try collecting into a `Vec<{integer}>`, then using `.try_into()`
+ |
+ = help: the trait `FromIterator<{integer}>` is not implemented for `[u32; 10]`
+note: required by a bound in `collect`
+ --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL
+ |
+LL | fn collect<B: FromIterator<Self::Item>>(self) -> B
+ | ^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `collect`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
fn main() {
let some_generated_vec = (0..10).collect();
//~^ ERROR the size for values of type `[i32]` cannot be known at compilation time
- //~| ERROR a value of type `[i32]` cannot be built since `[i32]` has no definite size
+ //~| ERROR a slice of type `[i32]` cannot be built since `[i32]` has no definite size
//~| NOTE try explicitly collecting into a `Vec<{integer}>`
//~| NOTE required by a bound in `collect`
//~| NOTE all local variables must have a statically known size
= note: all local variables must have a statically known size
= help: unsized locals are gated as an unstable feature
-error[E0277]: a value of type `[i32]` cannot be built since `[i32]` has no definite size
+error[E0277]: a slice of type `[i32]` cannot be built since `[i32]` has no definite size
--> $DIR/collect-into-slice.rs:7:38
|
LL | let some_generated_vec = (0..10).collect();
--- /dev/null
+// normalize-stderr-test "pref: Align \{\n *pow2: [1-3],\n *\}" -> "pref: $$PREF_ALIGN"
+#![crate_type = "lib"]
+#![feature(rustc_attrs)]
+
+use std::mem::MaybeUninit;
+
+enum HasNiche {
+ A,
+ B,
+ C,
+}
+
+// This should result in ScalarPair(Initialized, Union),
+// since the u8 payload will be uninit for `None`.
+#[rustc_layout(debug)]
+pub enum MissingPayloadField { //~ ERROR: layout_of
+ Some(u8),
+ None
+}
+
+// This should result in ScalarPair(Initialized, Initialized),
+// since the u8 field is present in all variants,
+// and hence will always be initialized.
+#[rustc_layout(debug)]
+pub enum CommonPayloadField { //~ ERROR: layout_of
+ A(u8),
+ B(u8),
+}
+
+// This should result in ScalarPair(Initialized, Union),
+// since, though a u8-sized field is present in all variants, it might be uninit.
+#[rustc_layout(debug)]
+pub enum CommonPayloadFieldIsMaybeUninit { //~ ERROR: layout_of
+ A(u8),
+ B(MaybeUninit<u8>),
+}
+
+// This should result in ScalarPair(Initialized, Union),
+// since only the niche field (used for the tag) is guaranteed to be initialized.
+#[rustc_layout(debug)]
+pub enum NicheFirst { //~ ERROR: layout_of
+ A(HasNiche, u8),
+ B,
+ C
+}
+
+// This should result in ScalarPair(Union, Initialized),
+// since only the niche field (used for the tag) is guaranteed to be initialized.
+#[rustc_layout(debug)]
+pub enum NicheSecond { //~ ERROR: layout_of
+ A(u8, HasNiche),
+ B,
+ C,
+}
--- /dev/null
+error: layout_of(MissingPayloadField) = Layout {
+ fields: Arbitrary {
+ offsets: [
+ Size {
+ raw: 0,
+ },
+ ],
+ memory_index: [
+ 0,
+ ],
+ },
+ variants: Multiple {
+ tag: Initialized {
+ value: Int(
+ I8,
+ false,
+ ),
+ valid_range: 0..=1,
+ },
+ tag_encoding: Direct,
+ tag_field: 0,
+ variants: [
+ Layout {
+ fields: Arbitrary {
+ offsets: [
+ Size {
+ raw: 1,
+ },
+ ],
+ memory_index: [
+ 0,
+ ],
+ },
+ variants: Single {
+ index: 0,
+ },
+ abi: Aggregate {
+ sized: true,
+ },
+ largest_niche: None,
+ align: AbiAndPrefAlign {
+ abi: Align {
+ pow2: 0,
+ },
+ pref: $PREF_ALIGN,
+ },
+ size: Size {
+ raw: 2,
+ },
+ },
+ Layout {
+ fields: Arbitrary {
+ offsets: [],
+ memory_index: [],
+ },
+ variants: Single {
+ index: 1,
+ },
+ abi: Aggregate {
+ sized: true,
+ },
+ largest_niche: None,
+ align: AbiAndPrefAlign {
+ abi: Align {
+ pow2: 0,
+ },
+ pref: $PREF_ALIGN,
+ },
+ size: Size {
+ raw: 1,
+ },
+ },
+ ],
+ },
+ abi: ScalarPair(
+ Initialized {
+ value: Int(
+ I8,
+ false,
+ ),
+ valid_range: 0..=1,
+ },
+ Union {
+ value: Int(
+ I8,
+ false,
+ ),
+ },
+ ),
+ largest_niche: Some(
+ Niche {
+ offset: Size {
+ raw: 0,
+ },
+ value: Int(
+ I8,
+ false,
+ ),
+ valid_range: 0..=1,
+ },
+ ),
+ align: AbiAndPrefAlign {
+ abi: Align {
+ pow2: 0,
+ },
+ pref: $PREF_ALIGN,
+ },
+ size: Size {
+ raw: 2,
+ },
+ }
+ --> $DIR/issue-96158-scalarpair-payload-might-be-uninit.rs:16:1
+ |
+LL | / pub enum MissingPayloadField {
+LL | | Some(u8),
+LL | | None
+LL | | }
+ | |_^
+
+error: layout_of(CommonPayloadField) = Layout {
+ fields: Arbitrary {
+ offsets: [
+ Size {
+ raw: 0,
+ },
+ ],
+ memory_index: [
+ 0,
+ ],
+ },
+ variants: Multiple {
+ tag: Initialized {
+ value: Int(
+ I8,
+ false,
+ ),
+ valid_range: 0..=1,
+ },
+ tag_encoding: Direct,
+ tag_field: 0,
+ variants: [
+ Layout {
+ fields: Arbitrary {
+ offsets: [
+ Size {
+ raw: 1,
+ },
+ ],
+ memory_index: [
+ 0,
+ ],
+ },
+ variants: Single {
+ index: 0,
+ },
+ abi: Aggregate {
+ sized: true,
+ },
+ largest_niche: None,
+ align: AbiAndPrefAlign {
+ abi: Align {
+ pow2: 0,
+ },
+ pref: $PREF_ALIGN,
+ },
+ size: Size {
+ raw: 2,
+ },
+ },
+ Layout {
+ fields: Arbitrary {
+ offsets: [
+ Size {
+ raw: 1,
+ },
+ ],
+ memory_index: [
+ 0,
+ ],
+ },
+ variants: Single {
+ index: 1,
+ },
+ abi: Aggregate {
+ sized: true,
+ },
+ largest_niche: None,
+ align: AbiAndPrefAlign {
+ abi: Align {
+ pow2: 0,
+ },
+ pref: $PREF_ALIGN,
+ },
+ size: Size {
+ raw: 2,
+ },
+ },
+ ],
+ },
+ abi: ScalarPair(
+ Initialized {
+ value: Int(
+ I8,
+ false,
+ ),
+ valid_range: 0..=1,
+ },
+ Initialized {
+ value: Int(
+ I8,
+ false,
+ ),
+ valid_range: 0..=255,
+ },
+ ),
+ largest_niche: Some(
+ Niche {
+ offset: Size {
+ raw: 0,
+ },
+ value: Int(
+ I8,
+ false,
+ ),
+ valid_range: 0..=1,
+ },
+ ),
+ align: AbiAndPrefAlign {
+ abi: Align {
+ pow2: 0,
+ },
+ pref: $PREF_ALIGN,
+ },
+ size: Size {
+ raw: 2,
+ },
+ }
+ --> $DIR/issue-96158-scalarpair-payload-might-be-uninit.rs:25:1
+ |
+LL | / pub enum CommonPayloadField {
+LL | | A(u8),
+LL | | B(u8),
+LL | | }
+ | |_^
+
+error: layout_of(CommonPayloadFieldIsMaybeUninit) = Layout {
+ fields: Arbitrary {
+ offsets: [
+ Size {
+ raw: 0,
+ },
+ ],
+ memory_index: [
+ 0,
+ ],
+ },
+ variants: Multiple {
+ tag: Initialized {
+ value: Int(
+ I8,
+ false,
+ ),
+ valid_range: 0..=1,
+ },
+ tag_encoding: Direct,
+ tag_field: 0,
+ variants: [
+ Layout {
+ fields: Arbitrary {
+ offsets: [
+ Size {
+ raw: 1,
+ },
+ ],
+ memory_index: [
+ 0,
+ ],
+ },
+ variants: Single {
+ index: 0,
+ },
+ abi: Aggregate {
+ sized: true,
+ },
+ largest_niche: None,
+ align: AbiAndPrefAlign {
+ abi: Align {
+ pow2: 0,
+ },
+ pref: $PREF_ALIGN,
+ },
+ size: Size {
+ raw: 2,
+ },
+ },
+ Layout {
+ fields: Arbitrary {
+ offsets: [
+ Size {
+ raw: 1,
+ },
+ ],
+ memory_index: [
+ 0,
+ ],
+ },
+ variants: Single {
+ index: 1,
+ },
+ abi: Aggregate {
+ sized: true,
+ },
+ largest_niche: None,
+ align: AbiAndPrefAlign {
+ abi: Align {
+ pow2: 0,
+ },
+ pref: $PREF_ALIGN,
+ },
+ size: Size {
+ raw: 2,
+ },
+ },
+ ],
+ },
+ abi: ScalarPair(
+ Initialized {
+ value: Int(
+ I8,
+ false,
+ ),
+ valid_range: 0..=1,
+ },
+ Union {
+ value: Int(
+ I8,
+ false,
+ ),
+ },
+ ),
+ largest_niche: Some(
+ Niche {
+ offset: Size {
+ raw: 0,
+ },
+ value: Int(
+ I8,
+ false,
+ ),
+ valid_range: 0..=1,
+ },
+ ),
+ align: AbiAndPrefAlign {
+ abi: Align {
+ pow2: 0,
+ },
+ pref: $PREF_ALIGN,
+ },
+ size: Size {
+ raw: 2,
+ },
+ }
+ --> $DIR/issue-96158-scalarpair-payload-might-be-uninit.rs:33:1
+ |
+LL | / pub enum CommonPayloadFieldIsMaybeUninit {
+LL | | A(u8),
+LL | | B(MaybeUninit<u8>),
+LL | | }
+ | |_^
+
+error: layout_of(NicheFirst) = Layout {
+ fields: Arbitrary {
+ offsets: [
+ Size {
+ raw: 0,
+ },
+ ],
+ memory_index: [
+ 0,
+ ],
+ },
+ variants: Multiple {
+ tag: Initialized {
+ value: Int(
+ I8,
+ false,
+ ),
+ valid_range: 0..=4,
+ },
+ tag_encoding: Niche {
+ dataful_variant: 0,
+ niche_variants: 1..=2,
+ niche_start: 3,
+ },
+ tag_field: 0,
+ variants: [
+ Layout {
+ fields: Arbitrary {
+ offsets: [
+ Size {
+ raw: 0,
+ },
+ Size {
+ raw: 1,
+ },
+ ],
+ memory_index: [
+ 0,
+ 1,
+ ],
+ },
+ variants: Single {
+ index: 0,
+ },
+ abi: ScalarPair(
+ Initialized {
+ value: Int(
+ I8,
+ false,
+ ),
+ valid_range: 0..=2,
+ },
+ Initialized {
+ value: Int(
+ I8,
+ false,
+ ),
+ valid_range: 0..=255,
+ },
+ ),
+ largest_niche: Some(
+ Niche {
+ offset: Size {
+ raw: 0,
+ },
+ value: Int(
+ I8,
+ false,
+ ),
+ valid_range: 0..=2,
+ },
+ ),
+ align: AbiAndPrefAlign {
+ abi: Align {
+ pow2: 0,
+ },
+ pref: $PREF_ALIGN,
+ },
+ size: Size {
+ raw: 2,
+ },
+ },
+ Layout {
+ fields: Arbitrary {
+ offsets: [],
+ memory_index: [],
+ },
+ variants: Single {
+ index: 1,
+ },
+ abi: Aggregate {
+ sized: true,
+ },
+ largest_niche: None,
+ align: AbiAndPrefAlign {
+ abi: Align {
+ pow2: 0,
+ },
+ pref: $PREF_ALIGN,
+ },
+ size: Size {
+ raw: 0,
+ },
+ },
+ Layout {
+ fields: Arbitrary {
+ offsets: [],
+ memory_index: [],
+ },
+ variants: Single {
+ index: 2,
+ },
+ abi: Aggregate {
+ sized: true,
+ },
+ largest_niche: None,
+ align: AbiAndPrefAlign {
+ abi: Align {
+ pow2: 0,
+ },
+ pref: $PREF_ALIGN,
+ },
+ size: Size {
+ raw: 0,
+ },
+ },
+ ],
+ },
+ abi: ScalarPair(
+ Initialized {
+ value: Int(
+ I8,
+ false,
+ ),
+ valid_range: 0..=4,
+ },
+ Union {
+ value: Int(
+ I8,
+ false,
+ ),
+ },
+ ),
+ largest_niche: Some(
+ Niche {
+ offset: Size {
+ raw: 0,
+ },
+ value: Int(
+ I8,
+ false,
+ ),
+ valid_range: 0..=4,
+ },
+ ),
+ align: AbiAndPrefAlign {
+ abi: Align {
+ pow2: 0,
+ },
+ pref: $PREF_ALIGN,
+ },
+ size: Size {
+ raw: 2,
+ },
+ }
+ --> $DIR/issue-96158-scalarpair-payload-might-be-uninit.rs:41:1
+ |
+LL | / pub enum NicheFirst {
+LL | | A(HasNiche, u8),
+LL | | B,
+LL | | C
+LL | | }
+ | |_^
+
+error: layout_of(NicheSecond) = Layout {
+ fields: Arbitrary {
+ offsets: [
+ Size {
+ raw: 1,
+ },
+ ],
+ memory_index: [
+ 0,
+ ],
+ },
+ variants: Multiple {
+ tag: Initialized {
+ value: Int(
+ I8,
+ false,
+ ),
+ valid_range: 0..=4,
+ },
+ tag_encoding: Niche {
+ dataful_variant: 0,
+ niche_variants: 1..=2,
+ niche_start: 3,
+ },
+ tag_field: 0,
+ variants: [
+ Layout {
+ fields: Arbitrary {
+ offsets: [
+ Size {
+ raw: 0,
+ },
+ Size {
+ raw: 1,
+ },
+ ],
+ memory_index: [
+ 0,
+ 1,
+ ],
+ },
+ variants: Single {
+ index: 0,
+ },
+ abi: ScalarPair(
+ Initialized {
+ value: Int(
+ I8,
+ false,
+ ),
+ valid_range: 0..=255,
+ },
+ Initialized {
+ value: Int(
+ I8,
+ false,
+ ),
+ valid_range: 0..=2,
+ },
+ ),
+ largest_niche: Some(
+ Niche {
+ offset: Size {
+ raw: 1,
+ },
+ value: Int(
+ I8,
+ false,
+ ),
+ valid_range: 0..=2,
+ },
+ ),
+ align: AbiAndPrefAlign {
+ abi: Align {
+ pow2: 0,
+ },
+ pref: $PREF_ALIGN,
+ },
+ size: Size {
+ raw: 2,
+ },
+ },
+ Layout {
+ fields: Arbitrary {
+ offsets: [],
+ memory_index: [],
+ },
+ variants: Single {
+ index: 1,
+ },
+ abi: Aggregate {
+ sized: true,
+ },
+ largest_niche: None,
+ align: AbiAndPrefAlign {
+ abi: Align {
+ pow2: 0,
+ },
+ pref: $PREF_ALIGN,
+ },
+ size: Size {
+ raw: 0,
+ },
+ },
+ Layout {
+ fields: Arbitrary {
+ offsets: [],
+ memory_index: [],
+ },
+ variants: Single {
+ index: 2,
+ },
+ abi: Aggregate {
+ sized: true,
+ },
+ largest_niche: None,
+ align: AbiAndPrefAlign {
+ abi: Align {
+ pow2: 0,
+ },
+ pref: $PREF_ALIGN,
+ },
+ size: Size {
+ raw: 0,
+ },
+ },
+ ],
+ },
+ abi: ScalarPair(
+ Union {
+ value: Int(
+ I8,
+ false,
+ ),
+ },
+ Initialized {
+ value: Int(
+ I8,
+ false,
+ ),
+ valid_range: 0..=4,
+ },
+ ),
+ largest_niche: Some(
+ Niche {
+ offset: Size {
+ raw: 1,
+ },
+ value: Int(
+ I8,
+ false,
+ ),
+ valid_range: 0..=4,
+ },
+ ),
+ align: AbiAndPrefAlign {
+ abi: Align {
+ pow2: 0,
+ },
+ pref: $PREF_ALIGN,
+ },
+ size: Size {
+ raw: 2,
+ },
+ }
+ --> $DIR/issue-96158-scalarpair-payload-might-be-uninit.rs:50:1
+ |
+LL | / pub enum NicheSecond {
+LL | | A(u8, HasNiche),
+LL | | B,
+LL | | C,
+LL | | }
+ | |_^
+
+error: aborting due to 5 previous errors
+
--- /dev/null
+#![feature(nll)]
+
+#[derive(Clone)]
+struct Foo<'a>(fn(&'a ()) -> &'a ());
+
+impl Copy for Foo<'static> {}
+
+fn mk_foo<'a>() -> Foo<'a> {
+ println!("mk_foo");
+ Foo(|x| x)
+}
+
+fn foo<'a>() -> [Foo<'a>; 100] {
+ [mk_foo::<'a>(); 100] //~ ERROR lifetime may not live long enough
+}
+
+fn main() {
+ foo();
+}
--- /dev/null
+error: lifetime may not live long enough
+ --> $DIR/copy_modulo_regions.rs:14:5
+ |
+LL | fn foo<'a>() -> [Foo<'a>; 100] {
+ | -- lifetime `'a` defined here
+LL | [mk_foo::<'a>(); 100]
+ | ^^^^^^^^^^^^^^^^^^^^^ returning this value requires that `'a` must outlive `'static`
+ |
+ = note: requirement occurs because of the type `Foo<'_>`, which makes the generic argument `'_` invariant
+ = note: the struct `Foo<'a>` is invariant over the parameter `'a`
+ = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
+
+error: aborting due to previous error
+
--- /dev/null
+// check-pass
+
+struct Foo<'a>(&'a ());
+
+fn with_fn() -> fn(Foo) {
+ |_| ()
+}
+
+fn with_impl_fn() -> impl Fn(Foo) {
+ |_| ()
+}
+
+fn with_where_fn<T>()
+where
+ T: Fn(Foo),
+{
+}
+
+fn main() {}
| | |
| | let's call the lifetime of this reference `'1`
| let's call the lifetime of this reference `'2`
+ |
+help: consider introducing a named lifetime parameter
+ |
+LL | pub fn foo<'a>(x: &mut Vec<&'a u8>, y: &'a u8) { x.push(y); }
+ | ++++ ++ ++
error: lifetime may not live long enough
--> $DIR/issue-90170-elision-mismatch.rs:5:44
| | |
| | let's call the lifetime of this reference `'1`
| let's call the lifetime of this reference `'2`
+ |
+help: consider introducing a named lifetime parameter
+ |
+LL | pub fn foo2<'a>(x: &mut Vec<&'a u8>, y: &'a u8) { x.push(y); }
+ | ++++ ~~ ++
error: lifetime may not live long enough
--> $DIR/issue-90170-elision-mismatch.rs:7:63
| | |
| | let's call the lifetime of this reference `'1`
| let's call the lifetime of this reference `'2`
+ |
+help: consider introducing a named lifetime parameter
+ |
+LL | pub fn foo3<'a>(_other: &'a [u8], x: &mut Vec<&'a u8>, y: &'a u8) { x.push(y); }
+ | ++ ++
error: aborting due to 3 previous errors
error[E0310]: the parameter type `T` may not live long enough
--> $DIR/lifetime-doesnt-live-long-enough.rs:19:10
|
-LL | struct Foo<T> {
- | - help: consider adding an explicit lifetime bound...: `T: 'static`
LL | foo: &'static T
| ^^^^^^^^^^ ...so that the reference type `&'static T` does not outlive the data it points at
+ |
+help: consider adding an explicit lifetime bound...
+ |
+LL | struct Foo<T: 'static> {
+ | +++++++++
error[E0309]: the parameter type `K` may not live long enough
--> $DIR/lifetime-doesnt-live-long-enough.rs:24:19
|
-LL | trait X<K>: Sized {
- | - help: consider adding an explicit lifetime bound...: `K: 'a`
LL | fn foo<'a, L: X<&'a Nested<K>>>();
| ^^^^^^^^^^^^^^^^ ...so that the reference type `&'a Nested<K>` does not outlive the data it points at
+ |
+help: consider adding an explicit lifetime bound...
+ |
+LL | trait X<K: 'a>: Sized {
+ | ++++
error[E0309]: the parameter type `Self` may not live long enough
--> $DIR/lifetime-doesnt-live-long-enough.rs:28:19
--> $DIR/lifetime-doesnt-live-long-enough.rs:32:22
|
LL | fn baz<'a, L, M: X<&'a Nested<L>>>() {
- | - ^^^^^^^^^^^^^^^^ ...so that the reference type `&'a Nested<L>` does not outlive the data it points at
- | |
- | help: consider adding an explicit lifetime bound...: `L: 'a`
+ | ^^^^^^^^^^^^^^^^ ...so that the reference type `&'a Nested<L>` does not outlive the data it points at
+ |
+help: consider adding an explicit lifetime bound...
+ |
+LL | fn baz<'a, L: 'a, M: X<&'a Nested<L>>>() {
+ | ++++
error[E0309]: the parameter type `K` may not live long enough
--> $DIR/lifetime-doesnt-live-long-enough.rs:41:33
|
-LL | impl<K> Nested<K> {
- | - help: consider adding an explicit lifetime bound...: `K: 'a`
LL | fn generic_in_parent<'a, L: X<&'a Nested<K>>>() {
| ^^^^^^^^^^^^^^^^ ...so that the reference type `&'a Nested<K>` does not outlive the data it points at
+ |
+help: consider adding an explicit lifetime bound...
+ |
+LL | impl<K: 'a> Nested<K> {
+ | ++++
error[E0309]: the parameter type `M` may not live long enough
--> $DIR/lifetime-doesnt-live-long-enough.rs:44:36
|
LL | fn generic_in_child<'a, 'b, L: X<&'a Nested<M>>, M: 'b>() {
- | ^^^^^^^^^^^^^^^^ -- help: consider adding an explicit lifetime bound...: `M: 'a +`
- | |
- | ...so that the reference type `&'a Nested<M>` does not outlive the data it points at
+ | ^^^^^^^^^^^^^^^^ ...so that the reference type `&'a Nested<M>` does not outlive the data it points at
+ |
+help: consider adding an explicit lifetime bound...
+ |
+LL | fn generic_in_child<'a, 'b, L: X<&'a Nested<M>>, M: 'b + 'a>() {
+ | ++++
error: aborting due to 6 previous errors
fn foo() -> impl Future<Item=(), Error=Box<dyn Error>> {
//~^ ERROR not satisfied
- //~| ERROR not satisfied
Ok(())
}
LL | fn foo() -> impl Future<Item=(), Error=Box<dyn Error>> {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Future` is not implemented for `Result<(), _>`
-error[E0277]: the trait bound `Result<(), _>: Future` is not satisfied
- --> $DIR/lifetime-elision-return-type-trait.rs:8:56
- |
-LL | fn foo() -> impl Future<Item=(), Error=Box<dyn Error>> {
- | ________________________________________________________^
-LL | |
-LL | |
-LL | | Ok(())
-LL | | }
- | |_^ the trait `Future` is not implemented for `Result<(), _>`
-
-error: aborting due to 2 previous errors
+error: aborting due to previous error
For more information about this error, try `rustc --explain E0277`.
| let's call the lifetime of this reference `'2`
LL | *v = x;
| ^^^^^^ assignment requires that `'1` must outlive `'2`
+ |
+help: consider introducing a named lifetime parameter
+ |
+LL | fn foo<'a>(&mut (ref mut v, w): &mut (&'a u8, &u8), x: &'a u8) {
+ | ++++ ++ ++
error: aborting due to previous error
| let's call the lifetime of this reference `'2`
LL | z.push((x,y));
| ^^^^^^^^^^^^^ argument requires that `'1` must outlive `'2`
+ |
+help: consider introducing a named lifetime parameter
+ |
+LL | fn foo<'a>(z: &mut Vec<(&'a u8,&u8)>, (x, y): (&'a u8, &u8)) {
+ | ++++ ++ ++
error: lifetime may not live long enough
--> $DIR/ex3-both-anon-regions-3.rs:2:5
| let's call the lifetime of this reference `'4`
LL | z.push((x,y));
| ^^^^^^^^^^^^^ argument requires that `'3` must outlive `'4`
+ |
+help: consider introducing a named lifetime parameter
+ |
+LL | fn foo<'a>(z: &mut Vec<(&u8,&'a u8)>, (x, y): (&u8, &'a u8)) {
+ | ++++ ++ ++
error: aborting due to 2 previous errors
| let's call the lifetime of this reference `'2`
LL | x
| ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1`
+ |
+help: consider introducing a named lifetime parameter and update trait if needed
+ |
+LL | fn foo<'a>(&'a self, x: &'a i32) -> &i32 {
+ | ++ ++
error: aborting due to previous error
| let's call the lifetime of this reference `'2`
LL | if true { x } else { self }
| ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1`
+ |
+help: consider introducing a named lifetime parameter and update trait if needed
+ |
+LL | fn foo<'a>(&'a self, x: &'a Foo) -> &Foo {
+ | ++ ++
error: aborting due to previous error
| let's call the lifetime of this reference `'2`
LL | y.push(z);
| ^^^^^^^^^ argument requires that `'1` must outlive `'2`
+ |
+help: consider introducing a named lifetime parameter
+ |
+LL | fn foo<'a>(x:fn(&u8, &u8), y: Vec<&'a u8>, z: &'a u8) {
+ | ++++ ++ ++
error[E0596]: cannot borrow `y` as mutable, as it is not declared as mutable
--> $DIR/ex3-both-anon-regions-using-fn-items.rs:2:3
| let's call the lifetime of this reference `'2`
LL | x.push(y);
| ^^^^^^^^^ argument requires that `'1` must outlive `'2`
+ |
+help: consider introducing a named lifetime parameter and update trait if needed
+ |
+LL | fn foo<'a>(x: &mut Vec<&'a u8>, y: &'a u8) {
+ | ++++ ++ ++
error: aborting due to previous error
| let's call the lifetime of this reference `'2`
LL | y.push(z);
| ^^^^^^^^^ argument requires that `'1` must outlive `'2`
+ |
+help: consider introducing a named lifetime parameter
+ |
+LL | fn foo<'a>(x:Box<dyn Fn(&'a u8, &'a u8)> , y: Vec<&u8>, z: &u8) {
+ | ++++ ++ ++
error[E0596]: cannot borrow `y` as mutable, as it is not declared as mutable
--> $DIR/ex3-both-anon-regions-using-trait-objects.rs:2:3
| let's call the lifetime of this reference `'2`
LL | x.push(y);
| ^^^^^^^^^ argument requires that `'1` must outlive `'2`
+ |
+help: consider introducing a named lifetime parameter
+ |
+LL | fn foo<'a>(x: &mut Vec<&'a u8>, y: &'a u8) {
+ | ++++ ++ ++
error: aborting due to previous error
--> $DIR/issue_74400.rs:12:5
|
LL | f(data, identity)
- | ^^^^^^^^^^^^^^^^^
+ | ^^^^^^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds
|
- = help: consider adding an explicit lifetime bound `T: 'static`...
+help: consider adding an explicit lifetime bound...
+ |
+LL | fn g<T: 'static>(data: &[T]) {
+ | +++++++++
error[E0308]: mismatched types
--> $DIR/issue_74400.rs:12:5
--- /dev/null
+// build-pass
+#![allow(dead_code)]
+#![allow(non_camel_case_types)]
+#![warn(clashing_extern_declarations)]
+
+// pretty-expanded FIXME #23616
+
+mod a {
+ pub type rust_task = usize;
+ pub mod rustrt {
+ use super::rust_task;
+ extern "C" {
+ pub fn rust_task_is_unwinding(rt: *const rust_task) -> bool;
+ }
+ }
+}
+
+mod b {
+ pub type rust_task = bool;
+ pub mod rustrt {
+ use super::rust_task;
+ extern "C" {
+ pub fn rust_task_is_unwinding(rt: *const rust_task) -> bool;
+ //~^ WARN `rust_task_is_unwinding` redeclared with a different signature
+ }
+ }
+}
+
+pub fn main() {}
--- /dev/null
+warning: `rust_task_is_unwinding` redeclared with a different signature
+ --> $DIR/issue-1866.rs:23:13
+ |
+LL | pub fn rust_task_is_unwinding(rt: *const rust_task) -> bool;
+ | ------------------------------------------------------------ `rust_task_is_unwinding` previously declared here
+...
+LL | pub fn rust_task_is_unwinding(rt: *const rust_task) -> bool;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ this signature doesn't match the previous declaration
+ |
+note: the lint level is defined here
+ --> $DIR/issue-1866.rs:4:9
+ |
+LL | #![warn(clashing_extern_declarations)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ = note: expected `unsafe extern "C" fn(*const usize) -> bool`
+ found `unsafe extern "C" fn(*const bool) -> bool`
+
+warning: 1 warning emitted
+
--- /dev/null
+// run-pass
+#![allow(unused_variables)]
+// Regression test for Issue #20343.
+
+// pretty-expanded FIXME #23616
+
+#![deny(dead_code)]
+
+struct B { b: u32 }
+struct C;
+struct D;
+
+trait T<A> { fn dummy(&self, a: A) { } }
+impl<A> T<A> for () {}
+
+impl B {
+ // test for unused code in arguments
+ fn foo(B { b }: B) -> u32 { b }
+
+ // test for unused code in return type
+ fn bar() -> C { unsafe { ::std::mem::transmute(()) } }
+
+ // test for unused code in generics
+ fn baz<A: T<D>>() {}
+}
+
+pub fn main() {
+ let b = B { b: 3 };
+ B::foo(b);
+ B::bar();
+ B::baz::<()>();
+}
pub fn count_neutrons(&self) -> usize { self.neutrons } //~ WARNING unreachable_pub
pub(crate) fn count_electrons(&self) -> usize { self.electrons }
}
+ impl Clone for Hydrogen {
+ fn clone(&self) -> Hydrogen {
+ Hydrogen { neutrons: self.neutrons, electrons: self.electrons }
+ }
+ }
pub enum Helium {} //~ WARNING unreachable_pub
pub union Lithium { c1: usize, c2: u8 } //~ WARNING unreachable_pub
| help: consider restricting its visibility: `pub(crate)`
warning: unreachable `pub` item
- --> $DIR/unreachable_pub-pub_crate.rs:30:5
+ --> $DIR/unreachable_pub-pub_crate.rs:35:5
|
LL | pub enum Helium {}
| ---^^^^^^^^^^^^
= help: or consider exporting it for use by other crates
warning: unreachable `pub` item
- --> $DIR/unreachable_pub-pub_crate.rs:31:5
+ --> $DIR/unreachable_pub-pub_crate.rs:36:5
|
LL | pub union Lithium { c1: usize, c2: u8 }
| ---^^^^^^^^^^^^^^
= help: or consider exporting it for use by other crates
warning: unreachable `pub` item
- --> $DIR/unreachable_pub-pub_crate.rs:32:5
+ --> $DIR/unreachable_pub-pub_crate.rs:37:5
|
LL | pub fn beryllium() {}
| ---^^^^^^^^^^^^^^^
= help: or consider exporting it for use by other crates
warning: unreachable `pub` item
- --> $DIR/unreachable_pub-pub_crate.rs:33:5
+ --> $DIR/unreachable_pub-pub_crate.rs:38:5
|
LL | pub trait Boron {}
| ---^^^^^^^^^^^^
= help: or consider exporting it for use by other crates
warning: unreachable `pub` item
- --> $DIR/unreachable_pub-pub_crate.rs:34:5
+ --> $DIR/unreachable_pub-pub_crate.rs:39:5
|
LL | pub const CARBON: usize = 1;
| ---^^^^^^^^^^^^^^^^^^^^^^^^^
= help: or consider exporting it for use by other crates
warning: unreachable `pub` item
- --> $DIR/unreachable_pub-pub_crate.rs:35:5
+ --> $DIR/unreachable_pub-pub_crate.rs:40:5
|
LL | pub static NITROGEN: usize = 2;
| ---^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= help: or consider exporting it for use by other crates
warning: unreachable `pub` item
- --> $DIR/unreachable_pub-pub_crate.rs:36:5
+ --> $DIR/unreachable_pub-pub_crate.rs:41:5
|
LL | pub type Oxygen = bool;
| ---^^^^^^^^^^^^^^^^^^^^
= help: or consider exporting it for use by other crates
warning: unreachable `pub` item
- --> $DIR/unreachable_pub-pub_crate.rs:39:47
+ --> $DIR/unreachable_pub-pub_crate.rs:44:47
|
LL | ($visibility: vis, $name: ident) => { $visibility struct $name {} }
| ^^^^^^^^^^^^^^^^^^^^^^^^
= note: this warning originates in the macro `define_empty_struct_with_visibility` (in Nightly builds, run with -Z macro-backtrace for more info)
warning: unreachable `pub` item
- --> $DIR/unreachable_pub-pub_crate.rs:45:9
+ --> $DIR/unreachable_pub-pub_crate.rs:50:9
|
LL | pub fn catalyze() -> bool;
| ---^^^^^^^^^^^^^^^^^^^^^^^
pub fn count_neutrons(&self) -> usize { self.neutrons } //~ WARNING unreachable_pub
crate fn count_electrons(&self) -> usize { self.electrons }
}
+ impl Clone for Hydrogen {
+ fn clone(&self) -> Hydrogen {
+ Hydrogen { neutrons: self.neutrons, electrons: self.electrons }
+ }
+ }
pub enum Helium {} //~ WARNING unreachable_pub
pub union Lithium { c1: usize, c2: u8 } //~ WARNING unreachable_pub
| help: consider restricting its visibility: `crate`
warning: unreachable `pub` item
- --> $DIR/unreachable_pub.rs:26:5
+ --> $DIR/unreachable_pub.rs:31:5
|
LL | pub enum Helium {}
| ---^^^^^^^^^^^^
= help: or consider exporting it for use by other crates
warning: unreachable `pub` item
- --> $DIR/unreachable_pub.rs:27:5
+ --> $DIR/unreachable_pub.rs:32:5
|
LL | pub union Lithium { c1: usize, c2: u8 }
| ---^^^^^^^^^^^^^^
= help: or consider exporting it for use by other crates
warning: unreachable `pub` item
- --> $DIR/unreachable_pub.rs:28:5
+ --> $DIR/unreachable_pub.rs:33:5
|
LL | pub fn beryllium() {}
| ---^^^^^^^^^^^^^^^
= help: or consider exporting it for use by other crates
warning: unreachable `pub` item
- --> $DIR/unreachable_pub.rs:29:5
+ --> $DIR/unreachable_pub.rs:34:5
|
LL | pub trait Boron {}
| ---^^^^^^^^^^^^
= help: or consider exporting it for use by other crates
warning: unreachable `pub` item
- --> $DIR/unreachable_pub.rs:30:5
+ --> $DIR/unreachable_pub.rs:35:5
|
LL | pub const CARBON: usize = 1;
| ---^^^^^^^^^^^^^^^^^^^^^^^^^
= help: or consider exporting it for use by other crates
warning: unreachable `pub` item
- --> $DIR/unreachable_pub.rs:31:5
+ --> $DIR/unreachable_pub.rs:36:5
|
LL | pub static NITROGEN: usize = 2;
| ---^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= help: or consider exporting it for use by other crates
warning: unreachable `pub` item
- --> $DIR/unreachable_pub.rs:32:5
+ --> $DIR/unreachable_pub.rs:37:5
|
LL | pub type Oxygen = bool;
| ---^^^^^^^^^^^^^^^^^^^^
= help: or consider exporting it for use by other crates
warning: unreachable `pub` item
- --> $DIR/unreachable_pub.rs:35:47
+ --> $DIR/unreachable_pub.rs:40:47
|
LL | ($visibility: vis, $name: ident) => { $visibility struct $name {} }
| ^^^^^^^^^^^^^^^^^^^^^^^^
= note: this warning originates in the macro `define_empty_struct_with_visibility` (in Nightly builds, run with -Z macro-backtrace for more info)
warning: unreachable `pub` item
- --> $DIR/unreachable_pub.rs:41:9
+ --> $DIR/unreachable_pub.rs:46:9
|
LL | pub fn catalyze() -> bool;
| ---^^^^^^^^^^^^^^^^^^^^^^^
--- /dev/null
+macro_rules! m { ($($t:tt)*) => { $($t)* } }
+
+fn main() {
+ m!($t); //~ ERROR expected expression
+}
--- /dev/null
+error: expected expression, found `$`
+ --> $DIR/issue-35450.rs:4:8
+ |
+LL | m!($t);
+ | ^ expected expression
+
+error: aborting due to previous error
+
--- /dev/null
+fn main() {
+ include!(line!()); //~ ERROR argument must be a string literal
+}
--- /dev/null
+error: argument must be a string literal
+ --> $DIR/issue-41776.rs:2:14
+ |
+LL | include!(line!());
+ | ^^^^^^^
+
+error: aborting due to previous error
+
--- /dev/null
+// run-pass
+
+#![feature(decl_macro)]
+
+pub struct Foo {
+ bar: u32,
+}
+pub macro pattern($a:pat) {
+ Foo { bar: $a }
+}
+
+fn main() {
+ match (Foo { bar: 3 }) {
+ pattern!(3) => println!("Test OK"),
+ _ => unreachable!(),
+ }
+}
| ^^^^^^^^^^^^^^^^^^^^^^^^^
|
= note: expanding `println! { "Hello, World!" }`
- = note: to `$crate :: io :: _print($crate :: format_args_nl! ("Hello, World!"))`
+ = note: to `{ $crate :: io :: _print($crate :: format_args_nl! ("Hello, World!")) ; }`
// compile-flags: -Zunpretty=hir,typed
// check-pass
-pub fn main() ({
+fn main() ({
(if (true as bool)
({ } as
()) else if (let Some(a) =
--- /dev/null
+fn main() {
+ let arr = &[0,1,2,3];
+ for _i in 0..arr.len().rev() { //~ERROR not an iterator
+ // The above error used to say “the method `rev` exists for type `usize`”.
+ // This regression test ensures it doesn't say that any more.
+ }
+}
--- /dev/null
+error[E0599]: `usize` is not an iterator
+ --> $DIR/issue-90315.rs:3:26
+ |
+LL | for _i in 0..arr.len().rev() {
+ | ^^^ `usize` is not an iterator
+ |
+ = note: the following trait bounds were not satisfied:
+ `usize: Iterator`
+ which is required by `&mut usize: Iterator`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0599`.
--- /dev/null
+trait T {
+ fn f(&self, _: ()) {
+ None::<()>.map(Self::f);
+ }
+ //~^^ ERROR function is expected to take a single 0-tuple as argument
+}
+
+fn main() {}
--- /dev/null
+error[E0593]: function is expected to take a single 0-tuple as argument, but it takes 2 distinct arguments
+ --> $DIR/issue-47706-trait.rs:3:24
+ |
+LL | fn f(&self, _: ()) {
+ | ------------------ takes 2 distinct arguments
+LL | None::<()>.map(Self::f);
+ | --- ^^^^^^^ expected function that takes a single 0-tuple as argument
+ | |
+ | required by a bound introduced by this call
+ |
+note: required by a bound in `Option::<T>::map`
+ --> $SRC_DIR/core/src/option.rs:LL:COL
+ |
+LL | F: ~const FnOnce(T) -> U,
+ | ^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Option::<T>::map`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0593`.
--- /dev/null
+pub struct Foo {
+ foo: Option<i32>,
+}
+
+impl Foo {
+ pub fn new(foo: Option<i32>, _: ()) -> Foo {
+ Foo { foo }
+ }
+
+ pub fn map(self) -> Option<Foo> {
+ self.foo.map(Foo::new)
+ }
+ //~^^ ERROR function is expected to take 1 argument, but it takes 2 arguments [E0593]
+}
+
+enum Qux {
+ Bar(i32),
+}
+
+fn foo<F>(f: F)
+where
+ F: Fn(),
+{
+}
+
+fn main() {
+ foo(Qux::Bar);
+}
+//~^^ ERROR function is expected to take 0 arguments, but it takes 1 argument [E0593]
--- /dev/null
+error[E0593]: function is expected to take 1 argument, but it takes 2 arguments
+ --> $DIR/issue-47706.rs:11:22
+ |
+LL | pub fn new(foo: Option<i32>, _: ()) -> Foo {
+ | ------------------------------------------ takes 2 arguments
+...
+LL | self.foo.map(Foo::new)
+ | --- ^^^^^^^^ expected function that takes 1 argument
+ | |
+ | required by a bound introduced by this call
+ |
+note: required by a bound in `Option::<T>::map`
+ --> $SRC_DIR/core/src/option.rs:LL:COL
+ |
+LL | F: ~const FnOnce(T) -> U,
+ | ^^^^^^^^^^^^^^^^^^^^^ required by this bound in `Option::<T>::map`
+
+error[E0593]: function is expected to take 0 arguments, but it takes 1 argument
+ --> $DIR/issue-47706.rs:27:9
+ |
+LL | Bar(i32),
+ | -------- takes 1 argument
+...
+LL | foo(Qux::Bar);
+ | --- ^^^^^^^^ expected function that takes 0 arguments
+ | |
+ | required by a bound introduced by this call
+ |
+note: required by a bound in `foo`
+ --> $DIR/issue-47706.rs:22:8
+ |
+LL | fn foo<F>(f: F)
+ | --- required by a bound in this
+LL | where
+LL | F: Fn(),
+ | ^^^^ required by this bound in `foo`
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0593`.
fn duplicate_custom_3<T>(t: S<T>) -> (S<T>, S<T>)
where
- T: A,
- T: B, T: Trait, T: Copy
- //~^ HELP consider further restricting type parameter `T`
+ T: A + Trait + Copy,
+ //~^ HELP consider further restricting this bound
+ T: B,
{
(t, t) //~ use of moved value: `t`
}
-fn duplicate_custom_4<T: A>(t: S<T>) -> (S<T>, S<T>)
+fn duplicate_custom_4<T: A + Trait + Copy>(t: S<T>) -> (S<T>, S<T>)
+//~^ HELP consider further restricting this bound
where
- T: B + Trait + Copy,
- //~^ HELP consider further restricting this bound
+ T: B,
{
(t, t) //~ use of moved value: `t`
}
fn existing_colon_in_where<T>(t: T)
where
- T: Copy,
- //~^ HELP consider further restricting this bound
+ T:, T: Copy
+ //~^ HELP consider further restricting type parameter `T`
{
[t, t]; //~ use of moved value: `t`
}
fn duplicate_custom_3<T>(t: S<T>) -> (S<T>, S<T>)
where
T: A,
+ //~^ HELP consider further restricting this bound
T: B,
- //~^ HELP consider further restricting type parameter `T`
{
(t, t) //~ use of moved value: `t`
}
fn duplicate_custom_4<T: A>(t: S<T>) -> (S<T>, S<T>)
+//~^ HELP consider further restricting this bound
where
T: B,
- //~^ HELP consider further restricting this bound
{
(t, t) //~ use of moved value: `t`
}
fn existing_colon_in_where<T>(t: T)
where
T:,
- //~^ HELP consider further restricting this bound
+ //~^ HELP consider further restricting type parameter `T`
{
[t, t]; //~ use of moved value: `t`
}
| |
| value moved here
|
-help: consider further restricting type parameter `T`
+help: consider further restricting this bound
|
-LL | T: B, T: Trait, T: Copy
- | ~~~~~~~~~~~~~~~~~~~
+LL | T: A + Trait + Copy,
+ | ++++++++++++++
error[E0382]: use of moved value: `t`
--> $DIR/use_of_moved_value_copy_suggestions.rs:69:9
|
help: consider further restricting this bound
|
-LL | T: B + Trait + Copy,
- | ++++++++++++++
+LL | fn duplicate_custom_4<T: A + Trait + Copy>(t: S<T>) -> (S<T>, S<T>)
+ | ++++++++++++++
error[E0382]: use of moved value: `t`
--> $DIR/use_of_moved_value_copy_suggestions.rs:83:9
| |
| value moved here
|
-help: consider further restricting this bound
+help: consider further restricting type parameter `T`
|
-LL | T: Copy,
- | ++++
+LL | T:, T: Copy
+ | ~~~~~~~~~
error[E0382]: use of moved value: `t`
--> $DIR/use_of_moved_value_copy_suggestions.rs:75:9
+++ /dev/null
-// run-pass
-
-#![allow(dead_code)]
-
-use std::mem::{size_of, align_of};
-use std::os::raw::c_int;
-
-// The two enums that follow are designed so that bugs trigger layout optimization.
-// Specifically, if either of the following reprs used here is not detected by the compiler,
-// then the sizes will be wrong.
-
-#[repr(C, u8)]
-enum E1 {
- A(u8, u16, u8),
- B(u8, u16, u8)
-}
-
-#[repr(u8, C)]
-enum E2 {
- A(u8, u16, u8),
- B(u8, u16, u8)
-}
-
-// Check that repr(int) and repr(C) are in fact different from the above
-
-#[repr(u8)]
-enum E3 {
- A(u8, u16, u8),
- B(u8, u16, u8)
-}
-
-#[repr(u16)]
-enum E4 {
- A(u8, u16, u8),
- B(u8, u16, u8)
-}
-
-#[repr(u32)]
-enum E5 {
- A(u8, u16, u8),
- B(u8, u16, u8)
-}
-
-#[repr(u64)]
-enum E6 {
- A(u8, u16, u8),
- B(u8, u16, u8)
-}
-
-#[repr(C)]
-enum E7 {
- A(u8, u16, u8),
- B(u8, u16, u8)
-}
-
-// From pr 37429
-
-#[repr(C,packed)]
-pub struct p0f_api_query {
- pub magic: u32,
- pub addr_type: u8,
- pub addr: [u8; 16],
-}
-
-pub fn main() {
- assert_eq!(size_of::<E1>(), 8);
- assert_eq!(size_of::<E2>(), 8);
- assert_eq!(size_of::<E3>(), 6);
- assert_eq!(size_of::<E4>(), 8);
- assert_eq!(size_of::<E5>(), align_size(10, align_of::<u32>()));
- assert_eq!(size_of::<E6>(), align_size(14, align_of::<u64>()));
- assert_eq!(size_of::<E7>(), align_size(6 + size_of::<c_int>(), align_of::<c_int>()));
- assert_eq!(size_of::<p0f_api_query>(), 21);
-}
-
-fn align_size(size: usize, align: usize) -> usize {
- if size % align != 0 {
- size + (align - (size % align))
- } else {
- size
- }
-}
note: impl defined here, but it is not `const`
--> $SRC_DIR/core/src/iter/traits/collect.rs:LL:COL
|
-LL | impl<I: Iterator> IntoIterator for I {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+LL | impl<I: ~const Iterator> const IntoIterator for I {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: calls in constants are limited to constant functions, tuple structs and tuple variants
error[E0658]: mutable references are not allowed in constants
--- /dev/null
+fn main() {
+ 0.....{loop{}1};
+ //~^ ERROR unexpected token
+ //~| ERROR mismatched types
+}
--- /dev/null
+error: unexpected token: `...`
+ --> $DIR/issue-96335.rs:2:6
+ |
+LL | 0.....{loop{}1};
+ | ^^^
+ |
+help: use `..` for an exclusive range
+ |
+LL | 0....{loop{}1};
+ | ~~
+help: or `..=` for an inclusive range
+ |
+LL | 0..=..{loop{}1};
+ | ~~~
+
+error[E0308]: mismatched types
+ --> $DIR/issue-96335.rs:2:9
+ |
+LL | 0.....{loop{}1};
+ | ----^^^^^^^^^^^
+ | | |
+ | | expected integer, found struct `RangeTo`
+ | arguments to this function are incorrect
+ |
+ = note: expected type `{integer}`
+ found struct `RangeTo<{integer}>`
+note: associated function defined here
+ --> $SRC_DIR/core/src/ops/range.rs:LL:COL
+ |
+LL | pub const fn new(start: Idx, end: Idx) -> Self {
+ | ^^^
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
... |
LL | | require(value);
LL | | });
- | |_____^
+ | |_____^ ...so that the type `T` will meet its required lifetime bounds
+ |
+help: consider adding an explicit lifetime bound...
|
- = help: consider adding an explicit lifetime bound `T: 'a`...
+LL | T: Trait<'a> + 'a,
+ | ++++
error: aborting due to previous error
--- /dev/null
+// rust-lang/rust#45696: This test checks the compiler won't infinite loop when
+// you declare a variable of type `struct A(Box<A>, ...);` (which is impossible
+// to construct but *is* possible to declare; see also issues #4287, #44933,
+// and #52852).
+//
+// We will explicitly test NLL, and migration modes; thus we will also skip the
+// automated compare-mode=nll.
+
+// run-pass
+
+// This test has structs and functions that are by definition unusable
+// all over the place, so just go ahead and allow dead_code
+#![allow(dead_code)]
+
+// direct regular recursion with indirect ownership via box
+struct C { field: Box<C> }
+
+// direct non-regular recursion with indirect ownership via box
+struct D { field: Box<(D, D)> }
+
+// indirect regular recursion with indirect ownership via box.
+struct E { field: F }
+struct F { field: Box<E> }
+
+// indirect non-regular recursion with indirect ownership via box.
+struct G { field: (H, H) }
+struct H { field: Box<G> }
+
+// These enums are cases that are not currently hit by the
+// `visit_terminator_drop` recursion down a type's structural
+// definition.
+//
+// But it seems prudent to include them in this test as variants on
+// the above, in that they are similarly non-constructable data types
+// with destructors that would diverge.
+enum I { One(Box<I>) }
+enum J { One(Box<J>), Two(Box<J>) }
+
+fn impossible_to_call_c(_c: C) { }
+fn impossible_to_call_d(_d: D) { }
+fn impossible_to_call_e(_e: E) { }
+fn impossible_to_call_f(_f: F) { }
+fn impossible_to_call_g(_g: G) { }
+fn impossible_to_call_h(_h: H) { }
+fn impossible_to_call_i(_i: I) { }
+fn impossible_to_call_j(_j: J) { }
+
+fn main() {
+
+}
--- /dev/null
+error: lifetime may not live long enough
+ --> $DIR/issue-52213.rs:3:20
+ |
+LL | fn transmute_lifetime<'a, 'b, T>(t: &'a (T,)) -> &'b T {
+ | -- -- lifetime `'b` defined here
+ | |
+ | lifetime `'a` defined here
+LL | match (&t,) {
+LL | ((u,),) => u,
+ | ^ function was supposed to return data with lifetime `'b` but it is returning data with lifetime `'a`
+ |
+ = help: consider adding the following bound: `'a: 'b`
+
+error: aborting due to previous error
+
--- /dev/null
+fn transmute_lifetime<'a, 'b, T>(t: &'a (T,)) -> &'b T {
+ match (&t,) { //~ ERROR cannot infer an appropriate lifetime
+ ((u,),) => u,
+ }
+}
+
+fn main() {
+ let x = {
+ let y = Box::new((42,));
+ transmute_lifetime(&y)
+ };
+
+ println!("{}", x);
+}
--- /dev/null
+error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
+ --> $DIR/issue-52213.rs:2:11
+ |
+LL | match (&t,) {
+ | ^^^^^
+ |
+note: first, the lifetime cannot outlive the lifetime `'a` as defined here...
+ --> $DIR/issue-52213.rs:1:23
+ |
+LL | fn transmute_lifetime<'a, 'b, T>(t: &'a (T,)) -> &'b T {
+ | ^^
+note: ...so that the types are compatible
+ --> $DIR/issue-52213.rs:2:11
+ |
+LL | match (&t,) {
+ | ^^^^^
+ = note: expected `(&&(T,),)`
+ found `(&&'a (T,),)`
+note: but, the lifetime must be valid for the lifetime `'b` as defined here...
+ --> $DIR/issue-52213.rs:1:27
+ |
+LL | fn transmute_lifetime<'a, 'b, T>(t: &'a (T,)) -> &'b T {
+ | ^^
+note: ...so that reference does not outlive borrowed content
+ --> $DIR/issue-52213.rs:3:20
+ |
+LL | ((u,),) => u,
+ | ^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0495`.
| -- lifetime `'a` defined here
LL | x
| ^ returning this value requires that `'a` must outlive `'static`
+ |
+help: to declare that the trait object captures data from argument `x`, you can add an explicit `'a` lifetime bound
+ |
+LL | fn bar<'a>(x: &'a u32) -> &'static dyn Debug + 'a {
+ | ++++
error: aborting due to previous error
--> $DIR/impl-trait-outlives.rs:11:5
|
LL | x
- | ^
+ | ^ ...so that the type `T` will meet its required lifetime bounds
|
- = help: consider adding an explicit lifetime bound `T: 'a`...
+help: consider adding an explicit lifetime bound...
+ |
+LL | T: Debug + 'a,
+ | ++++
error[E0309]: the parameter type `T` may not live long enough
--> $DIR/impl-trait-outlives.rs:26:5
|
LL | x
- | ^
+ | ^ ...so that the type `T` will meet its required lifetime bounds
+ |
+help: consider adding an explicit lifetime bound...
|
- = help: consider adding an explicit lifetime bound `T: 'a`...
+LL | T: 'b + Debug + 'a,
+ | ++++
error: aborting due to 2 previous errors
--> $DIR/projection-implied-bounds.rs:30:18
|
LL | twice(value, |value_ref, item| invoke2(value_ref, item));
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds
|
- = help: consider adding an explicit lifetime bound `T: 'static`...
+help: consider adding an explicit lifetime bound...
+ |
+LL | fn generic2<T: Iterator + 'static>(value: T) {
+ | +++++++++
error: aborting due to previous error
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: consider adding an explicit lifetime bound `<T as Iterator>::Item: 'a`...
+ = note: ...so that the type `<T as Iterator>::Item` will meet its required lifetime bounds
note: external requirements
--> $DIR/projection-no-regions-closure.rs:34:23
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: consider adding an explicit lifetime bound `<T as Iterator>::Item: 'a`...
+ = note: ...so that the type `<T as Iterator>::Item` will meet its required lifetime bounds
note: external requirements
--> $DIR/projection-no-regions-closure.rs:52:23
| ^^^^^^^^^^^^^^^^^^
|
= help: consider adding an explicit lifetime bound `<T as Iterator>::Item: 'a`...
+ = note: ...so that the type `<T as Iterator>::Item` will meet its required lifetime bounds
error[E0309]: the associated type `<T as Iterator>::Item` may not live long enough
--> $DIR/projection-no-regions-fn.rs:28:5
| ^^^^^^^^^^^^^^^^^^
|
= help: consider adding an explicit lifetime bound `<T as Iterator>::Item: 'a`...
+ = note: ...so that the type `<T as Iterator>::Item` will meet its required lifetime bounds
error: aborting due to 2 previous errors
--> $DIR/projection-one-region-closure.rs:45:29
|
LL | with_signature(cell, t, |cell, t| require(cell, t));
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds
+ |
+help: consider adding an explicit lifetime bound...
|
- = help: consider adding an explicit lifetime bound `T: 'a`...
+LL | T: Anything<'b> + 'a,
+ | ++++
error: lifetime may not live long enough
--> $DIR/projection-one-region-closure.rs:45:39
--> $DIR/projection-one-region-closure.rs:56:29
|
LL | with_signature(cell, t, |cell, t| require(cell, t));
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds
+ |
+help: consider adding an explicit lifetime bound...
|
- = help: consider adding an explicit lifetime bound `T: 'a`...
+LL | T: Anything<'b> + 'a,
+ | ++++
error: lifetime may not live long enough
--> $DIR/projection-one-region-closure.rs:56:39
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: consider adding an explicit lifetime bound `<T as Anything<ReEarlyBound(0, 'b), ReEarlyBound(1, 'c)>>::AssocType: 'a`...
+ = note: ...so that the type `<T as Anything<ReEarlyBound(0, 'b), ReEarlyBound(1, 'c)>>::AssocType` will meet its required lifetime bounds
note: external requirements
--> $DIR/projection-two-region-trait-bound-closure.rs:48:29
| ^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: consider adding an explicit lifetime bound `<T as Anything<ReEarlyBound(1, 'b), ReEarlyBound(2, 'c)>>::AssocType: 'a`...
+ = note: ...so that the type `<T as Anything<ReEarlyBound(1, 'b), ReEarlyBound(2, 'c)>>::AssocType` will meet its required lifetime bounds
note: external requirements
--> $DIR/projection-two-region-trait-bound-closure.rs:61:29
| ^^^^^^^^^^^^^^^^
|
= help: consider adding an explicit lifetime bound `<T as MyTrait<'_>>::Output: 'a`...
+ = note: ...so that the type `<T as MyTrait<'_>>::Output` will meet its required lifetime bounds
error: aborting due to previous error
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
|
= help: consider adding an explicit lifetime bound `<T as MyTrait<'_>>::Output: 'a`...
+ = note: ...so that the type `<T as MyTrait<'_>>::Output` will meet its required lifetime bounds
error: aborting due to previous error
--> $DIR/projection-where-clause-none.rs:16:5
|
LL | bar::<T::Output>()
- | ^^^^^^^^^^^^^^^^
+ | ^^^^^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds
|
- = help: consider adding an explicit lifetime bound `T: 'a`...
+help: consider adding an explicit lifetime bound...
+ |
+LL | T: MyTrait<'a> + 'a,
+ | ++++
error: aborting due to previous error
--> $DIR/ty-param-closure-approximate-lower-bound.rs:29:24
|
LL | twice(cell, value, |a, b| invoke(a, b));
- | ^^^^^^^^^^^^^^^^^^^
+ | ^^^^^^^^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds
+ |
+help: consider adding an explicit lifetime bound...
|
- = help: consider adding an explicit lifetime bound `T: 'a`...
+LL | fn generic_fail<'a, T: 'a>(cell: Cell<&'a ()>, value: T) {
+ | ++++
error: aborting due to previous error
--> $DIR/ty-param-closure-outlives-from-return-type.rs:26:23
|
LL | with_signature(x, |y| y)
- | ^^^^^
+ | ^^^^^ ...so that the type `T` will meet its required lifetime bounds
+ |
+help: consider adding an explicit lifetime bound...
|
- = help: consider adding an explicit lifetime bound `T: 'a`...
+LL | T: Debug + 'a,
+ | ++++
error[E0309]: the parameter type `T` may not live long enough
--> $DIR/ty-param-closure-outlives-from-return-type.rs:41:5
|
LL | x
- | ^
+ | ^ ...so that the type `T` will meet its required lifetime bounds
+ |
+help: consider adding an explicit lifetime bound...
|
- = help: consider adding an explicit lifetime bound `T: 'a`...
+LL | T: 'b + Debug + 'a,
+ | ++++
error: aborting due to 2 previous errors
... |
LL | | require(&x, &y)
LL | | })
- | |_____^
+ | |_____^ ...so that the type `T` will meet its required lifetime bounds
+ |
+help: consider adding an explicit lifetime bound...
|
- = help: consider adding an explicit lifetime bound `T: 'a`...
+LL | fn no_region<'a, T: 'a>(a: Cell<&'a ()>, b: T) {
+ | ++++
note: external requirements
--> $DIR/ty-param-closure-outlives-from-where-clause.rs:43:26
LL | | // See `correct_region`
LL | | require(&x, &y)
LL | | })
- | |_____^
+ | |_____^ ...so that the type `T` will meet its required lifetime bounds
+ |
+help: consider adding an explicit lifetime bound...
|
- = help: consider adding an explicit lifetime bound `T: 'a`...
+LL | T: 'b + 'a,
+ | ++++
note: external requirements
--> $DIR/ty-param-closure-outlives-from-where-clause.rs:77:26
--> $DIR/ty-param-fn-body.rs:19:5
|
LL | outlives(cell, t)
- | ^^^^^^^^^^^^^^^^^
+ | ^^^^^^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds
|
- = help: consider adding an explicit lifetime bound `T: 'a`...
+help: consider adding an explicit lifetime bound...
+ |
+LL | fn region_static<'a, T: 'a>(cell: Cell<&'a usize>, t: T) {
+ | ++++
error: aborting due to previous error
--> $DIR/ty-param-fn.rs:11:5
|
LL | x
- | ^
+ | ^ ...so that the type `T` will meet its required lifetime bounds
|
- = help: consider adding an explicit lifetime bound `T: 'a`...
+help: consider adding an explicit lifetime bound...
+ |
+LL | T: Debug + 'a,
+ | ++++
error[E0309]: the parameter type `T` may not live long enough
--> $DIR/ty-param-fn.rs:26:5
|
LL | x
- | ^
+ | ^ ...so that the type `T` will meet its required lifetime bounds
+ |
+help: consider adding an explicit lifetime bound...
|
- = help: consider adding an explicit lifetime bound `T: 'a`...
+LL | T: 'b + Debug + 'a,
+ | ++++
error: aborting due to 2 previous errors
...
LL | ss.r
| ^^^^ returning this value requires that `'1` must outlive `'static`
+ |
+help: to declare that the trait object captures data from argument `ss`, you can add an explicit `'_` lifetime bound
+ |
+LL | fn load(ss: &mut SomeStruct) -> Box<dyn SomeTrait + '_> {
+ | ++++
error[E0507]: cannot move out of `ss.r` which is behind a mutable reference
--> $DIR/object-lifetime-default-from-box-error.rs:18:5
--- /dev/null
+trait Foo {
+ fn foo<T>(&self, val: T);
+}
+
+trait Bar: Foo { }
+
+pub struct Thing;
+
+impl Foo for Thing {
+ fn foo<T>(&self, val: T) { }
+}
+
+impl Bar for Thing { }
+
+fn main() {
+ let mut thing = Thing;
+ let test: &mut dyn Bar = &mut thing;
+ //~^ ERROR E0038
+ //~| ERROR E0038
+}
--- /dev/null
+error[E0038]: the trait `Bar` cannot be made into an object
+ --> $DIR/issue-19538.rs:17:15
+ |
+LL | let test: &mut dyn Bar = &mut thing;
+ | ^^^^^^^^^^^^ `Bar` cannot be made into an object
+ |
+note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
+ --> $DIR/issue-19538.rs:2:8
+ |
+LL | fn foo<T>(&self, val: T);
+ | ^^^ ...because method `foo` has generic type parameters
+...
+LL | trait Bar: Foo { }
+ | --- this trait cannot be made into an object...
+ = help: consider moving `foo` to another trait
+
+error[E0038]: the trait `Bar` cannot be made into an object
+ --> $DIR/issue-19538.rs:17:30
+ |
+LL | let test: &mut dyn Bar = &mut thing;
+ | ^^^^^^^^^^ `Bar` cannot be made into an object
+ |
+note: for a trait to be "object safe" it needs to allow building a vtable to allow the call to be resolvable dynamically; for more information visit <https://doc.rust-lang.org/reference/items/traits.html#object-safety>
+ --> $DIR/issue-19538.rs:2:8
+ |
+LL | fn foo<T>(&self, val: T);
+ | ^^^ ...because method `foo` has generic type parameters
+...
+LL | trait Bar: Foo { }
+ | --- this trait cannot be made into an object...
+ = help: consider moving `foo` to another trait
+ = note: required because of the requirements on the impl of `CoerceUnsized<&mut dyn Bar>` for `&mut Thing`
+ = note: required by cast to type `&mut dyn Bar`
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0038`.
| |
| pattern doesn't bind `c`
+error[E0408]: variable `d` is not bound in all patterns
+ --> $DIR/missing-bindings.rs:45:33
+ |
+LL | let (A(A(a, b) | B(c), d) | B(e)) = Y;
+ | - ^^^^ pattern doesn't bind `d`
+ | |
+ | variable not in all patterns
+
+error[E0408]: variable `e` is not bound in all patterns
+ --> $DIR/missing-bindings.rs:45:10
+ |
+LL | let (A(A(a, b) | B(c), d) | B(e)) = Y;
+ | ^^^^^^^^^^^^^^^^^^^^ - variable not in all patterns
+ | |
+ | pattern doesn't bind `e`
+
error[E0408]: variable `a` is not bound in all patterns
--> $DIR/missing-bindings.rs:45:33
|
| |
| variable not in all patterns
-error[E0408]: variable `d` is not bound in all patterns
- --> $DIR/missing-bindings.rs:45:33
- |
-LL | let (A(A(a, b) | B(c), d) | B(e)) = Y;
- | - ^^^^ pattern doesn't bind `d`
- | |
- | variable not in all patterns
-
-error[E0408]: variable `e` is not bound in all patterns
- --> $DIR/missing-bindings.rs:45:10
- |
-LL | let (A(A(a, b) | B(c), d) | B(e)) = Y;
- | ^^^^^^^^^^^^^^^^^^^^ - variable not in all patterns
- | |
- | pattern doesn't bind `e`
-
error[E0408]: variable `a` is not bound in all patterns
--> $DIR/missing-bindings.rs:61:29
|
--- /dev/null
+fn main() {
+ (if foobar) //~ ERROR expected `{`, found `)`
+}
--- /dev/null
+error: expected `{`, found `)`
+ --> $DIR/issue-61858.rs:2:15
+ |
+LL | (if foobar)
+ | -- ^ expected `{`
+ | |
+ | this `if` expression has a condition, but no block
+
+error: aborting due to previous error
+
LL | }
| - the item list ends here
|
- = note: keyword order for functions declaration is `default`, `pub`, `const`, `async`, `unsafe`, `extern`
+ = note: keyword order for functions declaration is `pub`, `default`, `const`, `async`, `unsafe`, `extern`
error: aborting due to previous error
//~| NOTE expected one of `extern` or `fn`
//~| HELP `const` must come before `async unsafe`
//~| SUGGESTION const async unsafe
-//~| NOTE keyword order for functions declaration is `default`, `pub`, `const`, `async`, `unsafe`, `extern`
+//~| NOTE keyword order for functions declaration is `pub`, `default`, `const`, `async`, `unsafe`, `extern`
| | expected one of `extern` or `fn`
| help: `const` must come before `async unsafe`: `const async unsafe`
|
- = note: keyword order for functions declaration is `default`, `pub`, `const`, `async`, `unsafe`, `extern`
+ = note: keyword order for functions declaration is `pub`, `default`, `const`, `async`, `unsafe`, `extern`
error: aborting due to previous error
//~| NOTE expected one of `extern` or `fn`
//~| HELP `async` must come before `unsafe`
//~| SUGGESTION async unsafe
-//~| NOTE keyword order for functions declaration is `default`, `pub`, `const`, `async`, `unsafe`, `extern`
+//~| NOTE keyword order for functions declaration is `pub`, `default`, `const`, `async`, `unsafe`, `extern`
| | expected one of `extern` or `fn`
| help: `async` must come before `unsafe`: `async unsafe`
|
- = note: keyword order for functions declaration is `default`, `pub`, `const`, `async`, `unsafe`, `extern`
+ = note: keyword order for functions declaration is `pub`, `default`, `const`, `async`, `unsafe`, `extern`
error: aborting due to previous error
//~| NOTE expected one of `extern` or `fn`
//~| HELP `const` must come before `unsafe`
//~| SUGGESTION const unsafe
-//~| NOTE keyword order for functions declaration is `default`, `pub`, `const`, `async`, `unsafe`, `extern`
+//~| NOTE keyword order for functions declaration is `pub`, `default`, `const`, `async`, `unsafe`, `extern`
| | expected one of `extern` or `fn`
| help: `const` must come before `unsafe`: `const unsafe`
|
- = note: keyword order for functions declaration is `default`, `pub`, `const`, `async`, `unsafe`, `extern`
+ = note: keyword order for functions declaration is `pub`, `default`, `const`, `async`, `unsafe`, `extern`
error: aborting due to previous error
//~| NOTE expected `fn`
//~| HELP `unsafe` must come before `extern`
//~| SUGGESTION unsafe extern
-//~| NOTE keyword order for functions declaration is `default`, `pub`, `const`, `async`, `unsafe`, `extern`
+//~| NOTE keyword order for functions declaration is `pub`, `default`, `const`, `async`, `unsafe`, `extern`
| | expected `fn`
| help: `unsafe` must come before `extern`: `unsafe extern`
|
- = note: keyword order for functions declaration is `default`, `pub`, `const`, `async`, `unsafe`, `extern`
+ = note: keyword order for functions declaration is `pub`, `default`, `const`, `async`, `unsafe`, `extern`
error: aborting due to previous error
static s: &'static str =
+ r#""## //~ ERROR too many `#` when terminating raw string
+;
+
+static s2: &'static str =
r#"
- "## //~ too many `#` when terminating raw string
+ "#### //~ ERROR too many `#` when terminating raw string
;
+
+const A: &'static str = r"" //~ ERROR expected `;`, found `#`
+
+// Test
+#[test]
+fn test() {}
+
+const B: &'static str = r""## //~ ERROR too many `#` when terminating raw string
+
+// Test
+#[test]
+fn test2() {}
+
+fn main() {}
error: too many `#` when terminating raw string
- --> $DIR/raw-str-unbalanced.rs:3:9
+ --> $DIR/raw-str-unbalanced.rs:2:10
|
-LL | "##
- | ^ help: remove the extra `#`
+LL | r#""##
+ | -----^ help: remove the extra `#`
+ | |
+ | this raw string started with 1 `#`
+
+error: too many `#` when terminating raw string
+ --> $DIR/raw-str-unbalanced.rs:7:9
+ |
+LL | / r#"
+LL | | "####
+ | | -^^^ help: remove the extra `#`s
+ | |________|
+ | this raw string started with 1 `#`
+
+error: expected `;`, found `#`
+ --> $DIR/raw-str-unbalanced.rs:10:28
+ |
+LL | const A: &'static str = r""
+ | ^ help: add `;` here
+...
+LL | #[test]
+ | - unexpected token
+
+error: too many `#` when terminating raw string
+ --> $DIR/raw-str-unbalanced.rs:16:28
|
- = note: the raw string started with 1 `#`s
+LL | const B: &'static str = r""##
+ | ---^^ help: remove the extra `#`s
+ | |
+ | this raw string started with 0 `#`s
-error: aborting due to previous error
+error: aborting due to 4 previous errors
--- /dev/null
+fn main() {
+ let x = (1, 2, 3);
+ match x {
+ (_a, _x @ ..) => {}
+ _ => {}
+ }
+}
+//~^^^^ ERROR `_x @` is not allowed in a tuple
+//~| ERROR: `..` patterns are not allowed here
+//~| ERROR: mismatched types
--- /dev/null
+error: `_x @` is not allowed in a tuple
+ --> $DIR/issue-72574-1.rs:4:14
+ |
+LL | (_a, _x @ ..) => {}
+ | ^^^^^^^ this is only allowed in slice patterns
+ |
+ = help: remove this and bind each tuple field independently
+help: if you don't need to use the contents of _x, discard the tuple's remaining fields
+ |
+LL | (_a, ..) => {}
+ | ~~
+
+error: `..` patterns are not allowed here
+ --> $DIR/issue-72574-1.rs:4:19
+ |
+LL | (_a, _x @ ..) => {}
+ | ^^
+ |
+ = note: only allowed in tuple, tuple struct, and slice patterns
+
+error[E0308]: mismatched types
+ --> $DIR/issue-72574-1.rs:4:9
+ |
+LL | match x {
+ | - this expression has type `({integer}, {integer}, {integer})`
+LL | (_a, _x @ ..) => {}
+ | ^^^^^^^^^^^^^ expected a tuple with 3 elements, found one with 2 elements
+ |
+ = note: expected tuple `({integer}, {integer}, {integer})`
+ found tuple `(_, _)`
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
= note: the matched value is of type `Foo`
help: ensure that all possible cases are being handled by adding a match arm with a wildcard pattern or an explicit pattern as shown
|
-LL ~ Foo(2, b) => println!("{}", b),
+LL ~ Foo(2, b) => println!("{}", b)
LL + Foo(_, _) => todo!()
|
LL | | }
| |_____^
+note: the above error was encountered while instantiating `fn finish::<[generator@$DIR/generators.rs:35:5: 39:6], u32, u32>`
+ --> $DIR/generators.rs:86:5
+ |
+LL | finish(unused_type::<u32>());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
error: item has unused generic parameters
--> $DIR/generators.rs:60:5
|
LL | | }
| |_____^
+note: the above error was encountered while instantiating `fn finish::<[generator@$DIR/generators.rs:60:5: 64:6], u32, u32>`
+ --> $DIR/generators.rs:89:5
+ |
+LL | finish(unused_const::<1u32>());
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+
error: aborting due to 2 previous errors; 1 warning emitted
LL | fn bar<I>() {
| ^^^ - generic parameter `I` is unused
+note: the above error was encountered while instantiating `fn foo::<std::slice::Iter<u32>, T>`
+ --> $DIR/predicates.rs:85:5
+ |
+LL | foo(x.iter());
+ | ^^^^^^^^^^^^^
+
error: aborting due to 6 previous errors
// compile-flags: -Z span-debug
// edition:2018
//
-// Tests the pretty-printing behavior of inserting `NoDelim` groups
+// Tests the pretty-printing behavior of inserting `Invisible`-delimited groups
#![no_std] // Don't load unnecessary hygiene information from std
extern crate std;
--- /dev/null
+error[E0478]: lifetime bound not satisfied
+ --> $DIR/issue-28848.rs:14:5
+ |
+LL | Foo::<'a, 'b>::xmute(u)
+ | ^^^^^^^^^^^^^
+ |
+note: lifetime parameter instantiated with the lifetime `'b` as defined here
+ --> $DIR/issue-28848.rs:13:16
+ |
+LL | pub fn foo<'a, 'b>(u: &'b ()) -> &'a () {
+ | ^^
+note: but lifetime parameter must outlive the lifetime `'a` as defined here
+ --> $DIR/issue-28848.rs:13:12
+ |
+LL | pub fn foo<'a, 'b>(u: &'b ()) -> &'a () {
+ | ^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0478`.
error: lifetime may not live long enough
- --> $DIR/issue-28848.rs:10:5
+ --> $DIR/issue-28848.rs:14:5
|
LL | pub fn foo<'a, 'b>(u: &'b ()) -> &'a () {
| -- -- lifetime `'b` defined here
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
+
struct Foo<'a, 'b: 'a>(&'a &'b ());
impl<'a, 'b> Foo<'a, 'b> {
}
pub fn foo<'a, 'b>(u: &'b ()) -> &'a () {
- Foo::<'a, 'b>::xmute(u) //~ ERROR lifetime bound not satisfied
+ Foo::<'a, 'b>::xmute(u)
+ //[base]~^ ERROR lifetime bound not satisfied
+ //[nll]~^^ ERROR lifetime may not live long enough
}
fn main() {}
+++ /dev/null
-error[E0478]: lifetime bound not satisfied
- --> $DIR/issue-28848.rs:10:5
- |
-LL | Foo::<'a, 'b>::xmute(u)
- | ^^^^^^^^^^^^^
- |
-note: lifetime parameter instantiated with the lifetime `'b` as defined here
- --> $DIR/issue-28848.rs:9:16
- |
-LL | pub fn foo<'a, 'b>(u: &'b ()) -> &'a () {
- | ^^
-note: but lifetime parameter must outlive the lifetime `'a` as defined here
- --> $DIR/issue-28848.rs:9:12
- |
-LL | pub fn foo<'a, 'b>(u: &'b ()) -> &'a () {
- | ^^
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0478`.
--- /dev/null
+error[E0308]: `if` and `else` have incompatible types
+ --> $DIR/region-invariant-static-error-reporting.rs:21:9
+ |
+LL | let bad = if x.is_some() {
+ | _______________-
+LL | | x.unwrap()
+ | | ---------- expected because of this
+LL | | } else {
+LL | | mk_static()
+ | | ^^^^^^^^^^^ lifetime mismatch
+LL | | };
+ | |_____- `if` and `else` have incompatible types
+ |
+ = note: expected struct `Invariant<'a>`
+ found struct `Invariant<'static>`
+note: the lifetime `'a` as defined here...
+ --> $DIR/region-invariant-static-error-reporting.rs:17:10
+ |
+LL | fn unify<'a>(x: Option<Invariant<'a>>, f: fn(Invariant<'a>)) {
+ | ^^
+ = note: ...does not necessarily outlive the static lifetime
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0308`.
error[E0521]: borrowed data escapes outside of function
- --> $DIR/region-invariant-static-error-reporting.rs:15:9
+ --> $DIR/region-invariant-static-error-reporting.rs:19:9
|
LL | fn unify<'a>(x: Option<Invariant<'a>>, f: fn(Invariant<'a>)) {
| -- - `x` is a reference that is only valid in the function body
// over time, but this test used to exhibit some pretty bogus messages
// that were not remotely helpful.
-// error-pattern:the lifetime `'a`
-// error-pattern:the static lifetime
+// revisions: base nll
+// ignore-compare-mode-nll
+//[base] error-pattern:the lifetime `'a`
+//[base] error-pattern:the static lifetime
+//[nll] compile-flags: -Z borrowck=mir
+//[nll] error-pattern:argument requires that `'a` must outlive `'static`
struct Invariant<'a>(Option<&'a mut &'a mut ()>);
fn unify<'a>(x: Option<Invariant<'a>>, f: fn(Invariant<'a>)) {
let bad = if x.is_some() {
- x.unwrap()
+ x.unwrap() //[nll]~ ERROR borrowed data escapes outside of function [E0521]
} else {
- mk_static()
+ mk_static() //[base]~ ERROR `if` and `else` have incompatible types [E0308]
};
f(bad);
}
+++ /dev/null
-error[E0308]: `if` and `else` have incompatible types
- --> $DIR/region-invariant-static-error-reporting.rs:17:9
- |
-LL | let bad = if x.is_some() {
- | _______________-
-LL | | x.unwrap()
- | | ---------- expected because of this
-LL | | } else {
-LL | | mk_static()
- | | ^^^^^^^^^^^ lifetime mismatch
-LL | | };
- | |_____- `if` and `else` have incompatible types
- |
- = note: expected struct `Invariant<'a>`
- found struct `Invariant<'static>`
-note: the lifetime `'a` as defined here...
- --> $DIR/region-invariant-static-error-reporting.rs:13:10
- |
-LL | fn unify<'a>(x: Option<Invariant<'a>>, f: fn(Invariant<'a>)) {
- | ^^
- = note: ...does not necessarily outlive the static lifetime
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0308`.
--- /dev/null
+error[E0623]: lifetime mismatch
+ --> $DIR/region-lifetime-bounds-on-fns-where-clause.rs:12:10
+ |
+LL | fn b<'a, 'b>(x: &mut &'a isize, y: &mut &'b isize) {
+ | --------- --------- these two types are declared with different lifetimes...
+LL | // Illegal now because there is no `'b:'a` declaration.
+LL | *x = *y;
+ | ^^ ...but data from `y` flows into `x` here
+
+error[E0623]: lifetime mismatch
+ --> $DIR/region-lifetime-bounds-on-fns-where-clause.rs:20:7
+ |
+LL | fn c<'a,'b>(x: &mut &'a isize, y: &mut &'b isize) {
+ | --------- --------- these two types are declared with different lifetimes...
+...
+LL | a(x, y);
+ | ^ ...but data from `y` flows into `x` here
+
+error[E0308]: mismatched types
+ --> $DIR/region-lifetime-bounds-on-fns-where-clause.rs:28:43
+ |
+LL | let _: fn(&mut &isize, &mut &isize) = a;
+ | ^ one type is more general than the other
+ |
+ = note: expected fn pointer `for<'r, 's, 't0, 't1> fn(&'r mut &'s isize, &'t0 mut &'t1 isize)`
+ found fn pointer `for<'r, 's> fn(&'r mut &isize, &'s mut &isize)`
+
+error: aborting due to 3 previous errors
+
+Some errors have detailed explanations: E0308, E0623.
+For more information about an error, try `rustc --explain E0308`.
error: lifetime may not live long enough
- --> $DIR/region-lifetime-bounds-on-fns-where-clause.rs:8:5
+ --> $DIR/region-lifetime-bounds-on-fns-where-clause.rs:12:5
|
LL | fn b<'a, 'b>(x: &mut &'a isize, y: &mut &'b isize) {
| -- -- lifetime `'b` defined here
= help: consider adding the following bound: `'b: 'a`
error: lifetime may not live long enough
- --> $DIR/region-lifetime-bounds-on-fns-where-clause.rs:14:5
+ --> $DIR/region-lifetime-bounds-on-fns-where-clause.rs:20:5
|
LL | fn c<'a,'b>(x: &mut &'a isize, y: &mut &'b isize) {
| -- -- lifetime `'b` defined here
= help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
error[E0308]: mismatched types
- --> $DIR/region-lifetime-bounds-on-fns-where-clause.rs:20:12
+ --> $DIR/region-lifetime-bounds-on-fns-where-clause.rs:28:12
|
LL | let _: fn(&mut &isize, &mut &isize) = a;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other
found fn pointer `for<'r, 's> fn(&'r mut &isize, &'s mut &isize)`
error[E0308]: mismatched types
- --> $DIR/region-lifetime-bounds-on-fns-where-clause.rs:20:12
+ --> $DIR/region-lifetime-bounds-on-fns-where-clause.rs:28:12
|
LL | let _: fn(&mut &isize, &mut &isize) = a;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
+
fn a<'a, 'b>(x: &mut &'a isize, y: &mut &'b isize) where 'b: 'a {
// Note: this is legal because of the `'b:'a` declaration.
*x = *y;
fn b<'a, 'b>(x: &mut &'a isize, y: &mut &'b isize) {
// Illegal now because there is no `'b:'a` declaration.
- *x = *y; //~ ERROR E0623
+ *x = *y;
+ //[base]~^ ERROR E0623
+ //[nll]~^^ ERROR lifetime may not live long enough
}
fn c<'a,'b>(x: &mut &'a isize, y: &mut &'b isize) {
// Here we try to call `foo` but do not know that `'a` and `'b` are
// related as required.
- a(x, y); //~ ERROR lifetime mismatch [E0623]
+ a(x, y);
+ //[base]~^ ERROR lifetime mismatch [E0623]
+ //[nll]~^^ ERROR lifetime may not live long enough
}
fn d() {
// 'a and 'b are early bound in the function `a` because they appear
// inconstraints:
- let _: fn(&mut &isize, &mut &isize) = a; //~ ERROR mismatched types
+ let _: fn(&mut &isize, &mut &isize) = a;
+ //~^ ERROR mismatched types [E0308]
+ //[nll]~^^ ERROR mismatched types [E0308]
}
fn e() {
+++ /dev/null
-error[E0623]: lifetime mismatch
- --> $DIR/region-lifetime-bounds-on-fns-where-clause.rs:8:10
- |
-LL | fn b<'a, 'b>(x: &mut &'a isize, y: &mut &'b isize) {
- | --------- --------- these two types are declared with different lifetimes...
-LL | // Illegal now because there is no `'b:'a` declaration.
-LL | *x = *y;
- | ^^ ...but data from `y` flows into `x` here
-
-error[E0623]: lifetime mismatch
- --> $DIR/region-lifetime-bounds-on-fns-where-clause.rs:14:7
- |
-LL | fn c<'a,'b>(x: &mut &'a isize, y: &mut &'b isize) {
- | --------- --------- these two types are declared with different lifetimes...
-...
-LL | a(x, y);
- | ^ ...but data from `y` flows into `x` here
-
-error[E0308]: mismatched types
- --> $DIR/region-lifetime-bounds-on-fns-where-clause.rs:20:43
- |
-LL | let _: fn(&mut &isize, &mut &isize) = a;
- | ^ one type is more general than the other
- |
- = note: expected fn pointer `for<'r, 's, 't0, 't1> fn(&'r mut &'s isize, &'t0 mut &'t1 isize)`
- found fn pointer `for<'r, 's> fn(&'r mut &isize, &'s mut &isize)`
-
-error: aborting due to 3 previous errors
-
-Some errors have detailed explanations: E0308, E0623.
-For more information about an error, try `rustc --explain E0308`.
--- /dev/null
+error[E0623]: lifetime mismatch
+ --> $DIR/region-multiple-lifetime-bounds-on-fns-where-clause.rs:13:10
+ |
+LL | fn b<'a, 'b, 'c>(x: &mut &'a isize, y: &mut &'b isize, z: &mut &'c isize) {
+ | --------- --------- these two types are declared with different lifetimes...
+LL | // Illegal now because there is no `'b:'a` declaration.
+LL | *x = *y;
+ | ^^ ...but data from `y` flows into `x` here
+
+error[E0623]: lifetime mismatch
+ --> $DIR/region-multiple-lifetime-bounds-on-fns-where-clause.rs:16:10
+ |
+LL | fn b<'a, 'b, 'c>(x: &mut &'a isize, y: &mut &'b isize, z: &mut &'c isize) {
+ | --------- ---------
+ | |
+ | these two types are declared with different lifetimes...
+...
+LL | *z = *y;
+ | ^^ ...but data from `y` flows into `z` here
+
+error[E0623]: lifetime mismatch
+ --> $DIR/region-multiple-lifetime-bounds-on-fns-where-clause.rs:22:7
+ |
+LL | fn c<'a,'b, 'c>(x: &mut &'a isize, y: &mut &'b isize, z: &mut &'c isize) {
+ | --------- --------- these two types are declared with different lifetimes...
+...
+LL | a(x, y, z);
+ | ^ ...but data from `y` flows into `x` here
+
+error[E0308]: mismatched types
+ --> $DIR/region-multiple-lifetime-bounds-on-fns-where-clause.rs:30:56
+ |
+LL | let _: fn(&mut &isize, &mut &isize, &mut &isize) = a;
+ | ^ one type is more general than the other
+ |
+ = note: expected fn pointer `for<'r, 's, 't0, 't1, 't2, 't3> fn(&'r mut &'s isize, &'t0 mut &'t1 isize, &'t2 mut &'t3 isize)`
+ found fn pointer `for<'r, 's, 't0> fn(&'r mut &isize, &'s mut &isize, &'t0 mut &isize)`
+
+error: aborting due to 4 previous errors
+
+Some errors have detailed explanations: E0308, E0623.
+For more information about an error, try `rustc --explain E0308`.
error: lifetime may not live long enough
- --> $DIR/region-multiple-lifetime-bounds-on-fns-where-clause.rs:9:5
+ --> $DIR/region-multiple-lifetime-bounds-on-fns-where-clause.rs:13:5
|
LL | fn b<'a, 'b, 'c>(x: &mut &'a isize, y: &mut &'b isize, z: &mut &'c isize) {
| -- -- lifetime `'b` defined here
= help: consider adding the following bound: `'b: 'a`
error: lifetime may not live long enough
- --> $DIR/region-multiple-lifetime-bounds-on-fns-where-clause.rs:16:5
+ --> $DIR/region-multiple-lifetime-bounds-on-fns-where-clause.rs:22:5
|
LL | fn c<'a,'b, 'c>(x: &mut &'a isize, y: &mut &'b isize, z: &mut &'c isize) {
| -- -- lifetime `'b` defined here
= help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
error[E0308]: mismatched types
- --> $DIR/region-multiple-lifetime-bounds-on-fns-where-clause.rs:22:12
+ --> $DIR/region-multiple-lifetime-bounds-on-fns-where-clause.rs:30:12
|
LL | let _: fn(&mut &isize, &mut &isize, &mut &isize) = a;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other
found fn pointer `for<'r, 's, 't0> fn(&'r mut &isize, &'s mut &isize, &'t0 mut &isize)`
error[E0308]: mismatched types
- --> $DIR/region-multiple-lifetime-bounds-on-fns-where-clause.rs:22:12
+ --> $DIR/region-multiple-lifetime-bounds-on-fns-where-clause.rs:30:12
|
LL | let _: fn(&mut &isize, &mut &isize, &mut &isize) = a;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other
found fn pointer `for<'r, 's, 't0> fn(&'r mut &isize, &'s mut &isize, &'t0 mut &isize)`
error[E0308]: mismatched types
- --> $DIR/region-multiple-lifetime-bounds-on-fns-where-clause.rs:22:12
+ --> $DIR/region-multiple-lifetime-bounds-on-fns-where-clause.rs:30:12
|
LL | let _: fn(&mut &isize, &mut &isize, &mut &isize) = a;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other
+++ /dev/null
-error: lifetime may not live long enough
- --> $DIR/region-multiple-lifetime-bounds-on-fns-where-clause.rs:9:5
- |
-LL | fn b<'a, 'b, 'c>(x: &mut &'a isize, y: &mut &'b isize, z: &mut &'c isize) {
- | -- -- lifetime `'b` defined here
- | |
- | lifetime `'a` defined here
-LL | // Illegal now because there is no `'b:'a` declaration.
-LL | *x = *y;
- | ^^^^^^^ assignment requires that `'b` must outlive `'a`
- |
- = help: consider adding the following bound: `'b: 'a`
-
-error: lifetime may not live long enough
- --> $DIR/region-multiple-lifetime-bounds-on-fns-where-clause.rs:10:5
- |
-LL | fn b<'a, 'b, 'c>(x: &mut &'a isize, y: &mut &'b isize, z: &mut &'c isize) {
- | -- -- lifetime `'c` defined here
- | |
- | lifetime `'b` defined here
-...
-LL | *z = *y;
- | ^^^^^^^ assignment requires that `'b` must outlive `'c`
- |
- = help: consider adding the following bound: `'b: 'c`
-
-help: add bound `'b: 'a + 'c`
-
-error: lifetime may not live long enough
- --> $DIR/region-multiple-lifetime-bounds-on-fns-where-clause.rs:16:5
- |
-LL | fn c<'a,'b, 'c>(x: &mut &'a isize, y: &mut &'b isize, z: &mut &'c isize) {
- | -- -- lifetime `'b` defined here
- | |
- | lifetime `'a` defined here
-...
-LL | a(x, y, z);
- | ^^^^^^^^^^ argument requires that `'b` must outlive `'a`
- |
- = help: consider adding the following bound: `'b: 'a`
- = note: requirement occurs because of a mutable reference to &isize
- = note: mutable references are invariant over their type parameter
- = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
-
-error: lifetime may not live long enough
- --> $DIR/region-multiple-lifetime-bounds-on-fns-where-clause.rs:16:5
- |
-LL | fn c<'a,'b, 'c>(x: &mut &'a isize, y: &mut &'b isize, z: &mut &'c isize) {
- | -- -- lifetime `'c` defined here
- | |
- | lifetime `'b` defined here
-...
-LL | a(x, y, z);
- | ^^^^^^^^^^ argument requires that `'b` must outlive `'c`
- |
- = help: consider adding the following bound: `'b: 'c`
- = note: requirement occurs because of a mutable reference to &isize
- = note: mutable references are invariant over their type parameter
- = help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
-
-help: add bound `'b: 'a + 'c`
-
-error: higher-ranked subtype error
- --> $DIR/region-multiple-lifetime-bounds-on-fns-where-clause.rs:22:12
- |
-LL | let _: fn(&mut &isize, &mut &isize, &mut &isize) = a;
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: higher-ranked subtype error
- --> $DIR/region-multiple-lifetime-bounds-on-fns-where-clause.rs:22:12
- |
-LL | let _: fn(&mut &isize, &mut &isize, &mut &isize) = a;
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: higher-ranked subtype error
- --> $DIR/region-multiple-lifetime-bounds-on-fns-where-clause.rs:22:12
- |
-LL | let _: fn(&mut &isize, &mut &isize, &mut &isize) = a;
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
-error: aborting due to 7 previous errors
-
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
+
fn a<'a, 'b, 'c>(x: &mut &'a isize, y: &mut &'b isize, z: &mut &'c isize) where 'b: 'a + 'c {
// Note: this is legal because of the `'b:'a` declaration.
*x = *y;
fn b<'a, 'b, 'c>(x: &mut &'a isize, y: &mut &'b isize, z: &mut &'c isize) {
// Illegal now because there is no `'b:'a` declaration.
- *x = *y; //~ ERROR E0623
- *z = *y; //~ ERROR E0623
+ *x = *y;
+ //[base]~^ ERROR E0623
+ //[nll]~^^ ERROR lifetime may not live long enough
+ *z = *y; //[base]~ ERROR E0623
}
fn c<'a,'b, 'c>(x: &mut &'a isize, y: &mut &'b isize, z: &mut &'c isize) {
// Here we try to call `foo` but do not know that `'a` and `'b` are
// related as required.
- a(x, y, z); //~ ERROR lifetime mismatch [E0623]
+ a(x, y, z);
+ //[base]~^ ERROR lifetime mismatch [E0623]
+ //[nll]~^^ ERROR lifetime may not live long enough
}
fn d() {
// 'a and 'b are early bound in the function `a` because they appear
// inconstraints:
- let _: fn(&mut &isize, &mut &isize, &mut &isize) = a; //~ ERROR E0308
+ let _: fn(&mut &isize, &mut &isize, &mut &isize) = a;
+ //~^ ERROR E0308
+ //[nll]~^^ ERROR mismatched types [E0308]
+ //[nll]~| ERROR mismatched types [E0308]
}
fn e() {
+++ /dev/null
-error[E0623]: lifetime mismatch
- --> $DIR/region-multiple-lifetime-bounds-on-fns-where-clause.rs:9:10
- |
-LL | fn b<'a, 'b, 'c>(x: &mut &'a isize, y: &mut &'b isize, z: &mut &'c isize) {
- | --------- --------- these two types are declared with different lifetimes...
-LL | // Illegal now because there is no `'b:'a` declaration.
-LL | *x = *y;
- | ^^ ...but data from `y` flows into `x` here
-
-error[E0623]: lifetime mismatch
- --> $DIR/region-multiple-lifetime-bounds-on-fns-where-clause.rs:10:10
- |
-LL | fn b<'a, 'b, 'c>(x: &mut &'a isize, y: &mut &'b isize, z: &mut &'c isize) {
- | --------- ---------
- | |
- | these two types are declared with different lifetimes...
-...
-LL | *z = *y;
- | ^^ ...but data from `y` flows into `z` here
-
-error[E0623]: lifetime mismatch
- --> $DIR/region-multiple-lifetime-bounds-on-fns-where-clause.rs:16:7
- |
-LL | fn c<'a,'b, 'c>(x: &mut &'a isize, y: &mut &'b isize, z: &mut &'c isize) {
- | --------- --------- these two types are declared with different lifetimes...
-...
-LL | a(x, y, z);
- | ^ ...but data from `y` flows into `x` here
-
-error[E0308]: mismatched types
- --> $DIR/region-multiple-lifetime-bounds-on-fns-where-clause.rs:22:56
- |
-LL | let _: fn(&mut &isize, &mut &isize, &mut &isize) = a;
- | ^ one type is more general than the other
- |
- = note: expected fn pointer `for<'r, 's, 't0, 't1, 't2, 't3> fn(&'r mut &'s isize, &'t0 mut &'t1 isize, &'t2 mut &'t3 isize)`
- found fn pointer `for<'r, 's, 't0> fn(&'r mut &isize, &'s mut &isize, &'t0 mut &isize)`
-
-error: aborting due to 4 previous errors
-
-Some errors have detailed explanations: E0308, E0623.
-For more information about an error, try `rustc --explain E0308`.
--- /dev/null
+error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements
+ --> $DIR/region-object-lifetime-2.rs:14:7
+ |
+LL | x.borrowed()
+ | ^^^^^^^^
+ |
+note: first, the lifetime cannot outlive the lifetime `'a` as defined here...
+ --> $DIR/region-object-lifetime-2.rs:13:42
+ |
+LL | fn borrowed_receiver_different_lifetimes<'a,'b>(x: &'a dyn Foo) -> &'b () {
+ | ^^
+note: ...so that reference does not outlive borrowed content
+ --> $DIR/region-object-lifetime-2.rs:14:5
+ |
+LL | x.borrowed()
+ | ^
+note: but, the lifetime must be valid for the lifetime `'b` as defined here...
+ --> $DIR/region-object-lifetime-2.rs:13:45
+ |
+LL | fn borrowed_receiver_different_lifetimes<'a,'b>(x: &'a dyn Foo) -> &'b () {
+ | ^^
+note: ...so that reference does not outlive borrowed content
+ --> $DIR/region-object-lifetime-2.rs:14:5
+ |
+LL | x.borrowed()
+ | ^^^^^^^^^^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0495`.
error: lifetime may not live long enough
- --> $DIR/region-object-lifetime-2.rs:10:5
+ --> $DIR/region-object-lifetime-2.rs:14:5
|
LL | fn borrowed_receiver_different_lifetimes<'a,'b>(x: &'a dyn Foo) -> &'b () {
| -- -- lifetime `'b` defined here
// Various tests related to testing how region inference works
// with respect to the object receivers.
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
+
trait Foo {
fn borrowed<'a>(&'a self) -> &'a ();
}
// Borrowed receiver but two distinct lifetimes, we get an error.
fn borrowed_receiver_different_lifetimes<'a,'b>(x: &'a dyn Foo) -> &'b () {
- x.borrowed() //~ ERROR cannot infer
+ x.borrowed()
+ //[base]~^ ERROR cannot infer
+ //[nll]~^^ ERROR lifetime may not live long enough
}
fn main() {}
+++ /dev/null
-error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements
- --> $DIR/region-object-lifetime-2.rs:10:7
- |
-LL | x.borrowed()
- | ^^^^^^^^
- |
-note: first, the lifetime cannot outlive the lifetime `'a` as defined here...
- --> $DIR/region-object-lifetime-2.rs:9:42
- |
-LL | fn borrowed_receiver_different_lifetimes<'a,'b>(x: &'a dyn Foo) -> &'b () {
- | ^^
-note: ...so that reference does not outlive borrowed content
- --> $DIR/region-object-lifetime-2.rs:10:5
- |
-LL | x.borrowed()
- | ^
-note: but, the lifetime must be valid for the lifetime `'b` as defined here...
- --> $DIR/region-object-lifetime-2.rs:9:45
- |
-LL | fn borrowed_receiver_different_lifetimes<'a,'b>(x: &'a dyn Foo) -> &'b () {
- | ^^
-note: ...so that reference does not outlive borrowed content
- --> $DIR/region-object-lifetime-2.rs:10:5
- |
-LL | x.borrowed()
- | ^^^^^^^^^^^^
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0495`.
--- /dev/null
+error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements
+ --> $DIR/region-object-lifetime-4.rs:16:7
+ |
+LL | x.borrowed()
+ | ^^^^^^^^
+ |
+note: first, the lifetime cannot outlive the lifetime `'a` as defined here...
+ --> $DIR/region-object-lifetime-4.rs:15:41
+ |
+LL | fn borrowed_receiver_related_lifetimes2<'a,'b>(x: &'a (dyn Foo + 'b)) -> &'b () {
+ | ^^
+note: ...so that reference does not outlive borrowed content
+ --> $DIR/region-object-lifetime-4.rs:16:5
+ |
+LL | x.borrowed()
+ | ^
+note: but, the lifetime must be valid for the lifetime `'b` as defined here...
+ --> $DIR/region-object-lifetime-4.rs:15:44
+ |
+LL | fn borrowed_receiver_related_lifetimes2<'a,'b>(x: &'a (dyn Foo + 'b)) -> &'b () {
+ | ^^
+note: ...so that reference does not outlive borrowed content
+ --> $DIR/region-object-lifetime-4.rs:16:5
+ |
+LL | x.borrowed()
+ | ^^^^^^^^^^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0495`.
error: lifetime may not live long enough
- --> $DIR/region-object-lifetime-4.rs:12:5
+ --> $DIR/region-object-lifetime-4.rs:16:5
|
LL | fn borrowed_receiver_related_lifetimes2<'a,'b>(x: &'a (dyn Foo + 'b)) -> &'b () {
| -- -- lifetime `'b` defined here
// Various tests related to testing how region inference works
// with respect to the object receivers.
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
+
trait Foo {
fn borrowed<'a>(&'a self) -> &'a ();
}
// with the longer lifetime when (from the signature) we only know
// that it lives as long as the shorter lifetime. Therefore, error.
fn borrowed_receiver_related_lifetimes2<'a,'b>(x: &'a (dyn Foo + 'b)) -> &'b () {
- x.borrowed() //~ ERROR cannot infer
+ x.borrowed()
+ //[base]~^ ERROR cannot infer
+ //[nll]~^^ ERROR lifetime may not live long enough
}
fn main() {}
+++ /dev/null
-error[E0495]: cannot infer an appropriate lifetime for autoref due to conflicting requirements
- --> $DIR/region-object-lifetime-4.rs:12:7
- |
-LL | x.borrowed()
- | ^^^^^^^^
- |
-note: first, the lifetime cannot outlive the lifetime `'a` as defined here...
- --> $DIR/region-object-lifetime-4.rs:11:41
- |
-LL | fn borrowed_receiver_related_lifetimes2<'a,'b>(x: &'a (dyn Foo + 'b)) -> &'b () {
- | ^^
-note: ...so that reference does not outlive borrowed content
- --> $DIR/region-object-lifetime-4.rs:12:5
- |
-LL | x.borrowed()
- | ^
-note: but, the lifetime must be valid for the lifetime `'b` as defined here...
- --> $DIR/region-object-lifetime-4.rs:11:44
- |
-LL | fn borrowed_receiver_related_lifetimes2<'a,'b>(x: &'a (dyn Foo + 'b)) -> &'b () {
- | ^^
-note: ...so that reference does not outlive borrowed content
- --> $DIR/region-object-lifetime-4.rs:12:5
- |
-LL | x.borrowed()
- | ^^^^^^^^^^^^
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0495`.
--- /dev/null
+error[E0759]: `v` has an anonymous lifetime `'_` but it needs to satisfy a `'static` lifetime requirement
+ --> $DIR/region-object-lifetime-in-coercion.rs:12:46
+ |
+LL | fn a(v: &[u8]) -> Box<dyn Foo + 'static> {
+ | ----- this data with an anonymous lifetime `'_`...
+LL | let x: Box<dyn Foo + 'static> = Box::new(v);
+ | ^ ...is used and required to live as long as `'static` here
+ |
+help: consider changing the trait object's explicit `'static` bound to the lifetime of argument `v`
+ |
+LL | fn a(v: &[u8]) -> Box<dyn Foo + '_> {
+ | ~~
+help: alternatively, add an explicit `'static` bound to this reference
+ |
+LL | fn a(v: &'static [u8]) -> Box<dyn Foo + 'static> {
+ | ~~~~~~~~~~~~~
+
+error[E0759]: `v` has an anonymous lifetime `'_` but it needs to satisfy a `'static` lifetime requirement
+ --> $DIR/region-object-lifetime-in-coercion.rs:19:14
+ |
+LL | fn b(v: &[u8]) -> Box<dyn Foo + 'static> {
+ | ----- this data with an anonymous lifetime `'_`...
+LL | Box::new(v)
+ | ^ ...is used and required to live as long as `'static` here
+ |
+note: `'static` lifetime requirement introduced by the return type
+ --> $DIR/region-object-lifetime-in-coercion.rs:18:33
+ |
+LL | fn b(v: &[u8]) -> Box<dyn Foo + 'static> {
+ | ^^^^^^^ `'static` requirement introduced here
+LL | Box::new(v)
+ | ----------- because of this returned expression
+help: consider changing the trait object's explicit `'static` bound to the lifetime of argument `v`
+ |
+LL | fn b(v: &[u8]) -> Box<dyn Foo + '_> {
+ | ~~
+help: alternatively, add an explicit `'static` bound to this reference
+ |
+LL | fn b(v: &'static [u8]) -> Box<dyn Foo + 'static> {
+ | ~~~~~~~~~~~~~
+
+error[E0759]: `v` has an anonymous lifetime `'_` but it needs to satisfy a `'static` lifetime requirement
+ --> $DIR/region-object-lifetime-in-coercion.rs:27:14
+ |
+LL | fn c(v: &[u8]) -> Box<dyn Foo> {
+ | ----- this data with an anonymous lifetime `'_`...
+...
+LL | Box::new(v)
+ | ^ ...is used and required to live as long as `'static` here
+ |
+note: `'static` lifetime requirement introduced by the return type
+ --> $DIR/region-object-lifetime-in-coercion.rs:24:23
+ |
+LL | fn c(v: &[u8]) -> Box<dyn Foo> {
+ | ^^^^^^^ `'static` requirement introduced here
+...
+LL | Box::new(v)
+ | ----------- because of this returned expression
+help: to declare that the trait object captures data from argument `v`, you can add an explicit `'_` lifetime bound
+ |
+LL | fn c(v: &[u8]) -> Box<dyn Foo + '_> {
+ | ++++
+
+error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
+ --> $DIR/region-object-lifetime-in-coercion.rs:33:14
+ |
+LL | Box::new(v)
+ | ^
+ |
+note: first, the lifetime cannot outlive the lifetime `'a` as defined here...
+ --> $DIR/region-object-lifetime-in-coercion.rs:32:6
+ |
+LL | fn d<'a,'b>(v: &'a [u8]) -> Box<dyn Foo+'b> {
+ | ^^
+note: ...so that the expression is assignable
+ --> $DIR/region-object-lifetime-in-coercion.rs:33:14
+ |
+LL | Box::new(v)
+ | ^
+ = note: expected `&[u8]`
+ found `&'a [u8]`
+note: but, the lifetime must be valid for the lifetime `'b` as defined here...
+ --> $DIR/region-object-lifetime-in-coercion.rs:32:9
+ |
+LL | fn d<'a,'b>(v: &'a [u8]) -> Box<dyn Foo+'b> {
+ | ^^
+note: ...so that the types are compatible
+ --> $DIR/region-object-lifetime-in-coercion.rs:33:5
+ |
+LL | Box::new(v)
+ | ^^^^^^^^^^^
+ = note: expected `Box<(dyn Foo + 'b)>`
+ found `Box<dyn Foo>`
+
+error: aborting due to 4 previous errors
+
+Some errors have detailed explanations: E0495, E0759.
+For more information about an error, try `rustc --explain E0495`.
error: lifetime may not live long enough
- --> $DIR/region-object-lifetime-in-coercion.rs:8:12
+ --> $DIR/region-object-lifetime-in-coercion.rs:12:12
|
LL | fn a(v: &[u8]) -> Box<dyn Foo + 'static> {
| - let's call the lifetime of this reference `'1`
LL | let x: Box<dyn Foo + 'static> = Box::new(v);
| ^^^^^^^^^^^^^^^^^^^^^^ type annotation requires that `'1` must outlive `'static`
+ |
+help: consider changing the trait object's explicit `'static` bound to the lifetime of argument `v`
+ |
+LL | fn a(v: &[u8]) -> Box<dyn Foo + '_> {
+ | ~~
+help: alternatively, add an explicit `'static` bound to this reference
+ |
+LL | fn a(v: &'static [u8]) -> Box<dyn Foo + 'static> {
+ | ~~~~~~~~~~~~~
error: lifetime may not live long enough
- --> $DIR/region-object-lifetime-in-coercion.rs:13:5
+ --> $DIR/region-object-lifetime-in-coercion.rs:19:5
|
LL | fn b(v: &[u8]) -> Box<dyn Foo + 'static> {
| - let's call the lifetime of this reference `'1`
LL | Box::new(v)
| ^^^^^^^^^^^ returning this value requires that `'1` must outlive `'static`
+ |
+help: consider changing the trait object's explicit `'static` bound to the lifetime of argument `v`
+ |
+LL | fn b(v: &[u8]) -> Box<dyn Foo + '_> {
+ | ~~
+help: alternatively, add an explicit `'static` bound to this reference
+ |
+LL | fn b(v: &'static [u8]) -> Box<dyn Foo + 'static> {
+ | ~~~~~~~~~~~~~
error: lifetime may not live long enough
- --> $DIR/region-object-lifetime-in-coercion.rs:19:5
+ --> $DIR/region-object-lifetime-in-coercion.rs:27:5
|
LL | fn c(v: &[u8]) -> Box<dyn Foo> {
| - let's call the lifetime of this reference `'1`
...
LL | Box::new(v)
| ^^^^^^^^^^^ returning this value requires that `'1` must outlive `'static`
+ |
+help: to declare that the trait object captures data from argument `v`, you can add an explicit `'_` lifetime bound
+ |
+LL | fn c(v: &[u8]) -> Box<dyn Foo + '_> {
+ | ++++
error: lifetime may not live long enough
- --> $DIR/region-object-lifetime-in-coercion.rs:23:5
+ --> $DIR/region-object-lifetime-in-coercion.rs:33:5
|
LL | fn d<'a,'b>(v: &'a [u8]) -> Box<dyn Foo+'b> {
| -- -- lifetime `'b` defined here
// Test that attempts to implicitly coerce a value into an
// object respect the lifetime bound on the object type.
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
+
trait Foo {}
impl<'a> Foo for &'a [u8] {}
fn a(v: &[u8]) -> Box<dyn Foo + 'static> {
- let x: Box<dyn Foo + 'static> = Box::new(v); //~ ERROR E0759
+ let x: Box<dyn Foo + 'static> = Box::new(v);
+ //[base]~^ ERROR E0759
+ //[nll]~^^ ERROR lifetime may not live long enough
x
}
fn b(v: &[u8]) -> Box<dyn Foo + 'static> {
- Box::new(v) //~ ERROR E0759
+ Box::new(v)
+ //[base]~^ ERROR E0759
+ //[nll]~^^ ERROR lifetime may not live long enough
}
fn c(v: &[u8]) -> Box<dyn Foo> {
// same as previous case due to RFC 599
- Box::new(v) //~ ERROR E0759
+ Box::new(v)
+ //[base]~^ ERROR E0759
+ //[nll]~^^ ERROR lifetime may not live long enough
}
fn d<'a,'b>(v: &'a [u8]) -> Box<dyn Foo+'b> {
- Box::new(v) //~ ERROR cannot infer an appropriate lifetime due to conflicting
+ Box::new(v)
+ //[base]~^ ERROR cannot infer an appropriate lifetime due to conflicting
+ //[nll]~^^ ERROR lifetime may not live long enough
}
fn e<'a:'b,'b>(v: &'a [u8]) -> Box<dyn Foo+'b> {
+++ /dev/null
-error[E0759]: `v` has an anonymous lifetime `'_` but it needs to satisfy a `'static` lifetime requirement
- --> $DIR/region-object-lifetime-in-coercion.rs:8:46
- |
-LL | fn a(v: &[u8]) -> Box<dyn Foo + 'static> {
- | ----- this data with an anonymous lifetime `'_`...
-LL | let x: Box<dyn Foo + 'static> = Box::new(v);
- | ^ ...is used and required to live as long as `'static` here
- |
-help: consider changing the trait object's explicit `'static` bound to the lifetime of argument `v`
- |
-LL | fn a(v: &[u8]) -> Box<dyn Foo + '_> {
- | ~~
-help: alternatively, add an explicit `'static` bound to this reference
- |
-LL | fn a(v: &'static [u8]) -> Box<dyn Foo + 'static> {
- | ~~~~~~~~~~~~~
-
-error[E0759]: `v` has an anonymous lifetime `'_` but it needs to satisfy a `'static` lifetime requirement
- --> $DIR/region-object-lifetime-in-coercion.rs:13:14
- |
-LL | fn b(v: &[u8]) -> Box<dyn Foo + 'static> {
- | ----- this data with an anonymous lifetime `'_`...
-LL | Box::new(v)
- | ^ ...is used and required to live as long as `'static` here
- |
-note: `'static` lifetime requirement introduced by the return type
- --> $DIR/region-object-lifetime-in-coercion.rs:12:33
- |
-LL | fn b(v: &[u8]) -> Box<dyn Foo + 'static> {
- | ^^^^^^^ `'static` requirement introduced here
-LL | Box::new(v)
- | ----------- because of this returned expression
-help: consider changing the trait object's explicit `'static` bound to the lifetime of argument `v`
- |
-LL | fn b(v: &[u8]) -> Box<dyn Foo + '_> {
- | ~~
-help: alternatively, add an explicit `'static` bound to this reference
- |
-LL | fn b(v: &'static [u8]) -> Box<dyn Foo + 'static> {
- | ~~~~~~~~~~~~~
-
-error[E0759]: `v` has an anonymous lifetime `'_` but it needs to satisfy a `'static` lifetime requirement
- --> $DIR/region-object-lifetime-in-coercion.rs:19:14
- |
-LL | fn c(v: &[u8]) -> Box<dyn Foo> {
- | ----- this data with an anonymous lifetime `'_`...
-...
-LL | Box::new(v)
- | ^ ...is used and required to live as long as `'static` here
- |
-note: `'static` lifetime requirement introduced by the return type
- --> $DIR/region-object-lifetime-in-coercion.rs:16:23
- |
-LL | fn c(v: &[u8]) -> Box<dyn Foo> {
- | ^^^^^^^ `'static` requirement introduced here
-...
-LL | Box::new(v)
- | ----------- because of this returned expression
-help: to declare that the trait object captures data from argument `v`, you can add an explicit `'_` lifetime bound
- |
-LL | fn c(v: &[u8]) -> Box<dyn Foo + '_> {
- | ++++
-
-error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
- --> $DIR/region-object-lifetime-in-coercion.rs:23:14
- |
-LL | Box::new(v)
- | ^
- |
-note: first, the lifetime cannot outlive the lifetime `'a` as defined here...
- --> $DIR/region-object-lifetime-in-coercion.rs:22:6
- |
-LL | fn d<'a,'b>(v: &'a [u8]) -> Box<dyn Foo+'b> {
- | ^^
-note: ...so that the expression is assignable
- --> $DIR/region-object-lifetime-in-coercion.rs:23:14
- |
-LL | Box::new(v)
- | ^
- = note: expected `&[u8]`
- found `&'a [u8]`
-note: but, the lifetime must be valid for the lifetime `'b` as defined here...
- --> $DIR/region-object-lifetime-in-coercion.rs:22:9
- |
-LL | fn d<'a,'b>(v: &'a [u8]) -> Box<dyn Foo+'b> {
- | ^^
-note: ...so that the types are compatible
- --> $DIR/region-object-lifetime-in-coercion.rs:23:5
- |
-LL | Box::new(v)
- | ^^^^^^^^^^^
- = note: expected `Box<(dyn Foo + 'b)>`
- found `Box<dyn Foo>`
-
-error: aborting due to 4 previous errors
-
-Some errors have detailed explanations: E0495, E0759.
-For more information about an error, try `rustc --explain E0495`.
--- /dev/null
+error[E0759]: `self` has an anonymous lifetime `'_` but it needs to satisfy a `'static` lifetime requirement
+ --> $DIR/regions-addr-of-self.rs:11:37
+ |
+LL | pub fn chase_cat(&mut self) {
+ | --------- this data with an anonymous lifetime `'_`...
+LL | let p: &'static mut usize = &mut self.cats_chased;
+ | ^^^^^^^^^^^^^^^^^^^^^ ...is used and required to live as long as `'static` here
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0759`.
error: lifetime may not live long enough
- --> $DIR/regions-addr-of-self.rs:7:16
+ --> $DIR/regions-addr-of-self.rs:11:16
|
LL | pub fn chase_cat(&mut self) {
| - let's call the lifetime of this reference `'1`
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
+
struct Dog {
cats_chased: usize,
}
impl Dog {
pub fn chase_cat(&mut self) {
- let p: &'static mut usize = &mut self.cats_chased; //~ ERROR E0759
+ let p: &'static mut usize = &mut self.cats_chased;
+ //[base]~^ ERROR E0759
+ //[nll]~^^ ERROR lifetime may not live long enough
*p += 1;
}
+++ /dev/null
-error[E0759]: `self` has an anonymous lifetime `'_` but it needs to satisfy a `'static` lifetime requirement
- --> $DIR/regions-addr-of-self.rs:7:37
- |
-LL | pub fn chase_cat(&mut self) {
- | --------- this data with an anonymous lifetime `'_`...
-LL | let p: &'static mut usize = &mut self.cats_chased;
- | ^^^^^^^^^^^^^^^^^^^^^ ...is used and required to live as long as `'static` here
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0759`.
--- /dev/null
+error[E0495]: cannot infer an appropriate lifetime for borrow expression due to conflicting requirements
+ --> $DIR/regions-addr-of-upvar-self.rs:12:41
+ |
+LL | let p: &'static mut usize = &mut self.food;
+ | ^^^^^^^^^^^^^^
+ |
+note: first, the lifetime cannot outlive the lifetime `'_` as defined here...
+ --> $DIR/regions-addr-of-upvar-self.rs:11:18
+ |
+LL | let _f = || {
+ | ^^
+note: ...so that closure can access `self`
+ --> $DIR/regions-addr-of-upvar-self.rs:12:41
+ |
+LL | let p: &'static mut usize = &mut self.food;
+ | ^^^^^^^^^^^^^^
+ = note: but, the lifetime must be valid for the static lifetime...
+note: ...so that reference does not outlive borrowed content
+ --> $DIR/regions-addr-of-upvar-self.rs:12:41
+ |
+LL | let p: &'static mut usize = &mut self.food;
+ | ^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0495`.
error: lifetime may not live long enough
- --> $DIR/regions-addr-of-upvar-self.rs:8:20
+ --> $DIR/regions-addr-of-upvar-self.rs:12:20
|
LL | let _f = || {
| -- lifetime `'1` represents this closure's body
= note: closure implements `FnMut`, so references to captured variables can't escape the closure
error: lifetime may not live long enough
- --> $DIR/regions-addr-of-upvar-self.rs:8:20
+ --> $DIR/regions-addr-of-upvar-self.rs:12:20
|
LL | pub fn chase_cat(&mut self) {
| - let's call the lifetime of this reference `'1`
| ^^^^^^^^^^^^^^^^^^ type annotation requires that `'1` must outlive `'static`
error[E0597]: `self` does not live long enough
- --> $DIR/regions-addr-of-upvar-self.rs:8:46
+ --> $DIR/regions-addr-of-upvar-self.rs:12:46
|
LL | let _f = || {
| -- value captured here
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
+
struct Dog {
food: usize,
}
impl Dog {
pub fn chase_cat(&mut self) {
let _f = || {
- let p: &'static mut usize = &mut self.food; //~ ERROR cannot infer
+ let p: &'static mut usize = &mut self.food;
+ //[base]~^ ERROR cannot infer
+ //[nll]~^^ ERROR lifetime may not live long enough
+ //[nll]~^^^ ERROR lifetime may not live long enough
+ //[nll]~^^^^ ERROR E0597
*p = 3;
};
}
+++ /dev/null
-error[E0495]: cannot infer an appropriate lifetime for borrow expression due to conflicting requirements
- --> $DIR/regions-addr-of-upvar-self.rs:8:41
- |
-LL | let p: &'static mut usize = &mut self.food;
- | ^^^^^^^^^^^^^^
- |
-note: first, the lifetime cannot outlive the lifetime `'_` as defined here...
- --> $DIR/regions-addr-of-upvar-self.rs:7:18
- |
-LL | let _f = || {
- | ^^
-note: ...so that closure can access `self`
- --> $DIR/regions-addr-of-upvar-self.rs:8:41
- |
-LL | let p: &'static mut usize = &mut self.food;
- | ^^^^^^^^^^^^^^
- = note: but, the lifetime must be valid for the static lifetime...
-note: ...so that reference does not outlive borrowed content
- --> $DIR/regions-addr-of-upvar-self.rs:8:41
- |
-LL | let p: &'static mut usize = &mut self.food;
- | ^^^^^^^^^^^^^^
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0495`.
--- /dev/null
+error[E0477]: the type `&'a isize` does not fulfill the required lifetime
+ --> $DIR/regions-bounded-by-trait-requiring-static.rs:26:5
+ |
+LL | assert_send::<&'a isize>();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+note: type must satisfy the static lifetime as required by this binding
+ --> $DIR/regions-bounded-by-trait-requiring-static.rs:10:18
+ |
+LL | fn assert_send<T:'static>() { }
+ | ^^^^^^^
+
+error[E0477]: the type `&'a str` does not fulfill the required lifetime
+ --> $DIR/regions-bounded-by-trait-requiring-static.rs:32:5
+ |
+LL | assert_send::<&'a str>();
+ | ^^^^^^^^^^^^^^^^^^^^^^
+ |
+note: type must satisfy the static lifetime as required by this binding
+ --> $DIR/regions-bounded-by-trait-requiring-static.rs:10:18
+ |
+LL | fn assert_send<T:'static>() { }
+ | ^^^^^^^
+
+error[E0477]: the type `&'a [isize]` does not fulfill the required lifetime
+ --> $DIR/regions-bounded-by-trait-requiring-static.rs:38:5
+ |
+LL | assert_send::<&'a [isize]>();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+note: type must satisfy the static lifetime as required by this binding
+ --> $DIR/regions-bounded-by-trait-requiring-static.rs:10:18
+ |
+LL | fn assert_send<T:'static>() { }
+ | ^^^^^^^
+
+error[E0477]: the type `Box<&'a isize>` does not fulfill the required lifetime
+ --> $DIR/regions-bounded-by-trait-requiring-static.rs:54:5
+ |
+LL | assert_send::<Box<&'a isize>>();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+note: type must satisfy the static lifetime as required by this binding
+ --> $DIR/regions-bounded-by-trait-requiring-static.rs:10:18
+ |
+LL | fn assert_send<T:'static>() { }
+ | ^^^^^^^
+
+error[E0477]: the type `*const &'a isize` does not fulfill the required lifetime
+ --> $DIR/regions-bounded-by-trait-requiring-static.rs:67:5
+ |
+LL | assert_send::<*const &'a isize>();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+note: type must satisfy the static lifetime as required by this binding
+ --> $DIR/regions-bounded-by-trait-requiring-static.rs:10:18
+ |
+LL | fn assert_send<T:'static>() { }
+ | ^^^^^^^
+
+error[E0477]: the type `*mut &'a isize` does not fulfill the required lifetime
+ --> $DIR/regions-bounded-by-trait-requiring-static.rs:73:5
+ |
+LL | assert_send::<*mut &'a isize>();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+note: type must satisfy the static lifetime as required by this binding
+ --> $DIR/regions-bounded-by-trait-requiring-static.rs:10:18
+ |
+LL | fn assert_send<T:'static>() { }
+ | ^^^^^^^
+
+error: aborting due to 6 previous errors
+
+For more information about this error, try `rustc --explain E0477`.
error: lifetime may not live long enough
- --> $DIR/regions-bounded-by-trait-requiring-static.rs:22:5
+ --> $DIR/regions-bounded-by-trait-requiring-static.rs:26:5
|
LL | fn param_not_ok<'a>(x: &'a isize) {
| -- lifetime `'a` defined here
| ^^^^^^^^^^^^^^^^^^^^^^^^ requires that `'a` must outlive `'static`
error: lifetime may not live long enough
- --> $DIR/regions-bounded-by-trait-requiring-static.rs:26:5
+ --> $DIR/regions-bounded-by-trait-requiring-static.rs:32:5
|
LL | fn param_not_ok1<'a>(_: &'a isize) {
| -- lifetime `'a` defined here
| ^^^^^^^^^^^^^^^^^^^^^^ requires that `'a` must outlive `'static`
error: lifetime may not live long enough
- --> $DIR/regions-bounded-by-trait-requiring-static.rs:30:5
+ --> $DIR/regions-bounded-by-trait-requiring-static.rs:38:5
|
LL | fn param_not_ok2<'a>(_: &'a isize) {
| -- lifetime `'a` defined here
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ requires that `'a` must outlive `'static`
error: lifetime may not live long enough
- --> $DIR/regions-bounded-by-trait-requiring-static.rs:44:5
+ --> $DIR/regions-bounded-by-trait-requiring-static.rs:54:5
|
LL | fn box_with_region_not_ok<'a>() {
| -- lifetime `'a` defined here
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ requires that `'a` must outlive `'static`
error: lifetime may not live long enough
- --> $DIR/regions-bounded-by-trait-requiring-static.rs:55:5
+ --> $DIR/regions-bounded-by-trait-requiring-static.rs:67:5
|
LL | fn unsafe_ok2<'a>(_: &'a isize) {
| -- lifetime `'a` defined here
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ requires that `'a` must outlive `'static`
error: lifetime may not live long enough
- --> $DIR/regions-bounded-by-trait-requiring-static.rs:59:5
+ --> $DIR/regions-bounded-by-trait-requiring-static.rs:73:5
|
LL | fn unsafe_ok3<'a>(_: &'a isize) {
| -- lifetime `'a` defined here
// in this file all test region bound and lifetime violations that are
// detected during type check.
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
+
trait Dummy : 'static { }
fn assert_send<T:'static>() { }
// otherwise lifetime pointers are not ok
fn param_not_ok<'a>(x: &'a isize) {
- assert_send::<&'a isize>(); //~ ERROR does not fulfill the required lifetime
+ assert_send::<&'a isize>();
+ //[base]~^ ERROR does not fulfill the required lifetime
+ //[nll]~^^ ERROR lifetime may not live long enough
}
fn param_not_ok1<'a>(_: &'a isize) {
- assert_send::<&'a str>(); //~ ERROR does not fulfill the required lifetime
+ assert_send::<&'a str>();
+ //[base]~^ ERROR does not fulfill the required lifetime
+ //[nll]~^^ ERROR lifetime may not live long enough
}
fn param_not_ok2<'a>(_: &'a isize) {
- assert_send::<&'a [isize]>(); //~ ERROR does not fulfill the required lifetime
+ assert_send::<&'a [isize]>();
+ //[base]~^ ERROR does not fulfill the required lifetime
+ //[nll]~^^ ERROR lifetime may not live long enough
}
// boxes are ok
// but not if they own a bad thing
fn box_with_region_not_ok<'a>() {
- assert_send::<Box<&'a isize>>(); //~ ERROR does not fulfill the required lifetime
+ assert_send::<Box<&'a isize>>();
+ //[base]~^ ERROR does not fulfill the required lifetime
+ //[nll]~^^ ERROR lifetime may not live long enough
}
// raw pointers are ok unless they point at unsendable things
}
fn unsafe_ok2<'a>(_: &'a isize) {
- assert_send::<*const &'a isize>(); //~ ERROR does not fulfill the required lifetime
+ assert_send::<*const &'a isize>();
+ //[base]~^ ERROR does not fulfill the required lifetime
+ //[nll]~^^ ERROR lifetime may not live long enough
}
fn unsafe_ok3<'a>(_: &'a isize) {
- assert_send::<*mut &'a isize>(); //~ ERROR does not fulfill the required lifetime
+ assert_send::<*mut &'a isize>();
+ //[base]~^ ERROR does not fulfill the required lifetime
+ //[nll]~^^ ERROR lifetime may not live long enough
}
fn main() {
+++ /dev/null
-error[E0477]: the type `&'a isize` does not fulfill the required lifetime
- --> $DIR/regions-bounded-by-trait-requiring-static.rs:22:5
- |
-LL | assert_send::<&'a isize>();
- | ^^^^^^^^^^^^^^^^^^^^^^^^
- |
-note: type must satisfy the static lifetime as required by this binding
- --> $DIR/regions-bounded-by-trait-requiring-static.rs:6:18
- |
-LL | fn assert_send<T:'static>() { }
- | ^^^^^^^
-
-error[E0477]: the type `&'a str` does not fulfill the required lifetime
- --> $DIR/regions-bounded-by-trait-requiring-static.rs:26:5
- |
-LL | assert_send::<&'a str>();
- | ^^^^^^^^^^^^^^^^^^^^^^
- |
-note: type must satisfy the static lifetime as required by this binding
- --> $DIR/regions-bounded-by-trait-requiring-static.rs:6:18
- |
-LL | fn assert_send<T:'static>() { }
- | ^^^^^^^
-
-error[E0477]: the type `&'a [isize]` does not fulfill the required lifetime
- --> $DIR/regions-bounded-by-trait-requiring-static.rs:30:5
- |
-LL | assert_send::<&'a [isize]>();
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^
- |
-note: type must satisfy the static lifetime as required by this binding
- --> $DIR/regions-bounded-by-trait-requiring-static.rs:6:18
- |
-LL | fn assert_send<T:'static>() { }
- | ^^^^^^^
-
-error[E0477]: the type `Box<&'a isize>` does not fulfill the required lifetime
- --> $DIR/regions-bounded-by-trait-requiring-static.rs:44:5
- |
-LL | assert_send::<Box<&'a isize>>();
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- |
-note: type must satisfy the static lifetime as required by this binding
- --> $DIR/regions-bounded-by-trait-requiring-static.rs:6:18
- |
-LL | fn assert_send<T:'static>() { }
- | ^^^^^^^
-
-error[E0477]: the type `*const &'a isize` does not fulfill the required lifetime
- --> $DIR/regions-bounded-by-trait-requiring-static.rs:55:5
- |
-LL | assert_send::<*const &'a isize>();
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- |
-note: type must satisfy the static lifetime as required by this binding
- --> $DIR/regions-bounded-by-trait-requiring-static.rs:6:18
- |
-LL | fn assert_send<T:'static>() { }
- | ^^^^^^^
-
-error[E0477]: the type `*mut &'a isize` does not fulfill the required lifetime
- --> $DIR/regions-bounded-by-trait-requiring-static.rs:59:5
- |
-LL | assert_send::<*mut &'a isize>();
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- |
-note: type must satisfy the static lifetime as required by this binding
- --> $DIR/regions-bounded-by-trait-requiring-static.rs:6:18
- |
-LL | fn assert_send<T:'static>() { }
- | ^^^^^^^
-
-error: aborting due to 6 previous errors
-
-For more information about this error, try `rustc --explain E0477`.
--- /dev/null
+error[E0623]: lifetime mismatch
+ --> $DIR/regions-bounded-method-type-parameters-cross-crate.rs:23:7
+ |
+LL | fn call_bigger_region<'x, 'y>(a: Inv<'x>, b: Inv<'y>) {
+ | ------- ------- these two types are declared with different lifetimes...
+LL | // Here the value provided for 'y is 'y, and hence 'y:'x does not hold.
+LL | a.bigger_region(b)
+ | ^^^^^^^^^^^^^ ...but data from `b` flows into `a` here
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0623`.
error: lifetime may not live long enough
- --> $DIR/regions-bounded-method-type-parameters-cross-crate.rs:20:5
+ --> $DIR/regions-bounded-method-type-parameters-cross-crate.rs:23:5
|
LL | fn call_bigger_region<'x, 'y>(a: Inv<'x>, b: Inv<'y>) {
| -- -- lifetime `'y` defined here
// aux-build:rbmtp_cross_crate_lib.rs
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
// Check explicit region bounds on methods in the cross crate case.
fn call_bigger_region<'x, 'y>(a: Inv<'x>, b: Inv<'y>) {
// Here the value provided for 'y is 'y, and hence 'y:'x does not hold.
- a.bigger_region(b) //~ ERROR lifetime mismatch [E0623]
+ a.bigger_region(b)
+ //[base]~^ ERROR lifetime mismatch [E0623]
+ //[nll]~^^ ERROR lifetime may not live long enough
}
fn main() { }
+++ /dev/null
-error[E0623]: lifetime mismatch
- --> $DIR/regions-bounded-method-type-parameters-cross-crate.rs:20:7
- |
-LL | fn call_bigger_region<'x, 'y>(a: Inv<'x>, b: Inv<'y>) {
- | ------- ------- these two types are declared with different lifetimes...
-LL | // Here the value provided for 'y is 'y, and hence 'y:'x does not hold.
-LL | a.bigger_region(b)
- | ^^^^^^^^^^^^^ ...but data from `b` flows into `a` here
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0623`.
--- /dev/null
+error[E0623]: lifetime mismatch
+ --> $DIR/regions-bounded-method-type-parameters-trait-bound.rs:24:7
+ |
+LL | fn caller2<'a,'b,F:Foo<'a>>(a: Inv<'a>, b: Inv<'b>, f: F) {
+ | ------- ------- these two types are declared with different lifetimes...
+LL | // Here the value provided for 'y is 'b, and hence 'b:'a does not hold.
+LL | f.method(b);
+ | ^^^^^^ ...but data from `b` flows into `a` here
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0623`.
error: lifetime may not live long enough
- --> $DIR/regions-bounded-method-type-parameters-trait-bound.rs:20:5
+ --> $DIR/regions-bounded-method-type-parameters-trait-bound.rs:24:5
|
LL | fn caller2<'a,'b,F:Foo<'a>>(a: Inv<'a>, b: Inv<'b>, f: F) {
| -- -- lifetime `'b` defined here
// nominal types (but not on other types) and that they are type
// checked.
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
+
struct Inv<'a> { // invariant w/r/t 'a
x: &'a mut &'a isize
}
fn caller2<'a,'b,F:Foo<'a>>(a: Inv<'a>, b: Inv<'b>, f: F) {
// Here the value provided for 'y is 'b, and hence 'b:'a does not hold.
- f.method(b); //~ ERROR lifetime mismatch [E0623]
+ f.method(b);
+ //[base]~^ ERROR lifetime mismatch [E0623]
+ //[nll]~^^ ERROR lifetime may not live long enough
}
fn caller3<'a,'b:'a,F:Foo<'a>>(a: Inv<'a>, b: Inv<'b>, f: F) {
+++ /dev/null
-error[E0623]: lifetime mismatch
- --> $DIR/regions-bounded-method-type-parameters-trait-bound.rs:20:7
- |
-LL | fn caller2<'a,'b,F:Foo<'a>>(a: Inv<'a>, b: Inv<'b>, f: F) {
- | ------- ------- these two types are declared with different lifetimes...
-LL | // Here the value provided for 'y is 'b, and hence 'b:'a does not hold.
-LL | f.method(b);
- | ^^^^^^ ...but data from `b` flows into `a` here
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0623`.
--- /dev/null
+error[E0477]: the type `&'a isize` does not fulfill the required lifetime
+ --> $DIR/regions-bounded-method-type-parameters.rs:16:9
+ |
+LL | Foo.some_method::<&'a isize>();
+ | ^^^^^^^^^^^
+ |
+note: type must satisfy the static lifetime as required by this binding
+ --> $DIR/regions-bounded-method-type-parameters.rs:12:22
+ |
+LL | fn some_method<A:'static>(self) { }
+ | ^^^^^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0477`.
error: lifetime may not live long enough
- --> $DIR/regions-bounded-method-type-parameters.rs:12:9
+ --> $DIR/regions-bounded-method-type-parameters.rs:16:9
|
LL | fn caller<'a>(x: &isize) {
| -- lifetime `'a` defined here
// nominal types (but not on other types) and that they are type
// checked.
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
+
struct Foo;
impl Foo {
fn caller<'a>(x: &isize) {
Foo.some_method::<&'a isize>();
- //~^ ERROR does not fulfill the required lifetime
+ //[base]~^ ERROR does not fulfill the required lifetime
+ //[nll]~^^ ERROR lifetime may not live long enough
}
fn main() { }
+++ /dev/null
-error[E0477]: the type `&'a isize` does not fulfill the required lifetime
- --> $DIR/regions-bounded-method-type-parameters.rs:12:9
- |
-LL | Foo.some_method::<&'a isize>();
- | ^^^^^^^^^^^
- |
-note: type must satisfy the static lifetime as required by this binding
- --> $DIR/regions-bounded-method-type-parameters.rs:8:22
- |
-LL | fn some_method<A:'static>(self) { }
- | ^^^^^^^
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0477`.
--- /dev/null
+error[E0308]: mismatched types
+ --> $DIR/regions-bounds.rs:13:12
+ |
+LL | return e;
+ | ^ lifetime mismatch
+ |
+ = note: expected struct `TupleStruct<'b>`
+ found struct `TupleStruct<'a>`
+note: the lifetime `'a` as defined here...
+ --> $DIR/regions-bounds.rs:12:10
+ |
+LL | fn a_fn1<'a,'b>(e: TupleStruct<'a>) -> TupleStruct<'b> {
+ | ^^
+note: ...does not necessarily outlive the lifetime `'b` as defined here
+ --> $DIR/regions-bounds.rs:12:13
+ |
+LL | fn a_fn1<'a,'b>(e: TupleStruct<'a>) -> TupleStruct<'b> {
+ | ^^
+
+error[E0308]: mismatched types
+ --> $DIR/regions-bounds.rs:19:12
+ |
+LL | return e;
+ | ^ lifetime mismatch
+ |
+ = note: expected struct `Struct<'b>`
+ found struct `Struct<'a>`
+note: the lifetime `'a` as defined here...
+ --> $DIR/regions-bounds.rs:18:10
+ |
+LL | fn a_fn3<'a,'b>(e: Struct<'a>) -> Struct<'b> {
+ | ^^
+note: ...does not necessarily outlive the lifetime `'b` as defined here
+ --> $DIR/regions-bounds.rs:18:13
+ |
+LL | fn a_fn3<'a,'b>(e: Struct<'a>) -> Struct<'b> {
+ | ^^
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
error: lifetime may not live long enough
- --> $DIR/regions-bounds.rs:9:12
+ --> $DIR/regions-bounds.rs:13:12
|
LL | fn a_fn1<'a,'b>(e: TupleStruct<'a>) -> TupleStruct<'b> {
| -- -- lifetime `'b` defined here
= help: consider adding the following bound: `'a: 'b`
error: lifetime may not live long enough
- --> $DIR/regions-bounds.rs:13:12
+ --> $DIR/regions-bounds.rs:19:12
|
LL | fn a_fn3<'a,'b>(e: Struct<'a>) -> Struct<'b> {
| -- -- lifetime `'b` defined here
// nominal types (but not on other types) and that they are type
// checked.
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
+
struct TupleStruct<'a>(&'a isize);
struct Struct<'a> { x:&'a isize }
fn a_fn1<'a,'b>(e: TupleStruct<'a>) -> TupleStruct<'b> {
- return e; //~ ERROR mismatched types
+ return e;
+ //[base]~^ ERROR mismatched types
+ //[nll]~^^ ERROR lifetime may not live long enough
}
fn a_fn3<'a,'b>(e: Struct<'a>) -> Struct<'b> {
- return e; //~ ERROR mismatched types
+ return e;
+ //[base]~^ ERROR mismatched types
+ //[nll]~^^ ERROR lifetime may not live long enough
}
fn main() { }
+++ /dev/null
-error[E0308]: mismatched types
- --> $DIR/regions-bounds.rs:9:12
- |
-LL | return e;
- | ^ lifetime mismatch
- |
- = note: expected struct `TupleStruct<'b>`
- found struct `TupleStruct<'a>`
-note: the lifetime `'a` as defined here...
- --> $DIR/regions-bounds.rs:8:10
- |
-LL | fn a_fn1<'a,'b>(e: TupleStruct<'a>) -> TupleStruct<'b> {
- | ^^
-note: ...does not necessarily outlive the lifetime `'b` as defined here
- --> $DIR/regions-bounds.rs:8:13
- |
-LL | fn a_fn1<'a,'b>(e: TupleStruct<'a>) -> TupleStruct<'b> {
- | ^^
-
-error[E0308]: mismatched types
- --> $DIR/regions-bounds.rs:13:12
- |
-LL | return e;
- | ^ lifetime mismatch
- |
- = note: expected struct `Struct<'b>`
- found struct `Struct<'a>`
-note: the lifetime `'a` as defined here...
- --> $DIR/regions-bounds.rs:12:10
- |
-LL | fn a_fn3<'a,'b>(e: Struct<'a>) -> Struct<'b> {
- | ^^
-note: ...does not necessarily outlive the lifetime `'b` as defined here
- --> $DIR/regions-bounds.rs:12:13
- |
-LL | fn a_fn3<'a,'b>(e: Struct<'a>) -> Struct<'b> {
- | ^^
-
-error: aborting due to 2 previous errors
-
-For more information about this error, try `rustc --explain E0308`.
--- /dev/null
+error[E0310]: the associated type `<T as Iter>::Item` may not live long enough
+ --> $DIR/regions-close-associated-type-into-object.rs:19:5
+ |
+LL | Box::new(item)
+ | ^^^^^^^^^^^^^^
+ |
+ = help: consider adding an explicit lifetime bound `<T as Iter>::Item: 'static`...
+ = note: ...so that the type `<T as Iter>::Item` will meet its required lifetime bounds
+
+error[E0310]: the associated type `<T as Iter>::Item` may not live long enough
+ --> $DIR/regions-close-associated-type-into-object.rs:26:5
+ |
+LL | Box::new(item)
+ | ^^^^^^^^^^^^^^
+ |
+ = help: consider adding an explicit lifetime bound `<T as Iter>::Item: 'static`...
+ = note: ...so that the type `Box<<T as Iter>::Item>` will meet its required lifetime bounds
+
+error[E0309]: the associated type `<T as Iter>::Item` may not live long enough
+ --> $DIR/regions-close-associated-type-into-object.rs:32:5
+ |
+LL | Box::new(item)
+ | ^^^^^^^^^^^^^^
+ |
+ = help: consider adding an explicit lifetime bound `<T as Iter>::Item: 'a`...
+ = note: ...so that the type `<T as Iter>::Item` will meet its required lifetime bounds
+
+error[E0309]: the associated type `<T as Iter>::Item` may not live long enough
+ --> $DIR/regions-close-associated-type-into-object.rs:39:5
+ |
+LL | Box::new(item)
+ | ^^^^^^^^^^^^^^
+ |
+ = help: consider adding an explicit lifetime bound `<T as Iter>::Item: 'a`...
+ = note: ...so that the type `Box<<T as Iter>::Item>` will meet its required lifetime bounds
+
+error: aborting due to 4 previous errors
+
+Some errors have detailed explanations: E0309, E0310.
+For more information about an error, try `rustc --explain E0309`.
error[E0310]: the associated type `<T as Iter>::Item` may not live long enough
- --> $DIR/regions-close-associated-type-into-object.rs:15:5
+ --> $DIR/regions-close-associated-type-into-object.rs:19:5
|
LL | Box::new(item)
| ^^^^^^^^^^^^^^
|
= help: consider adding an explicit lifetime bound `<T as Iter>::Item: 'static`...
+ = note: ...so that the type `<T as Iter>::Item` will meet its required lifetime bounds
error[E0310]: the associated type `<T as Iter>::Item` may not live long enough
- --> $DIR/regions-close-associated-type-into-object.rs:22:5
+ --> $DIR/regions-close-associated-type-into-object.rs:26:5
|
LL | Box::new(item)
| ^^^^^^^^^^^^^^
|
= help: consider adding an explicit lifetime bound `<T as Iter>::Item: 'static`...
+ = note: ...so that the type `<T as Iter>::Item` will meet its required lifetime bounds
error[E0309]: the associated type `<T as Iter>::Item` may not live long enough
- --> $DIR/regions-close-associated-type-into-object.rs:28:5
+ --> $DIR/regions-close-associated-type-into-object.rs:32:5
|
LL | Box::new(item)
| ^^^^^^^^^^^^^^
|
= help: consider adding an explicit lifetime bound `<T as Iter>::Item: 'a`...
+ = note: ...so that the type `<T as Iter>::Item` will meet its required lifetime bounds
error[E0309]: the associated type `<T as Iter>::Item` may not live long enough
- --> $DIR/regions-close-associated-type-into-object.rs:35:5
+ --> $DIR/regions-close-associated-type-into-object.rs:39:5
|
LL | Box::new(item)
| ^^^^^^^^^^^^^^
|
= help: consider adding an explicit lifetime bound `<T as Iter>::Item: 'a`...
+ = note: ...so that the type `<T as Iter>::Item` will meet its required lifetime bounds
error: aborting due to 4 previous errors
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
+
trait X {}
+++ /dev/null
-error[E0310]: the associated type `<T as Iter>::Item` may not live long enough
- --> $DIR/regions-close-associated-type-into-object.rs:15:5
- |
-LL | Box::new(item)
- | ^^^^^^^^^^^^^^
- |
- = help: consider adding an explicit lifetime bound `<T as Iter>::Item: 'static`...
- = note: ...so that the type `<T as Iter>::Item` will meet its required lifetime bounds
-
-error[E0310]: the associated type `<T as Iter>::Item` may not live long enough
- --> $DIR/regions-close-associated-type-into-object.rs:22:5
- |
-LL | Box::new(item)
- | ^^^^^^^^^^^^^^
- |
- = help: consider adding an explicit lifetime bound `<T as Iter>::Item: 'static`...
- = note: ...so that the type `Box<<T as Iter>::Item>` will meet its required lifetime bounds
-
-error[E0309]: the associated type `<T as Iter>::Item` may not live long enough
- --> $DIR/regions-close-associated-type-into-object.rs:28:5
- |
-LL | Box::new(item)
- | ^^^^^^^^^^^^^^
- |
- = help: consider adding an explicit lifetime bound `<T as Iter>::Item: 'a`...
- = note: ...so that the type `<T as Iter>::Item` will meet its required lifetime bounds
-
-error[E0309]: the associated type `<T as Iter>::Item` may not live long enough
- --> $DIR/regions-close-associated-type-into-object.rs:35:5
- |
-LL | Box::new(item)
- | ^^^^^^^^^^^^^^
- |
- = help: consider adding an explicit lifetime bound `<T as Iter>::Item: 'a`...
- = note: ...so that the type `Box<<T as Iter>::Item>` will meet its required lifetime bounds
-
-error: aborting due to 4 previous errors
-
-Some errors have detailed explanations: E0309, E0310.
-For more information about an error, try `rustc --explain E0309`.
--- /dev/null
+error[E0759]: `v` has lifetime `'a` but it needs to satisfy a `'static` lifetime requirement
+ --> $DIR/regions-close-object-into-object-2.rs:13:16
+ |
+LL | fn g<'a, T: 'static>(v: Box<dyn A<T> + 'a>) -> Box<dyn X + 'static> {
+ | ------------------ this data with lifetime `'a`...
+LL | Box::new(B(&*v)) as Box<dyn X>
+ | ^^^ ...is used and required to live as long as `'static` here
+ |
+note: `'static` lifetime requirement introduced by the return type
+ --> $DIR/regions-close-object-into-object-2.rs:12:60
+ |
+LL | fn g<'a, T: 'static>(v: Box<dyn A<T> + 'a>) -> Box<dyn X + 'static> {
+ | ^^^^^^^ `'static` requirement introduced here
+LL | Box::new(B(&*v)) as Box<dyn X>
+ | ------------------------------ because of this returned expression
+help: consider changing the trait object's explicit `'static` bound to the lifetime of argument `v`
+ |
+LL | fn g<'a, T: 'static>(v: Box<dyn A<T> + 'a>) -> Box<dyn X + 'a> {
+ | ~~
+help: alternatively, add an explicit `'static` bound to this reference
+ |
+LL | fn g<'a, T: 'static>(v: Box<(dyn A<T> + 'static)>) -> Box<dyn X + 'static> {
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0759`.
error: lifetime may not live long enough
- --> $DIR/regions-close-object-into-object-2.rs:9:5
+ --> $DIR/regions-close-object-into-object-2.rs:13:5
|
LL | fn g<'a, T: 'static>(v: Box<dyn A<T> + 'a>) -> Box<dyn X + 'static> {
| -- lifetime `'a` defined here
LL | Box::new(B(&*v)) as Box<dyn X>
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ returning this value requires that `'a` must outlive `'static`
+ |
+help: consider changing the trait object's explicit `'static` bound to the lifetime of argument `v`
+ |
+LL | fn g<'a, T: 'static>(v: Box<dyn A<T> + 'a>) -> Box<dyn X + 'a> {
+ | ~~
+help: alternatively, add an explicit `'static` bound to this reference
+ |
+LL | fn g<'a, T: 'static>(v: Box<(dyn A<T> + 'static)>) -> Box<dyn X + 'static> {
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~
error[E0515]: cannot return value referencing local data `*v`
- --> $DIR/regions-close-object-into-object-2.rs:9:5
+ --> $DIR/regions-close-object-into-object-2.rs:13:5
|
LL | Box::new(B(&*v)) as Box<dyn X>
| ^^^^^^^^^^^---^^^^^^^^^^^^^^^^
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
+
trait A<T> { }
struct B<'a, T:'a>(&'a (dyn A<T> + 'a));
impl<'a, T> X for B<'a, T> {}
fn g<'a, T: 'static>(v: Box<dyn A<T> + 'a>) -> Box<dyn X + 'static> {
- Box::new(B(&*v)) as Box<dyn X> //~ ERROR E0759
+ Box::new(B(&*v)) as Box<dyn X>
+ //[base]~^ ERROR E0759
+ //[nll]~^^ ERROR lifetime may not live long enough
+ //[nll]~| ERROR cannot return value referencing local data `*v` [E0515]
}
fn main() { }
+++ /dev/null
-error[E0759]: `v` has lifetime `'a` but it needs to satisfy a `'static` lifetime requirement
- --> $DIR/regions-close-object-into-object-2.rs:9:16
- |
-LL | fn g<'a, T: 'static>(v: Box<dyn A<T> + 'a>) -> Box<dyn X + 'static> {
- | ------------------ this data with lifetime `'a`...
-LL | Box::new(B(&*v)) as Box<dyn X>
- | ^^^ ...is used and required to live as long as `'static` here
- |
-note: `'static` lifetime requirement introduced by the return type
- --> $DIR/regions-close-object-into-object-2.rs:8:60
- |
-LL | fn g<'a, T: 'static>(v: Box<dyn A<T> + 'a>) -> Box<dyn X + 'static> {
- | ^^^^^^^ `'static` requirement introduced here
-LL | Box::new(B(&*v)) as Box<dyn X>
- | ------------------------------ because of this returned expression
-help: consider changing the trait object's explicit `'static` bound to the lifetime of argument `v`
- |
-LL | fn g<'a, T: 'static>(v: Box<dyn A<T> + 'a>) -> Box<dyn X + 'a> {
- | ~~
-help: alternatively, add an explicit `'static` bound to this reference
- |
-LL | fn g<'a, T: 'static>(v: Box<(dyn A<T> + 'static)>) -> Box<dyn X + 'static> {
- | ~~~~~~~~~~~~~~~~~~~~~~~~~
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0759`.
--- /dev/null
+error[E0759]: `v` has lifetime `'a` but it needs to satisfy a `'static` lifetime requirement
+ --> $DIR/regions-close-object-into-object-4.rs:13:16
+ |
+LL | fn i<'a, T, U>(v: Box<dyn A<U>+'a>) -> Box<dyn X + 'static> {
+ | ---------------- this data with lifetime `'a`...
+LL | Box::new(B(&*v)) as Box<dyn X>
+ | ^^^ ...is used and required to live as long as `'static` here
+ |
+note: `'static` lifetime requirement introduced by the return type
+ --> $DIR/regions-close-object-into-object-4.rs:12:52
+ |
+LL | fn i<'a, T, U>(v: Box<dyn A<U>+'a>) -> Box<dyn X + 'static> {
+ | ^^^^^^^ `'static` requirement introduced here
+LL | Box::new(B(&*v)) as Box<dyn X>
+ | ------------------------------ because of this returned expression
+help: consider changing the trait object's explicit `'static` bound to the lifetime of argument `v`
+ |
+LL | fn i<'a, T, U>(v: Box<dyn A<U>+'a>) -> Box<dyn X + 'a> {
+ | ~~
+help: alternatively, add an explicit `'static` bound to this reference
+ |
+LL | fn i<'a, T, U>(v: Box<(dyn A<U> + 'static)>) -> Box<dyn X + 'static> {
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0759`.
error[E0310]: the parameter type `U` may not live long enough
- --> $DIR/regions-close-object-into-object-4.rs:9:5
+ --> $DIR/regions-close-object-into-object-4.rs:13:5
|
LL | Box::new(B(&*v)) as Box<dyn X>
- | ^^^^^^^^
+ | ^^^^^^^^ ...so that the type `U` will meet its required lifetime bounds
|
- = help: consider adding an explicit lifetime bound `U: 'static`...
+help: consider adding an explicit lifetime bound...
+ |
+LL | fn i<'a, T, U: 'static>(v: Box<dyn A<U>+'a>) -> Box<dyn X + 'static> {
+ | +++++++++
error[E0310]: the parameter type `U` may not live long enough
- --> $DIR/regions-close-object-into-object-4.rs:9:5
+ --> $DIR/regions-close-object-into-object-4.rs:13:5
|
LL | Box::new(B(&*v)) as Box<dyn X>
- | ^^^^^^^^^^^^^^^^
+ | ^^^^^^^^^^^^^^^^ ...so that the type `U` will meet its required lifetime bounds
+ |
+help: consider adding an explicit lifetime bound...
|
- = help: consider adding an explicit lifetime bound `U: 'static`...
+LL | fn i<'a, T, U: 'static>(v: Box<dyn A<U>+'a>) -> Box<dyn X + 'static> {
+ | +++++++++
error[E0310]: the parameter type `U` may not live long enough
- --> $DIR/regions-close-object-into-object-4.rs:9:5
+ --> $DIR/regions-close-object-into-object-4.rs:13:5
|
LL | Box::new(B(&*v)) as Box<dyn X>
- | ^^^^^^^^^^^^^^^^
+ | ^^^^^^^^^^^^^^^^ ...so that the type `U` will meet its required lifetime bounds
|
- = help: consider adding an explicit lifetime bound `U: 'static`...
+help: consider adding an explicit lifetime bound...
+ |
+LL | fn i<'a, T, U: 'static>(v: Box<dyn A<U>+'a>) -> Box<dyn X + 'static> {
+ | +++++++++
error: lifetime may not live long enough
- --> $DIR/regions-close-object-into-object-4.rs:9:5
+ --> $DIR/regions-close-object-into-object-4.rs:13:5
|
LL | fn i<'a, T, U>(v: Box<dyn A<U>+'a>) -> Box<dyn X + 'static> {
| -- lifetime `'a` defined here
LL | Box::new(B(&*v)) as Box<dyn X>
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ returning this value requires that `'a` must outlive `'static`
+ |
+help: consider changing the trait object's explicit `'static` bound to the lifetime of argument `v`
+ |
+LL | fn i<'a, T, U>(v: Box<dyn A<U>+'a>) -> Box<dyn X + 'a> {
+ | ~~
+help: alternatively, add an explicit `'static` bound to this reference
+ |
+LL | fn i<'a, T, U>(v: Box<(dyn A<U> + 'static)>) -> Box<dyn X + 'static> {
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~
error[E0515]: cannot return value referencing local data `*v`
- --> $DIR/regions-close-object-into-object-4.rs:9:5
+ --> $DIR/regions-close-object-into-object-4.rs:13:5
|
LL | Box::new(B(&*v)) as Box<dyn X>
| ^^^^^^^^^^^---^^^^^^^^^^^^^^^^
| returns a value referencing data owned by the current function
error[E0310]: the parameter type `U` may not live long enough
- --> $DIR/regions-close-object-into-object-4.rs:9:14
+ --> $DIR/regions-close-object-into-object-4.rs:13:14
|
LL | Box::new(B(&*v)) as Box<dyn X>
- | ^^^^^^
+ | ^^^^^^ ...so that the type `U` will meet its required lifetime bounds
+ |
+help: consider adding an explicit lifetime bound...
|
- = help: consider adding an explicit lifetime bound `U: 'static`...
+LL | fn i<'a, T, U: 'static>(v: Box<dyn A<U>+'a>) -> Box<dyn X + 'static> {
+ | +++++++++
error: aborting due to 6 previous errors
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
+
trait A<T> { }
struct B<'a, T:'a>(&'a (dyn A<T> + 'a));
impl<'a, T> X for B<'a, T> {}
fn i<'a, T, U>(v: Box<dyn A<U>+'a>) -> Box<dyn X + 'static> {
- Box::new(B(&*v)) as Box<dyn X> //~ ERROR E0759
+ Box::new(B(&*v)) as Box<dyn X>
+ //[base]~^ ERROR E0759
+ //[nll]~^^ ERROR the parameter type `U` may not live long enough [E0310]
+ //[nll]~| ERROR the parameter type `U` may not live long enough [E0310]
+ //[nll]~| ERROR the parameter type `U` may not live long enough [E0310]
+ //[nll]~| ERROR lifetime may not live long enough
+ //[nll]~| ERROR cannot return value referencing local data `*v` [E0515]
+ //[nll]~| ERROR the parameter type `U` may not live long enough [E0310]
+
}
fn main() {}
+++ /dev/null
-error[E0759]: `v` has lifetime `'a` but it needs to satisfy a `'static` lifetime requirement
- --> $DIR/regions-close-object-into-object-4.rs:9:16
- |
-LL | fn i<'a, T, U>(v: Box<dyn A<U>+'a>) -> Box<dyn X + 'static> {
- | ---------------- this data with lifetime `'a`...
-LL | Box::new(B(&*v)) as Box<dyn X>
- | ^^^ ...is used and required to live as long as `'static` here
- |
-note: `'static` lifetime requirement introduced by the return type
- --> $DIR/regions-close-object-into-object-4.rs:8:52
- |
-LL | fn i<'a, T, U>(v: Box<dyn A<U>+'a>) -> Box<dyn X + 'static> {
- | ^^^^^^^ `'static` requirement introduced here
-LL | Box::new(B(&*v)) as Box<dyn X>
- | ------------------------------ because of this returned expression
-help: consider changing the trait object's explicit `'static` bound to the lifetime of argument `v`
- |
-LL | fn i<'a, T, U>(v: Box<dyn A<U>+'a>) -> Box<dyn X + 'a> {
- | ~~
-help: alternatively, add an explicit `'static` bound to this reference
- |
-LL | fn i<'a, T, U>(v: Box<(dyn A<U> + 'static)>) -> Box<dyn X + 'static> {
- | ~~~~~~~~~~~~~~~~~~~~~~~~~
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0759`.
--- /dev/null
+error[E0310]: the parameter type `T` may not live long enough
+ --> $DIR/regions-close-object-into-object-5.rs:21:5
+ |
+LL | Box::new(B(&*v)) as Box<dyn X>
+ | ^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds...
+ |
+note: ...that is required by this bound
+ --> $DIR/regions-close-object-into-object-5.rs:13:17
+ |
+LL | struct B<'a, T: 'a>(&'a (A<T> + 'a));
+ | ^^
+help: consider adding an explicit lifetime bound...
+ |
+LL | fn f<'a, T: 'static, U>(v: Box<A<T> + 'static>) -> Box<X + 'static> {
+ | +++++++++
+
+error[E0310]: the parameter type `T` may not live long enough
+ --> $DIR/regions-close-object-into-object-5.rs:21:5
+ |
+LL | Box::new(B(&*v)) as Box<dyn X>
+ | ^^^^^^^^^^^^^^^^ ...so that the type `B<'_, T>` will meet its required lifetime bounds
+ |
+help: consider adding an explicit lifetime bound...
+ |
+LL | fn f<'a, T: 'static, U>(v: Box<A<T> + 'static>) -> Box<X + 'static> {
+ | +++++++++
+
+error[E0310]: the parameter type `T` may not live long enough
+ --> $DIR/regions-close-object-into-object-5.rs:21:14
+ |
+LL | Box::new(B(&*v)) as Box<dyn X>
+ | ^ ...so that the type `T` will meet its required lifetime bounds...
+ |
+note: ...that is required by this bound
+ --> $DIR/regions-close-object-into-object-5.rs:13:17
+ |
+LL | struct B<'a, T: 'a>(&'a (A<T> + 'a));
+ | ^^
+help: consider adding an explicit lifetime bound...
+ |
+LL | fn f<'a, T: 'static, U>(v: Box<A<T> + 'static>) -> Box<X + 'static> {
+ | +++++++++
+
+error[E0310]: the parameter type `T` may not live long enough
+ --> $DIR/regions-close-object-into-object-5.rs:21:14
+ |
+LL | Box::new(B(&*v)) as Box<dyn X>
+ | ^^^^^^ ...so that the type `T` will meet its required lifetime bounds...
+ |
+note: ...that is required by this bound
+ --> $DIR/regions-close-object-into-object-5.rs:13:17
+ |
+LL | struct B<'a, T: 'a>(&'a (A<T> + 'a));
+ | ^^
+help: consider adding an explicit lifetime bound...
+ |
+LL | fn f<'a, T: 'static, U>(v: Box<A<T> + 'static>) -> Box<X + 'static> {
+ | +++++++++
+
+error[E0310]: the parameter type `T` may not live long enough
+ --> $DIR/regions-close-object-into-object-5.rs:21:16
+ |
+LL | Box::new(B(&*v)) as Box<dyn X>
+ | ^^^ ...so that the reference type `&dyn A<T>` does not outlive the data it points at
+ |
+help: consider adding an explicit lifetime bound...
+ |
+LL | fn f<'a, T: 'static, U>(v: Box<A<T> + 'static>) -> Box<X + 'static> {
+ | +++++++++
+
+error[E0310]: the parameter type `T` may not live long enough
+ --> $DIR/regions-close-object-into-object-5.rs:21:16
+ |
+LL | Box::new(B(&*v)) as Box<dyn X>
+ | ^^^ ...so that the type `(dyn A<T> + 'static)` is not borrowed for too long
+ |
+help: consider adding an explicit lifetime bound...
+ |
+LL | fn f<'a, T: 'static, U>(v: Box<A<T> + 'static>) -> Box<X + 'static> {
+ | +++++++++
+
+error[E0310]: the parameter type `T` may not live long enough
+ --> $DIR/regions-close-object-into-object-5.rs:21:16
+ |
+LL | Box::new(B(&*v)) as Box<dyn X>
+ | ^^^ ...so that the type `(dyn A<T> + 'static)` is not borrowed for too long
+ |
+help: consider adding an explicit lifetime bound...
+ |
+LL | fn f<'a, T: 'static, U>(v: Box<A<T> + 'static>) -> Box<X + 'static> {
+ | +++++++++
+
+error: aborting due to 7 previous errors
+
+For more information about this error, try `rustc --explain E0310`.
error[E0310]: the parameter type `T` may not live long enough
- --> $DIR/regions-close-object-into-object-5.rs:17:5
+ --> $DIR/regions-close-object-into-object-5.rs:21:5
|
LL | Box::new(B(&*v)) as Box<dyn X>
- | ^^^^^^^^
+ | ^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds
|
- = help: consider adding an explicit lifetime bound `T: 'static`...
+help: consider adding an explicit lifetime bound...
+ |
+LL | fn f<'a, T: 'static, U>(v: Box<A<T> + 'static>) -> Box<X + 'static> {
+ | +++++++++
error[E0310]: the parameter type `T` may not live long enough
- --> $DIR/regions-close-object-into-object-5.rs:17:5
+ --> $DIR/regions-close-object-into-object-5.rs:21:5
|
LL | Box::new(B(&*v)) as Box<dyn X>
- | ^^^^^^^^^^^^^^^^
+ | ^^^^^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds
+ |
+help: consider adding an explicit lifetime bound...
|
- = help: consider adding an explicit lifetime bound `T: 'static`...
+LL | fn f<'a, T: 'static, U>(v: Box<A<T> + 'static>) -> Box<X + 'static> {
+ | +++++++++
error[E0310]: the parameter type `T` may not live long enough
- --> $DIR/regions-close-object-into-object-5.rs:17:5
+ --> $DIR/regions-close-object-into-object-5.rs:21:5
|
LL | Box::new(B(&*v)) as Box<dyn X>
- | ^^^^^^^^^^^^^^^^
+ | ^^^^^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds
|
- = help: consider adding an explicit lifetime bound `T: 'static`...
+help: consider adding an explicit lifetime bound...
+ |
+LL | fn f<'a, T: 'static, U>(v: Box<A<T> + 'static>) -> Box<X + 'static> {
+ | +++++++++
error[E0515]: cannot return value referencing local data `*v`
- --> $DIR/regions-close-object-into-object-5.rs:17:5
+ --> $DIR/regions-close-object-into-object-5.rs:21:5
|
LL | Box::new(B(&*v)) as Box<dyn X>
| ^^^^^^^^^^^---^^^^^^^^^^^^^^^^
| returns a value referencing data owned by the current function
error[E0310]: the parameter type `T` may not live long enough
- --> $DIR/regions-close-object-into-object-5.rs:17:14
+ --> $DIR/regions-close-object-into-object-5.rs:21:14
|
LL | Box::new(B(&*v)) as Box<dyn X>
- | ^^^^^^
+ | ^^^^^^ ...so that the type `T` will meet its required lifetime bounds
+ |
+help: consider adding an explicit lifetime bound...
|
- = help: consider adding an explicit lifetime bound `T: 'static`...
+LL | fn f<'a, T: 'static, U>(v: Box<A<T> + 'static>) -> Box<X + 'static> {
+ | +++++++++
error: aborting due to 5 previous errors
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
+
#![allow(warnings)]
//~| ERROR the parameter type `T` may not live long enough
//~| ERROR the parameter type `T` may not live long enough
//~| ERROR the parameter type `T` may not live long enough
- //~| ERROR the parameter type `T` may not live long enough
- //~| ERROR the parameter type `T` may not live long enough
- //~| ERROR the parameter type `T` may not live long enough
+ //[base]~| ERROR the parameter type `T` may not live long enough
+ //[base]~| ERROR the parameter type `T` may not live long enough
+ //[base]~| ERROR the parameter type `T` may not live long enough
+ //[nll]~| ERROR cannot return value referencing local data `*v` [E0515]
}
fn main() {}
+++ /dev/null
-error[E0310]: the parameter type `T` may not live long enough
- --> $DIR/regions-close-object-into-object-5.rs:17:5
- |
-LL | fn f<'a, T, U>(v: Box<A<T> + 'static>) -> Box<X + 'static> {
- | - help: consider adding an explicit lifetime bound...: `T: 'static`
-LL | // oh dear!
-LL | Box::new(B(&*v)) as Box<dyn X>
- | ^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds...
- |
-note: ...that is required by this bound
- --> $DIR/regions-close-object-into-object-5.rs:9:17
- |
-LL | struct B<'a, T: 'a>(&'a (A<T> + 'a));
- | ^^
-
-error[E0310]: the parameter type `T` may not live long enough
- --> $DIR/regions-close-object-into-object-5.rs:17:5
- |
-LL | fn f<'a, T, U>(v: Box<A<T> + 'static>) -> Box<X + 'static> {
- | - help: consider adding an explicit lifetime bound...: `T: 'static`
-LL | // oh dear!
-LL | Box::new(B(&*v)) as Box<dyn X>
- | ^^^^^^^^^^^^^^^^ ...so that the type `B<'_, T>` will meet its required lifetime bounds
-
-error[E0310]: the parameter type `T` may not live long enough
- --> $DIR/regions-close-object-into-object-5.rs:17:14
- |
-LL | fn f<'a, T, U>(v: Box<A<T> + 'static>) -> Box<X + 'static> {
- | - help: consider adding an explicit lifetime bound...: `T: 'static`
-LL | // oh dear!
-LL | Box::new(B(&*v)) as Box<dyn X>
- | ^ ...so that the type `T` will meet its required lifetime bounds...
- |
-note: ...that is required by this bound
- --> $DIR/regions-close-object-into-object-5.rs:9:17
- |
-LL | struct B<'a, T: 'a>(&'a (A<T> + 'a));
- | ^^
-
-error[E0310]: the parameter type `T` may not live long enough
- --> $DIR/regions-close-object-into-object-5.rs:17:14
- |
-LL | fn f<'a, T, U>(v: Box<A<T> + 'static>) -> Box<X + 'static> {
- | - help: consider adding an explicit lifetime bound...: `T: 'static`
-LL | // oh dear!
-LL | Box::new(B(&*v)) as Box<dyn X>
- | ^^^^^^ ...so that the type `T` will meet its required lifetime bounds...
- |
-note: ...that is required by this bound
- --> $DIR/regions-close-object-into-object-5.rs:9:17
- |
-LL | struct B<'a, T: 'a>(&'a (A<T> + 'a));
- | ^^
-
-error[E0310]: the parameter type `T` may not live long enough
- --> $DIR/regions-close-object-into-object-5.rs:17:16
- |
-LL | fn f<'a, T, U>(v: Box<A<T> + 'static>) -> Box<X + 'static> {
- | - help: consider adding an explicit lifetime bound...: `T: 'static`
-LL | // oh dear!
-LL | Box::new(B(&*v)) as Box<dyn X>
- | ^^^ ...so that the reference type `&dyn A<T>` does not outlive the data it points at
-
-error[E0310]: the parameter type `T` may not live long enough
- --> $DIR/regions-close-object-into-object-5.rs:17:16
- |
-LL | fn f<'a, T, U>(v: Box<A<T> + 'static>) -> Box<X + 'static> {
- | - help: consider adding an explicit lifetime bound...: `T: 'static`
-LL | // oh dear!
-LL | Box::new(B(&*v)) as Box<dyn X>
- | ^^^ ...so that the type `(dyn A<T> + 'static)` is not borrowed for too long
-
-error[E0310]: the parameter type `T` may not live long enough
- --> $DIR/regions-close-object-into-object-5.rs:17:16
- |
-LL | fn f<'a, T, U>(v: Box<A<T> + 'static>) -> Box<X + 'static> {
- | - help: consider adding an explicit lifetime bound...: `T: 'static`
-LL | // oh dear!
-LL | Box::new(B(&*v)) as Box<dyn X>
- | ^^^ ...so that the type `(dyn A<T> + 'static)` is not borrowed for too long
-
-error: aborting due to 7 previous errors
-
-For more information about this error, try `rustc --explain E0310`.
--- /dev/null
+error[E0310]: the parameter type `A` may not live long enough
+ --> $DIR/regions-close-over-type-parameter-1.rs:15:5
+ |
+LL | Box::new(v) as Box<dyn SomeTrait + 'static>
+ | ^^^^^^^^^^^ ...so that the type `A` will meet its required lifetime bounds
+ |
+help: consider adding an explicit lifetime bound...
+ |
+LL | fn make_object1<A: SomeTrait + 'static>(v: A) -> Box<dyn SomeTrait + 'static> {
+ | +++++++++
+
+error[E0309]: the parameter type `A` may not live long enough
+ --> $DIR/regions-close-over-type-parameter-1.rs:24:5
+ |
+LL | Box::new(v) as Box<dyn SomeTrait + 'b>
+ | ^^^^^^^^^^^ ...so that the type `A` will meet its required lifetime bounds
+ |
+help: consider adding an explicit lifetime bound...
+ |
+LL | fn make_object3<'a, 'b, A: SomeTrait + 'a + 'b>(v: A) -> Box<dyn SomeTrait + 'b> {
+ | ++++
+
+error: aborting due to 2 previous errors
+
+Some errors have detailed explanations: E0309, E0310.
+For more information about an error, try `rustc --explain E0309`.
error[E0310]: the parameter type `A` may not live long enough
- --> $DIR/regions-close-over-type-parameter-1.rs:12:5
+ --> $DIR/regions-close-over-type-parameter-1.rs:15:5
|
LL | Box::new(v) as Box<dyn SomeTrait + 'static>
- | ^^^^^^^^^^^
+ | ^^^^^^^^^^^ ...so that the type `A` will meet its required lifetime bounds
|
- = help: consider adding an explicit lifetime bound `A: 'static`...
+help: consider adding an explicit lifetime bound...
+ |
+LL | fn make_object1<A: SomeTrait + 'static>(v: A) -> Box<dyn SomeTrait + 'static> {
+ | +++++++++
error[E0309]: the parameter type `A` may not live long enough
- --> $DIR/regions-close-over-type-parameter-1.rs:21:5
+ --> $DIR/regions-close-over-type-parameter-1.rs:24:5
|
LL | Box::new(v) as Box<dyn SomeTrait + 'b>
- | ^^^^^^^^^^^
+ | ^^^^^^^^^^^ ...so that the type `A` will meet its required lifetime bounds
+ |
+help: consider adding an explicit lifetime bound...
|
- = help: consider adding an explicit lifetime bound `A: 'b`...
+LL | fn make_object3<'a, 'b, A: SomeTrait + 'a + 'b>(v: A) -> Box<dyn SomeTrait + 'b> {
+ | ++++
error: aborting due to 2 previous errors
// an object. This should yield errors unless `A` (and the object)
// both have suitable bounds.
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
trait SomeTrait {
fn get(&self) -> isize;
+++ /dev/null
-error[E0310]: the parameter type `A` may not live long enough
- --> $DIR/regions-close-over-type-parameter-1.rs:12:5
- |
-LL | fn make_object1<A: SomeTrait>(v: A) -> Box<dyn SomeTrait + 'static> {
- | -- help: consider adding an explicit lifetime bound...: `A: 'static +`
-LL | Box::new(v) as Box<dyn SomeTrait + 'static>
- | ^^^^^^^^^^^ ...so that the type `A` will meet its required lifetime bounds
-
-error[E0309]: the parameter type `A` may not live long enough
- --> $DIR/regions-close-over-type-parameter-1.rs:21:5
- |
-LL | fn make_object3<'a, 'b, A: SomeTrait + 'a>(v: A) -> Box<dyn SomeTrait + 'b> {
- | -- help: consider adding an explicit lifetime bound...: `A: 'b +`
-LL | Box::new(v) as Box<dyn SomeTrait + 'b>
- | ^^^^^^^^^^^ ...so that the type `A` will meet its required lifetime bounds
-
-error: aborting due to 2 previous errors
-
-Some errors have detailed explanations: E0309, E0310.
-For more information about an error, try `rustc --explain E0309`.
--- /dev/null
+error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
+ --> $DIR/regions-close-over-type-parameter-multiple.rs:23:5
+ |
+LL | Box::new(v) as Box<dyn SomeTrait + 'a>
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+note: first, the lifetime cannot outlive the lifetime `'a` as defined here...
+ --> $DIR/regions-close-over-type-parameter-multiple.rs:21:20
+ |
+LL | fn make_object_bad<'a,'b,'c,A:SomeTrait+'a+'b>(v: A) -> Box<dyn SomeTrait + 'c> {
+ | ^^
+note: ...so that the declared lifetime parameter bounds are satisfied
+ --> $DIR/regions-close-over-type-parameter-multiple.rs:23:5
+ |
+LL | Box::new(v) as Box<dyn SomeTrait + 'a>
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+note: but, the lifetime must be valid for the lifetime `'c` as defined here...
+ --> $DIR/regions-close-over-type-parameter-multiple.rs:21:26
+ |
+LL | fn make_object_bad<'a,'b,'c,A:SomeTrait+'a+'b>(v: A) -> Box<dyn SomeTrait + 'c> {
+ | ^^
+note: ...so that the types are compatible
+ --> $DIR/regions-close-over-type-parameter-multiple.rs:23:5
+ |
+LL | Box::new(v) as Box<dyn SomeTrait + 'a>
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ = note: expected `Box<(dyn SomeTrait + 'c)>`
+ found `Box<dyn SomeTrait>`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0495`.
error: lifetime may not live long enough
- --> $DIR/regions-close-over-type-parameter-multiple.rs:20:5
+ --> $DIR/regions-close-over-type-parameter-multiple.rs:23:5
|
LL | fn make_object_bad<'a,'b,'c,A:SomeTrait+'a+'b>(v: A) -> Box<dyn SomeTrait + 'c> {
| -- -- lifetime `'c` defined here
// Various tests where we over type parameters with multiple lifetime
// bounds.
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
trait SomeTrait { fn get(&self) -> isize; }
fn make_object_bad<'a,'b,'c,A:SomeTrait+'a+'b>(v: A) -> Box<dyn SomeTrait + 'c> {
// A outlives 'a AND 'b...but not 'c.
- Box::new(v) as Box<dyn SomeTrait + 'a> //~ ERROR cannot infer an appropriate lifetime
+ Box::new(v) as Box<dyn SomeTrait + 'a>
+ //[base]~^ ERROR cannot infer an appropriate lifetime
+ //[nll]~^^ ERROR lifetime may not live long enough
}
fn main() {
+++ /dev/null
-error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
- --> $DIR/regions-close-over-type-parameter-multiple.rs:20:5
- |
-LL | Box::new(v) as Box<dyn SomeTrait + 'a>
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- |
-note: first, the lifetime cannot outlive the lifetime `'a` as defined here...
- --> $DIR/regions-close-over-type-parameter-multiple.rs:18:20
- |
-LL | fn make_object_bad<'a,'b,'c,A:SomeTrait+'a+'b>(v: A) -> Box<dyn SomeTrait + 'c> {
- | ^^
-note: ...so that the declared lifetime parameter bounds are satisfied
- --> $DIR/regions-close-over-type-parameter-multiple.rs:20:5
- |
-LL | Box::new(v) as Box<dyn SomeTrait + 'a>
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-note: but, the lifetime must be valid for the lifetime `'c` as defined here...
- --> $DIR/regions-close-over-type-parameter-multiple.rs:18:26
- |
-LL | fn make_object_bad<'a,'b,'c,A:SomeTrait+'a+'b>(v: A) -> Box<dyn SomeTrait + 'c> {
- | ^^
-note: ...so that the types are compatible
- --> $DIR/regions-close-over-type-parameter-multiple.rs:20:5
- |
-LL | Box::new(v) as Box<dyn SomeTrait + 'a>
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- = note: expected `Box<(dyn SomeTrait + 'c)>`
- found `Box<dyn SomeTrait>`
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0495`.
--- /dev/null
+error[E0310]: the parameter type `T` may not live long enough
+ --> $DIR/regions-close-param-into-object.rs:10:5
+ |
+LL | Box::new(v)
+ | ^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds
+ |
+help: consider adding an explicit lifetime bound...
+ |
+LL | where T : X + 'static
+ | +++++++++
+
+error[E0310]: the parameter type `T` may not live long enough
+ --> $DIR/regions-close-param-into-object.rs:16:5
+ |
+LL | Box::new(v)
+ | ^^^^^^^^^^^ ...so that the type `Box<T>` will meet its required lifetime bounds
+ |
+help: consider adding an explicit lifetime bound...
+ |
+LL | fn p2<T: 'static>(v: Box<T>) -> Box<dyn X + 'static>
+ | +++++++++
+
+error[E0309]: the parameter type `T` may not live long enough
+ --> $DIR/regions-close-param-into-object.rs:22:5
+ |
+LL | Box::new(v)
+ | ^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds
+ |
+help: consider adding an explicit lifetime bound...
+ |
+LL | where T : X + 'a
+ | ++++
+
+error[E0309]: the parameter type `T` may not live long enough
+ --> $DIR/regions-close-param-into-object.rs:28:5
+ |
+LL | Box::new(v)
+ | ^^^^^^^^^^^ ...so that the type `Box<T>` will meet its required lifetime bounds
+ |
+help: consider adding an explicit lifetime bound...
+ |
+LL | fn p4<'a,T: 'a>(v: Box<T>) -> Box<dyn X + 'a>
+ | ++++
+
+error: aborting due to 4 previous errors
+
+Some errors have detailed explanations: E0309, E0310.
+For more information about an error, try `rustc --explain E0309`.
error[E0310]: the parameter type `T` may not live long enough
- --> $DIR/regions-close-param-into-object.rs:6:5
+ --> $DIR/regions-close-param-into-object.rs:10:5
|
LL | Box::new(v)
- | ^^^^^^^^^^^
+ | ^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds
|
- = help: consider adding an explicit lifetime bound `T: 'static`...
+help: consider adding an explicit lifetime bound...
+ |
+LL | where T : X + 'static
+ | +++++++++
error[E0310]: the parameter type `T` may not live long enough
- --> $DIR/regions-close-param-into-object.rs:12:5
+ --> $DIR/regions-close-param-into-object.rs:16:5
|
LL | Box::new(v)
- | ^^^^^^^^^^^
+ | ^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds
+ |
+help: consider adding an explicit lifetime bound...
|
- = help: consider adding an explicit lifetime bound `T: 'static`...
+LL | fn p2<T: 'static>(v: Box<T>) -> Box<dyn X + 'static>
+ | +++++++++
error[E0309]: the parameter type `T` may not live long enough
- --> $DIR/regions-close-param-into-object.rs:18:5
+ --> $DIR/regions-close-param-into-object.rs:22:5
|
LL | Box::new(v)
- | ^^^^^^^^^^^
+ | ^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds
|
- = help: consider adding an explicit lifetime bound `T: 'a`...
+help: consider adding an explicit lifetime bound...
+ |
+LL | where T : X + 'a
+ | ++++
error[E0309]: the parameter type `T` may not live long enough
- --> $DIR/regions-close-param-into-object.rs:24:5
+ --> $DIR/regions-close-param-into-object.rs:28:5
|
LL | Box::new(v)
- | ^^^^^^^^^^^
+ | ^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds
+ |
+help: consider adding an explicit lifetime bound...
|
- = help: consider adding an explicit lifetime bound `T: 'a`...
+LL | fn p4<'a,T: 'a>(v: Box<T>) -> Box<dyn X + 'a>
+ | ++++
error: aborting due to 4 previous errors
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
+
trait X { fn foo(&self) {} }
fn p1<T>(v: T) -> Box<dyn X + 'static>
+++ /dev/null
-error[E0310]: the parameter type `T` may not live long enough
- --> $DIR/regions-close-param-into-object.rs:6:5
- |
-LL | fn p1<T>(v: T) -> Box<dyn X + 'static>
- | - help: consider adding an explicit lifetime bound...: `T: 'static`
-...
-LL | Box::new(v)
- | ^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds
-
-error[E0310]: the parameter type `T` may not live long enough
- --> $DIR/regions-close-param-into-object.rs:12:5
- |
-LL | fn p2<T>(v: Box<T>) -> Box<dyn X + 'static>
- | - help: consider adding an explicit lifetime bound...: `T: 'static`
-...
-LL | Box::new(v)
- | ^^^^^^^^^^^ ...so that the type `Box<T>` will meet its required lifetime bounds
-
-error[E0309]: the parameter type `T` may not live long enough
- --> $DIR/regions-close-param-into-object.rs:18:5
- |
-LL | fn p3<'a,T>(v: T) -> Box<dyn X + 'a>
- | - help: consider adding an explicit lifetime bound...: `T: 'a`
-...
-LL | Box::new(v)
- | ^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds
-
-error[E0309]: the parameter type `T` may not live long enough
- --> $DIR/regions-close-param-into-object.rs:24:5
- |
-LL | fn p4<'a,T>(v: Box<T>) -> Box<dyn X + 'a>
- | - help: consider adding an explicit lifetime bound...: `T: 'a`
-...
-LL | Box::new(v)
- | ^^^^^^^^^^^ ...so that the type `Box<T>` will meet its required lifetime bounds
-
-error: aborting due to 4 previous errors
-
-Some errors have detailed explanations: E0309, E0310.
-For more information about an error, try `rustc --explain E0309`.
--- /dev/null
+error[E0623]: lifetime mismatch
+ --> $DIR/regions-creating-enums3.rs:11:5
+ |
+LL | fn mk_add_bad1<'a,'b>(x: &'a Ast<'a>, y: &'b Ast<'b>) -> Ast<'a> {
+ | ----------- -------
+ | |
+ | this parameter and the return type are declared with different lifetimes...
+LL | Ast::Add(x, y)
+ | ^^^^^^^^^^^^^^ ...but data from `y` is returned here
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0623`.
error: lifetime may not live long enough
- --> $DIR/regions-creating-enums3.rs:7:5
+ --> $DIR/regions-creating-enums3.rs:11:5
|
LL | fn mk_add_bad1<'a,'b>(x: &'a Ast<'a>, y: &'b Ast<'b>) -> Ast<'a> {
| -- -- lifetime `'b` defined here
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
+
enum Ast<'a> {
Num(usize),
Add(&'a Ast<'a>, &'a Ast<'a>)
}
fn mk_add_bad1<'a,'b>(x: &'a Ast<'a>, y: &'b Ast<'b>) -> Ast<'a> {
- Ast::Add(x, y) //~ ERROR lifetime mismatch [E0623]
+ Ast::Add(x, y)
+ //[base]~^ ERROR lifetime mismatch [E0623]
+ //[nll]~^^ ERROR lifetime may not live long enough
}
fn main() {
+++ /dev/null
-error[E0623]: lifetime mismatch
- --> $DIR/regions-creating-enums3.rs:7:5
- |
-LL | fn mk_add_bad1<'a,'b>(x: &'a Ast<'a>, y: &'b Ast<'b>) -> Ast<'a> {
- | ----------- -------
- | |
- | this parameter and the return type are declared with different lifetimes...
-LL | Ast::Add(x, y)
- | ^^^^^^^^^^^^^^ ...but data from `y` is returned here
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0623`.
--- /dev/null
+error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'a` due to conflicting requirements
+ --> $DIR/regions-creating-enums4.rs:11:5
+ |
+LL | Ast::Add(x, y)
+ | ^^^^^^^^
+ |
+note: first, the lifetime cannot outlive the lifetime `'a` as defined here...
+ --> $DIR/regions-creating-enums4.rs:10:16
+ |
+LL | fn mk_add_bad2<'a,'b>(x: &'a Ast<'a>, y: &'a Ast<'a>, z: &Ast) -> Ast<'b> {
+ | ^^
+note: ...so that the expression is assignable
+ --> $DIR/regions-creating-enums4.rs:11:14
+ |
+LL | Ast::Add(x, y)
+ | ^
+ = note: expected `&Ast<'_>`
+ found `&Ast<'a>`
+note: but, the lifetime must be valid for the lifetime `'b` as defined here...
+ --> $DIR/regions-creating-enums4.rs:10:19
+ |
+LL | fn mk_add_bad2<'a,'b>(x: &'a Ast<'a>, y: &'a Ast<'a>, z: &Ast) -> Ast<'b> {
+ | ^^
+note: ...so that the types are compatible
+ --> $DIR/regions-creating-enums4.rs:11:5
+ |
+LL | Ast::Add(x, y)
+ | ^^^^^^^^^^^^^^
+ = note: expected `Ast<'b>`
+ found `Ast<'_>`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0495`.
error: lifetime may not live long enough
- --> $DIR/regions-creating-enums4.rs:7:5
+ --> $DIR/regions-creating-enums4.rs:11:5
|
LL | fn mk_add_bad2<'a,'b>(x: &'a Ast<'a>, y: &'a Ast<'a>, z: &Ast) -> Ast<'b> {
| -- -- lifetime `'b` defined here
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
+
enum Ast<'a> {
Num(usize),
Add(&'a Ast<'a>, &'a Ast<'a>)
}
fn mk_add_bad2<'a,'b>(x: &'a Ast<'a>, y: &'a Ast<'a>, z: &Ast) -> Ast<'b> {
- Ast::Add(x, y) //~ ERROR cannot infer
+ Ast::Add(x, y)
+ //[base]~^ ERROR cannot infer
+ //[nll]~^^ ERROR lifetime may not live long enough
}
fn main() {
+++ /dev/null
-error[E0495]: cannot infer an appropriate lifetime for lifetime parameter `'a` due to conflicting requirements
- --> $DIR/regions-creating-enums4.rs:7:5
- |
-LL | Ast::Add(x, y)
- | ^^^^^^^^
- |
-note: first, the lifetime cannot outlive the lifetime `'a` as defined here...
- --> $DIR/regions-creating-enums4.rs:6:16
- |
-LL | fn mk_add_bad2<'a,'b>(x: &'a Ast<'a>, y: &'a Ast<'a>, z: &Ast) -> Ast<'b> {
- | ^^
-note: ...so that the expression is assignable
- --> $DIR/regions-creating-enums4.rs:7:14
- |
-LL | Ast::Add(x, y)
- | ^
- = note: expected `&Ast<'_>`
- found `&Ast<'a>`
-note: but, the lifetime must be valid for the lifetime `'b` as defined here...
- --> $DIR/regions-creating-enums4.rs:6:19
- |
-LL | fn mk_add_bad2<'a,'b>(x: &'a Ast<'a>, y: &'a Ast<'a>, z: &Ast) -> Ast<'b> {
- | ^^
-note: ...so that the types are compatible
- --> $DIR/regions-creating-enums4.rs:7:5
- |
-LL | Ast::Add(x, y)
- | ^^^^^^^^^^^^^^
- = note: expected `Ast<'b>`
- found `Ast<'_>`
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0495`.
--- /dev/null
+error[E0312]: lifetime of reference outlives lifetime of borrowed content...
+ --> $DIR/regions-early-bound-error-method.rs:24:9
+ |
+LL | g2.get()
+ | ^^^^^^^^
+ |
+note: ...the reference is valid for the lifetime `'a` as defined here...
+ --> $DIR/regions-early-bound-error-method.rs:22:6
+ |
+LL | impl<'a> Box<'a> {
+ | ^^
+note: ...but the borrowed content is only valid for the lifetime `'b` as defined here
+ --> $DIR/regions-early-bound-error-method.rs:23:11
+ |
+LL | fn or<'b,G:GetRef<'b>>(&self, g2: G) -> &'a isize {
+ | ^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0312`.
error: lifetime may not live long enough
- --> $DIR/regions-early-bound-error-method.rs:20:9
+ --> $DIR/regions-early-bound-error-method.rs:24:9
|
LL | impl<'a> Box<'a> {
| -- lifetime `'a` defined here
// Tests that you can use a fn lifetime parameter as part of
// the value for a type parameter in a bound.
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
+
trait GetRef<'a> {
fn get(&self) -> &'a isize;
}
impl<'a> Box<'a> {
fn or<'b,G:GetRef<'b>>(&self, g2: G) -> &'a isize {
g2.get()
- //~^ ERROR E0312
+ //[base]~^ ERROR E0312
+ //[nll]~^^ ERROR lifetime may not live long enough
}
}
+++ /dev/null
-error[E0312]: lifetime of reference outlives lifetime of borrowed content...
- --> $DIR/regions-early-bound-error-method.rs:20:9
- |
-LL | g2.get()
- | ^^^^^^^^
- |
-note: ...the reference is valid for the lifetime `'a` as defined here...
- --> $DIR/regions-early-bound-error-method.rs:18:6
- |
-LL | impl<'a> Box<'a> {
- | ^^
-note: ...but the borrowed content is only valid for the lifetime `'b` as defined here
- --> $DIR/regions-early-bound-error-method.rs:19:11
- |
-LL | fn or<'b,G:GetRef<'b>>(&self, g2: G) -> &'a isize {
- | ^^
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0312`.
--- /dev/null
+error[E0312]: lifetime of reference outlives lifetime of borrowed content...
+ --> $DIR/regions-early-bound-error.rs:23:5
+ |
+LL | g1.get()
+ | ^^^^^^^^
+ |
+note: ...the reference is valid for the lifetime `'b` as defined here...
+ --> $DIR/regions-early-bound-error.rs:22:11
+ |
+LL | fn get<'a,'b,G:GetRef<'a, isize>>(g1: G, b: &'b isize) -> &'b isize {
+ | ^^
+note: ...but the borrowed content is only valid for the lifetime `'a` as defined here
+ --> $DIR/regions-early-bound-error.rs:22:8
+ |
+LL | fn get<'a,'b,G:GetRef<'a, isize>>(g1: G, b: &'b isize) -> &'b isize {
+ | ^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0312`.
error: lifetime may not live long enough
- --> $DIR/regions-early-bound-error.rs:19:5
+ --> $DIR/regions-early-bound-error.rs:23:5
|
LL | fn get<'a,'b,G:GetRef<'a, isize>>(g1: G, b: &'b isize) -> &'b isize {
| -- -- lifetime `'b` defined here
// Tests that you can use a fn lifetime parameter as part of
// the value for a type parameter in a bound.
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
+
trait GetRef<'a, T> {
fn get(&self) -> &'a T;
}
fn get<'a,'b,G:GetRef<'a, isize>>(g1: G, b: &'b isize) -> &'b isize {
g1.get()
- //~^ ERROR E0312
+ //[base]~^ ERROR E0312
+ //[nll]~^^ ERROR lifetime may not live long enough
}
fn main() {
+++ /dev/null
-error[E0312]: lifetime of reference outlives lifetime of borrowed content...
- --> $DIR/regions-early-bound-error.rs:19:5
- |
-LL | g1.get()
- | ^^^^^^^^
- |
-note: ...the reference is valid for the lifetime `'b` as defined here...
- --> $DIR/regions-early-bound-error.rs:18:11
- |
-LL | fn get<'a,'b,G:GetRef<'a, isize>>(g1: G, b: &'b isize) -> &'b isize {
- | ^^
-note: ...but the borrowed content is only valid for the lifetime `'a` as defined here
- --> $DIR/regions-early-bound-error.rs:18:8
- |
-LL | fn get<'a,'b,G:GetRef<'a, isize>>(g1: G, b: &'b isize) -> &'b isize {
- | ^^
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0312`.
--- /dev/null
+error[E0308]: mismatched types
+ --> $DIR/regions-fn-subtyping-return-static-fail.rs:52:12
+ |
+LL | want_G(baz);
+ | ^^^ one type is more general than the other
+ |
+ = note: expected fn pointer `for<'cx> fn(&'cx S) -> &'static S`
+ found fn pointer `for<'r> fn(&'r S) -> &'r S`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0308`.
error[E0308]: mismatched types
- --> $DIR/regions-fn-subtyping-return-static-fail.rs:48:5
+ --> $DIR/regions-fn-subtyping-return-static-fail.rs:52:5
|
LL | want_G(baz);
| ^^^^^^^^^^^ one type is more general than the other
// This can safely be considered to be an instance of `F` because all
// lifetimes are sublifetimes of 'static.
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
+
#![allow(dead_code)]
#![allow(unused_variables)]
+++ /dev/null
-error[E0308]: mismatched types
- --> $DIR/regions-fn-subtyping-return-static-fail.rs:48:12
- |
-LL | want_G(baz);
- | ^^^ one type is more general than the other
- |
- = note: expected fn pointer `for<'cx> fn(&'cx S) -> &'static S`
- found fn pointer `for<'r> fn(&'r S) -> &'r S`
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0308`.
--- /dev/null
+error[E0623]: lifetime mismatch
+ --> $DIR/regions-free-region-ordering-callee.rs:17:5
+ |
+LL | fn ordering2<'a, 'b>(x: &'a &'b usize, y: &'a usize) -> &'b usize {
+ | ------------- ---------
+ | |
+ | this parameter and the return type are declared with different lifetimes...
+LL | // However, it is not safe to assume that 'b <= 'a
+LL | &*y
+ | ^^^ ...but data from `x` is returned here
+
+error[E0623]: lifetime mismatch
+ --> $DIR/regions-free-region-ordering-callee.rs:24:24
+ |
+LL | fn ordering3<'a, 'b>(x: &'a usize, y: &'b usize) -> &'a &'b usize {
+ | --------- -------------
+ | |
+ | this parameter and the return type are declared with different lifetimes...
+LL | // Do not infer an ordering from the return value.
+LL | let z: &'b usize = &*x;
+ | ^^^ ...but data from `x` is returned here
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0623`.
error: lifetime may not live long enough
- --> $DIR/regions-free-region-ordering-callee.rs:13:5
+ --> $DIR/regions-free-region-ordering-callee.rs:17:5
|
LL | fn ordering2<'a, 'b>(x: &'a &'b usize, y: &'a usize) -> &'b usize {
| -- -- lifetime `'b` defined here
= help: consider adding the following bound: `'a: 'b`
error: lifetime may not live long enough
- --> $DIR/regions-free-region-ordering-callee.rs:18:12
+ --> $DIR/regions-free-region-ordering-callee.rs:24:12
|
LL | fn ordering3<'a, 'b>(x: &'a usize, y: &'b usize) -> &'a &'b usize {
| -- -- lifetime `'b` defined here
// that appear in their parameter list. See also
// regions-free-region-ordering-caller.rs
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
+
fn ordering1<'a, 'b>(x: &'a &'b usize) -> &'a usize {
// It is safe to assume that 'a <= 'b due to the type of x
let y: &'b usize = &**x;
fn ordering2<'a, 'b>(x: &'a &'b usize, y: &'a usize) -> &'b usize {
// However, it is not safe to assume that 'b <= 'a
- &*y //~ ERROR lifetime mismatch [E0623]
+ &*y
+ //[base]~^ ERROR lifetime mismatch [E0623]
+ //[nll]~^^ ERROR lifetime may not live long enough
}
fn ordering3<'a, 'b>(x: &'a usize, y: &'b usize) -> &'a &'b usize {
// Do not infer an ordering from the return value.
let z: &'b usize = &*x;
- //~^ ERROR lifetime mismatch [E0623]
+ //[base]~^ ERROR lifetime mismatch [E0623]
+ //[nll]~^^ ERROR lifetime may not live long enough
panic!();
}
+++ /dev/null
-error[E0623]: lifetime mismatch
- --> $DIR/regions-free-region-ordering-callee.rs:13:5
- |
-LL | fn ordering2<'a, 'b>(x: &'a &'b usize, y: &'a usize) -> &'b usize {
- | ------------- ---------
- | |
- | this parameter and the return type are declared with different lifetimes...
-LL | // However, it is not safe to assume that 'b <= 'a
-LL | &*y
- | ^^^ ...but data from `x` is returned here
-
-error[E0623]: lifetime mismatch
- --> $DIR/regions-free-region-ordering-callee.rs:18:24
- |
-LL | fn ordering3<'a, 'b>(x: &'a usize, y: &'b usize) -> &'a &'b usize {
- | --------- -------------
- | |
- | this parameter and the return type are declared with different lifetimes...
-LL | // Do not infer an ordering from the return value.
-LL | let z: &'b usize = &*x;
- | ^^^ ...but data from `x` is returned here
-
-error: aborting due to 2 previous errors
-
-For more information about this error, try `rustc --explain E0623`.
--- /dev/null
+error[E0495]: cannot infer an appropriate lifetime for borrow expression due to conflicting requirements
+ --> $DIR/regions-free-region-ordering-incorrect.rs:21:21
+ |
+LL | None => &self.val
+ | ^^^^^^^^^
+ |
+note: first, the lifetime cannot outlive the lifetime `'a` as defined here...
+ --> $DIR/regions-free-region-ordering-incorrect.rs:18:12
+ |
+LL | fn get<'a>(&'a self) -> &'b T {
+ | ^^
+note: ...so that reference does not outlive borrowed content
+ --> $DIR/regions-free-region-ordering-incorrect.rs:21:21
+ |
+LL | None => &self.val
+ | ^^^^^^^^^
+note: but, the lifetime must be valid for the lifetime `'b` as defined here...
+ --> $DIR/regions-free-region-ordering-incorrect.rs:17:6
+ |
+LL | impl<'b, T> Node<'b, T> {
+ | ^^
+note: ...so that reference does not outlive borrowed content
+ --> $DIR/regions-free-region-ordering-incorrect.rs:19:9
+ |
+LL | / match self.next {
+LL | | Some(ref next) => next.get(),
+LL | | None => &self.val
+LL | | }
+ | |_________^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0495`.
error: lifetime may not live long enough
- --> $DIR/regions-free-region-ordering-incorrect.rs:15:9
+ --> $DIR/regions-free-region-ordering-incorrect.rs:19:9
|
LL | impl<'b, T> Node<'b, T> {
| -- lifetime `'b` defined here
//
// This test began its life as a test for issue #4325.
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
+
struct Node<'b, T: 'b> {
val: T,
next: Option<&'b Node<'b, T>>
impl<'b, T> Node<'b, T> {
fn get<'a>(&'a self) -> &'b T {
- match self.next {
+ match self.next { //[nll]~ ERROR lifetime may not live long enough
Some(ref next) => next.get(),
- None => &self.val //~ ERROR cannot infer
+ None => &self.val //[base]~ ERROR cannot infer
}
}
}
+++ /dev/null
-error[E0495]: cannot infer an appropriate lifetime for borrow expression due to conflicting requirements
- --> $DIR/regions-free-region-ordering-incorrect.rs:17:21
- |
-LL | None => &self.val
- | ^^^^^^^^^
- |
-note: first, the lifetime cannot outlive the lifetime `'a` as defined here...
- --> $DIR/regions-free-region-ordering-incorrect.rs:14:12
- |
-LL | fn get<'a>(&'a self) -> &'b T {
- | ^^
-note: ...so that reference does not outlive borrowed content
- --> $DIR/regions-free-region-ordering-incorrect.rs:17:21
- |
-LL | None => &self.val
- | ^^^^^^^^^
-note: but, the lifetime must be valid for the lifetime `'b` as defined here...
- --> $DIR/regions-free-region-ordering-incorrect.rs:13:6
- |
-LL | impl<'b, T> Node<'b, T> {
- | ^^
-note: ...so that reference does not outlive borrowed content
- --> $DIR/regions-free-region-ordering-incorrect.rs:15:9
- |
-LL | / match self.next {
-LL | | Some(ref next) => next.get(),
-LL | | None => &self.val
-LL | | }
- | |_________^
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0495`.
// 'a : 'b
fn test<'a,'b>(x: &'a i32) -> &'b i32
- where 'a: 'static
+ where 'a: 'static //~ WARN unnecessary lifetime parameter `'a`
{
x
}
--- /dev/null
+warning: unnecessary lifetime parameter `'a`
+ --> $DIR/regions-free-region-outlives-static-outlives-free-region.rs:12:11
+ |
+LL | where 'a: 'static
+ | ^^
+ |
+ = help: you can use the `'static` lifetime directly, in place of `'a`
+
+warning: 1 warning emitted
+
--- /dev/null
+error[E0309]: the parameter type `T` may not live long enough
+ --> $DIR/regions-implied-bounds-projection-gap-1.rs:20:10
+ |
+LL | wf::<&'x T>();
+ | ^^^^^ ...so that the reference type `&'x T` does not outlive the data it points at
+ |
+help: consider adding an explicit lifetime bound...
+ |
+LL | fn func<'x, T:Trait1<'x> + 'x>(t: &'x T::Foo)
+ | ++++
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0309`.
error[E0309]: the parameter type `T` may not live long enough
- --> $DIR/regions-implied-bounds-projection-gap-1.rs:16:5
+ --> $DIR/regions-implied-bounds-projection-gap-1.rs:20:5
|
LL | wf::<&'x T>();
- | ^^^^^^^^^^^
+ | ^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds
|
- = help: consider adding an explicit lifetime bound `T: 'x`...
+help: consider adding an explicit lifetime bound...
+ |
+LL | fn func<'x, T:Trait1<'x> + 'x>(t: &'x T::Foo)
+ | ++++
error: aborting due to previous error
// there might be other ways for the caller of `func` to show that
// `T::Foo: 'x` holds (e.g., where-clause).
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
+
trait Trait1<'x> {
type Foo;
}
+++ /dev/null
-error[E0309]: the parameter type `T` may not live long enough
- --> $DIR/regions-implied-bounds-projection-gap-1.rs:16:10
- |
-LL | fn func<'x, T:Trait1<'x>>(t: &'x T::Foo)
- | -- help: consider adding an explicit lifetime bound...: `T: 'x +`
-LL | {
-LL | wf::<&'x T>();
- | ^^^^^ ...so that the reference type `&'x T` does not outlive the data it points at
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0309`.
--- /dev/null
+error[E0309]: the parameter type `Self` may not live long enough
+ --> $DIR/regions-infer-bound-from-trait-self.rs:50:9
+ |
+LL | check_bound(x, self)
+ | ^^^^^^^^^^^
+ |
+ = help: consider adding an explicit lifetime bound `Self: 'a`...
+ = note: ...so that the type `Self` will meet its required lifetime bounds...
+note: ...that is required by this bound
+ --> $DIR/regions-infer-bound-from-trait-self.rs:16:21
+ |
+LL | fn check_bound<'a,A:'a>(x: Inv<'a>, a: A) { }
+ | ^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0309`.
error[E0309]: the parameter type `Self` may not live long enough
- --> $DIR/regions-infer-bound-from-trait-self.rs:46:9
+ --> $DIR/regions-infer-bound-from-trait-self.rs:50:9
|
LL | check_bound(x, self)
| ^^^^^^^^^^^^^^^^^^^^
|
= help: consider adding an explicit lifetime bound `Self: 'a`...
+ = note: ...so that the type `Self` will meet its required lifetime bounds
error: aborting due to previous error
// Test that we can derive lifetime bounds on `Self` from trait
// inheritance.
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
+
trait Static : 'static { }
trait Is<'a> : 'a { }
+++ /dev/null
-error[E0309]: the parameter type `Self` may not live long enough
- --> $DIR/regions-infer-bound-from-trait-self.rs:46:9
- |
-LL | check_bound(x, self)
- | ^^^^^^^^^^^
- |
- = help: consider adding an explicit lifetime bound `Self: 'a`...
- = note: ...so that the type `Self` will meet its required lifetime bounds...
-note: ...that is required by this bound
- --> $DIR/regions-infer-bound-from-trait-self.rs:12:21
- |
-LL | fn check_bound<'a,A:'a>(x: Inv<'a>, a: A) { }
- | ^^
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0309`.
--- /dev/null
+error[E0309]: the parameter type `A` may not live long enough
+ --> $DIR/regions-infer-bound-from-trait.rs:37:5
+ |
+LL | check_bound(x, a)
+ | ^^^^^^^^^^^ ...so that the type `A` will meet its required lifetime bounds...
+ |
+note: ...that is required by this bound
+ --> $DIR/regions-infer-bound-from-trait.rs:16:21
+ |
+LL | fn check_bound<'a,A:'a>(x: Inv<'a>, a: A) { }
+ | ^^
+help: consider adding an explicit lifetime bound...
+ |
+LL | fn bar1<'a,A: 'a>(x: Inv<'a>, a: A) {
+ | ++++
+
+error[E0309]: the parameter type `A` may not live long enough
+ --> $DIR/regions-infer-bound-from-trait.rs:41:5
+ |
+LL | check_bound(x, a)
+ | ^^^^^^^^^^^ ...so that the type `A` will meet its required lifetime bounds...
+ |
+note: ...that is required by this bound
+ --> $DIR/regions-infer-bound-from-trait.rs:16:21
+ |
+LL | fn check_bound<'a,A:'a>(x: Inv<'a>, a: A) { }
+ | ^^
+help: consider adding an explicit lifetime bound...
+ |
+LL | fn bar2<'a,'b,A:Is<'b> + 'a>(x: Inv<'a>, y: Inv<'b>, a: A) {
+ | ++++
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0309`.
error[E0309]: the parameter type `A` may not live long enough
- --> $DIR/regions-infer-bound-from-trait.rs:33:5
+ --> $DIR/regions-infer-bound-from-trait.rs:37:5
|
LL | check_bound(x, a)
- | ^^^^^^^^^^^^^^^^^
+ | ^^^^^^^^^^^^^^^^^ ...so that the type `A` will meet its required lifetime bounds
+ |
+help: consider adding an explicit lifetime bound...
|
- = help: consider adding an explicit lifetime bound `A: 'a`...
+LL | fn bar1<'a,A: 'a>(x: Inv<'a>, a: A) {
+ | ++++
error[E0309]: the parameter type `A` may not live long enough
- --> $DIR/regions-infer-bound-from-trait.rs:37:5
+ --> $DIR/regions-infer-bound-from-trait.rs:41:5
|
LL | check_bound(x, a)
- | ^^^^^^^^^^^^^^^^^
+ | ^^^^^^^^^^^^^^^^^ ...so that the type `A` will meet its required lifetime bounds
+ |
+help: consider adding an explicit lifetime bound...
|
- = help: consider adding an explicit lifetime bound `A: 'a`...
+LL | fn bar2<'a,'b,A:Is<'b> + 'a>(x: Inv<'a>, y: Inv<'b>, a: A) {
+ | ++++
error: aborting due to 2 previous errors
// Test that we can derive lifetime bounds on type parameters
// from trait inheritance.
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
+
trait Static : 'static { }
trait Is<'a> : 'a { }
+++ /dev/null
-error[E0309]: the parameter type `A` may not live long enough
- --> $DIR/regions-infer-bound-from-trait.rs:33:5
- |
-LL | fn bar1<'a,A>(x: Inv<'a>, a: A) {
- | - help: consider adding an explicit lifetime bound...: `A: 'a`
-LL | check_bound(x, a)
- | ^^^^^^^^^^^ ...so that the type `A` will meet its required lifetime bounds...
- |
-note: ...that is required by this bound
- --> $DIR/regions-infer-bound-from-trait.rs:12:21
- |
-LL | fn check_bound<'a,A:'a>(x: Inv<'a>, a: A) { }
- | ^^
-
-error[E0309]: the parameter type `A` may not live long enough
- --> $DIR/regions-infer-bound-from-trait.rs:37:5
- |
-LL | fn bar2<'a,'b,A:Is<'b>>(x: Inv<'a>, y: Inv<'b>, a: A) {
- | -- help: consider adding an explicit lifetime bound...: `A: 'a +`
-LL | check_bound(x, a)
- | ^^^^^^^^^^^ ...so that the type `A` will meet its required lifetime bounds...
- |
-note: ...that is required by this bound
- --> $DIR/regions-infer-bound-from-trait.rs:12:21
- |
-LL | fn check_bound<'a,A:'a>(x: Inv<'a>, a: A) { }
- | ^^
-
-error: aborting due to 2 previous errors
-
-For more information about this error, try `rustc --explain E0309`.
--- /dev/null
+error[E0623]: lifetime mismatch
+ --> $DIR/regions-infer-contravariance-due-to-decl.rs:29:35
+ |
+LL | fn use_<'short,'long>(c: Contravariant<'short>,
+ | --------------------- these two types are declared with different lifetimes...
+LL | s: &'short isize,
+LL | l: &'long isize,
+ | ------------
+...
+LL | let _: Contravariant<'long> = c;
+ | ^ ...but data from `c` flows into `l` here
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0623`.
error: lifetime may not live long enough
- --> $DIR/regions-infer-contravariance-due-to-decl.rs:25:12
+ --> $DIR/regions-infer-contravariance-due-to-decl.rs:29:12
|
LL | fn use_<'short,'long>(c: Contravariant<'short>,
| ------ ----- lifetime `'long` defined here
// Note: see variance-regions-*.rs for the tests that check that the
// variance inference works in the first place.
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
+
use std::marker;
// This is contravariant with respect to 'a, meaning that
// 'short <= 'long, this would be true if the Contravariant type were
// covariant with respect to its parameter 'a.
- let _: Contravariant<'long> = c; //~ ERROR E0623
+ let _: Contravariant<'long> = c;
+ //[base]~^ ERROR E0623
+ //[nll]~^^ ERROR lifetime may not live long enough
}
fn main() {}
+++ /dev/null
-error[E0623]: lifetime mismatch
- --> $DIR/regions-infer-contravariance-due-to-decl.rs:25:35
- |
-LL | fn use_<'short,'long>(c: Contravariant<'short>,
- | --------------------- these two types are declared with different lifetimes...
-LL | s: &'short isize,
-LL | l: &'long isize,
- | ------------
-...
-LL | let _: Contravariant<'long> = c;
- | ^ ...but data from `c` flows into `l` here
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0623`.
--- /dev/null
+error[E0623]: lifetime mismatch
+ --> $DIR/regions-infer-covariance-due-to-decl.rs:26:32
+ |
+LL | fn use_<'short,'long>(c: Covariant<'long>,
+ | ----------------
+LL | s: &'short isize,
+ | ------------- these two types are declared with different lifetimes...
+...
+LL | let _: Covariant<'short> = c;
+ | ^ ...but data from `s` flows into `c` here
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0623`.
error: lifetime may not live long enough
- --> $DIR/regions-infer-covariance-due-to-decl.rs:22:12
+ --> $DIR/regions-infer-covariance-due-to-decl.rs:26:12
|
LL | fn use_<'short,'long>(c: Covariant<'long>,
| ------ ----- lifetime `'long` defined here
// Note: see variance-regions-*.rs for the tests that check that the
// variance inference works in the first place.
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
+
use std::marker;
struct Covariant<'a> {
// 'short <= 'long, this would be true if the Covariant type were
// contravariant with respect to its parameter 'a.
- let _: Covariant<'short> = c; //~ ERROR E0623
+ let _: Covariant<'short> = c;
+ //[base]~^ ERROR E0623
+ //[nll]~^^ ERROR lifetime may not live long enough
}
fn main() {}
+++ /dev/null
-error[E0623]: lifetime mismatch
- --> $DIR/regions-infer-covariance-due-to-decl.rs:22:32
- |
-LL | fn use_<'short,'long>(c: Covariant<'long>,
- | ----------------
-LL | s: &'short isize,
- | ------------- these two types are declared with different lifetimes...
-...
-LL | let _: Covariant<'short> = c;
- | ^ ...but data from `s` flows into `c` here
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0623`.
--- /dev/null
+error[E0308]: mismatched types
+ --> $DIR/regions-infer-invariance-due-to-decl.rs:16:5
+ |
+LL | b_isize
+ | ^^^^^^^ lifetime mismatch
+ |
+ = note: expected struct `Invariant<'static>`
+ found struct `Invariant<'r>`
+note: the lifetime `'r` as defined here...
+ --> $DIR/regions-infer-invariance-due-to-decl.rs:15:23
+ |
+LL | fn to_longer_lifetime<'r>(b_isize: Invariant<'r>) -> Invariant<'static> {
+ | ^^
+ = note: ...does not necessarily outlive the static lifetime
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0308`.
error: lifetime may not live long enough
- --> $DIR/regions-infer-invariance-due-to-decl.rs:12:5
+ --> $DIR/regions-infer-invariance-due-to-decl.rs:16:5
|
LL | fn to_longer_lifetime<'r>(b_isize: Invariant<'r>) -> Invariant<'static> {
| -- lifetime `'r` defined here
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
+
use std::marker;
struct Invariant<'a> {
}
fn to_longer_lifetime<'r>(b_isize: Invariant<'r>) -> Invariant<'static> {
- b_isize //~ ERROR mismatched types
+ b_isize
+ //[base]~^ ERROR mismatched types
+ //[nll]~^^ ERROR lifetime may not live long enough
}
fn main() {
+++ /dev/null
-error[E0308]: mismatched types
- --> $DIR/regions-infer-invariance-due-to-decl.rs:12:5
- |
-LL | b_isize
- | ^^^^^^^ lifetime mismatch
- |
- = note: expected struct `Invariant<'static>`
- found struct `Invariant<'r>`
-note: the lifetime `'r` as defined here...
- --> $DIR/regions-infer-invariance-due-to-decl.rs:11:23
- |
-LL | fn to_longer_lifetime<'r>(b_isize: Invariant<'r>) -> Invariant<'static> {
- | ^^
- = note: ...does not necessarily outlive the static lifetime
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0308`.
--- /dev/null
+error[E0308]: mismatched types
+ --> $DIR/regions-infer-invariance-due-to-mutability-3.rs:14:5
+ |
+LL | b_isize
+ | ^^^^^^^ lifetime mismatch
+ |
+ = note: expected struct `Invariant<'static>`
+ found struct `Invariant<'r>`
+note: the lifetime `'r` as defined here...
+ --> $DIR/regions-infer-invariance-due-to-mutability-3.rs:13:23
+ |
+LL | fn to_longer_lifetime<'r>(b_isize: Invariant<'r>) -> Invariant<'static> {
+ | ^^
+ = note: ...does not necessarily outlive the static lifetime
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0308`.
error: lifetime may not live long enough
- --> $DIR/regions-infer-invariance-due-to-mutability-3.rs:10:5
+ --> $DIR/regions-infer-invariance-due-to-mutability-3.rs:14:5
|
LL | fn to_longer_lifetime<'r>(b_isize: Invariant<'r>) -> Invariant<'static> {
| -- lifetime `'r` defined here
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
+
struct Invariant<'a> {
f: Box<dyn FnOnce(&mut &'a isize) + 'static>,
}
}
fn to_longer_lifetime<'r>(b_isize: Invariant<'r>) -> Invariant<'static> {
- b_isize //~ ERROR mismatched types
+ b_isize
+ //[base]~^ ERROR mismatched types
+ //[nll]~^^ ERROR lifetime may not live long enough
}
fn main() {
+++ /dev/null
-error[E0308]: mismatched types
- --> $DIR/regions-infer-invariance-due-to-mutability-3.rs:10:5
- |
-LL | b_isize
- | ^^^^^^^ lifetime mismatch
- |
- = note: expected struct `Invariant<'static>`
- found struct `Invariant<'r>`
-note: the lifetime `'r` as defined here...
- --> $DIR/regions-infer-invariance-due-to-mutability-3.rs:9:23
- |
-LL | fn to_longer_lifetime<'r>(b_isize: Invariant<'r>) -> Invariant<'static> {
- | ^^
- = note: ...does not necessarily outlive the static lifetime
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0308`.
--- /dev/null
+error[E0308]: mismatched types
+ --> $DIR/regions-infer-invariance-due-to-mutability-4.rs:14:5
+ |
+LL | b_isize
+ | ^^^^^^^ lifetime mismatch
+ |
+ = note: expected struct `Invariant<'static>`
+ found struct `Invariant<'r>`
+note: the lifetime `'r` as defined here...
+ --> $DIR/regions-infer-invariance-due-to-mutability-4.rs:13:23
+ |
+LL | fn to_longer_lifetime<'r>(b_isize: Invariant<'r>) -> Invariant<'static> {
+ | ^^
+ = note: ...does not necessarily outlive the static lifetime
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0308`.
error: lifetime may not live long enough
- --> $DIR/regions-infer-invariance-due-to-mutability-4.rs:10:5
+ --> $DIR/regions-infer-invariance-due-to-mutability-4.rs:14:5
|
LL | fn to_longer_lifetime<'r>(b_isize: Invariant<'r>) -> Invariant<'static> {
| -- lifetime `'r` defined here
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
+
struct Invariant<'a> {
f: Box<dyn FnOnce() -> *mut &'a isize + 'static>,
}
}
fn to_longer_lifetime<'r>(b_isize: Invariant<'r>) -> Invariant<'static> {
- b_isize //~ ERROR mismatched types
+ b_isize
+ //[base]~^ ERROR mismatched types
+ //[nll]~^^ ERROR lifetime may not live long enough
}
fn main() {
+++ /dev/null
-error[E0308]: mismatched types
- --> $DIR/regions-infer-invariance-due-to-mutability-4.rs:10:5
- |
-LL | b_isize
- | ^^^^^^^ lifetime mismatch
- |
- = note: expected struct `Invariant<'static>`
- found struct `Invariant<'r>`
-note: the lifetime `'r` as defined here...
- --> $DIR/regions-infer-invariance-due-to-mutability-4.rs:9:23
- |
-LL | fn to_longer_lifetime<'r>(b_isize: Invariant<'r>) -> Invariant<'static> {
- | ^^
- = note: ...does not necessarily outlive the static lifetime
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0308`.
--- /dev/null
+error[E0308]: mismatched types
+ --> $DIR/regions-infer-not-param.rs:19:54
+ |
+LL | fn take_direct<'a,'b>(p: Direct<'a>) -> Direct<'b> { p }
+ | ^ lifetime mismatch
+ |
+ = note: expected struct `Direct<'b>`
+ found struct `Direct<'a>`
+note: the lifetime `'a` as defined here...
+ --> $DIR/regions-infer-not-param.rs:19:16
+ |
+LL | fn take_direct<'a,'b>(p: Direct<'a>) -> Direct<'b> { p }
+ | ^^
+note: ...does not necessarily outlive the lifetime `'b` as defined here
+ --> $DIR/regions-infer-not-param.rs:19:19
+ |
+LL | fn take_direct<'a,'b>(p: Direct<'a>) -> Direct<'b> { p }
+ | ^^
+
+error[E0308]: mismatched types
+ --> $DIR/regions-infer-not-param.rs:25:63
+ |
+LL | fn take_indirect2<'a,'b>(p: Indirect2<'a>) -> Indirect2<'b> { p }
+ | ^ lifetime mismatch
+ |
+ = note: expected struct `Indirect2<'b>`
+ found struct `Indirect2<'a>`
+note: the lifetime `'a` as defined here...
+ --> $DIR/regions-infer-not-param.rs:25:19
+ |
+LL | fn take_indirect2<'a,'b>(p: Indirect2<'a>) -> Indirect2<'b> { p }
+ | ^^
+note: ...does not necessarily outlive the lifetime `'b` as defined here
+ --> $DIR/regions-infer-not-param.rs:25:22
+ |
+LL | fn take_indirect2<'a,'b>(p: Indirect2<'a>) -> Indirect2<'b> { p }
+ | ^^
+
+error[E0308]: mismatched types
+ --> $DIR/regions-infer-not-param.rs:25:63
+ |
+LL | fn take_indirect2<'a,'b>(p: Indirect2<'a>) -> Indirect2<'b> { p }
+ | ^ lifetime mismatch
+ |
+ = note: expected struct `Indirect2<'b>`
+ found struct `Indirect2<'a>`
+note: the lifetime `'b` as defined here...
+ --> $DIR/regions-infer-not-param.rs:25:22
+ |
+LL | fn take_indirect2<'a,'b>(p: Indirect2<'a>) -> Indirect2<'b> { p }
+ | ^^
+note: ...does not necessarily outlive the lifetime `'a` as defined here
+ --> $DIR/regions-infer-not-param.rs:25:19
+ |
+LL | fn take_indirect2<'a,'b>(p: Indirect2<'a>) -> Indirect2<'b> { p }
+ | ^^
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
error: lifetime may not live long enough
- --> $DIR/regions-infer-not-param.rs:15:54
+ --> $DIR/regions-infer-not-param.rs:19:54
|
LL | fn take_direct<'a,'b>(p: Direct<'a>) -> Direct<'b> { p }
| -- -- lifetime `'b` defined here ^ function was supposed to return data with lifetime `'b` but it is returning data with lifetime `'a`
= help: consider adding the following bound: `'a: 'b`
error: lifetime may not live long enough
- --> $DIR/regions-infer-not-param.rs:19:63
+ --> $DIR/regions-infer-not-param.rs:25:63
|
LL | fn take_indirect2<'a,'b>(p: Indirect2<'a>) -> Indirect2<'b> { p }
| -- -- lifetime `'b` defined here ^ function was supposed to return data with lifetime `'a` but it is returning data with lifetime `'b`
= help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
error: lifetime may not live long enough
- --> $DIR/regions-infer-not-param.rs:19:63
+ --> $DIR/regions-infer-not-param.rs:25:63
|
LL | fn take_indirect2<'a,'b>(p: Indirect2<'a>) -> Indirect2<'b> { p }
| -- -- lifetime `'b` defined here ^ function was supposed to return data with lifetime `'b` but it is returning data with lifetime `'a`
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
+
struct Direct<'a> {
f: &'a isize
}
g: Box<dyn FnOnce(Direct<'a>) + 'static>
}
-fn take_direct<'a,'b>(p: Direct<'a>) -> Direct<'b> { p } //~ ERROR mismatched types
+fn take_direct<'a,'b>(p: Direct<'a>) -> Direct<'b> { p }
+//[base]~^ ERROR mismatched types
+//[nll]~^^ ERROR lifetime may not live long enough
fn take_indirect1(p: Indirect1) -> Indirect1 { p }
-fn take_indirect2<'a,'b>(p: Indirect2<'a>) -> Indirect2<'b> { p } //~ ERROR mismatched types
-//~| expected struct `Indirect2<'b>`
-//~| found struct `Indirect2<'a>`
-//~| ERROR mismatched types
-//~| expected struct `Indirect2<'b>`
-//~| found struct `Indirect2<'a>`
+fn take_indirect2<'a,'b>(p: Indirect2<'a>) -> Indirect2<'b> { p }
+//[base]~^ ERROR mismatched types
+//[base]~| expected struct `Indirect2<'b>`
+//[base]~| found struct `Indirect2<'a>`
+//[base]~| ERROR mismatched types
+//[base]~| expected struct `Indirect2<'b>`
+//[base]~| found struct `Indirect2<'a>`
+//[nll]~^^^^^^^ ERROR lifetime may not live long enough
+//[nll]~| ERROR lifetime may not live long enough
fn main() {}
+++ /dev/null
-error[E0308]: mismatched types
- --> $DIR/regions-infer-not-param.rs:15:54
- |
-LL | fn take_direct<'a,'b>(p: Direct<'a>) -> Direct<'b> { p }
- | ^ lifetime mismatch
- |
- = note: expected struct `Direct<'b>`
- found struct `Direct<'a>`
-note: the lifetime `'a` as defined here...
- --> $DIR/regions-infer-not-param.rs:15:16
- |
-LL | fn take_direct<'a,'b>(p: Direct<'a>) -> Direct<'b> { p }
- | ^^
-note: ...does not necessarily outlive the lifetime `'b` as defined here
- --> $DIR/regions-infer-not-param.rs:15:19
- |
-LL | fn take_direct<'a,'b>(p: Direct<'a>) -> Direct<'b> { p }
- | ^^
-
-error[E0308]: mismatched types
- --> $DIR/regions-infer-not-param.rs:19:63
- |
-LL | fn take_indirect2<'a,'b>(p: Indirect2<'a>) -> Indirect2<'b> { p }
- | ^ lifetime mismatch
- |
- = note: expected struct `Indirect2<'b>`
- found struct `Indirect2<'a>`
-note: the lifetime `'a` as defined here...
- --> $DIR/regions-infer-not-param.rs:19:19
- |
-LL | fn take_indirect2<'a,'b>(p: Indirect2<'a>) -> Indirect2<'b> { p }
- | ^^
-note: ...does not necessarily outlive the lifetime `'b` as defined here
- --> $DIR/regions-infer-not-param.rs:19:22
- |
-LL | fn take_indirect2<'a,'b>(p: Indirect2<'a>) -> Indirect2<'b> { p }
- | ^^
-
-error[E0308]: mismatched types
- --> $DIR/regions-infer-not-param.rs:19:63
- |
-LL | fn take_indirect2<'a,'b>(p: Indirect2<'a>) -> Indirect2<'b> { p }
- | ^ lifetime mismatch
- |
- = note: expected struct `Indirect2<'b>`
- found struct `Indirect2<'a>`
-note: the lifetime `'b` as defined here...
- --> $DIR/regions-infer-not-param.rs:19:22
- |
-LL | fn take_indirect2<'a,'b>(p: Indirect2<'a>) -> Indirect2<'b> { p }
- | ^^
-note: ...does not necessarily outlive the lifetime `'a` as defined here
- --> $DIR/regions-infer-not-param.rs:19:19
- |
-LL | fn take_indirect2<'a,'b>(p: Indirect2<'a>) -> Indirect2<'b> { p }
- | ^^
-
-error: aborting due to 3 previous errors
-
-For more information about this error, try `rustc --explain E0308`.
--- /dev/null
+error[E0308]: mismatched types
+ --> $DIR/regions-infer-paramd-indirect.rs:26:18
+ |
+LL | self.f = b;
+ | ^ lifetime mismatch
+ |
+ = note: expected struct `Box<Box<&'a isize>>`
+ found struct `Box<Box<&isize>>`
+note: the anonymous lifetime defined here...
+ --> $DIR/regions-infer-paramd-indirect.rs:25:36
+ |
+LL | fn set_f_bad(&mut self, b: Box<B>) {
+ | ^
+note: ...does not necessarily outlive the lifetime `'a` as defined here
+ --> $DIR/regions-infer-paramd-indirect.rs:20:6
+ |
+LL | impl<'a> SetF<'a> for C<'a> {
+ | ^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0308`.
error: lifetime may not live long enough
- --> $DIR/regions-infer-paramd-indirect.rs:22:9
+ --> $DIR/regions-infer-paramd-indirect.rs:26:9
|
LL | impl<'a> SetF<'a> for C<'a> {
| -- lifetime `'a` defined here
// Check that we correctly infer that b and c must be region
// parameterized because they reference a which requires a region.
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
+
type A<'a> = &'a isize;
type B<'a> = Box<A<'a>>;
fn set_f_bad(&mut self, b: Box<B>) {
self.f = b;
- //~^ ERROR mismatched types
- //~| expected struct `Box<Box<&'a isize>>`
- //~| found struct `Box<Box<&isize>>`
- //~| lifetime mismatch
+ //[base]~^ ERROR mismatched types
+ //[base]~| expected struct `Box<Box<&'a isize>>`
+ //[base]~| found struct `Box<Box<&isize>>`
+ //[base]~| lifetime mismatch
+ //[nll]~^^^^^ ERROR lifetime may not live long enough
}
}
+++ /dev/null
-error[E0308]: mismatched types
- --> $DIR/regions-infer-paramd-indirect.rs:22:18
- |
-LL | self.f = b;
- | ^ lifetime mismatch
- |
- = note: expected struct `Box<Box<&'a isize>>`
- found struct `Box<Box<&isize>>`
-note: the anonymous lifetime defined here...
- --> $DIR/regions-infer-paramd-indirect.rs:21:36
- |
-LL | fn set_f_bad(&mut self, b: Box<B>) {
- | ^
-note: ...does not necessarily outlive the lifetime `'a` as defined here
- --> $DIR/regions-infer-paramd-indirect.rs:16:6
- |
-LL | impl<'a> SetF<'a> for C<'a> {
- | ^^
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0308`.
--- /dev/null
+error[E0623]: lifetime mismatch
+ --> $DIR/regions-lifetime-bounds-on-fns.rs:12:10
+ |
+LL | fn b<'a, 'b>(x: &mut &'a isize, y: &mut &'b isize) {
+ | --------- --------- these two types are declared with different lifetimes...
+LL | // Illegal now because there is no `'b:'a` declaration.
+LL | *x = *y;
+ | ^^ ...but data from `y` flows into `x` here
+
+error[E0623]: lifetime mismatch
+ --> $DIR/regions-lifetime-bounds-on-fns.rs:20:7
+ |
+LL | fn c<'a,'b>(x: &mut &'a isize, y: &mut &'b isize) {
+ | --------- --------- these two types are declared with different lifetimes...
+...
+LL | a(x, y);
+ | ^ ...but data from `y` flows into `x` here
+
+error[E0308]: mismatched types
+ --> $DIR/regions-lifetime-bounds-on-fns.rs:28:43
+ |
+LL | let _: fn(&mut &isize, &mut &isize) = a;
+ | ^ one type is more general than the other
+ |
+ = note: expected fn pointer `for<'r, 's, 't0, 't1> fn(&'r mut &'s isize, &'t0 mut &'t1 isize)`
+ found fn pointer `for<'r, 's> fn(&'r mut &isize, &'s mut &isize)`
+
+error: aborting due to 3 previous errors
+
+Some errors have detailed explanations: E0308, E0623.
+For more information about an error, try `rustc --explain E0308`.
error: lifetime may not live long enough
- --> $DIR/regions-lifetime-bounds-on-fns.rs:8:5
+ --> $DIR/regions-lifetime-bounds-on-fns.rs:12:5
|
LL | fn b<'a, 'b>(x: &mut &'a isize, y: &mut &'b isize) {
| -- -- lifetime `'b` defined here
= help: consider adding the following bound: `'b: 'a`
error: lifetime may not live long enough
- --> $DIR/regions-lifetime-bounds-on-fns.rs:14:5
+ --> $DIR/regions-lifetime-bounds-on-fns.rs:20:5
|
LL | fn c<'a,'b>(x: &mut &'a isize, y: &mut &'b isize) {
| -- -- lifetime `'b` defined here
= help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
error[E0308]: mismatched types
- --> $DIR/regions-lifetime-bounds-on-fns.rs:20:12
+ --> $DIR/regions-lifetime-bounds-on-fns.rs:28:12
|
LL | let _: fn(&mut &isize, &mut &isize) = a;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other
found fn pointer `for<'r, 's> fn(&'r mut &isize, &'s mut &isize)`
error[E0308]: mismatched types
- --> $DIR/regions-lifetime-bounds-on-fns.rs:20:12
+ --> $DIR/regions-lifetime-bounds-on-fns.rs:28:12
|
LL | let _: fn(&mut &isize, &mut &isize) = a;
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
+
fn a<'a, 'b:'a>(x: &mut &'a isize, y: &mut &'b isize) {
// Note: this is legal because of the `'b:'a` declaration.
*x = *y;
fn b<'a, 'b>(x: &mut &'a isize, y: &mut &'b isize) {
// Illegal now because there is no `'b:'a` declaration.
- *x = *y; //~ ERROR E0623
+ *x = *y;
+ //[base]~^ ERROR lifetime mismatch [E0623]
+ //[nll]~^^ ERROR lifetime may not live long enough
}
fn c<'a,'b>(x: &mut &'a isize, y: &mut &'b isize) {
// Here we try to call `foo` but do not know that `'a` and `'b` are
// related as required.
- a(x, y); //~ ERROR lifetime mismatch [E0623]
+ a(x, y);
+ //[base]~^ ERROR lifetime mismatch [E0623]
+ //[nll]~^^ ERROR lifetime may not live long enough
}
fn d() {
// 'a and 'b are early bound in the function `a` because they appear
// inconstraints:
- let _: fn(&mut &isize, &mut &isize) = a; //~ ERROR E0308
+ let _: fn(&mut &isize, &mut &isize) = a;
+ //~^ ERROR mismatched types [E0308]
+ //[nll]~^^ ERROR mismatched types [E0308]
}
fn e() {
+++ /dev/null
-error[E0623]: lifetime mismatch
- --> $DIR/regions-lifetime-bounds-on-fns.rs:8:10
- |
-LL | fn b<'a, 'b>(x: &mut &'a isize, y: &mut &'b isize) {
- | --------- --------- these two types are declared with different lifetimes...
-LL | // Illegal now because there is no `'b:'a` declaration.
-LL | *x = *y;
- | ^^ ...but data from `y` flows into `x` here
-
-error[E0623]: lifetime mismatch
- --> $DIR/regions-lifetime-bounds-on-fns.rs:14:7
- |
-LL | fn c<'a,'b>(x: &mut &'a isize, y: &mut &'b isize) {
- | --------- --------- these two types are declared with different lifetimes...
-...
-LL | a(x, y);
- | ^ ...but data from `y` flows into `x` here
-
-error[E0308]: mismatched types
- --> $DIR/regions-lifetime-bounds-on-fns.rs:20:43
- |
-LL | let _: fn(&mut &isize, &mut &isize) = a;
- | ^ one type is more general than the other
- |
- = note: expected fn pointer `for<'r, 's, 't0, 't1> fn(&'r mut &'s isize, &'t0 mut &'t1 isize)`
- found fn pointer `for<'r, 's> fn(&'r mut &isize, &'s mut &isize)`
-
-error: aborting due to 3 previous errors
-
-Some errors have detailed explanations: E0308, E0623.
-For more information about an error, try `rustc --explain E0308`.
--- /dev/null
+error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
+ --> $DIR/regions-nested-fns.rs:9:18
+ |
+LL | let mut ay = &y;
+ | ^^
+ |
+note: first, the lifetime cannot outlive the anonymous lifetime #1 defined here...
+ --> $DIR/regions-nested-fns.rs:13:58
+ |
+LL | ignore::<Box<dyn for<'z> FnMut(&'z isize)>>(Box::new(|z| {
+ | __________________________________________________________^
+LL | | ay = x;
+LL | | ay = &y;
+LL | |
+LL | | ay = z;
+LL | |
+LL | | }));
+ | |_____^
+note: ...so that reference does not outlive borrowed content
+ --> $DIR/regions-nested-fns.rs:17:14
+ |
+LL | ay = z;
+ | ^
+note: but, the lifetime must be valid for the anonymous lifetime #1 defined here...
+ --> $DIR/regions-nested-fns.rs:21:72
+ |
+LL | ignore::< Box<dyn for<'z> FnMut(&'z isize) -> &'z isize>>(Box::new(|z| {
+ | ________________________________________________________________________^
+LL | | if false { return x; }
+LL | |
+LL | |
+LL | | if false { return ay; }
+LL | | return z;
+LL | | }));
+ | |_____^
+note: ...so that the types are compatible
+ --> $DIR/regions-nested-fns.rs:21:76
+ |
+LL | ignore::< Box<dyn for<'z> FnMut(&'z isize) -> &'z isize>>(Box::new(|z| {
+ | ____________________________________________________________________________^
+LL | | if false { return x; }
+LL | |
+LL | |
+LL | | if false { return ay; }
+LL | | return z;
+LL | | }));
+ | |_____^
+ = note: expected `&isize`
+ found `&isize`
+
+error[E0312]: lifetime of reference outlives lifetime of borrowed content...
+ --> $DIR/regions-nested-fns.rs:22:27
+ |
+LL | if false { return x; }
+ | ^
+ |
+note: ...the reference is valid for the anonymous lifetime #1 defined here...
+ --> $DIR/regions-nested-fns.rs:21:72
+ |
+LL | ignore::< Box<dyn for<'z> FnMut(&'z isize) -> &'z isize>>(Box::new(|z| {
+ | ________________________________________________________________________^
+LL | | if false { return x; }
+LL | |
+LL | |
+LL | | if false { return ay; }
+LL | | return z;
+LL | | }));
+ | |_____^
+note: ...but the borrowed content is only valid for the lifetime `'x` as defined here
+ --> $DIR/regions-nested-fns.rs:7:11
+ |
+LL | fn nested<'x>(x: &'x isize) {
+ | ^^
+
+error: aborting due to 2 previous errors
+
+Some errors have detailed explanations: E0312, E0495.
+For more information about an error, try `rustc --explain E0312`.
error[E0521]: borrowed data escapes outside of closure
- --> $DIR/regions-nested-fns.rs:10:9
+ --> $DIR/regions-nested-fns.rs:17:9
|
LL | let mut ay = &y;
| ------ `ay` declared here, outside of the closure body
-LL |
+...
LL | ignore::<Box<dyn for<'z> FnMut(&'z isize)>>(Box::new(|z| {
| - `z` is a reference that is only valid in the closure body
...
| ^^^^^^ `z` escapes the closure body here
error[E0597]: `y` does not live long enough
- --> $DIR/regions-nested-fns.rs:5:18
+ --> $DIR/regions-nested-fns.rs:9:18
|
LL | let mut ay = &y;
| ^^ borrowed value does not live long enough
| - `y` dropped here while still borrowed
error[E0597]: `y` does not live long enough
- --> $DIR/regions-nested-fns.rs:9:15
+ --> $DIR/regions-nested-fns.rs:15:15
|
LL | ignore::<Box<dyn for<'z> FnMut(&'z isize)>>(Box::new(|z| {
| --- value captured here
| - `y` dropped here while still borrowed
error: lifetime may not live long enough
- --> $DIR/regions-nested-fns.rs:14:27
+ --> $DIR/regions-nested-fns.rs:22:27
|
LL | fn nested<'x>(x: &'x isize) {
| -- lifetime `'x` defined here
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
+
fn ignore<T>(t: T) {}
fn nested<'x>(x: &'x isize) {
let y = 3;
- let mut ay = &y; //~ ERROR E0495
+ let mut ay = &y;
+ //[base]~^ ERROR E0495
+ //[nll]~^^ ERROR `y` does not live long enough [E0597]
ignore::<Box<dyn for<'z> FnMut(&'z isize)>>(Box::new(|z| {
ay = x;
ay = &y;
+ //[nll]~^ ERROR `y` does not live long enough
ay = z;
+ //[nll]~^ ERROR borrowed data escapes outside of closure [E0521]
}));
ignore::< Box<dyn for<'z> FnMut(&'z isize) -> &'z isize>>(Box::new(|z| {
- if false { return x; } //~ ERROR E0312
+ if false { return x; }
+ //[base]~^ ERROR E0312
+ //[nll]~^^ ERROR lifetime may not live long enough
if false { return ay; }
return z;
}));
+++ /dev/null
-error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
- --> $DIR/regions-nested-fns.rs:5:18
- |
-LL | let mut ay = &y;
- | ^^
- |
-note: first, the lifetime cannot outlive the anonymous lifetime #1 defined here...
- --> $DIR/regions-nested-fns.rs:7:58
- |
-LL | ignore::<Box<dyn for<'z> FnMut(&'z isize)>>(Box::new(|z| {
- | __________________________________________________________^
-LL | | ay = x;
-LL | | ay = &y;
-LL | | ay = z;
-LL | | }));
- | |_____^
-note: ...so that reference does not outlive borrowed content
- --> $DIR/regions-nested-fns.rs:10:14
- |
-LL | ay = z;
- | ^
-note: but, the lifetime must be valid for the anonymous lifetime #1 defined here...
- --> $DIR/regions-nested-fns.rs:13:72
- |
-LL | ignore::< Box<dyn for<'z> FnMut(&'z isize) -> &'z isize>>(Box::new(|z| {
- | ________________________________________________________________________^
-LL | | if false { return x; }
-LL | | if false { return ay; }
-LL | | return z;
-LL | | }));
- | |_____^
-note: ...so that the types are compatible
- --> $DIR/regions-nested-fns.rs:13:76
- |
-LL | ignore::< Box<dyn for<'z> FnMut(&'z isize) -> &'z isize>>(Box::new(|z| {
- | ____________________________________________________________________________^
-LL | | if false { return x; }
-LL | | if false { return ay; }
-LL | | return z;
-LL | | }));
- | |_____^
- = note: expected `&isize`
- found `&isize`
-
-error[E0312]: lifetime of reference outlives lifetime of borrowed content...
- --> $DIR/regions-nested-fns.rs:14:27
- |
-LL | if false { return x; }
- | ^
- |
-note: ...the reference is valid for the anonymous lifetime #1 defined here...
- --> $DIR/regions-nested-fns.rs:13:72
- |
-LL | ignore::< Box<dyn for<'z> FnMut(&'z isize) -> &'z isize>>(Box::new(|z| {
- | ________________________________________________________________________^
-LL | | if false { return x; }
-LL | | if false { return ay; }
-LL | | return z;
-LL | | }));
- | |_____^
-note: ...but the borrowed content is only valid for the lifetime `'x` as defined here
- --> $DIR/regions-nested-fns.rs:3:11
- |
-LL | fn nested<'x>(x: &'x isize) {
- | ^^
-
-error: aborting due to 2 previous errors
-
-Some errors have detailed explanations: E0312, E0495.
-For more information about an error, try `rustc --explain E0312`.
--- /dev/null
+error[E0491]: in type `&'a WithAssoc<TheType<'b>>`, reference has a longer lifetime than the data it references
+ --> $DIR/regions-outlives-projection-container.rs:40:13
+ |
+LL | let _x: &'a WithAssoc<TheType<'b>> = loop { };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+note: the pointer is valid for the lifetime `'a` as defined here
+ --> $DIR/regions-outlives-projection-container.rs:32:15
+ |
+LL | fn with_assoc<'a,'b>() {
+ | ^^
+note: but the referenced data is only valid for the lifetime `'b` as defined here
+ --> $DIR/regions-outlives-projection-container.rs:32:18
+ |
+LL | fn with_assoc<'a,'b>() {
+ | ^^
+
+error[E0491]: in type `&'a WithoutAssoc<TheType<'b>>`, reference has a longer lifetime than the data it references
+ --> $DIR/regions-outlives-projection-container.rs:59:13
+ |
+LL | let _x: &'a WithoutAssoc<TheType<'b>> = loop { };
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+note: the pointer is valid for the lifetime `'a` as defined here
+ --> $DIR/regions-outlives-projection-container.rs:55:18
+ |
+LL | fn without_assoc<'a,'b>() {
+ | ^^
+note: but the referenced data is only valid for the lifetime `'b` as defined here
+ --> $DIR/regions-outlives-projection-container.rs:55:21
+ |
+LL | fn without_assoc<'a,'b>() {
+ | ^^
+
+error[E0491]: in type `&'a WithAssoc<TheType<'b>>`, reference has a longer lifetime than the data it references
+ --> $DIR/regions-outlives-projection-container.rs:69:12
+ |
+LL | call::<&'a WithAssoc<TheType<'b>>>();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+note: the pointer is valid for the lifetime `'a` as defined here
+ --> $DIR/regions-outlives-projection-container.rs:64:20
+ |
+LL | fn call_with_assoc<'a,'b>() {
+ | ^^
+note: but the referenced data is only valid for the lifetime `'b` as defined here
+ --> $DIR/regions-outlives-projection-container.rs:64:23
+ |
+LL | fn call_with_assoc<'a,'b>() {
+ | ^^
+
+error[E0491]: in type `&'a WithoutAssoc<TheType<'b>>`, reference has a longer lifetime than the data it references
+ --> $DIR/regions-outlives-projection-container.rs:77:12
+ |
+LL | call::<&'a WithoutAssoc<TheType<'b>>>();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+note: the pointer is valid for the lifetime `'a` as defined here
+ --> $DIR/regions-outlives-projection-container.rs:74:23
+ |
+LL | fn call_without_assoc<'a,'b>() {
+ | ^^
+note: but the referenced data is only valid for the lifetime `'b` as defined here
+ --> $DIR/regions-outlives-projection-container.rs:74:26
+ |
+LL | fn call_without_assoc<'a,'b>() {
+ | ^^
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0491`.
error: lifetime may not live long enough
- --> $DIR/regions-outlives-projection-container.rs:36:13
+ --> $DIR/regions-outlives-projection-container.rs:40:13
|
LL | fn with_assoc<'a,'b>() {
| -- -- lifetime `'b` defined here
= help: consider adding the following bound: `'b: 'a`
error: lifetime may not live long enough
- --> $DIR/regions-outlives-projection-container.rs:54:13
+ --> $DIR/regions-outlives-projection-container.rs:59:13
|
LL | fn without_assoc<'a,'b>() {
| -- -- lifetime `'b` defined here
= help: consider adding the following bound: `'b: 'a`
error: lifetime may not live long enough
- --> $DIR/regions-outlives-projection-container.rs:63:5
+ --> $DIR/regions-outlives-projection-container.rs:69:5
|
LL | fn call_with_assoc<'a,'b>() {
| -- -- lifetime `'b` defined here
= help: consider adding the following bound: `'b: 'a`
error: lifetime may not live long enough
- --> $DIR/regions-outlives-projection-container.rs:70:5
+ --> $DIR/regions-outlives-projection-container.rs:77:5
|
LL | fn call_without_assoc<'a,'b>() {
| -- -- lifetime `'b` defined here
// type of a bound that appears in the where clause on a struct must
// outlive the location in which the type appears. Issue #22246.
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
+
#![allow(dead_code)]
#![feature(rustc_attrs)]
// FIXME (#54943) NLL doesn't enforce WF condition in unreachable code if
// `_x` is changed to `_`
let _x: &'a WithAssoc<TheType<'b>> = loop { };
- //~^ ERROR reference has a longer lifetime
+ //[base]~^ ERROR reference has a longer lifetime
+ //[nll]~^^ ERROR lifetime may not live long enough
}
fn with_assoc1<'a,'b>() where 'b : 'a {
// that `'b:'a` holds because the `'b` appears in `TheType<'b>`.
let _x: &'a WithoutAssoc<TheType<'b>> = loop { };
- //~^ ERROR reference has a longer lifetime
+ //[base]~^ ERROR reference has a longer lifetime
+ //[nll]~^^ ERROR lifetime may not live long enough
}
fn call_with_assoc<'a,'b>() {
// no data.
call::<&'a WithAssoc<TheType<'b>>>();
- //~^ ERROR reference has a longer lifetime
+ //[base]~^ ERROR reference has a longer lifetime
+ //[nll]~^^ ERROR lifetime may not live long enough
}
fn call_without_assoc<'a,'b>() {
// As `without_assoc`, but in a distinct scenario.
- call::<&'a WithoutAssoc<TheType<'b>>>(); //~ ERROR reference has a longer lifetime
+ call::<&'a WithoutAssoc<TheType<'b>>>();
+ //[base]~^ ERROR reference has a longer lifetime
+ //[nll]~^^ ERROR lifetime may not live long enough
}
fn call<T>() { }
+++ /dev/null
-error[E0491]: in type `&'a WithAssoc<TheType<'b>>`, reference has a longer lifetime than the data it references
- --> $DIR/regions-outlives-projection-container.rs:36:13
- |
-LL | let _x: &'a WithAssoc<TheType<'b>> = loop { };
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^
- |
-note: the pointer is valid for the lifetime `'a` as defined here
- --> $DIR/regions-outlives-projection-container.rs:28:15
- |
-LL | fn with_assoc<'a,'b>() {
- | ^^
-note: but the referenced data is only valid for the lifetime `'b` as defined here
- --> $DIR/regions-outlives-projection-container.rs:28:18
- |
-LL | fn with_assoc<'a,'b>() {
- | ^^
-
-error[E0491]: in type `&'a WithoutAssoc<TheType<'b>>`, reference has a longer lifetime than the data it references
- --> $DIR/regions-outlives-projection-container.rs:54:13
- |
-LL | let _x: &'a WithoutAssoc<TheType<'b>> = loop { };
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- |
-note: the pointer is valid for the lifetime `'a` as defined here
- --> $DIR/regions-outlives-projection-container.rs:50:18
- |
-LL | fn without_assoc<'a,'b>() {
- | ^^
-note: but the referenced data is only valid for the lifetime `'b` as defined here
- --> $DIR/regions-outlives-projection-container.rs:50:21
- |
-LL | fn without_assoc<'a,'b>() {
- | ^^
-
-error[E0491]: in type `&'a WithAssoc<TheType<'b>>`, reference has a longer lifetime than the data it references
- --> $DIR/regions-outlives-projection-container.rs:63:12
- |
-LL | call::<&'a WithAssoc<TheType<'b>>>();
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^
- |
-note: the pointer is valid for the lifetime `'a` as defined here
- --> $DIR/regions-outlives-projection-container.rs:58:20
- |
-LL | fn call_with_assoc<'a,'b>() {
- | ^^
-note: but the referenced data is only valid for the lifetime `'b` as defined here
- --> $DIR/regions-outlives-projection-container.rs:58:23
- |
-LL | fn call_with_assoc<'a,'b>() {
- | ^^
-
-error[E0491]: in type `&'a WithoutAssoc<TheType<'b>>`, reference has a longer lifetime than the data it references
- --> $DIR/regions-outlives-projection-container.rs:70:12
- |
-LL | call::<&'a WithoutAssoc<TheType<'b>>>();
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- |
-note: the pointer is valid for the lifetime `'a` as defined here
- --> $DIR/regions-outlives-projection-container.rs:67:23
- |
-LL | fn call_without_assoc<'a,'b>() {
- | ^^
-note: but the referenced data is only valid for the lifetime `'b` as defined here
- --> $DIR/regions-outlives-projection-container.rs:67:26
- |
-LL | fn call_without_assoc<'a,'b>() {
- | ^^
-
-error: aborting due to 4 previous errors
-
-For more information about this error, try `rustc --explain E0491`.
--- /dev/null
+error[E0759]: `x` has an anonymous lifetime `'_` but it needs to satisfy a `'static` lifetime requirement
+ --> $DIR/regions-proc-bound-capture.rs:13:14
+ |
+LL | fn static_proc(x: &isize) -> Box<dyn FnMut() -> (isize) + 'static> {
+ | ------ this data with an anonymous lifetime `'_`...
+LL | // This is illegal, because the region bound on `proc` is 'static.
+LL | Box::new(move || { *x })
+ | ^^^^^^^^^^^^^^ ...is used and required to live as long as `'static` here
+ |
+note: `'static` lifetime requirement introduced by the return type
+ --> $DIR/regions-proc-bound-capture.rs:11:59
+ |
+LL | fn static_proc(x: &isize) -> Box<dyn FnMut() -> (isize) + 'static> {
+ | ^^^^^^^ `'static` requirement introduced here
+LL | // This is illegal, because the region bound on `proc` is 'static.
+LL | Box::new(move || { *x })
+ | ------------------------ because of this returned expression
+help: consider changing the trait object's explicit `'static` bound to the lifetime of argument `x`
+ |
+LL | fn static_proc(x: &isize) -> Box<dyn FnMut() -> (isize) + '_> {
+ | ~~
+help: alternatively, add an explicit `'static` bound to this reference
+ |
+LL | fn static_proc(x: &'static isize) -> Box<dyn FnMut() -> (isize) + 'static> {
+ | ~~~~~~~~~~~~~~
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0759`.
error: lifetime may not live long enough
- --> $DIR/regions-proc-bound-capture.rs:9:5
+ --> $DIR/regions-proc-bound-capture.rs:13:5
|
LL | fn static_proc(x: &isize) -> Box<dyn FnMut() -> (isize) + 'static> {
| - let's call the lifetime of this reference `'1`
LL | // This is illegal, because the region bound on `proc` is 'static.
LL | Box::new(move || { *x })
| ^^^^^^^^^^^^^^^^^^^^^^^^ returning this value requires that `'1` must outlive `'static`
+ |
+help: consider changing the trait object's explicit `'static` bound to the lifetime of argument `x`
+ |
+LL | fn static_proc(x: &isize) -> Box<dyn FnMut() -> (isize) + '_> {
+ | ~~
+help: alternatively, add an explicit `'static` bound to this reference
+ |
+LL | fn static_proc(x: &'static isize) -> Box<dyn FnMut() -> (isize) + 'static> {
+ | ~~~~~~~~~~~~~~
error: aborting due to previous error
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
+
fn borrowed_proc<'a>(x: &'a isize) -> Box<dyn FnMut()->(isize) + 'a> {
// This is legal, because the region bound on `proc`
// states that it captures `x`.
fn static_proc(x: &isize) -> Box<dyn FnMut() -> (isize) + 'static> {
// This is illegal, because the region bound on `proc` is 'static.
- Box::new(move || { *x }) //~ ERROR E0759
+ Box::new(move || { *x })
+ //[base]~^ ERROR E0759
+ //[nll]~^^ ERROR lifetime may not live long enough
}
fn main() { }
+++ /dev/null
-error[E0759]: `x` has an anonymous lifetime `'_` but it needs to satisfy a `'static` lifetime requirement
- --> $DIR/regions-proc-bound-capture.rs:9:14
- |
-LL | fn static_proc(x: &isize) -> Box<dyn FnMut() -> (isize) + 'static> {
- | ------ this data with an anonymous lifetime `'_`...
-LL | // This is illegal, because the region bound on `proc` is 'static.
-LL | Box::new(move || { *x })
- | ^^^^^^^^^^^^^^ ...is used and required to live as long as `'static` here
- |
-note: `'static` lifetime requirement introduced by the return type
- --> $DIR/regions-proc-bound-capture.rs:7:59
- |
-LL | fn static_proc(x: &isize) -> Box<dyn FnMut() -> (isize) + 'static> {
- | ^^^^^^^ `'static` requirement introduced here
-LL | // This is illegal, because the region bound on `proc` is 'static.
-LL | Box::new(move || { *x })
- | ------------------------ because of this returned expression
-help: consider changing the trait object's explicit `'static` bound to the lifetime of argument `x`
- |
-LL | fn static_proc(x: &isize) -> Box<dyn FnMut() -> (isize) + '_> {
- | ~~
-help: alternatively, add an explicit `'static` bound to this reference
- |
-LL | fn static_proc(x: &'static isize) -> Box<dyn FnMut() -> (isize) + 'static> {
- | ~~~~~~~~~~~~~~
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0759`.
--- /dev/null
+error[E0623]: lifetime mismatch
+ --> $DIR/regions-reborrow-from-shorter-mut-ref-mut-ref.rs:8:5
+ |
+LL | fn copy_borrowed_ptr<'a, 'b, 'c>(p: &'a mut &'b mut &'c mut isize) -> &'b mut isize {
+ | ----------------------------- -------------
+ | |
+ | this parameter and the return type are declared with different lifetimes...
+LL | &mut ***p
+ | ^^^^^^^^^ ...but data from `p` is returned here
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0623`.
error: lifetime may not live long enough
- --> $DIR/regions-reborrow-from-shorter-mut-ref-mut-ref.rs:4:5
+ --> $DIR/regions-reborrow-from-shorter-mut-ref-mut-ref.rs:8:5
|
LL | fn copy_borrowed_ptr<'a, 'b, 'c>(p: &'a mut &'b mut &'c mut isize) -> &'b mut isize {
| -- -- lifetime `'b` defined here
// Issue #8624. Test for reborrowing with 3 levels, not just two.
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
+
fn copy_borrowed_ptr<'a, 'b, 'c>(p: &'a mut &'b mut &'c mut isize) -> &'b mut isize {
- &mut ***p //~ ERROR lifetime mismatch [E0623]
+ &mut ***p
+ //[base]~^ ERROR lifetime mismatch [E0623]
+ //[nll]~^^ ERROR lifetime may not live long enough
}
fn main() {
+++ /dev/null
-error[E0623]: lifetime mismatch
- --> $DIR/regions-reborrow-from-shorter-mut-ref-mut-ref.rs:4:5
- |
-LL | fn copy_borrowed_ptr<'a, 'b, 'c>(p: &'a mut &'b mut &'c mut isize) -> &'b mut isize {
- | ----------------------------- -------------
- | |
- | this parameter and the return type are declared with different lifetimes...
-LL | &mut ***p
- | ^^^^^^^^^ ...but data from `p` is returned here
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0623`.
--- /dev/null
+error[E0623]: lifetime mismatch
+ --> $DIR/regions-reborrow-from-shorter-mut-ref.rs:10:5
+ |
+LL | fn copy_borrowed_ptr<'a, 'b>(p: &'a mut &'b mut isize) -> &'b mut isize {
+ | --------------------- -------------
+ | |
+ | this parameter and the return type are declared with different lifetimes...
+LL | &mut **p
+ | ^^^^^^^^ ...but data from `p` is returned here
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0623`.
error: lifetime may not live long enough
- --> $DIR/regions-reborrow-from-shorter-mut-ref.rs:6:5
+ --> $DIR/regions-reborrow-from-shorter-mut-ref.rs:10:5
|
LL | fn copy_borrowed_ptr<'a, 'b>(p: &'a mut &'b mut isize) -> &'b mut isize {
| -- -- lifetime `'b` defined here
// pointer which is backed by another `&'a mut` can only be done
// for `'a` (which must be a sublifetime of `'b`).
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
+
fn copy_borrowed_ptr<'a, 'b>(p: &'a mut &'b mut isize) -> &'b mut isize {
- &mut **p //~ ERROR lifetime mismatch [E0623]
+ &mut **p
+ //[base]~^ ERROR lifetime mismatch [E0623]
+ //[nll]~^^ ERROR lifetime may not live long enough
}
fn main() {
+++ /dev/null
-error[E0623]: lifetime mismatch
- --> $DIR/regions-reborrow-from-shorter-mut-ref.rs:6:5
- |
-LL | fn copy_borrowed_ptr<'a, 'b>(p: &'a mut &'b mut isize) -> &'b mut isize {
- | --------------------- -------------
- | |
- | this parameter and the return type are declared with different lifetimes...
-LL | &mut **p
- | ^^^^^^^^ ...but data from `p` is returned here
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0623`.
--- /dev/null
+error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
+ --> $DIR/regions-ret-borrowed-1.rs:14:14
+ |
+LL | with(|o| o)
+ | ^
+ |
+note: first, the lifetime cannot outlive the anonymous lifetime #1 defined here...
+ --> $DIR/regions-ret-borrowed-1.rs:14:10
+ |
+LL | with(|o| o)
+ | ^^^^^
+note: ...so that the types are compatible
+ --> $DIR/regions-ret-borrowed-1.rs:14:14
+ |
+LL | with(|o| o)
+ | ^
+ = note: expected `&isize`
+ found `&isize`
+note: but, the lifetime must be valid for the lifetime `'a` as defined here...
+ --> $DIR/regions-ret-borrowed-1.rs:13:14
+ |
+LL | fn return_it<'a>() -> &'a isize {
+ | ^^
+note: ...so that reference does not outlive borrowed content
+ --> $DIR/regions-ret-borrowed-1.rs:14:5
+ |
+LL | with(|o| o)
+ | ^^^^^^^^^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0495`.
error: lifetime may not live long enough
- --> $DIR/regions-ret-borrowed-1.rs:10:14
+ --> $DIR/regions-ret-borrowed-1.rs:14:14
|
LL | with(|o| o)
| -- ^ returning this value requires that `'1` must outlive `'2`
// some point regions-ret-borrowed reported an error but this file did
// not, due to special hardcoding around the anonymous region.
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
+
fn with<R, F>(f: F) -> R where F: for<'a> FnOnce(&'a isize) -> R {
f(&3)
}
fn return_it<'a>() -> &'a isize {
with(|o| o)
- //~^ ERROR cannot infer
+ //[base]~^ ERROR cannot infer an appropriate lifetime due to conflicting requirements [E0495]
+ //[nll]~^^ ERROR lifetime may not live long enough
}
fn main() {
+++ /dev/null
-error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
- --> $DIR/regions-ret-borrowed-1.rs:10:14
- |
-LL | with(|o| o)
- | ^
- |
-note: first, the lifetime cannot outlive the anonymous lifetime #1 defined here...
- --> $DIR/regions-ret-borrowed-1.rs:10:10
- |
-LL | with(|o| o)
- | ^^^^^
-note: ...so that the types are compatible
- --> $DIR/regions-ret-borrowed-1.rs:10:14
- |
-LL | with(|o| o)
- | ^
- = note: expected `&isize`
- found `&isize`
-note: but, the lifetime must be valid for the lifetime `'a` as defined here...
- --> $DIR/regions-ret-borrowed-1.rs:9:14
- |
-LL | fn return_it<'a>() -> &'a isize {
- | ^^
-note: ...so that reference does not outlive borrowed content
- --> $DIR/regions-ret-borrowed-1.rs:10:5
- |
-LL | with(|o| o)
- | ^^^^^^^^^^^
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0495`.
--- /dev/null
+error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
+ --> $DIR/regions-ret-borrowed.rs:17:14
+ |
+LL | with(|o| o)
+ | ^
+ |
+note: first, the lifetime cannot outlive the anonymous lifetime #1 defined here...
+ --> $DIR/regions-ret-borrowed.rs:17:10
+ |
+LL | with(|o| o)
+ | ^^^^^
+note: ...so that the types are compatible
+ --> $DIR/regions-ret-borrowed.rs:17:14
+ |
+LL | with(|o| o)
+ | ^
+ = note: expected `&isize`
+ found `&isize`
+note: but, the lifetime must be valid for the lifetime `'a` as defined here...
+ --> $DIR/regions-ret-borrowed.rs:16:14
+ |
+LL | fn return_it<'a>() -> &'a isize {
+ | ^^
+note: ...so that reference does not outlive borrowed content
+ --> $DIR/regions-ret-borrowed.rs:17:5
+ |
+LL | with(|o| o)
+ | ^^^^^^^^^^^
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0495`.
error: lifetime may not live long enough
- --> $DIR/regions-ret-borrowed.rs:13:14
+ --> $DIR/regions-ret-borrowed.rs:17:14
|
LL | with(|o| o)
| -- ^ returning this value requires that `'1` must outlive `'2`
// used to successfully compile because we failed to account for the
// fact that fn(x: &isize) rebound the region &.
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
+
fn with<R, F>(f: F) -> R where F: FnOnce(&isize) -> R {
f(&3)
}
fn return_it<'a>() -> &'a isize {
with(|o| o)
- //~^ ERROR cannot infer
+ //[base]~^ ERROR cannot infer an appropriate lifetime due to conflicting requirements [E0495]
+ //[nll]~^^ ERROR lifetime may not live long enough
}
fn main() {
+++ /dev/null
-error[E0495]: cannot infer an appropriate lifetime due to conflicting requirements
- --> $DIR/regions-ret-borrowed.rs:13:14
- |
-LL | with(|o| o)
- | ^
- |
-note: first, the lifetime cannot outlive the anonymous lifetime #1 defined here...
- --> $DIR/regions-ret-borrowed.rs:13:10
- |
-LL | with(|o| o)
- | ^^^^^
-note: ...so that the types are compatible
- --> $DIR/regions-ret-borrowed.rs:13:14
- |
-LL | with(|o| o)
- | ^
- = note: expected `&isize`
- found `&isize`
-note: but, the lifetime must be valid for the lifetime `'a` as defined here...
- --> $DIR/regions-ret-borrowed.rs:12:14
- |
-LL | fn return_it<'a>() -> &'a isize {
- | ^^
-note: ...so that reference does not outlive borrowed content
- --> $DIR/regions-ret-borrowed.rs:13:5
- |
-LL | with(|o| o)
- | ^^^^^^^^^^^
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0495`.
// run-pass
+
fn invariant_id<'a,'b>(t: &'b mut &'static ()) -> &'b mut &'a ()
where 'a: 'static { t }
+//~^ WARN unnecessary lifetime parameter `'a`
+
fn static_id<'a>(t: &'a ()) -> &'static ()
where 'a: 'static { t }
+//~^ WARN unnecessary lifetime parameter `'a`
+
fn static_id_indirect<'a,'b>(t: &'a ()) -> &'static ()
where 'a: 'b, 'b: 'static { t }
+//~^ WARN unnecessary lifetime parameter `'b`
+
fn ref_id<'a>(t: &'a ()) -> &'a () where 'static: 'a { t }
static UNIT: () = ();
--- /dev/null
+warning: unnecessary lifetime parameter `'a`
+ --> $DIR/regions-static-bound-rpass.rs:4:11
+ |
+LL | where 'a: 'static { t }
+ | ^^
+ |
+ = help: you can use the `'static` lifetime directly, in place of `'a`
+
+warning: unnecessary lifetime parameter `'a`
+ --> $DIR/regions-static-bound-rpass.rs:8:11
+ |
+LL | where 'a: 'static { t }
+ | ^^
+ |
+ = help: you can use the `'static` lifetime directly, in place of `'a`
+
+warning: unnecessary lifetime parameter `'b`
+ --> $DIR/regions-static-bound-rpass.rs:12:19
+ |
+LL | where 'a: 'b, 'b: 'static { t }
+ | ^^
+ |
+ = help: you can use the `'static` lifetime directly, in place of `'b`
+
+warning: 3 warnings emitted
+
--- /dev/null
+warning: unnecessary lifetime parameter `'a`
+ --> $DIR/regions-static-bound.rs:6:11
+ |
+LL | where 'a: 'static { t }
+ | ^^
+ |
+ = help: you can use the `'static` lifetime directly, in place of `'a`
+
+warning: unnecessary lifetime parameter `'b`
+ --> $DIR/regions-static-bound.rs:10:19
+ |
+LL | where 'a: 'b, 'b: 'static { t }
+ | ^^
+ |
+ = help: you can use the `'static` lifetime directly, in place of `'b`
+
+error[E0312]: lifetime of reference outlives lifetime of borrowed content...
+ --> $DIR/regions-static-bound.rs:14:5
+ |
+LL | t
+ | ^
+ |
+ = note: ...the reference is valid for the static lifetime...
+note: ...but the borrowed content is only valid for the lifetime `'a` as defined here
+ --> $DIR/regions-static-bound.rs:13:24
+ |
+LL | fn static_id_wrong_way<'a>(t: &'a ()) -> &'static () where 'static: 'a {
+ | ^^
+
+error[E0759]: `u` has an anonymous lifetime `'_` but it needs to satisfy a `'static` lifetime requirement
+ --> $DIR/regions-static-bound.rs:20:5
+ |
+LL | fn error(u: &(), v: &()) {
+ | --- this data with an anonymous lifetime `'_`...
+LL | static_id(&u);
+ | ^^^^^^^^^ -- ...is used here...
+ |
+note: ...and is required to live as long as `'static` here
+ --> $DIR/regions-static-bound.rs:20:5
+ |
+LL | static_id(&u);
+ | ^^^^^^^^^
+
+error[E0759]: `v` has an anonymous lifetime `'_` but it needs to satisfy a `'static` lifetime requirement
+ --> $DIR/regions-static-bound.rs:23:5
+ |
+LL | fn error(u: &(), v: &()) {
+ | --- this data with an anonymous lifetime `'_`...
+...
+LL | static_id_indirect(&v);
+ | ^^^^^^^^^^^^^^^^^^ -- ...is used here...
+ |
+note: ...and is required to live as long as `'static` here
+ --> $DIR/regions-static-bound.rs:23:5
+ |
+LL | static_id_indirect(&v);
+ | ^^^^^^^^^^^^^^^^^^
+
+error: aborting due to 3 previous errors; 2 warnings emitted
+
+Some errors have detailed explanations: E0312, E0759.
+For more information about an error, try `rustc --explain E0312`.
+warning: unnecessary lifetime parameter `'a`
+ --> $DIR/regions-static-bound.rs:6:11
+ |
+LL | where 'a: 'static { t }
+ | ^^
+ |
+ = help: you can use the `'static` lifetime directly, in place of `'a`
+
+warning: unnecessary lifetime parameter `'b`
+ --> $DIR/regions-static-bound.rs:10:19
+ |
+LL | where 'a: 'b, 'b: 'static { t }
+ | ^^
+ |
+ = help: you can use the `'static` lifetime directly, in place of `'b`
+
error: lifetime may not live long enough
- --> $DIR/regions-static-bound.rs:6:5
+ --> $DIR/regions-static-bound.rs:14:5
|
LL | fn static_id_wrong_way<'a>(t: &'a ()) -> &'static () where 'static: 'a {
| -- lifetime `'a` defined here
| ^ returning this value requires that `'a` must outlive `'static`
error[E0521]: borrowed data escapes outside of function
- --> $DIR/regions-static-bound.rs:10:5
+ --> $DIR/regions-static-bound.rs:20:5
|
LL | fn error(u: &(), v: &()) {
| - - let's call the lifetime of this reference `'1`
| argument requires that `'1` must outlive `'static`
error[E0521]: borrowed data escapes outside of function
- --> $DIR/regions-static-bound.rs:11:5
+ --> $DIR/regions-static-bound.rs:23:5
|
LL | fn error(u: &(), v: &()) {
| - - let's call the lifetime of this reference `'2`
| |
| `v` is a reference that is only valid in the function body
-LL | static_id(&u);
+...
LL | static_id_indirect(&v);
| ^^^^^^^^^^^^^^^^^^^^^^
| |
| `v` escapes the function body here
| argument requires that `'2` must outlive `'static`
-error: aborting due to 3 previous errors
+error: aborting due to 3 previous errors; 2 warnings emitted
For more information about this error, try `rustc --explain E0521`.
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
+
fn static_id<'a,'b>(t: &'a ()) -> &'static ()
where 'a: 'static { t }
+//~^ WARN unnecessary lifetime parameter `'a`
+
fn static_id_indirect<'a,'b>(t: &'a ()) -> &'static ()
where 'a: 'b, 'b: 'static { t }
+//~^ WARN unnecessary lifetime parameter `'b`
+
fn static_id_wrong_way<'a>(t: &'a ()) -> &'static () where 'static: 'a {
- t //~ ERROR E0312
+ t
+ //[base]~^ ERROR E0312
+ //[nll]~^^ ERROR lifetime may not live long enough
}
fn error(u: &(), v: &()) {
- static_id(&u); //~ ERROR `u` has an anonymous lifetime `'_` but it needs to satisfy a `'static` lifetime requirement [E0759]
- static_id_indirect(&v); //~ ERROR `v` has an anonymous lifetime `'_` but it needs to satisfy a `'static` lifetime requirement [E0759]
+ static_id(&u);
+ //[base]~^ ERROR `u` has an anonymous lifetime `'_` but it needs to satisfy a `'static` lifetime requirement [E0759]
+ //[nll]~^^ ERROR borrowed data escapes outside of function [E0521]
+ static_id_indirect(&v);
+ //[base]~^ ERROR `v` has an anonymous lifetime `'_` but it needs to satisfy a `'static` lifetime requirement [E0759]
+ //[nll]~^^ ERROR borrowed data escapes outside of function [E0521]
}
fn main() {}
+++ /dev/null
-error[E0312]: lifetime of reference outlives lifetime of borrowed content...
- --> $DIR/regions-static-bound.rs:6:5
- |
-LL | t
- | ^
- |
- = note: ...the reference is valid for the static lifetime...
-note: ...but the borrowed content is only valid for the lifetime `'a` as defined here
- --> $DIR/regions-static-bound.rs:5:24
- |
-LL | fn static_id_wrong_way<'a>(t: &'a ()) -> &'static () where 'static: 'a {
- | ^^
-
-error[E0759]: `u` has an anonymous lifetime `'_` but it needs to satisfy a `'static` lifetime requirement
- --> $DIR/regions-static-bound.rs:10:5
- |
-LL | fn error(u: &(), v: &()) {
- | --- this data with an anonymous lifetime `'_`...
-LL | static_id(&u);
- | ^^^^^^^^^ -- ...is used here...
- |
-note: ...and is required to live as long as `'static` here
- --> $DIR/regions-static-bound.rs:10:5
- |
-LL | static_id(&u);
- | ^^^^^^^^^
-
-error[E0759]: `v` has an anonymous lifetime `'_` but it needs to satisfy a `'static` lifetime requirement
- --> $DIR/regions-static-bound.rs:11:5
- |
-LL | fn error(u: &(), v: &()) {
- | --- this data with an anonymous lifetime `'_`...
-LL | static_id(&u);
-LL | static_id_indirect(&v);
- | ^^^^^^^^^^^^^^^^^^ -- ...is used here...
- |
-note: ...and is required to live as long as `'static` here
- --> $DIR/regions-static-bound.rs:11:5
- |
-LL | static_id_indirect(&v);
- | ^^^^^^^^^^^^^^^^^^
-
-error: aborting due to 3 previous errors
-
-Some errors have detailed explanations: E0312, E0759.
-For more information about an error, try `rustc --explain E0312`.
--- /dev/null
+error[E0478]: lifetime bound not satisfied
+ --> $DIR/regions-trait-object-subtyping.rs:19:5
+ |
+LL | x
+ | ^
+ |
+note: lifetime parameter instantiated with the lifetime `'a` as defined here
+ --> $DIR/regions-trait-object-subtyping.rs:17:9
+ |
+LL | fn foo3<'a,'b>(x: &'a mut dyn Dummy) -> &'b mut dyn Dummy {
+ | ^^
+note: but lifetime parameter must outlive the lifetime `'b` as defined here
+ --> $DIR/regions-trait-object-subtyping.rs:17:12
+ |
+LL | fn foo3<'a,'b>(x: &'a mut dyn Dummy) -> &'b mut dyn Dummy {
+ | ^^
+
+error[E0495]: cannot infer an appropriate lifetime for automatic coercion due to conflicting requirements
+ --> $DIR/regions-trait-object-subtyping.rs:19:5
+ |
+LL | x
+ | ^
+ |
+note: first, the lifetime cannot outlive the lifetime `'a` as defined here...
+ --> $DIR/regions-trait-object-subtyping.rs:17:9
+ |
+LL | fn foo3<'a,'b>(x: &'a mut dyn Dummy) -> &'b mut dyn Dummy {
+ | ^^
+note: ...so that reference does not outlive borrowed content
+ --> $DIR/regions-trait-object-subtyping.rs:19:5
+ |
+LL | x
+ | ^
+note: but, the lifetime must be valid for the lifetime `'b` as defined here...
+ --> $DIR/regions-trait-object-subtyping.rs:17:12
+ |
+LL | fn foo3<'a,'b>(x: &'a mut dyn Dummy) -> &'b mut dyn Dummy {
+ | ^^
+note: ...so that the types are compatible
+ --> $DIR/regions-trait-object-subtyping.rs:19:5
+ |
+LL | x
+ | ^
+ = note: expected `&'b mut (dyn Dummy + 'b)`
+ found `&mut (dyn Dummy + 'b)`
+
+error[E0308]: mismatched types
+ --> $DIR/regions-trait-object-subtyping.rs:28:5
+ |
+LL | x
+ | ^ lifetime mismatch
+ |
+ = note: expected struct `Wrapper<&'b mut (dyn Dummy + 'b)>`
+ found struct `Wrapper<&'a mut (dyn Dummy + 'a)>`
+note: the lifetime `'b` as defined here...
+ --> $DIR/regions-trait-object-subtyping.rs:26:15
+ |
+LL | fn foo4<'a:'b,'b>(x: Wrapper<&'a mut dyn Dummy>) -> Wrapper<&'b mut dyn Dummy> {
+ | ^^
+note: ...does not necessarily outlive the lifetime `'a` as defined here
+ --> $DIR/regions-trait-object-subtyping.rs:26:9
+ |
+LL | fn foo4<'a:'b,'b>(x: Wrapper<&'a mut dyn Dummy>) -> Wrapper<&'b mut dyn Dummy> {
+ | ^^
+
+error: aborting due to 3 previous errors
+
+Some errors have detailed explanations: E0308, E0478, E0495.
+For more information about an error, try `rustc --explain E0308`.
error: lifetime may not live long enough
- --> $DIR/regions-trait-object-subtyping.rs:15:5
+ --> $DIR/regions-trait-object-subtyping.rs:19:5
|
LL | fn foo3<'a,'b>(x: &'a mut dyn Dummy) -> &'b mut dyn Dummy {
| -- -- lifetime `'b` defined here
= help: see <https://doc.rust-lang.org/nomicon/subtyping.html> for more information about variance
error: lifetime may not live long enough
- --> $DIR/regions-trait-object-subtyping.rs:22:5
+ --> $DIR/regions-trait-object-subtyping.rs:28:5
|
LL | fn foo4<'a:'b,'b>(x: Wrapper<&'a mut dyn Dummy>) -> Wrapper<&'b mut dyn Dummy> {
| -- -- lifetime `'b` defined here
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
+
trait Dummy { fn dummy(&self); }
fn foo1<'a:'b,'b>(x: &'a mut (dyn Dummy+'a)) -> &'b mut (dyn Dummy+'b) {
fn foo3<'a,'b>(x: &'a mut dyn Dummy) -> &'b mut dyn Dummy {
// Without knowing 'a:'b, we can't coerce
- x //~ ERROR lifetime bound not satisfied
- //~^ ERROR cannot infer an appropriate lifetime
+ x
+ //[base]~^ ERROR lifetime bound not satisfied
+ //[base]~| ERROR cannot infer an appropriate lifetime
+ //[nll]~^^^ ERROR lifetime may not live long enough
}
struct Wrapper<T>(T);
fn foo4<'a:'b,'b>(x: Wrapper<&'a mut dyn Dummy>) -> Wrapper<&'b mut dyn Dummy> {
// We can't coerce because it is packed in `Wrapper`
- x //~ ERROR mismatched types
+ x
+ //[base]~^ ERROR mismatched types
+ //[nll]~^^ ERROR lifetime may not live long enough
}
fn main() {}
+++ /dev/null
-error[E0478]: lifetime bound not satisfied
- --> $DIR/regions-trait-object-subtyping.rs:15:5
- |
-LL | x
- | ^
- |
-note: lifetime parameter instantiated with the lifetime `'a` as defined here
- --> $DIR/regions-trait-object-subtyping.rs:13:9
- |
-LL | fn foo3<'a,'b>(x: &'a mut dyn Dummy) -> &'b mut dyn Dummy {
- | ^^
-note: but lifetime parameter must outlive the lifetime `'b` as defined here
- --> $DIR/regions-trait-object-subtyping.rs:13:12
- |
-LL | fn foo3<'a,'b>(x: &'a mut dyn Dummy) -> &'b mut dyn Dummy {
- | ^^
-
-error[E0495]: cannot infer an appropriate lifetime for automatic coercion due to conflicting requirements
- --> $DIR/regions-trait-object-subtyping.rs:15:5
- |
-LL | x
- | ^
- |
-note: first, the lifetime cannot outlive the lifetime `'a` as defined here...
- --> $DIR/regions-trait-object-subtyping.rs:13:9
- |
-LL | fn foo3<'a,'b>(x: &'a mut dyn Dummy) -> &'b mut dyn Dummy {
- | ^^
-note: ...so that reference does not outlive borrowed content
- --> $DIR/regions-trait-object-subtyping.rs:15:5
- |
-LL | x
- | ^
-note: but, the lifetime must be valid for the lifetime `'b` as defined here...
- --> $DIR/regions-trait-object-subtyping.rs:13:12
- |
-LL | fn foo3<'a,'b>(x: &'a mut dyn Dummy) -> &'b mut dyn Dummy {
- | ^^
-note: ...so that the types are compatible
- --> $DIR/regions-trait-object-subtyping.rs:15:5
- |
-LL | x
- | ^
- = note: expected `&'b mut (dyn Dummy + 'b)`
- found `&mut (dyn Dummy + 'b)`
-
-error[E0308]: mismatched types
- --> $DIR/regions-trait-object-subtyping.rs:22:5
- |
-LL | x
- | ^ lifetime mismatch
- |
- = note: expected struct `Wrapper<&'b mut (dyn Dummy + 'b)>`
- found struct `Wrapper<&'a mut (dyn Dummy + 'a)>`
-note: the lifetime `'b` as defined here...
- --> $DIR/regions-trait-object-subtyping.rs:20:15
- |
-LL | fn foo4<'a:'b,'b>(x: Wrapper<&'a mut dyn Dummy>) -> Wrapper<&'b mut dyn Dummy> {
- | ^^
-note: ...does not necessarily outlive the lifetime `'a` as defined here
- --> $DIR/regions-trait-object-subtyping.rs:20:9
- |
-LL | fn foo4<'a:'b,'b>(x: Wrapper<&'a mut dyn Dummy>) -> Wrapper<&'b mut dyn Dummy> {
- | ^^
-
-error: aborting due to 3 previous errors
-
-Some errors have detailed explanations: E0308, E0478, E0495.
-For more information about an error, try `rustc --explain E0308`.
--- /dev/null
+error[E0623]: lifetime mismatch
+ --> $DIR/regions-variance-contravariant-use-covariant-in-second-position.rs:29:30
+ |
+LL | fn use_<'short,'long>(c: S<'long, 'short>,
+ | ---------------- this type is declared with multiple lifetimes...
+...
+LL | let _: S<'long, 'long> = c;
+ | ^ ...but data with one lifetime flows into the other here
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0623`.
error: lifetime may not live long enough
- --> $DIR/regions-variance-contravariant-use-covariant-in-second-position.rs:25:12
+ --> $DIR/regions-variance-contravariant-use-covariant-in-second-position.rs:29:12
|
LL | fn use_<'short,'long>(c: S<'long, 'short>,
| ------ ----- lifetime `'long` defined here
// Note: see variance-regions-*.rs for the tests that check that the
// variance inference works in the first place.
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
+
// `S` is contravariant with respect to both parameters.
struct S<'a, 'b> {
f: &'a isize,
// 'short <= 'long, this would be true if the Contravariant type were
// covariant with respect to its parameter 'a.
- let _: S<'long, 'long> = c; //~ ERROR E0623
+ let _: S<'long, 'long> = c;
+ //[base]~^ ERROR E0623
+ //[nll]~^^ ERROR lifetime may not live long enough
}
fn main() {}
+++ /dev/null
-error[E0623]: lifetime mismatch
- --> $DIR/regions-variance-contravariant-use-covariant-in-second-position.rs:25:30
- |
-LL | fn use_<'short,'long>(c: S<'long, 'short>,
- | ---------------- this type is declared with multiple lifetimes...
-...
-LL | let _: S<'long, 'long> = c;
- | ^ ...but data with one lifetime flows into the other here
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0623`.
--- /dev/null
+error[E0623]: lifetime mismatch
+ --> $DIR/regions-variance-contravariant-use-covariant.rs:27:35
+ |
+LL | fn use_<'short,'long>(c: Contravariant<'short>,
+ | --------------------- these two types are declared with different lifetimes...
+LL | s: &'short isize,
+LL | l: &'long isize,
+ | ------------
+...
+LL | let _: Contravariant<'long> = c;
+ | ^ ...but data from `c` flows into `l` here
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0623`.
error: lifetime may not live long enough
- --> $DIR/regions-variance-contravariant-use-covariant.rs:23:12
+ --> $DIR/regions-variance-contravariant-use-covariant.rs:27:12
|
LL | fn use_<'short,'long>(c: Contravariant<'short>,
| ------ ----- lifetime `'long` defined here
// Note: see variance-regions-*.rs for the tests that check that the
// variance inference works in the first place.
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
+
// This is contravariant with respect to 'a, meaning that
// Contravariant<'long> <: Contravariant<'short> iff
// 'short <= 'long
// 'short <= 'long, this would be true if the Contravariant type were
// covariant with respect to its parameter 'a.
- let _: Contravariant<'long> = c; //~ ERROR E0623
+ let _: Contravariant<'long> = c;
+ //[base]~^ ERROR E0623
+ //[nll]~^^ ERROR lifetime may not live long enough
}
fn main() {}
+++ /dev/null
-error[E0623]: lifetime mismatch
- --> $DIR/regions-variance-contravariant-use-covariant.rs:23:35
- |
-LL | fn use_<'short,'long>(c: Contravariant<'short>,
- | --------------------- these two types are declared with different lifetimes...
-LL | s: &'short isize,
-LL | l: &'long isize,
- | ------------
-...
-LL | let _: Contravariant<'long> = c;
- | ^ ...but data from `c` flows into `l` here
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0623`.
--- /dev/null
+error[E0623]: lifetime mismatch
+ --> $DIR/regions-variance-covariant-use-contravariant.rs:27:32
+ |
+LL | fn use_<'short,'long>(c: Covariant<'long>,
+ | ----------------
+LL | s: &'short isize,
+ | ------------- these two types are declared with different lifetimes...
+...
+LL | let _: Covariant<'short> = c;
+ | ^ ...but data from `s` flows into `c` here
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0623`.
error: lifetime may not live long enough
- --> $DIR/regions-variance-covariant-use-contravariant.rs:23:12
+ --> $DIR/regions-variance-covariant-use-contravariant.rs:27:12
|
LL | fn use_<'short,'long>(c: Covariant<'long>,
| ------ ----- lifetime `'long` defined here
// Note: see variance-regions-*.rs for the tests that check that the
// variance inference works in the first place.
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
+
// This is covariant with respect to 'a, meaning that
// Covariant<'foo> <: Covariant<'static> because
// 'foo <= 'static
// 'short <= 'long, this would be true if the Covariant type were
// contravariant with respect to its parameter 'a.
- let _: Covariant<'short> = c; //~ ERROR E0623
+ let _: Covariant<'short> = c;
+ //[base]~^ ERROR E0623
+ //[nll]~^^ ERROR lifetime may not live long enough
}
fn main() {}
+++ /dev/null
-error[E0623]: lifetime mismatch
- --> $DIR/regions-variance-covariant-use-contravariant.rs:23:32
- |
-LL | fn use_<'short,'long>(c: Covariant<'long>,
- | ----------------
-LL | s: &'short isize,
- | ------------- these two types are declared with different lifetimes...
-...
-LL | let _: Covariant<'short> = c;
- | ^ ...but data from `s` flows into `c` here
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0623`.
--- /dev/null
+error[E0623]: lifetime mismatch
+ --> $DIR/regions-variance-invariant-use-contravariant.rs:24:32
+ |
+LL | fn use_<'short,'long>(c: Invariant<'long>,
+ | ----------------
+LL | s: &'short isize,
+ | ------------- these two types are declared with different lifetimes...
+...
+LL | let _: Invariant<'short> = c;
+ | ^ ...but data from `s` flows into `c` here
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0623`.
error: lifetime may not live long enough
- --> $DIR/regions-variance-invariant-use-contravariant.rs:20:12
+ --> $DIR/regions-variance-invariant-use-contravariant.rs:24:12
|
LL | fn use_<'short,'long>(c: Invariant<'long>,
| ------ ----- lifetime `'long` defined here
// Note: see variance-regions-*.rs for the tests that check that the
// variance inference works in the first place.
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
+
struct Invariant<'a> {
f: &'a mut &'a isize
}
// 'short <= 'long, this would be true if the Invariant type were
// contravariant with respect to its parameter 'a.
- let _: Invariant<'short> = c; //~ ERROR E0623
+ let _: Invariant<'short> = c;
+ //[base]~^ ERROR E0623
+ //[nll]~^^ ERROR lifetime may not live long enough
}
fn main() { }
+++ /dev/null
-error[E0623]: lifetime mismatch
- --> $DIR/regions-variance-invariant-use-contravariant.rs:20:32
- |
-LL | fn use_<'short,'long>(c: Invariant<'long>,
- | ----------------
-LL | s: &'short isize,
- | ------------- these two types are declared with different lifetimes...
-...
-LL | let _: Invariant<'short> = c;
- | ^ ...but data from `s` flows into `c` here
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0623`.
--- /dev/null
+error[E0308]: mismatched types
+ --> $DIR/regions-variance-invariant-use-covariant.rs:21:33
+ |
+LL | let _: Invariant<'static> = c;
+ | ^ lifetime mismatch
+ |
+ = note: expected struct `Invariant<'static>`
+ found struct `Invariant<'b>`
+note: the lifetime `'b` as defined here...
+ --> $DIR/regions-variance-invariant-use-covariant.rs:15:9
+ |
+LL | fn use_<'b>(c: Invariant<'b>) {
+ | ^^
+ = note: ...does not necessarily outlive the static lifetime
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0308`.
error: lifetime may not live long enough
- --> $DIR/regions-variance-invariant-use-covariant.rs:17:12
+ --> $DIR/regions-variance-invariant-use-covariant.rs:21:12
|
LL | fn use_<'b>(c: Invariant<'b>) {
| -- lifetime `'b` defined here
// Note: see variance-regions-*.rs for the tests that check that the
// variance inference works in the first place.
+// revisions: base nll
+// ignore-compare-mode-nll
+//[nll] compile-flags: -Z borrowck=mir
+
struct Invariant<'a> {
f: &'a mut &'a isize
}
// Since 'b <= 'static, this would be true if Invariant were covariant
// with respect to its parameter 'a.
- let _: Invariant<'static> = c; //~ ERROR mismatched types
+ let _: Invariant<'static> = c;
+ //[base]~^ ERROR mismatched types [E0308]
+ //[nll]~^^ ERROR lifetime may not live long enough
}
fn main() { }
+++ /dev/null
-error[E0308]: mismatched types
- --> $DIR/regions-variance-invariant-use-covariant.rs:17:33
- |
-LL | let _: Invariant<'static> = c;
- | ^ lifetime mismatch
- |
- = note: expected struct `Invariant<'static>`
- found struct `Invariant<'b>`
-note: the lifetime `'b` as defined here...
- --> $DIR/regions-variance-invariant-use-covariant.rs:11:9
- |
-LL | fn use_<'b>(c: Invariant<'b>) {
- | ^^
- = note: ...does not necessarily outlive the static lifetime
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0308`.
+++ /dev/null
-// run-pass
-
-static FOO: [isize; 4] = [32; 4];
-static BAR: [isize; 4] = [32, 32, 32, 32];
-
-pub fn main() {
- assert_eq!(FOO, BAR);
-}
--- /dev/null
+// check-pass
+
+#[derive(Clone, Default)]
+struct MaybeCopy<T>(T);
+
+impl Copy for MaybeCopy<u8> {}
+
+fn is_copy<T: Copy>(x: T) {
+ println!("{}", std::any::type_name::<T>());
+}
+
+fn main() {
+ is_copy(MaybeCopy::default());
+ [MaybeCopy::default(); 13];
+ // didn't work, because `Copy` was only checked in the mir
+}
--- /dev/null
+// run-pass
+
+static FOO: [isize; 4] = [32; 4];
+static BAR: [isize; 4] = [32, 32, 32, 32];
+
+pub fn main() {
+ assert_eq!(FOO, BAR);
+}
--- /dev/null
+// Tests that one can't run a destructor twice with the repeated vector
+// literal syntax.
+
+struct Foo {
+ x: isize,
+
+}
+
+impl Drop for Foo {
+ fn drop(&mut self) {
+ println!("Goodbye!");
+ }
+}
+
+fn main() {
+ let a = Foo { x: 3 };
+ let _ = [ a; 5 ];
+ //~^ ERROR the trait bound `Foo: Copy` is not satisfied [E0277]
+}
--- /dev/null
+error[E0277]: the trait bound `Foo: Copy` is not satisfied
+ --> $DIR/repeat-to-run-dtor-twice.rs:17:15
+ |
+LL | let _ = [ a; 5 ];
+ | ^ the trait `Copy` is not implemented for `Foo`
+ |
+ = note: the `Copy` trait is required because this value will be copied for each element of the array
+help: consider annotating `Foo` with `#[derive(Copy)]`
+ |
+LL | #[derive(Copy)]
+ |
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
--- /dev/null
+// Regression test for issue #3645
+
+fn main() {
+ let n = 1;
+ let a = [0; n];
+ //~^ ERROR attempt to use a non-constant value in a constant [E0435]
+ let b = [0; ()];
+ //~^ ERROR mismatched types
+ //~| expected `usize`, found `()`
+ let c = [0; true];
+ //~^ ERROR mismatched types
+ //~| expected `usize`, found `bool`
+ let d = [0; 0.5];
+ //~^ ERROR mismatched types
+ //~| expected `usize`, found floating-point number
+ let e = [0; "foo"];
+ //~^ ERROR mismatched types
+ //~| expected `usize`, found `&str`
+ let f = [0; -4_isize];
+ //~^ ERROR mismatched types
+ //~| expected `usize`, found `isize`
+ let f = [0_usize; -1_isize];
+ //~^ ERROR mismatched types
+ //~| expected `usize`, found `isize`
+ let f = [0; 4u8];
+ //~^ ERROR mismatched types
+ //~| expected `usize`, found `u8`
+ struct G {
+ g: (),
+ }
+ let g = [0; G { g: () }];
+ //~^ ERROR mismatched types
+ //~| expected `usize`, found struct `G`
+}
--- /dev/null
+error[E0435]: attempt to use a non-constant value in a constant
+ --> $DIR/repeat_count.rs:5:17
+ |
+LL | let n = 1;
+ | ----- help: consider using `const` instead of `let`: `const n`
+LL | let a = [0; n];
+ | ^ non-constant value
+
+error[E0308]: mismatched types
+ --> $DIR/repeat_count.rs:7:17
+ |
+LL | let b = [0; ()];
+ | ^^ expected `usize`, found `()`
+
+error[E0308]: mismatched types
+ --> $DIR/repeat_count.rs:10:17
+ |
+LL | let c = [0; true];
+ | ^^^^ expected `usize`, found `bool`
+
+error[E0308]: mismatched types
+ --> $DIR/repeat_count.rs:13:17
+ |
+LL | let d = [0; 0.5];
+ | ^^^ expected `usize`, found floating-point number
+
+error[E0308]: mismatched types
+ --> $DIR/repeat_count.rs:16:17
+ |
+LL | let e = [0; "foo"];
+ | ^^^^^ expected `usize`, found `&str`
+
+error[E0308]: mismatched types
+ --> $DIR/repeat_count.rs:19:17
+ |
+LL | let f = [0; -4_isize];
+ | ^^^^^^^^ expected `usize`, found `isize`
+ |
+ = note: `-4_isize` cannot fit into type `usize`
+
+error[E0308]: mismatched types
+ --> $DIR/repeat_count.rs:22:23
+ |
+LL | let f = [0_usize; -1_isize];
+ | ^^^^^^^^ expected `usize`, found `isize`
+ |
+ = note: `-1_isize` cannot fit into type `usize`
+
+error[E0308]: mismatched types
+ --> $DIR/repeat_count.rs:25:17
+ |
+LL | let f = [0; 4u8];
+ | ^^^ expected `usize`, found `u8`
+ |
+help: change the type of the numeric literal from `u8` to `usize`
+ |
+LL | let f = [0; 4usize];
+ | ~~~~~
+
+error[E0308]: mismatched types
+ --> $DIR/repeat_count.rs:31:17
+ |
+LL | let g = [0; G { g: () }];
+ | ^^^^^^^^^^^ expected `usize`, found struct `G`
+
+error: aborting due to 9 previous errors
+
+Some errors have detailed explanations: E0308, E0435.
+For more information about an error, try `rustc --explain E0308`.
+++ /dev/null
-// Tests that one can't run a destructor twice with the repeated vector
-// literal syntax.
-
-struct Foo {
- x: isize,
-
-}
-
-impl Drop for Foo {
- fn drop(&mut self) {
- println!("Goodbye!");
- }
-}
-
-fn main() {
- let a = Foo { x: 3 };
- let _ = [ a; 5 ];
- //~^ ERROR the trait bound `Foo: Copy` is not satisfied [E0277]
-}
+++ /dev/null
-error[E0277]: the trait bound `Foo: Copy` is not satisfied
- --> $DIR/repeat-to-run-dtor-twice.rs:17:13
- |
-LL | let _ = [ a; 5 ];
- | ^^^^^^^^ the trait `Copy` is not implemented for `Foo`
- |
- = note: the `Copy` trait is required because the repeated element will be copied
-help: consider annotating `Foo` with `#[derive(Copy)]`
- |
-LL | #[derive(Copy)]
- |
-
-error: aborting due to previous error
-
-For more information about this error, try `rustc --explain E0277`.
+++ /dev/null
-// Regression test for issue #3645
-
-fn main() {
- let n = 1;
- let a = [0; n];
- //~^ ERROR attempt to use a non-constant value in a constant [E0435]
- let b = [0; ()];
- //~^ ERROR mismatched types
- //~| expected `usize`, found `()`
- let c = [0; true];
- //~^ ERROR mismatched types
- //~| expected `usize`, found `bool`
- let d = [0; 0.5];
- //~^ ERROR mismatched types
- //~| expected `usize`, found floating-point number
- let e = [0; "foo"];
- //~^ ERROR mismatched types
- //~| expected `usize`, found `&str`
- let f = [0; -4_isize];
- //~^ ERROR mismatched types
- //~| expected `usize`, found `isize`
- let f = [0_usize; -1_isize];
- //~^ ERROR mismatched types
- //~| expected `usize`, found `isize`
- let f = [0; 4u8];
- //~^ ERROR mismatched types
- //~| expected `usize`, found `u8`
- struct G {
- g: (),
- }
- let g = [0; G { g: () }];
- //~^ ERROR mismatched types
- //~| expected `usize`, found struct `G`
-}
+++ /dev/null
-error[E0435]: attempt to use a non-constant value in a constant
- --> $DIR/repeat_count.rs:5:17
- |
-LL | let n = 1;
- | ----- help: consider using `const` instead of `let`: `const n`
-LL | let a = [0; n];
- | ^ non-constant value
-
-error[E0308]: mismatched types
- --> $DIR/repeat_count.rs:7:17
- |
-LL | let b = [0; ()];
- | ^^ expected `usize`, found `()`
-
-error[E0308]: mismatched types
- --> $DIR/repeat_count.rs:10:17
- |
-LL | let c = [0; true];
- | ^^^^ expected `usize`, found `bool`
-
-error[E0308]: mismatched types
- --> $DIR/repeat_count.rs:13:17
- |
-LL | let d = [0; 0.5];
- | ^^^ expected `usize`, found floating-point number
-
-error[E0308]: mismatched types
- --> $DIR/repeat_count.rs:16:17
- |
-LL | let e = [0; "foo"];
- | ^^^^^ expected `usize`, found `&str`
-
-error[E0308]: mismatched types
- --> $DIR/repeat_count.rs:19:17
- |
-LL | let f = [0; -4_isize];
- | ^^^^^^^^ expected `usize`, found `isize`
- |
- = note: `-4_isize` cannot fit into type `usize`
-
-error[E0308]: mismatched types
- --> $DIR/repeat_count.rs:22:23
- |
-LL | let f = [0_usize; -1_isize];
- | ^^^^^^^^ expected `usize`, found `isize`
- |
- = note: `-1_isize` cannot fit into type `usize`
-
-error[E0308]: mismatched types
- --> $DIR/repeat_count.rs:25:17
- |
-LL | let f = [0; 4u8];
- | ^^^ expected `usize`, found `u8`
- |
-help: change the type of the numeric literal from `u8` to `usize`
- |
-LL | let f = [0; 4usize];
- | ~~~~~
-
-error[E0308]: mismatched types
- --> $DIR/repeat_count.rs:31:17
- |
-LL | let g = [0; G { g: () }];
- | ^^^^^^^^^^^ expected `usize`, found struct `G`
-
-error: aborting due to 9 previous errors
-
-Some errors have detailed explanations: E0308, E0435.
-For more information about an error, try `rustc --explain E0308`.
--- /dev/null
+mod foo {
+ pub struct B(pub ());
+}
+
+mod baz {
+ fn foo() {
+ B(());
+ //~^ ERROR cannot find function, tuple struct or tuple variant `B` in this scope [E0425]
+ }
+}
+
+fn main() {}
--- /dev/null
+error[E0425]: cannot find function, tuple struct or tuple variant `B` in this scope
+ --> $DIR/issue-26545.rs:7:9
+ |
+LL | B(());
+ | ^ not found in this scope
+ |
+help: consider importing this tuple struct
+ |
+LL | use foo::B;
+ |
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0425`.
--- /dev/null
+enum A {
+ StructWithFields { x: () },
+ TupleWithFields(()),
+ Struct {},
+ Tuple(),
+ Unit,
+}
+
+enum B {
+ StructWithFields { x: () },
+ TupleWithFields(()),
+}
+
+enum C {
+ StructWithFields { x: () },
+ TupleWithFields(()),
+ Unit,
+}
+
+enum D {
+ TupleWithFields(()),
+ Unit,
+}
+
+fn main() {
+ // Only variants without fields are suggested (and others mentioned in a note) where an enum
+ // is used rather than a variant.
+
+ A.foo();
+ //~^ ERROR expected value, found enum `A`
+ B.foo();
+ //~^ ERROR expected value, found enum `B`
+ C.foo();
+ //~^ ERROR expected value, found enum `C`
+ D.foo();
+ //~^ ERROR expected value, found enum `D`
+
+ // Only tuple variants are suggested in calls or tuple struct pattern matching.
+
+ let x = A(3);
+ //~^ ERROR expected function, tuple struct or tuple variant, found enum `A`
+ if let A(3) = x { }
+ //~^ ERROR expected tuple struct or tuple variant, found enum `A`
+}
--- /dev/null
+error[E0423]: expected value, found enum `A`
+ --> $DIR/issue-73427.rs:29:5
+ |
+LL | A.foo();
+ | ^
+ |
+note: the enum is defined here
+ --> $DIR/issue-73427.rs:1:1
+ |
+LL | / enum A {
+LL | | StructWithFields { x: () },
+LL | | TupleWithFields(()),
+LL | | Struct {},
+LL | | Tuple(),
+LL | | Unit,
+LL | | }
+ | |_^
+help: you might have meant to use one of the following enum variants
+ |
+LL | (A::Struct {}).foo();
+ | ~~~~~~~~~~~~~~
+LL | (A::Tuple()).foo();
+ | ~~~~~~~~~~~~
+LL | A::Unit.foo();
+ | ~~~~~~~
+help: the following enum variants are available
+ |
+LL | (A::StructWithFields { /* fields */ }).foo();
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL | (A::TupleWithFields(/* fields */)).foo();
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error[E0423]: expected value, found enum `B`
+ --> $DIR/issue-73427.rs:31:5
+ |
+LL | B.foo();
+ | ^
+ |
+note: the enum is defined here
+ --> $DIR/issue-73427.rs:9:1
+ |
+LL | / enum B {
+LL | | StructWithFields { x: () },
+LL | | TupleWithFields(()),
+LL | | }
+ | |_^
+help: the following enum variants are available
+ |
+LL | (B::StructWithFields { /* fields */ }).foo();
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL | (B::TupleWithFields(/* fields */)).foo();
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error[E0423]: expected value, found enum `C`
+ --> $DIR/issue-73427.rs:33:5
+ |
+LL | C.foo();
+ | ^
+ |
+note: the enum is defined here
+ --> $DIR/issue-73427.rs:14:1
+ |
+LL | / enum C {
+LL | | StructWithFields { x: () },
+LL | | TupleWithFields(()),
+LL | | Unit,
+LL | | }
+ | |_^
+help: you might have meant to use the following enum variant
+ |
+LL | C::Unit.foo();
+ | ~~~~~~~
+help: the following enum variants are available
+ |
+LL | (C::StructWithFields { /* fields */ }).foo();
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+LL | (C::TupleWithFields(/* fields */)).foo();
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error[E0423]: expected value, found enum `D`
+ --> $DIR/issue-73427.rs:35:5
+ |
+LL | D.foo();
+ | ^
+ |
+note: the enum is defined here
+ --> $DIR/issue-73427.rs:20:1
+ |
+LL | / enum D {
+LL | | TupleWithFields(()),
+LL | | Unit,
+LL | | }
+ | |_^
+help: you might have meant to use the following enum variant
+ |
+LL | D::Unit.foo();
+ | ~~~~~~~
+help: the following enum variant is available
+ |
+LL | (D::TupleWithFields(/* fields */)).foo();
+ | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+
+error[E0423]: expected function, tuple struct or tuple variant, found enum `A`
+ --> $DIR/issue-73427.rs:40:13
+ |
+LL | let x = A(3);
+ | ^
+ |
+ = help: you might have meant to construct one of the enum's non-tuple variants
+note: the enum is defined here
+ --> $DIR/issue-73427.rs:1:1
+ |
+LL | / enum A {
+LL | | StructWithFields { x: () },
+LL | | TupleWithFields(()),
+LL | | Struct {},
+LL | | Tuple(),
+LL | | Unit,
+LL | | }
+ | |_^
+help: try to construct one of the enum's variants
+ |
+LL | let x = A::Tuple(3);
+ | ~~~~~~~~
+LL | let x = A::TupleWithFields(3);
+ | ~~~~~~~~~~~~~~~~~~
+
+error[E0532]: expected tuple struct or tuple variant, found enum `A`
+ --> $DIR/issue-73427.rs:42:12
+ |
+LL | if let A(3) = x { }
+ | ^
+ |
+ = help: you might have meant to match against one of the enum's non-tuple variants
+note: the enum is defined here
+ --> $DIR/issue-73427.rs:1:1
+ |
+LL | / enum A {
+LL | | StructWithFields { x: () },
+LL | | TupleWithFields(()),
+LL | | Struct {},
+LL | | Tuple(),
+LL | | Unit,
+LL | | }
+ | |_^
+help: try to match against one of the enum's variants
+ |
+LL | if let A::Tuple(3) = x { }
+ | ~~~~~~~~
+LL | if let A::TupleWithFields(3) = x { }
+ | ~~~~~~~~~~~~~~~~~~
+
+error: aborting due to 6 previous errors
+
+Some errors have detailed explanations: E0423, E0532.
+For more information about an error, try `rustc --explain E0423`.
enum E { A, B, c }
-mod m {
+pub mod m {
const CONST1: usize = 10;
- const Const2: usize = 20;
+ pub const Const2: usize = 20;
}
fn main() {
//~| ERROR variable `B` is bound inconsistently
//~| ERROR mismatched types
//~| ERROR variable `c` is not bound in all patterns
- //~| HELP consider making the path in the pattern qualified: `?::A`
+ //~| HELP if you meant to match on unit variant `E::A`, use the full path in the pattern
}
let z = (10, 20);
match z {
(CONST1, _) | (_, Const2) => ()
//~^ ERROR variable `CONST1` is not bound in all patterns
- //~| HELP consider making the path in the pattern qualified: `?::CONST1`
//~| ERROR variable `Const2` is not bound in all patterns
- //~| HELP consider making the path in the pattern qualified: `?::Const2`
+ //~| HELP if you meant to match on constant `m::Const2`, use the full path in the pattern
}
}
| | pattern doesn't bind `A`
| variable not in all patterns
|
-help: if you meant to match on a variant or a `const` item, consider making the path in the pattern qualified: `?::A`
- --> $DIR/resolve-inconsistent-names.rs:19:10
+help: if you meant to match on unit variant `E::A`, use the full path in the pattern
|
-LL | (A, B) | (ref B, c) | (c, A) => ()
- | ^
+LL | (E::A, B) | (ref B, c) | (c, A) => ()
+ | ~~~~
error[E0408]: variable `B` is not bound in all patterns
--> $DIR/resolve-inconsistent-names.rs:19:31
| |
| variable not in all patterns
|
-help: if you meant to match on a variant or a `const` item, consider making the path in the pattern qualified: `?::CONST1`
- --> $DIR/resolve-inconsistent-names.rs:30:10
+note: you might have meant to match on constant `m::CONST1`, which exists but is inaccessible
+ --> $DIR/resolve-inconsistent-names.rs:6:5
|
-LL | (CONST1, _) | (_, Const2) => ()
- | ^^^^^^
+LL | const CONST1: usize = 10;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ not accessible
error[E0408]: variable `Const2` is not bound in all patterns
--> $DIR/resolve-inconsistent-names.rs:30:9
| |
| pattern doesn't bind `Const2`
|
-help: if you meant to match on a variant or a `const` item, consider making the path in the pattern qualified: `?::Const2`
- --> $DIR/resolve-inconsistent-names.rs:30:27
+help: if you meant to match on constant `m::Const2`, use the full path in the pattern
|
-LL | (CONST1, _) | (_, Const2) => ()
- | ^^^^^^
+LL | (CONST1, _) | (_, m::Const2) => ()
+ | ~~~~~~~~~
error[E0308]: mismatched types
--> $DIR/resolve-inconsistent-names.rs:19:19
error[E0310]: the parameter type `U` may not live long enough
--> $DIR/dont-infer-static.rs:8:10
|
-LL | struct Foo<U> {
- | - help: consider adding an explicit lifetime bound...: `U: 'static`
LL | bar: Bar<U>
| ^^^^^^ ...so that the type `U` will meet its required lifetime bounds...
|
|
LL | struct Bar<T: 'static> {
| ^^^^^^^
+help: consider adding an explicit lifetime bound...
+ |
+LL | struct Foo<U: 'static> {
+ | +++++++++
error: aborting due to previous error
error[E0309]: the parameter type `T` may not live long enough
--> $DIR/regions-enum-not-wf.rs:17:18
|
-LL | enum Ref1<'a, T> {
- | - help: consider adding an explicit lifetime bound...: `T: 'a`
LL | Ref1Variant1(RequireOutlives<'a, T>),
| ^^^^^^^^^^^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds
+ |
+help: consider adding an explicit lifetime bound...
+ |
+LL | enum Ref1<'a, T: 'a> {
+ | ++++
error[E0309]: the parameter type `T` may not live long enough
--> $DIR/regions-enum-not-wf.rs:22:25
|
-LL | enum Ref2<'a, T> {
- | - help: consider adding an explicit lifetime bound...: `T: 'a`
-LL | Ref2Variant1,
LL | Ref2Variant2(isize, RequireOutlives<'a, T>),
| ^^^^^^^^^^^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds
+ |
+help: consider adding an explicit lifetime bound...
+ |
+LL | enum Ref2<'a, T: 'a> {
+ | ++++
error[E0309]: the parameter type `T` may not live long enough
--> $DIR/regions-enum-not-wf.rs:35:23
|
-LL | enum RefDouble<'a, 'b, T> {
- | - help: consider adding an explicit lifetime bound...: `T: 'b`
LL | RefDoubleVariant1(&'a RequireOutlives<'b, T>),
| ^^^^^^^^^^^^^^^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds
+ |
+help: consider adding an explicit lifetime bound...
+ |
+LL | enum RefDouble<'a, 'b, T: 'b> {
+ | ++++
error: aborting due to 3 previous errors
error[E0309]: the parameter type `T` may not live long enough
--> $DIR/regions-struct-not-wf.rs:13:16
|
-LL | impl<'a, T> Trait<'a, T> for usize {
- | - help: consider adding an explicit lifetime bound...: `T: 'a`
LL | type Out = &'a T;
| ^^^^^ ...so that the reference type `&'a T` does not outlive the data it points at
+ |
+help: consider adding an explicit lifetime bound...
+ |
+LL | impl<'a, T: 'a> Trait<'a, T> for usize {
+ | ++++
error[E0309]: the parameter type `T` may not live long enough
--> $DIR/regions-struct-not-wf.rs:21:16
|
-LL | impl<'a, T> Trait<'a, T> for u32 {
- | - help: consider adding an explicit lifetime bound...: `T: 'a`
LL | type Out = RefOk<'a, T>;
| ^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds...
|
|
LL | struct RefOk<'a, T:'a> {
| ^^
+help: consider adding an explicit lifetime bound...
+ |
+LL | impl<'a, T: 'a> Trait<'a, T> for u32 {
+ | ++++
error[E0491]: in type `&'a &'b T`, reference has a longer lifetime than the data it references
--> $DIR/regions-struct-not-wf.rs:25:16
pub struct Int(i32);
impl const std::ops::Add for i32 { //~ ERROR type annotations needed
- //~^ ERROR only traits defined in the current crate can be implemented for arbitrary types
+ //~^ ERROR only traits defined in the current crate can be implemented for primitive types
type Output = Self;
fn add(self, rhs: Self) -> Self {
-error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
+error[E0117]: only traits defined in the current crate can be implemented for primitive types
--> $DIR/const-and-non-const-impl.rs:5:1
|
LL | impl const std::ops::Add for i32 {
= note: can only be called if the required target features are available
error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block
- --> $DIR/safe-calls.rs:24:5
+ --> $DIR/safe-calls.rs:26:5
|
LL | avx_bmi2();
| ^^^^^^^^^^ call to function with `#[target_feature]`
= note: can only be called if the required target features are available
error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block
- --> $DIR/safe-calls.rs:25:5
+ --> $DIR/safe-calls.rs:29:5
|
LL | Quux.avx_bmi2();
| ^^^^^^^^^^^^^^^ call to function with `#[target_feature]`
= note: can only be called if the required target features are available
error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block
- --> $DIR/safe-calls.rs:30:5
+ --> $DIR/safe-calls.rs:36:5
|
LL | avx_bmi2();
| ^^^^^^^^^^ call to function with `#[target_feature]`
= note: can only be called if the required target features are available
error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block
- --> $DIR/safe-calls.rs:31:5
+ --> $DIR/safe-calls.rs:39:5
|
LL | Quux.avx_bmi2();
| ^^^^^^^^^^^^^^^ call to function with `#[target_feature]`
= note: can only be called if the required target features are available
error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block
- --> $DIR/safe-calls.rs:36:5
+ --> $DIR/safe-calls.rs:46:5
|
LL | sse2();
| ^^^^^^ call to function with `#[target_feature]`
= note: can only be called if the required target features are available
error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block
- --> $DIR/safe-calls.rs:37:5
+ --> $DIR/safe-calls.rs:49:5
|
LL | avx_bmi2();
| ^^^^^^^^^^ call to function with `#[target_feature]`
= note: can only be called if the required target features are available
error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block
- --> $DIR/safe-calls.rs:38:5
+ --> $DIR/safe-calls.rs:52:5
|
LL | Quux.avx_bmi2();
| ^^^^^^^^^^^^^^^ call to function with `#[target_feature]`
= note: can only be called if the required target features are available
error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block
- --> $DIR/safe-calls.rs:44:5
+ --> $DIR/safe-calls.rs:60:5
|
LL | sse2();
| ^^^^^^ call to function with `#[target_feature]`
= note: can only be called if the required target features are available
error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block
- --> $DIR/safe-calls.rs:47:18
+ --> $DIR/safe-calls.rs:65:18
|
LL | const name: () = sse2();
| ^^^^^^ call to function with `#[target_feature]`
}
fn foo() {
- sse2(); //~ ERROR call to function with `#[target_feature]` is unsafe
- avx_bmi2(); //~ ERROR call to function with `#[target_feature]` is unsafe
- Quux.avx_bmi2(); //~ ERROR call to function with `#[target_feature]` is unsafe
+ sse2();
+ //[mir]~^ ERROR call to function with `#[target_feature]` is unsafe
+ //[thir]~^^ ERROR call to function `sse2` with `#[target_feature]` is unsafe
+ avx_bmi2();
+ //[mir]~^ ERROR call to function with `#[target_feature]` is unsafe
+ //[thir]~^^ ERROR call to function `avx_bmi2` with `#[target_feature]` is unsafe
+ Quux.avx_bmi2();
+ //[mir]~^ ERROR call to function with `#[target_feature]` is unsafe
+ //[thir]~^^ ERROR call to function `Quux::avx_bmi2` with `#[target_feature]` is unsafe
}
#[target_feature(enable = "sse2")]
fn bar() {
- avx_bmi2(); //~ ERROR call to function with `#[target_feature]` is unsafe
- Quux.avx_bmi2(); //~ ERROR call to function with `#[target_feature]` is unsafe
+ avx_bmi2();
+ //[mir]~^ ERROR call to function with `#[target_feature]` is unsafe
+ //[thir]~^^ ERROR call to function `avx_bmi2` with `#[target_feature]` is unsafe
+ Quux.avx_bmi2();
+ //[mir]~^ ERROR call to function with `#[target_feature]` is unsafe
+ //[thir]~^^ ERROR call to function `Quux::avx_bmi2` with `#[target_feature]` is unsafe
}
#[target_feature(enable = "avx")]
fn baz() {
- sse2(); //~ ERROR call to function with `#[target_feature]` is unsafe
- avx_bmi2(); //~ ERROR call to function with `#[target_feature]` is unsafe
- Quux.avx_bmi2(); //~ ERROR call to function with `#[target_feature]` is unsafe
+ sse2();
+ //[mir]~^ ERROR call to function with `#[target_feature]` is unsafe
+ //[thir]~^^ ERROR call to function `sse2` with `#[target_feature]` is unsafe
+ avx_bmi2();
+ //[mir]~^ ERROR call to function with `#[target_feature]` is unsafe
+ //[thir]~^^ ERROR call to function `avx_bmi2` with `#[target_feature]` is unsafe
+ Quux.avx_bmi2();
+ //[mir]~^ ERROR call to function with `#[target_feature]` is unsafe
+ //[thir]~^^ ERROR call to function `Quux::avx_bmi2` with `#[target_feature]` is unsafe
}
#[target_feature(enable = "avx")]
#[target_feature(enable = "bmi2")]
fn qux() {
- sse2(); //~ ERROR call to function with `#[target_feature]` is unsafe
+ sse2();
+ //[mir]~^ ERROR call to function with `#[target_feature]` is unsafe
+ //[thir]~^^ ERROR call to function `sse2` with `#[target_feature]` is unsafe
}
-const name: () = sse2(); //~ ERROR call to function with `#[target_feature]` is unsafe
+const name: () = sse2();
+//[mir]~^ ERROR call to function with `#[target_feature]` is unsafe
+//[thir]~^^ ERROR call to function `sse2` with `#[target_feature]` is unsafe
fn main() {}
-error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block
+error[E0133]: call to function `sse2` with `#[target_feature]` is unsafe and requires unsafe function or block
--> $DIR/safe-calls.rs:23:5
|
LL | sse2();
|
= note: can only be called if the required target features are available
-error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block
- --> $DIR/safe-calls.rs:24:5
+error[E0133]: call to function `avx_bmi2` with `#[target_feature]` is unsafe and requires unsafe function or block
+ --> $DIR/safe-calls.rs:26:5
|
LL | avx_bmi2();
| ^^^^^^^^^^ call to function with `#[target_feature]`
|
= note: can only be called if the required target features are available
-error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block
- --> $DIR/safe-calls.rs:25:5
+error[E0133]: call to function `Quux::avx_bmi2` with `#[target_feature]` is unsafe and requires unsafe function or block
+ --> $DIR/safe-calls.rs:29:5
|
LL | Quux.avx_bmi2();
| ^^^^^^^^^^^^^^^ call to function with `#[target_feature]`
|
= note: can only be called if the required target features are available
-error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block
- --> $DIR/safe-calls.rs:30:5
+error[E0133]: call to function `avx_bmi2` with `#[target_feature]` is unsafe and requires unsafe function or block
+ --> $DIR/safe-calls.rs:36:5
|
LL | avx_bmi2();
| ^^^^^^^^^^ call to function with `#[target_feature]`
|
= note: can only be called if the required target features are available
-error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block
- --> $DIR/safe-calls.rs:31:5
+error[E0133]: call to function `Quux::avx_bmi2` with `#[target_feature]` is unsafe and requires unsafe function or block
+ --> $DIR/safe-calls.rs:39:5
|
LL | Quux.avx_bmi2();
| ^^^^^^^^^^^^^^^ call to function with `#[target_feature]`
|
= note: can only be called if the required target features are available
-error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block
- --> $DIR/safe-calls.rs:36:5
+error[E0133]: call to function `sse2` with `#[target_feature]` is unsafe and requires unsafe function or block
+ --> $DIR/safe-calls.rs:46:5
|
LL | sse2();
| ^^^^^^ call to function with `#[target_feature]`
|
= note: can only be called if the required target features are available
-error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block
- --> $DIR/safe-calls.rs:37:5
+error[E0133]: call to function `avx_bmi2` with `#[target_feature]` is unsafe and requires unsafe function or block
+ --> $DIR/safe-calls.rs:49:5
|
LL | avx_bmi2();
| ^^^^^^^^^^ call to function with `#[target_feature]`
|
= note: can only be called if the required target features are available
-error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block
- --> $DIR/safe-calls.rs:38:5
+error[E0133]: call to function `Quux::avx_bmi2` with `#[target_feature]` is unsafe and requires unsafe function or block
+ --> $DIR/safe-calls.rs:52:5
|
LL | Quux.avx_bmi2();
| ^^^^^^^^^^^^^^^ call to function with `#[target_feature]`
|
= note: can only be called if the required target features are available
-error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block
- --> $DIR/safe-calls.rs:44:5
+error[E0133]: call to function `sse2` with `#[target_feature]` is unsafe and requires unsafe function or block
+ --> $DIR/safe-calls.rs:60:5
|
LL | sse2();
| ^^^^^^ call to function with `#[target_feature]`
|
= note: can only be called if the required target features are available
-error[E0133]: call to function with `#[target_feature]` is unsafe and requires unsafe function or block
- --> $DIR/safe-calls.rs:47:18
+error[E0133]: call to function `sse2` with `#[target_feature]` is unsafe and requires unsafe function or block
+ --> $DIR/safe-calls.rs:65:18
|
LL | const name: () = sse2();
| ^^^^^^ call to function with `#[target_feature]`
--- /dev/null
+// check-pass
+// compile-flags: -Zsave-analysis
+
+#![feature(rustc_attrs)]
+#![allow(warnings)]
+
+#[derive(Debug)]
+struct Point {
+}
+
+struct NestedA<'a, 'b> {
+ x: &'a NestedB<'b>
+}
+
+struct NestedB<'a> {
+ x: &'a i32,
+}
+
+fn main() {
+}
| | |
| | let's call the lifetime of this reference `'1`
| let's call the lifetime of this reference `'2`
+ |
+help: consider introducing a named lifetime parameter and update trait if needed
+ |
+LL | fn a<'a>(self: Pin<&'a Foo>, f: &'a Foo) -> &Foo { f }
+ | ++++ ++ ++
error: lifetime may not live long enough
--> $DIR/arbitrary_self_types_pin_lifetime_mismatch.rs:14:69
| | |
| | let's call the lifetime of this reference `'1`
| let's call the lifetime of this reference `'2`
+ |
+help: consider introducing a named lifetime parameter and update trait if needed
+ |
+LL | fn c<'a>(self: Pin<&'a Self>, f: &'a Foo, g: &Foo) -> (Pin<&Foo>, &Foo) { (self, f) }
+ | ++++ ++ ++
error: lifetime may not live long enough
--> $DIR/arbitrary_self_types_pin_lifetime_mismatch.rs:21:58
| let's call the lifetime of this reference `'2`
LL | f
| ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1`
+ |
+help: consider introducing a named lifetime parameter and update trait if needed
+ |
+LL | fn ref_self<'a>(&'a self, f: &'a u32) -> &u32 {
+ | ++++ ++ ++
error: lifetime may not live long enough
--> $DIR/lt-ref-self.rs:23:9
| let's call the lifetime of this reference `'2`
LL | f
| ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1`
+ |
+help: consider introducing a named lifetime parameter and update trait if needed
+ |
+LL | fn ref_Self<'a>(self: &'a Self, f: &'a u32) -> &u32 {
+ | ++++ ++ ++
error: lifetime may not live long enough
--> $DIR/lt-ref-self.rs:29:9
| let's call the lifetime of this reference `'2`
LL | f
| ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1`
+ |
+help: consider introducing a named lifetime parameter and update trait if needed
+ |
+LL | fn box_ref_Self<'a>(self: Box<&'a Self>, f: &'a u32) -> &u32 {
+ | ++++ ++ ++
error: lifetime may not live long enough
--> $DIR/lt-ref-self.rs:35:9
| let's call the lifetime of this reference `'2`
LL | f
| ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1`
+ |
+help: consider introducing a named lifetime parameter and update trait if needed
+ |
+LL | fn pin_ref_Self<'a>(self: Pin<&'a Self>, f: &'a u32) -> &u32 {
+ | ++++ ++ ++
error: lifetime may not live long enough
--> $DIR/lt-ref-self.rs:41:9
| let's call the lifetime of this reference `'2`
LL | f
| ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1`
+ |
+help: consider introducing a named lifetime parameter and update trait if needed
+ |
+LL | fn box_box_ref_Self<'a>(self: Box<Box<&'a Self>>, f: &'a u32) -> &u32 {
+ | ++++ ++ ++
error: lifetime may not live long enough
--> $DIR/lt-ref-self.rs:47:9
| let's call the lifetime of this reference `'2`
LL | f
| ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1`
+ |
+help: consider introducing a named lifetime parameter and update trait if needed
+ |
+LL | fn box_pin_Self<'a>(self: Box<Pin<&'a Self>>, f: &'a u32) -> &u32 {
+ | ++++ ++ ++
error: aborting due to 6 previous errors
| let's call the lifetime of this reference `'2`
LL | f
| ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1`
+ |
+help: consider introducing a named lifetime parameter and update trait if needed
+ |
+LL | fn ref_self<'a>(&'a mut self, f: &'a u32) -> &u32 {
+ | ++++ ++ ++
error: lifetime may not live long enough
--> $DIR/ref-mut-self.rs:23:9
| let's call the lifetime of this reference `'2`
LL | f
| ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1`
+ |
+help: consider introducing a named lifetime parameter and update trait if needed
+ |
+LL | fn ref_Self<'a>(self: &'a mut Self, f: &'a u32) -> &u32 {
+ | ++++ ++ ++
error: lifetime may not live long enough
--> $DIR/ref-mut-self.rs:29:9
| let's call the lifetime of this reference `'2`
LL | f
| ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1`
+ |
+help: consider introducing a named lifetime parameter and update trait if needed
+ |
+LL | fn box_ref_Self<'a>(self: Box<&'a mut Self>, f: &'a u32) -> &u32 {
+ | ++++ ++ ++
error: lifetime may not live long enough
--> $DIR/ref-mut-self.rs:35:9
| let's call the lifetime of this reference `'2`
LL | f
| ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1`
+ |
+help: consider introducing a named lifetime parameter and update trait if needed
+ |
+LL | fn pin_ref_Self<'a>(self: Pin<&'a mut Self>, f: &'a u32) -> &u32 {
+ | ++++ ++ ++
error: lifetime may not live long enough
--> $DIR/ref-mut-self.rs:41:9
| let's call the lifetime of this reference `'2`
LL | f
| ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1`
+ |
+help: consider introducing a named lifetime parameter and update trait if needed
+ |
+LL | fn box_box_ref_Self<'a>(self: Box<Box<&'a mut Self>>, f: &'a u32) -> &u32 {
+ | ++++ ++ ++
error: lifetime may not live long enough
--> $DIR/ref-mut-self.rs:47:9
| let's call the lifetime of this reference `'2`
LL | f
| ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1`
+ |
+help: consider introducing a named lifetime parameter and update trait if needed
+ |
+LL | fn box_pin_ref_Self<'a>(self: Box<Pin<&'a mut Self>>, f: &'a u32) -> &u32 {
+ | ++++ ++ ++
error: aborting due to 6 previous errors
| let's call the lifetime of this reference `'2`
LL | f
| ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1`
+ |
+help: consider introducing a named lifetime parameter and update trait if needed
+ |
+LL | fn ref_Struct<'a>(self: &'a mut Struct, f: &'a u32) -> &u32 {
+ | ++++ ++ ++
error: lifetime may not live long enough
--> $DIR/ref-mut-struct.rs:21:9
| let's call the lifetime of this reference `'2`
LL | f
| ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1`
+ |
+help: consider introducing a named lifetime parameter and update trait if needed
+ |
+LL | fn box_ref_Struct<'a>(self: Box<&'a mut Struct>, f: &'a u32) -> &u32 {
+ | ++++ ++ ++
error: lifetime may not live long enough
--> $DIR/ref-mut-struct.rs:27:9
| let's call the lifetime of this reference `'2`
LL | f
| ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1`
+ |
+help: consider introducing a named lifetime parameter and update trait if needed
+ |
+LL | fn pin_ref_Struct<'a>(self: Pin<&'a mut Struct>, f: &'a u32) -> &u32 {
+ | ++++ ++ ++
error: lifetime may not live long enough
--> $DIR/ref-mut-struct.rs:33:9
| let's call the lifetime of this reference `'2`
LL | f
| ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1`
+ |
+help: consider introducing a named lifetime parameter and update trait if needed
+ |
+LL | fn box_box_ref_Struct<'a>(self: Box<Box<&'a mut Struct>>, f: &'a u32) -> &u32 {
+ | ++++ ++ ++
error: lifetime may not live long enough
--> $DIR/ref-mut-struct.rs:39:9
| let's call the lifetime of this reference `'2`
LL | f
| ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1`
+ |
+help: consider introducing a named lifetime parameter and update trait if needed
+ |
+LL | fn box_pin_ref_Struct<'a>(self: Box<Pin<&'a mut Struct>>, f: &'a u32) -> &u32 {
+ | ++++ ++ ++
error: aborting due to 5 previous errors
| let's call the lifetime of this reference `'2`
LL | f
| ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1`
+ |
+help: consider introducing a named lifetime parameter and update trait if needed
+ |
+LL | fn ref_self<'a>(&'a self, f: &'a u32) -> &u32 {
+ | ++++ ++ ++
error: lifetime may not live long enough
--> $DIR/ref-self.rs:33:9
| let's call the lifetime of this reference `'2`
LL | f
| ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1`
+ |
+help: consider introducing a named lifetime parameter and update trait if needed
+ |
+LL | fn ref_Self<'a>(self: &'a Self, f: &'a u32) -> &u32 {
+ | ++++ ++ ++
error: lifetime may not live long enough
--> $DIR/ref-self.rs:39:9
| let's call the lifetime of this reference `'2`
LL | f
| ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1`
+ |
+help: consider introducing a named lifetime parameter and update trait if needed
+ |
+LL | fn box_ref_Self<'a>(self: Box<&'a Self>, f: &'a u32) -> &u32 {
+ | ++++ ++ ++
error: lifetime may not live long enough
--> $DIR/ref-self.rs:45:9
| let's call the lifetime of this reference `'2`
LL | f
| ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1`
+ |
+help: consider introducing a named lifetime parameter and update trait if needed
+ |
+LL | fn pin_ref_Self<'a>(self: Pin<&'a Self>, f: &'a u32) -> &u32 {
+ | ++++ ++ ++
error: lifetime may not live long enough
--> $DIR/ref-self.rs:51:9
| let's call the lifetime of this reference `'2`
LL | f
| ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1`
+ |
+help: consider introducing a named lifetime parameter and update trait if needed
+ |
+LL | fn box_box_ref_Self<'a>(self: Box<Box<&'a Self>>, f: &'a u32) -> &u32 {
+ | ++++ ++ ++
error: lifetime may not live long enough
--> $DIR/ref-self.rs:57:9
| let's call the lifetime of this reference `'2`
LL | f
| ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1`
+ |
+help: consider introducing a named lifetime parameter and update trait if needed
+ |
+LL | fn box_pin_ref_Self<'a>(self: Box<Pin<&'a Self>>, f: &'a u32) -> &u32 {
+ | ++++ ++ ++
error: lifetime may not live long enough
--> $DIR/ref-self.rs:63:9
| let's call the lifetime of this reference `'2`
LL | f
| ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1`
+ |
+help: consider introducing a named lifetime parameter and update trait if needed
+ |
+LL | fn wrap_ref_Self_Self<'a>(self: Wrap<&'a Self, Self>, f: &'a u8) -> &u8 {
+ | ++++ ++ ++
error: aborting due to 7 previous errors
| let's call the lifetime of this reference `'2`
LL | f
| ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1`
+ |
+help: consider introducing a named lifetime parameter and update trait if needed
+ |
+LL | fn ref_Struct<'a>(self: &'a Struct, f: &'a u32) -> &u32 {
+ | ++++ ++ ++
error: lifetime may not live long enough
--> $DIR/ref-struct.rs:21:9
| let's call the lifetime of this reference `'2`
LL | f
| ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1`
+ |
+help: consider introducing a named lifetime parameter and update trait if needed
+ |
+LL | fn box_ref_Struct<'a>(self: Box<&'a Struct>, f: &'a u32) -> &u32 {
+ | ++++ ++ ++
error: lifetime may not live long enough
--> $DIR/ref-struct.rs:27:9
| let's call the lifetime of this reference `'2`
LL | f
| ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1`
+ |
+help: consider introducing a named lifetime parameter and update trait if needed
+ |
+LL | fn pin_ref_Struct<'a>(self: Pin<&'a Struct>, f: &'a u32) -> &u32 {
+ | ++++ ++ ++
error: lifetime may not live long enough
--> $DIR/ref-struct.rs:33:9
| let's call the lifetime of this reference `'2`
LL | f
| ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1`
+ |
+help: consider introducing a named lifetime parameter and update trait if needed
+ |
+LL | fn box_box_ref_Struct<'a>(self: Box<Box<&'a Struct>>, f: &'a u32) -> &u32 {
+ | ++++ ++ ++
error: lifetime may not live long enough
--> $DIR/ref-struct.rs:39:9
| let's call the lifetime of this reference `'2`
LL | f
| ^ associated function was supposed to return data with lifetime `'2` but it is returning data with lifetime `'1`
+ |
+help: consider introducing a named lifetime parameter and update trait if needed
+ |
+LL | fn box_pin_Struct<'a>(self: Box<Pin<&'a Struct>>, f: &'a u32) -> &u32 {
+ | ++++ ++ ++
error: aborting due to 5 previous errors
+error[E0408]: variable `d` is not bound in all patterns
+ --> $DIR/issue-39698.rs:10:37
+ |
+LL | T::T1(a, d) | T::T2(d, b) | T::T3(c) | T::T4(a) => { println!("{:?}", a); }
+ | - - ^^^^^^^^ ^^^^^^^^ pattern doesn't bind `d`
+ | | | |
+ | | | pattern doesn't bind `d`
+ | | variable not in all patterns
+ | variable not in all patterns
+
error[E0408]: variable `a` is not bound in all patterns
--> $DIR/issue-39698.rs:10:23
|
| | pattern doesn't bind `c`
| pattern doesn't bind `c`
-error[E0408]: variable `d` is not bound in all patterns
- --> $DIR/issue-39698.rs:10:37
- |
-LL | T::T1(a, d) | T::T2(d, b) | T::T3(c) | T::T4(a) => { println!("{:?}", a); }
- | - - ^^^^^^^^ ^^^^^^^^ pattern doesn't bind `d`
- | | | |
- | | | pattern doesn't bind `d`
- | | variable not in all patterns
- | variable not in all patterns
-
error: aborting due to 4 previous errors
For more information about this error, try `rustc --explain E0408`.
--> $DIR/static-lifetime-bound.rs:1:6
|
LL | fn f<'a: 'static>(_: &'a i32) {}
- | ^^^^^^^^^^^
+ | ^^
|
= help: you can use the `'static` lifetime directly, in place of `'a`
--- /dev/null
+// run-pass
+
+const X1: &'static [u8] = &[b'1'];
+const X2: &'static [u8] = b"1";
+const X3: &'static [u8; 1] = &[b'1'];
+const X4: &'static [u8; 1] = b"1";
+
+static Y1: u8 = X1[0];
+static Y2: u8 = X2[0];
+static Y3: u8 = X3[0];
+static Y4: u8 = X4[0];
+
+fn main() {
+ assert_eq!(Y1, Y2);
+ assert_eq!(Y1, Y3);
+ assert_eq!(Y1, Y4);
+}
--- /dev/null
+// run-pass
+
+#![allow(dead_code)]
+
+use std::mem::{size_of, align_of};
+use std::os::raw::c_int;
+
+// The two enums that follow are designed so that bugs trigger layout optimization.
+// Specifically, if either of the following reprs used here is not detected by the compiler,
+// then the sizes will be wrong.
+
+#[repr(C, u8)]
+enum E1 {
+ A(u8, u16, u8),
+ B(u8, u16, u8)
+}
+
+#[repr(u8, C)]
+enum E2 {
+ A(u8, u16, u8),
+ B(u8, u16, u8)
+}
+
+// Check that repr(int) and repr(C) are in fact different from the above
+
+#[repr(u8)]
+enum E3 {
+ A(u8, u16, u8),
+ B(u8, u16, u8)
+}
+
+#[repr(u16)]
+enum E4 {
+ A(u8, u16, u8),
+ B(u8, u16, u8)
+}
+
+#[repr(u32)]
+enum E5 {
+ A(u8, u16, u8),
+ B(u8, u16, u8)
+}
+
+#[repr(u64)]
+enum E6 {
+ A(u8, u16, u8),
+ B(u8, u16, u8)
+}
+
+#[repr(C)]
+enum E7 {
+ A(u8, u16, u8),
+ B(u8, u16, u8)
+}
+
+// From pr 37429
+
+#[repr(C,packed)]
+pub struct p0f_api_query {
+ pub magic: u32,
+ pub addr_type: u8,
+ pub addr: [u8; 16],
+}
+
+pub fn main() {
+ assert_eq!(size_of::<E1>(), 8);
+ assert_eq!(size_of::<E2>(), 8);
+ assert_eq!(size_of::<E3>(), 6);
+ assert_eq!(size_of::<E4>(), 8);
+ assert_eq!(size_of::<E5>(), align_size(10, align_of::<u32>()));
+ assert_eq!(size_of::<E6>(), align_size(14, align_of::<u64>()));
+ assert_eq!(size_of::<E7>(), align_size(6 + size_of::<c_int>(), align_of::<c_int>()));
+ assert_eq!(size_of::<p0f_api_query>(), 21);
+}
+
+fn align_size(size: usize, align: usize) -> usize {
+ if size % align != 0 {
+ size + (align - (size % align))
+ } else {
+ size
+ }
+}
+++ /dev/null
-pub fn main() {
- let _ = "foo".iter(); //~ ERROR no method named `iter` found for reference `&'static str` in the current scope
- let _ = "foo".foo(); //~ ERROR no method named `foo` found for reference `&'static str` in the current scope
- let _ = String::from("bar").iter(); //~ ERROR no method named `iter` found for struct `String` in the current scope
- let _ = (&String::from("bar")).iter(); //~ ERROR no method named `iter` found for reference `&String` in the current scope
- let _ = 0.iter(); //~ ERROR no method named `iter` found for type `{integer}` in the current scope
-}
+++ /dev/null
-error[E0599]: no method named `iter` found for reference `&'static str` in the current scope
- --> $DIR/suggest-using-chars.rs:2:19
- |
-LL | let _ = "foo".iter();
- | ^^^^ method not found in `&'static str`
- |
-help: because of the in-memory representation of `&str`, to obtain an `Iterator` over each of its codepoint use method `chars`
- |
-LL | let _ = "foo".chars();
- | ~~~~~
-
-error[E0599]: no method named `foo` found for reference `&'static str` in the current scope
- --> $DIR/suggest-using-chars.rs:3:19
- |
-LL | let _ = "foo".foo();
- | ^^^ method not found in `&'static str`
-
-error[E0599]: no method named `iter` found for struct `String` in the current scope
- --> $DIR/suggest-using-chars.rs:4:33
- |
-LL | let _ = String::from("bar").iter();
- | ^^^^ method not found in `String`
- |
-help: because of the in-memory representation of `&str`, to obtain an `Iterator` over each of its codepoint use method `chars`
- |
-LL | let _ = String::from("bar").chars();
- | ~~~~~
-
-error[E0599]: no method named `iter` found for reference `&String` in the current scope
- --> $DIR/suggest-using-chars.rs:5:36
- |
-LL | let _ = (&String::from("bar")).iter();
- | ^^^^ method not found in `&String`
- |
-help: because of the in-memory representation of `&str`, to obtain an `Iterator` over each of its codepoint use method `chars`
- |
-LL | let _ = (&String::from("bar")).chars();
- | ~~~~~
-
-error[E0599]: no method named `iter` found for type `{integer}` in the current scope
- --> $DIR/suggest-using-chars.rs:6:15
- |
-LL | let _ = 0.iter();
- | ^^^^ method not found in `{integer}`
-
-error: aborting due to 5 previous errors
-
-For more information about this error, try `rustc --explain E0599`.
}
#[allow(dead_code)]
-fn test_many_bounds_where<X>(x: X) where X: Sized, X: Sized, X: std::fmt::Debug {
+fn test_many_bounds_where<X>(x: X) where X: Sized + std::fmt::Debug, X: Sized {
println!("{:?}", x);
//~^ ERROR doesn't implement
}
| ^ `X` cannot be formatted using `{:?}` because it doesn't implement `Debug`
|
= note: this error originates in the macro `$crate::format_args_nl` (in Nightly builds, run with -Z macro-backtrace for more info)
-help: consider further restricting type parameter `X`
+help: consider further restricting this bound
|
-LL | fn test_many_bounds_where<X>(x: X) where X: Sized, X: Sized, X: std::fmt::Debug {
- | ++++++++++++++++++++
+LL | fn test_many_bounds_where<X>(x: X) where X: Sized + std::fmt::Debug, X: Sized {
+ | +++++++++++++++++
error[E0277]: the size for values of type `Self` cannot be known at compilation time
--> $DIR/bound-suggestions.rs:44:46
--- /dev/null
+struct Kind;
+
+struct Ty {
+ kind: Kind,
+}
+
+impl Ty {
+ fn kind(&self) -> Kind {
+ todo!()
+ }
+}
+
+struct InferOk<T> {
+ value: T,
+ predicates: Vec<()>,
+}
+
+fn foo(i: InferOk<Ty>) {
+ let k = i.kind();
+ //~^ no method named `kind` found for struct `InferOk` in the current scope
+}
+
+fn main() {}
--- /dev/null
+error[E0599]: no method named `kind` found for struct `InferOk` in the current scope
+ --> $DIR/field-has-method.rs:19:15
+ |
+LL | struct InferOk<T> {
+ | ----------------- method `kind` not found for this
+...
+LL | let k = i.kind();
+ | ^^^^ method not found in `InferOk<Ty>`
+ |
+help: one of the expressions' fields has a method of the same name
+ |
+LL | let k = i.value.kind();
+ | ++++++
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0599`.
fn foo() -> impl Bar {
//~^ ERROR the trait bound `(): Bar` is not satisfied
- //~| ERROR the trait bound `(): Bar` is not satisfied
- //~| HELP the following other types implement trait `Bar`:
5;
//~^ HELP remove this semicolon
}
fn bar() -> impl Bar {
//~^ ERROR the trait bound `(): Bar` is not satisfied
- //~| ERROR the trait bound `(): Bar` is not satisfied
- //~| HELP the following other types implement trait `Bar`:
//~| HELP the following other types implement trait `Bar`:
"";
}
|
LL | fn foo() -> impl Bar {
| ^^^^^^^^ the trait `Bar` is not implemented for `()`
-...
+LL |
LL | 5;
| -- help: remove this semicolon
| |
| this expression has type `{integer}`, which implements `Bar`
error[E0277]: the trait bound `(): Bar` is not satisfied
- --> $DIR/impl-trait-return-trailing-semicolon.rs:9:22
- |
-LL | fn foo() -> impl Bar {
- | ______________________^
-LL | |
-LL | |
-LL | |
-LL | | 5;
-LL | |
-LL | | }
- | |_^ the trait `Bar` is not implemented for `()`
- |
- = help: the following other types implement trait `Bar`:
- Qux
- i32
-
-error[E0277]: the trait bound `(): Bar` is not satisfied
- --> $DIR/impl-trait-return-trailing-semicolon.rs:17:13
+ --> $DIR/impl-trait-return-trailing-semicolon.rs:15:13
|
LL | fn bar() -> impl Bar {
| ^^^^^^^^ the trait `Bar` is not implemented for `()`
Qux
i32
-error[E0277]: the trait bound `(): Bar` is not satisfied
- --> $DIR/impl-trait-return-trailing-semicolon.rs:17:22
- |
-LL | fn bar() -> impl Bar {
- | ______________________^
-LL | |
-LL | |
-LL | |
-LL | |
-LL | | "";
-LL | | }
- | |_^ the trait `Bar` is not implemented for `()`
- |
- = help: the following other types implement trait `Bar`:
- Qux
- i32
-
-error: aborting due to 4 previous errors
+error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0277`.
help: the following trait defines an item `hello`, perhaps you need to restrict type parameter `impl Foo` with it:
|
LL | fn test(foo: impl Foo + Bar) {
- | ~~~~~~~~~~~~~~
+ | +++++
error: aborting due to previous error
|
LL | struct S<T>(T);
| ^^^^^^^^^^^^^^^ must implement `PartialEq<_>`
- = note: the trait `std::cmp::PartialEq` is not implemented for `S<T>`
help: consider annotating `S<T>` with `#[derive(PartialEq)]`
|
LL | #[derive(PartialEq)]
|
+help: consider introducing a `where` bound, but there might be an alternative better way to express this requirement
+ |
+LL | pub fn foo<T>(s: S<T>, t: S<T>) where S<T>: PartialEq {
+ | +++++++++++++++++++++
error: aborting due to previous error
= help: items from traits can only be used if the type parameter is bounded by the trait
help: the following trait defines an item `method`, perhaps you need to restrict type parameter `T` with it:
|
-LL | fn call_method<T: Foo + std::fmt::Debug>(x: &T) {
- | ~~~~~~~~
+LL | fn call_method<T: std::fmt::Debug + Foo>(x: &T) {
+ | +++++
error[E0599]: no method named `method` found for type parameter `T` in the current scope
--> $DIR/issue-21673.rs:10:7
help: the following trait defines an item `method`, perhaps you need to restrict type parameter `T` with it:
|
LL | fn call_method_2<T: Foo>(x: T) {
- | ~~~~~~
+ | +++++
error: aborting due to 2 previous errors
--- /dev/null
+fn main() {
+ let items = vec![1, 2, 3];
+ let ref_items: &[i32] = &items;
+ let items_clone: Vec<i32> = ref_items.clone();
+//~^ ERROR mismatched types
+
+ // in that case no suggestion will be triggered
+ let items_clone_2:Vec<i32> = items.clone();
+
+ let s = "hi";
+ let string: String = s.clone();
+//~^ ERROR mismatched types
+
+ // in that case no suggestion will be triggered
+ let s2 = "hi";
+ let string_2: String = s2.to_string();
+}
--- /dev/null
+error[E0308]: mismatched types
+ --> $DIR/issue-53692.rs:4:37
+ |
+LL | let items_clone: Vec<i32> = ref_items.clone();
+ | -------- ^^^^^^^^^^^^^^^^^
+ | | |
+ | | expected struct `Vec`, found `&[i32]`
+ | | help: try using a conversion method: `ref_items.to_vec()`
+ | expected due to this
+ |
+ = note: expected struct `Vec<i32>`
+ found reference `&[i32]`
+
+error[E0308]: mismatched types
+ --> $DIR/issue-53692.rs:11:30
+ |
+LL | let string: String = s.clone();
+ | ------ ^^^^^^^^^
+ | | |
+ | | expected struct `String`, found `&str`
+ | | help: try using a conversion method: `s.to_string()`
+ | expected due to this
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
// Don't suggest removing a semicolon if the last statement isn't an expression with semicolon
// (#81098)
fn wat() -> impl core::fmt::Display { //~ ERROR: `()` doesn't implement `std::fmt::Display`
- //~^ ERROR: `()` doesn't implement `std::fmt::Display`
fn why() {}
}
// Do it if the last statement is an expression with semicolon
// (#54771)
fn ok() -> impl core::fmt::Display { //~ ERROR: `()` doesn't implement `std::fmt::Display`
- //~^ ERROR: `()` doesn't implement `std::fmt::Display`
1;
}
= note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
error[E0277]: `()` doesn't implement `std::fmt::Display`
- --> $DIR/issue-81098.rs:3:37
- |
-LL | fn wat() -> impl core::fmt::Display {
- | _____________________________________^
-LL | |
-LL | | fn why() {}
-LL | | }
- | |_^ `()` cannot be formatted with the default formatter
- |
- = help: the trait `std::fmt::Display` is not implemented for `()`
- = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
-
-error[E0277]: `()` doesn't implement `std::fmt::Display`
- --> $DIR/issue-81098.rs:10:12
+ --> $DIR/issue-81098.rs:9:12
|
LL | fn ok() -> impl core::fmt::Display {
| ^^^^^^^^^^^^^^^^^^^^^^^ `()` cannot be formatted with the default formatter
-LL |
LL | 1;
| -- help: remove this semicolon
| |
= help: the trait `std::fmt::Display` is not implemented for `()`
= note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
-error[E0277]: `()` doesn't implement `std::fmt::Display`
- --> $DIR/issue-81098.rs:10:36
- |
-LL | fn ok() -> impl core::fmt::Display {
- | ____________________________________^
-LL | |
-LL | | 1;
-LL | | }
- | |_^ `()` cannot be formatted with the default formatter
- |
- = help: the trait `std::fmt::Display` is not implemented for `()`
- = note: in format strings you may be able to use `{:?}` (or {:#?} for pretty-print) instead
-
-error: aborting due to 4 previous errors
+error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0277`.
|
LL | fn func<T: Test>(foo: &Foo, t: T) {
| ^^^
+note: ...so that the type `T` will meet its required lifetime bounds
+ --> $DIR/missing-lifetimes-in-signature-2.rs:20:5
+ |
+LL | / foo.bar(move |_| {
+LL | |
+LL | | t.test();
+LL | | });
+ | |______^
+help: consider adding an explicit lifetime bound...
+ |
+LL | fn func<T: Test + 'a>(foo: &Foo, t: T) {
+ | ++++
error: aborting due to previous error
error[E0311]: the parameter type `T` may not live long enough
--> $DIR/missing-lifetimes-in-signature-2.rs:20:9
|
-LL | fn func<T: Test>(foo: &Foo, t: T) {
- | -- help: consider adding an explicit lifetime bound...: `T: 'a +`
LL | foo.bar(move |_| {
| ^^^
|
|
LL | F: 'a,
| ^^
+help: consider adding an explicit lifetime bound...
+ |
+LL | fn func<T: Test + 'a>(foo: &Foo, t: T) {
+ | ++++
error: aborting due to previous error
error[E0261]: use of undeclared lifetime name `'a`
- --> $DIR/missing-lifetimes-in-signature.rs:38:11
+ --> $DIR/missing-lifetimes-in-signature.rs:37:11
|
LL | fn baz<G: 'a, T>(g: G, dest: &mut T) -> impl FnOnce() + '_
| - ^^ undeclared lifetime
| ++++
error[E0311]: the parameter type `G` may not live long enough
- --> $DIR/missing-lifetimes-in-signature.rs:32:5
+ --> $DIR/missing-lifetimes-in-signature.rs:31:5
|
LL | / move || {
LL | | *dest = g.get();
|
LL | fn bar<G, T>(g: G, dest: &mut T) -> impl FnOnce() + '_
| ^^^^^^
+note: ...so that the type `G` will meet its required lifetime bounds
+ --> $DIR/missing-lifetimes-in-signature.rs:31:5
+ |
+LL | / move || {
+LL | | *dest = g.get();
+LL | | }
+ | |_____^
+help: consider adding an explicit lifetime bound...
+ |
+LL | G: Get<T> + 'a,
+ | ++++
error[E0311]: the parameter type `G` may not live long enough
- --> $DIR/missing-lifetimes-in-signature.rs:55:5
+ --> $DIR/missing-lifetimes-in-signature.rs:53:5
|
LL | / move || {
LL | | *dest = g.get();
| |_____^
|
note: the parameter type `G` must be valid for the anonymous lifetime defined here...
- --> $DIR/missing-lifetimes-in-signature.rs:49:34
+ --> $DIR/missing-lifetimes-in-signature.rs:48:34
|
LL | fn qux<'a, G: 'a, T>(g: G, dest: &mut T) -> impl FnOnce() + '_
| ^^^^^^
+note: ...so that the type `G` will meet its required lifetime bounds
+ --> $DIR/missing-lifetimes-in-signature.rs:53:5
+ |
+LL | / move || {
+LL | | *dest = g.get();
+LL | | }
+ | |_____^
+help: consider adding an explicit lifetime bound...
+ |
+LL | fn qux<'a, G: 'a + 'b, T>(g: G, dest: &mut T) -> impl FnOnce() + '_
+ | ++++
error[E0311]: the parameter type `G` may not live long enough
- --> $DIR/missing-lifetimes-in-signature.rs:65:9
+ --> $DIR/missing-lifetimes-in-signature.rs:62:9
|
LL | / move || {
LL | | *dest = g.get();
| |_________^
|
note: the parameter type `G` must be valid for the anonymous lifetime defined here...
- --> $DIR/missing-lifetimes-in-signature.rs:62:47
+ --> $DIR/missing-lifetimes-in-signature.rs:60:47
|
LL | fn qux<'b, G: Get<T> + 'b, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ {
| ^^^^^^
+note: ...so that the type `G` will meet its required lifetime bounds
+ --> $DIR/missing-lifetimes-in-signature.rs:62:9
+ |
+LL | / move || {
+LL | | *dest = g.get();
+LL | | }
+ | |_________^
+help: consider adding an explicit lifetime bound...
+ |
+LL | fn qux<'b, G: Get<T> + 'b + 'c, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ {
+ | ++++
error[E0311]: the parameter type `G` may not live long enough
- --> $DIR/missing-lifetimes-in-signature.rs:77:5
+ --> $DIR/missing-lifetimes-in-signature.rs:74:5
|
LL | / move || {
LL | | *dest = g.get();
| |_____^
|
note: the parameter type `G` must be valid for the anonymous lifetime defined here...
- --> $DIR/missing-lifetimes-in-signature.rs:72:34
+ --> $DIR/missing-lifetimes-in-signature.rs:69:34
|
LL | fn bat<'a, G: 'a, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ + 'a
| ^^^^^^
+note: ...so that the type `G` will meet its required lifetime bounds
+ --> $DIR/missing-lifetimes-in-signature.rs:74:5
+ |
+LL | / move || {
+LL | | *dest = g.get();
+LL | | }
+ | |_____^
+help: consider adding an explicit lifetime bound...
+ |
+LL | fn bat<'a, G: 'a + 'b, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ + 'a
+ | ++++
error[E0621]: explicit lifetime required in the type of `dest`
- --> $DIR/missing-lifetimes-in-signature.rs:77:5
+ --> $DIR/missing-lifetimes-in-signature.rs:74:5
|
LL | fn bat<'a, G: 'a, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ + 'a
| ------ help: add explicit lifetime `'a` to the type of `dest`: `&'a mut T`
| |_____^ lifetime `'a` required
error[E0309]: the parameter type `G` may not live long enough
- --> $DIR/missing-lifetimes-in-signature.rs:89:5
+ --> $DIR/missing-lifetimes-in-signature.rs:85:5
|
LL | / move || {
LL | | *dest = g.get();
LL | | }
- | |_____^
+ | |_____^ ...so that the type `G` will meet its required lifetime bounds
+ |
+help: consider adding an explicit lifetime bound...
|
- = help: consider adding an explicit lifetime bound `G: 'a`...
+LL | G: Get<T> + 'a,
+ | ++++
error: aborting due to 8 previous errors
where
G: Get<T>,
{
- //~^ ERROR the parameter type `G` may not live long enough
move || {
*dest = g.get();
}
where
G: Get<T>,
{
- //~^ ERROR the parameter type `G` may not live long enough
move || {
*dest = g.get();
}
impl<'a> Foo {
fn qux<'b, G: Get<T> + 'b, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ {
//~^ ERROR the parameter type `G` may not live long enough
- //~| ERROR the parameter type `G` may not live long enough
move || {
*dest = g.get();
}
where
G: Get<T>,
{
- //~^ ERROR the parameter type `G` may not live long enough
move || {
*dest = g.get();
}
error[E0261]: use of undeclared lifetime name `'a`
- --> $DIR/missing-lifetimes-in-signature.rs:38:11
+ --> $DIR/missing-lifetimes-in-signature.rs:37:11
|
LL | fn baz<G: 'a, T>(g: G, dest: &mut T) -> impl FnOnce() + '_
| - ^^ undeclared lifetime
|
LL | fn bar<G, T>(g: G, dest: &mut T) -> impl FnOnce() + '_
| ^^^^^^
-note: ...so that the type `[closure@$DIR/missing-lifetimes-in-signature.rs:32:5: 34:6]` will meet its required lifetime bounds
+note: ...so that the type `[closure@$DIR/missing-lifetimes-in-signature.rs:31:5: 33:6]` will meet its required lifetime bounds
--> $DIR/missing-lifetimes-in-signature.rs:26:37
|
LL | fn bar<G, T>(g: G, dest: &mut T) -> impl FnOnce() + '_
| ^^^^^^^^^^^^^^^^^^
help: consider introducing an explicit lifetime bound
|
-LL | fn bar<'a, G: 'a, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ + 'a
- | ~~~~~ ++++
-
-error[E0311]: the parameter type `G` may not live long enough
- --> $DIR/missing-lifetimes-in-signature.rs:30:1
- |
-LL | / {
-LL | |
-LL | | move || {
-LL | | *dest = g.get();
-LL | | }
-LL | | }
- | |_^
- |
-note: the parameter type `G` must be valid for the anonymous lifetime defined here...
- --> $DIR/missing-lifetimes-in-signature.rs:26:26
- |
-LL | fn bar<G, T>(g: G, dest: &mut T) -> impl FnOnce() + '_
- | ^^^^^^
-note: ...so that the type `[closure@$DIR/missing-lifetimes-in-signature.rs:32:5: 34:6]` will meet its required lifetime bounds
- --> $DIR/missing-lifetimes-in-signature.rs:30:1
- |
-LL | / {
-LL | |
-LL | | move || {
-LL | | *dest = g.get();
-LL | | }
-LL | | }
- | |_^
-help: consider introducing an explicit lifetime bound
- |
-LL ~ fn bar<'a, G: 'a, T>(g: G, dest: &mut T) -> impl FnOnce() + '_
+LL ~ fn bar<'a, G, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ + 'a
LL |
LL | where
-LL | G: Get<T>,
-LL | {
-LL |
- ...
+LL ~ G: Get<T> + 'a,
+ |
error[E0311]: the parameter type `G` may not live long enough
- --> $DIR/missing-lifetimes-in-signature.rs:49:45
+ --> $DIR/missing-lifetimes-in-signature.rs:48:45
|
LL | fn qux<'a, G: 'a, T>(g: G, dest: &mut T) -> impl FnOnce() + '_
| ^^^^^^^^^^^^^^^^^^
|
note: the parameter type `G` must be valid for the anonymous lifetime defined here...
- --> $DIR/missing-lifetimes-in-signature.rs:49:34
+ --> $DIR/missing-lifetimes-in-signature.rs:48:34
|
LL | fn qux<'a, G: 'a, T>(g: G, dest: &mut T) -> impl FnOnce() + '_
| ^^^^^^
-note: ...so that the type `[closure@$DIR/missing-lifetimes-in-signature.rs:55:5: 57:6]` will meet its required lifetime bounds
- --> $DIR/missing-lifetimes-in-signature.rs:49:45
+note: ...so that the type `[closure@$DIR/missing-lifetimes-in-signature.rs:53:5: 55:6]` will meet its required lifetime bounds
+ --> $DIR/missing-lifetimes-in-signature.rs:48:45
|
LL | fn qux<'a, G: 'a, T>(g: G, dest: &mut T) -> impl FnOnce() + '_
| ^^^^^^^^^^^^^^^^^^
help: consider introducing an explicit lifetime bound
|
-LL | fn qux<'b, 'a, G: 'b + 'a, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ + 'b
- | +++ ~~~~~~~ ++++
-
-error[E0311]: the parameter type `G` may not live long enough
- --> $DIR/missing-lifetimes-in-signature.rs:53:1
- |
-LL | / {
-LL | |
-LL | | move || {
-LL | | *dest = g.get();
-LL | | }
-LL | | }
- | |_^
- |
-note: the parameter type `G` must be valid for the anonymous lifetime defined here...
- --> $DIR/missing-lifetimes-in-signature.rs:49:34
- |
-LL | fn qux<'a, G: 'a, T>(g: G, dest: &mut T) -> impl FnOnce() + '_
- | ^^^^^^
-note: ...so that the type `[closure@$DIR/missing-lifetimes-in-signature.rs:55:5: 57:6]` will meet its required lifetime bounds
- --> $DIR/missing-lifetimes-in-signature.rs:53:1
- |
-LL | / {
-LL | |
-LL | | move || {
-LL | | *dest = g.get();
-LL | | }
-LL | | }
- | |_^
-help: consider introducing an explicit lifetime bound
- |
-LL ~ fn qux<'b, 'a, G: 'b + 'a, T>(g: G, dest: &mut T) -> impl FnOnce() + '_
-LL |
-LL | where
-LL | G: Get<T>,
-LL | {
-LL |
- ...
+LL | fn qux<'b, 'a, G: 'a + 'b, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ + 'b
+ | +++ ++++ ++++
error[E0311]: the parameter type `G` may not live long enough
- --> $DIR/missing-lifetimes-in-signature.rs:62:58
+ --> $DIR/missing-lifetimes-in-signature.rs:60:58
|
LL | fn qux<'b, G: Get<T> + 'b, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ {
| ^^^^^^^^^^^^^^^^^^
|
note: the parameter type `G` must be valid for the anonymous lifetime defined here...
- --> $DIR/missing-lifetimes-in-signature.rs:62:47
+ --> $DIR/missing-lifetimes-in-signature.rs:60:47
|
LL | fn qux<'b, G: Get<T> + 'b, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ {
| ^^^^^^
-note: ...so that the type `[closure@$DIR/missing-lifetimes-in-signature.rs:65:9: 67:10]` will meet its required lifetime bounds
- --> $DIR/missing-lifetimes-in-signature.rs:62:58
+note: ...so that the type `[closure@$DIR/missing-lifetimes-in-signature.rs:62:9: 64:10]` will meet its required lifetime bounds
+ --> $DIR/missing-lifetimes-in-signature.rs:60:58
|
LL | fn qux<'b, G: Get<T> + 'b, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ {
| ^^^^^^^^^^^^^^^^^^
help: consider introducing an explicit lifetime bound
|
-LL | fn qux<'c, 'b, G: 'c + Get<T> + 'b, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ + 'c {
- | +++ ~~~~~~~ ++++
-
-error[E0311]: the parameter type `G` may not live long enough
- --> $DIR/missing-lifetimes-in-signature.rs:62:77
- |
-LL | fn qux<'b, G: Get<T> + 'b, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ {
- | _____________________________________________________________________________^
-LL | |
-LL | |
-LL | | move || {
-LL | | *dest = g.get();
-LL | | }
-LL | | }
- | |_____^
- |
-note: the parameter type `G` must be valid for the anonymous lifetime defined here...
- --> $DIR/missing-lifetimes-in-signature.rs:62:47
- |
-LL | fn qux<'b, G: Get<T> + 'b, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ {
- | ^^^^^^
-note: ...so that the type `[closure@$DIR/missing-lifetimes-in-signature.rs:65:9: 67:10]` will meet its required lifetime bounds
- --> $DIR/missing-lifetimes-in-signature.rs:62:77
- |
-LL | fn qux<'b, G: Get<T> + 'b, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ {
- | _____________________________________________________________________________^
-LL | |
-LL | |
-LL | | move || {
-LL | | *dest = g.get();
-LL | | }
-LL | | }
- | |_____^
-help: consider introducing an explicit lifetime bound
- |
-LL ~ fn qux<'c, 'b, G: 'c + Get<T> + 'b, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ {
-LL |
-LL |
-LL | move || {
-LL | *dest = g.get();
-LL | }
- ...
+LL | fn qux<'c, 'b, G: Get<T> + 'b + 'c, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ + 'c {
+ | +++ ++++ ++++
error[E0621]: explicit lifetime required in the type of `dest`
- --> $DIR/missing-lifetimes-in-signature.rs:72:45
+ --> $DIR/missing-lifetimes-in-signature.rs:69:45
|
LL | fn bat<'a, G: 'a, T>(g: G, dest: &mut T) -> impl FnOnce() + '_ + 'a
| ------ ^^^^^^^^^^^^^^^^^^^^^^^ lifetime `'a` required
| help: add explicit lifetime `'a` to the type of `dest`: `&'a mut T`
error[E0309]: the parameter type `G` may not live long enough
- --> $DIR/missing-lifetimes-in-signature.rs:83:44
+ --> $DIR/missing-lifetimes-in-signature.rs:80:44
|
LL | fn bak<'a, G, T>(g: G, dest: &'a mut T) -> impl FnOnce() + 'a
- | - ^^^^^^^^^^^^^^^^^^ ...so that the type `[closure@$DIR/missing-lifetimes-in-signature.rs:89:5: 91:6]` will meet its required lifetime bounds
- | |
- | help: consider adding an explicit lifetime bound...: `G: 'a`
-
-error[E0309]: the parameter type `G` may not live long enough
- --> $DIR/missing-lifetimes-in-signature.rs:87:1
+ | ^^^^^^^^^^^^^^^^^^ ...so that the type `[closure@$DIR/missing-lifetimes-in-signature.rs:85:5: 87:6]` will meet its required lifetime bounds
|
-LL | fn bak<'a, G, T>(g: G, dest: &'a mut T) -> impl FnOnce() + 'a
- | - help: consider adding an explicit lifetime bound...: `G: 'a`
-...
-LL | / {
-LL | |
-LL | | move || {
-LL | | *dest = g.get();
-LL | | }
-LL | | }
- | |_^ ...so that the type `[closure@$DIR/missing-lifetimes-in-signature.rs:89:5: 91:6]` will meet its required lifetime bounds
+help: consider adding an explicit lifetime bound...
+ |
+LL | G: Get<T> + 'a,
+ | ++++
-error: aborting due to 11 previous errors
+error: aborting due to 7 previous errors
Some errors have detailed explanations: E0261, E0309, E0621, E0700.
For more information about an error, try `rustc --explain E0261`.
LL | | }
| |_________^ returning this value requires that `'1` must outlive `'static`
|
-help: to allow this `impl Trait` to capture borrowed data with lifetime `'1`, add `'_` as a bound
+help: to declare that the `impl Trait` captures data from argument `self`, you can add an explicit `'_` lifetime bound
|
LL | fn iter(&self) -> impl Iterator<Item = Box<dyn Foo>> + '_ {
| ++++
+help: to declare that the trait object captures data from argument `self`, you can add an explicit `'_` lifetime bound
+ |
+LL | fn iter(&self) -> impl Iterator<Item = Box<dyn Foo + '_>> {
+ | ++++
error: lifetime may not live long enough
--> $DIR/trait-object-nested-in-impl-trait.rs:39:9
LL | | remaining: self.0.iter(),
LL | | }
| |_________^ returning this value requires that `'1` must outlive `'static`
+ |
+help: to declare that the trait object captures data from argument `self`, you can add an explicit `'_` lifetime bound
+ |
+LL | fn iter(&self) -> impl Iterator<Item = Box<dyn Foo + '_>> + '_ {
+ | ++++
error: lifetime may not live long enough
--> $DIR/trait-object-nested-in-impl-trait.rs:50:9
LL | | remaining: self.0.iter(),
LL | | }
| |_________^ returning this value requires that `'a` must outlive `'static`
+ |
+help: to declare that the trait object captures data from argument `self`, you can add an explicit `'a` lifetime bound
+ |
+LL | fn iter<'a>(&'a self) -> impl Iterator<Item = Box<dyn Foo + 'a>> + 'a {
+ | ++++
error: lifetime may not live long enough
--> $DIR/trait-object-nested-in-impl-trait.rs:61:9
LL | | }
| |_________^ returning this value requires that `'a` must outlive `'static`
|
-help: to allow this `impl Trait` to capture borrowed data with lifetime `'a`, add `'a` as a bound
+help: to declare that the `impl Trait` captures data from argument `self`, you can add an explicit `'a` lifetime bound
|
LL | fn iter<'a>(&'a self) -> impl Iterator<Item = Box<dyn Foo>> + 'a {
| ++++
+help: to declare that the trait object captures data from argument `self`, you can add an explicit `'a` lifetime bound
+ |
+LL | fn iter<'a>(&'a self) -> impl Iterator<Item = Box<dyn Foo + 'a>> {
+ | ++++
error: aborting due to 4 previous errors
| ^^^^ required by this bound in `is_send`
help: consider further restricting this bound
|
-LL | fn use_bound_and_where<S: Sync>(val: S) where S: std::fmt::Debug + std::marker::Send {
- | +++++++++++++++++++
+LL | fn use_bound_and_where<S: Sync + std::marker::Send>(val: S) where S: std::fmt::Debug {
+ | +++++++++++++++++++
error[E0277]: `S` cannot be sent between threads safely
--> $DIR/restrict-type-argument.rs:28:13
--> $DIR/suggest-impl-trait-lifetime.rs:7:5
|
LL | bar(d);
- | ^^^^^^
+ | ^^^^^^ ...so that the type `impl Debug` will meet its required lifetime bounds
|
- = help: consider adding an explicit lifetime bound `impl Debug: 'static`...
+help: consider adding an explicit lifetime bound...
+ |
+LL | fn foo(d: impl Debug + 'static) {
+ | +++++++++
error: aborting due to previous error
error[E0310]: the parameter type `impl Debug` may not live long enough
--> $DIR/suggest-impl-trait-lifetime.rs:7:5
|
-LL | fn foo(d: impl Debug) {
- | ---------- help: consider adding an explicit lifetime bound...: `impl Debug + 'static`
-LL |
LL | bar(d);
| ^^^ ...so that the type `impl Debug` will meet its required lifetime bounds...
|
|
LL | fn bar(d: impl Debug + 'static) {
| ^^^^^^^
+help: consider adding an explicit lifetime bound...
+ |
+LL | fn foo(d: impl Debug + 'static) {
+ | +++++++++
error: aborting due to previous error
--- /dev/null
+pub fn main() {
+ let _ = "foo".iter(); //~ ERROR no method named `iter` found for reference `&'static str` in the current scope
+ let _ = "foo".foo(); //~ ERROR no method named `foo` found for reference `&'static str` in the current scope
+ let _ = String::from("bar").iter(); //~ ERROR no method named `iter` found for struct `String` in the current scope
+ let _ = (&String::from("bar")).iter(); //~ ERROR no method named `iter` found for reference `&String` in the current scope
+ let _ = 0.iter(); //~ ERROR no method named `iter` found for type `{integer}` in the current scope
+}
--- /dev/null
+error[E0599]: no method named `iter` found for reference `&'static str` in the current scope
+ --> $DIR/suggest-using-chars.rs:2:19
+ |
+LL | let _ = "foo".iter();
+ | ^^^^ method not found in `&'static str`
+ |
+help: because of the in-memory representation of `&str`, to obtain an `Iterator` over each of its codepoint use method `chars`
+ |
+LL | let _ = "foo".chars();
+ | ~~~~~
+
+error[E0599]: no method named `foo` found for reference `&'static str` in the current scope
+ --> $DIR/suggest-using-chars.rs:3:19
+ |
+LL | let _ = "foo".foo();
+ | ^^^ method not found in `&'static str`
+
+error[E0599]: no method named `iter` found for struct `String` in the current scope
+ --> $DIR/suggest-using-chars.rs:4:33
+ |
+LL | let _ = String::from("bar").iter();
+ | ^^^^ method not found in `String`
+ |
+help: because of the in-memory representation of `&str`, to obtain an `Iterator` over each of its codepoint use method `chars`
+ |
+LL | let _ = String::from("bar").chars();
+ | ~~~~~
+
+error[E0599]: no method named `iter` found for reference `&String` in the current scope
+ --> $DIR/suggest-using-chars.rs:5:36
+ |
+LL | let _ = (&String::from("bar")).iter();
+ | ^^^^ method not found in `&String`
+ |
+help: because of the in-memory representation of `&str`, to obtain an `Iterator` over each of its codepoint use method `chars`
+ |
+LL | let _ = (&String::from("bar")).chars();
+ | ~~~~~
+
+error[E0599]: no method named `iter` found for type `{integer}` in the current scope
+ --> $DIR/suggest-using-chars.rs:6:15
+ |
+LL | let _ = 0.iter();
+ | ^^^^ method not found in `{integer}`
+
+error: aborting due to 5 previous errors
+
+For more information about this error, try `rustc --explain E0599`.
--- /dev/null
+// build-pass
+// only-x86-windows
+#![crate_type = "cdylib"]
+#![feature(abi_vectorcall)]
+
+#[no_mangle]
+extern "stdcall" fn foo(_: bool) {}
+
+#[no_mangle]
+extern "fastcall" fn bar(_: u8) {}
+
+#[no_mangle]
+extern "vectorcall" fn baz(_: u16) {}
--- /dev/null
+// run-pass
+// ignore-emscripten no threads support
+
+use std::thread;
+
+struct Foo;
+
+impl Drop for Foo {
+ fn drop(&mut self) {
+ println!("test2");
+ }
+}
+
+thread_local!(static FOO: Foo = Foo);
+
+fn main() {
+ // Off the main thread due to #28129, be sure to initialize FOO first before
+ // calling `println!`
+ thread::spawn(|| {
+ FOO.with(|_| {});
+ println!("test1");
+ }).join().unwrap();
+}
error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
- --> $DIR/issue-43733.rs:19:5
+ --> $DIR/issue-43733.rs:21:5
|
LL | __KEY.get(Default::default)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function
= note: consult the function's documentation for information on how to avoid undefined behavior
error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
- --> $DIR/issue-43733.rs:22:42
+ --> $DIR/issue-43733.rs:26:42
|
LL | static FOO: std::thread::LocalKey<Foo> = std::thread::LocalKey::new(__getit);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function
// revisions: mir thir
// [thir]compile-flags: -Z thir-unsafeck
+// normalize-stderr-test: "__FastLocalKeyInner::<T>::get" -> "$$LOCALKEYINNER::<T>::get"
+// normalize-stderr-test: "__OsLocalKeyInner::<T>::get" -> "$$LOCALKEYINNER::<T>::get"
#![feature(thread_local)]
#![feature(cfg_target_thread_local, thread_local_internals)]
static __KEY: std::thread::__OsLocalKeyInner<Foo> = std::thread::__OsLocalKeyInner::new();
fn __getit(_: Option<&mut Option<RefCell<String>>>) -> std::option::Option<&'static Foo> {
- __KEY.get(Default::default) //~ ERROR call to unsafe function is unsafe
+ __KEY.get(Default::default)
+ //[mir]~^ ERROR call to unsafe function is unsafe
+ //[thir]~^^ ERROR call to unsafe function `__
}
static FOO: std::thread::LocalKey<Foo> = std::thread::LocalKey::new(__getit);
-//~^ ERROR call to unsafe function is unsafe
+//[mir]~^ ERROR call to unsafe function is unsafe
+//[thir]~^^ ERROR call to unsafe function `LocalKey::<T>::new`
fn main() {
FOO.with(|foo| println!("{}", foo.borrow()));
-error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
- --> $DIR/issue-43733.rs:19:5
+error[E0133]: call to unsafe function `$LOCALKEYINNER::<T>::get` is unsafe and requires unsafe function or block
+ --> $DIR/issue-43733.rs:21:5
|
LL | __KEY.get(Default::default)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function
|
= note: consult the function's documentation for information on how to avoid undefined behavior
-error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
- --> $DIR/issue-43733.rs:22:42
+error[E0133]: call to unsafe function `LocalKey::<T>::new` is unsafe and requires unsafe function or block
+ --> $DIR/issue-43733.rs:26:42
|
LL | static FOO: std::thread::LocalKey<Foo> = std::thread::LocalKey::new(__getit);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ call to unsafe function
--- /dev/null
+struct Thing<X>(X);
+
+trait Method<T> {
+ fn method(self, _: i32) -> T;
+}
+
+impl<X> Method<i32> for Thing<X> {
+ fn method(self, _: i32) -> i32 { 0 }
+}
+
+impl<X> Method<u32> for Thing<X> {
+ fn method(self, _: i32) -> u32 { 0 }
+}
+
+fn main() {
+ let thing = Thing(true);
+ thing.method(42);
+ //~^ ERROR type annotations needed
+ //~| ERROR type annotations needed
+}
--- /dev/null
+error[E0282]: type annotations needed
+ --> $DIR/do-not-mention-type-params-by-name-in-suggestion-issue-96292.rs:17:11
+ |
+LL | thing.method(42);
+ | ------^^^^^^----
+ | | |
+ | | cannot infer type for type parameter `T` declared on the trait `Method`
+ | this method call resolves to `T`
+
+error[E0283]: type annotations needed
+ --> $DIR/do-not-mention-type-params-by-name-in-suggestion-issue-96292.rs:17:11
+ |
+LL | thing.method(42);
+ | ------^^^^^^----
+ | | |
+ | | cannot infer type for type parameter `T` declared on the trait `Method`
+ | this method call resolves to `T`
+ |
+note: multiple `impl`s satisfying `Thing<bool>: Method<_>` found
+ --> $DIR/do-not-mention-type-params-by-name-in-suggestion-issue-96292.rs:7:1
+ |
+LL | impl<X> Method<i32> for Thing<X> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+...
+LL | impl<X> Method<u32> for Thing<X> {
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+help: use the fully qualified path for the potential candidates
+ |
+LL | <Thing<_> as Method<i32>>::method(thing, 42);
+ | ++++++++++++++++++++++++++++++++++ ~
+LL | <Thing<_> as Method<u32>>::method(thing, 42);
+ | ++++++++++++++++++++++++++++++++++ ~
+
+error: aborting due to 2 previous errors
+
+Some errors have detailed explanations: E0282, E0283.
+For more information about an error, try `rustc --explain E0282`.
--- /dev/null
+use std::mem;
+
+trait Misc {}
+
+fn size_of_copy<T: Copy+?Sized>() -> usize { mem::size_of::<T>() }
+
+fn main() {
+ size_of_copy::<dyn Misc + Copy>();
+ //~^ ERROR only auto traits can be used as additional traits in a trait object
+ //~| ERROR only auto traits can be used as additional traits in a trait object
+ //~| ERROR the trait bound `dyn Misc: Copy` is not satisfied
+}
--- /dev/null
+error[E0225]: only auto traits can be used as additional traits in a trait object
+ --> $DIR/issue-32963.rs:8:31
+ |
+LL | size_of_copy::<dyn Misc + Copy>();
+ | ---- ^^^^ additional non-auto trait
+ | |
+ | first non-auto trait
+ |
+ = help: consider creating a new trait with all of these as supertraits and using that trait here instead: `trait NewTrait: Misc + Copy {}`
+ = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit <https://doc.rust-lang.org/reference/special-types-and-traits.html#auto-traits>
+
+error[E0225]: only auto traits can be used as additional traits in a trait object
+ --> $DIR/issue-32963.rs:8:31
+ |
+LL | size_of_copy::<dyn Misc + Copy>();
+ | ---- ^^^^ additional non-auto trait
+ | |
+ | first non-auto trait
+ |
+ = help: consider creating a new trait with all of these as supertraits and using that trait here instead: `trait NewTrait: Misc + Copy {}`
+ = note: auto-traits like `Send` and `Sync` are traits that have special properties; for more information on them, visit <https://doc.rust-lang.org/reference/special-types-and-traits.html#auto-traits>
+
+error[E0277]: the trait bound `dyn Misc: Copy` is not satisfied
+ --> $DIR/issue-32963.rs:8:5
+ |
+LL | size_of_copy::<dyn Misc + Copy>();
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `dyn Misc`
+ |
+note: required by a bound in `size_of_copy`
+ --> $DIR/issue-32963.rs:5:20
+ |
+LL | fn size_of_copy<T: Copy+?Sized>() -> usize { mem::size_of::<T>() }
+ | ^^^^ required by this bound in `size_of_copy`
+
+error: aborting due to 3 previous errors
+
+Some errors have detailed explanations: E0225, E0277.
+For more information about an error, try `rustc --explain E0225`.
--- /dev/null
+// run-pass
+#![allow(dead_code)]
+
+pub fn main() {
+ let _id: &Mat2<f64> = &Matrix::identity(1.0);
+}
+
+pub trait Index<Index,Result> { fn get(&self, _: Index) -> Result { panic!() } }
+pub trait Dimensional<T>: Index<usize, T> { }
+
+pub struct Mat2<T> { x: T }
+pub struct Vec2<T> { x: T }
+
+impl<T> Dimensional<Vec2<T>> for Mat2<T> { }
+impl<T> Index<usize, Vec2<T>> for Mat2<T> { }
+
+impl<T> Dimensional<T> for Vec2<T> { }
+impl<T> Index<usize, T> for Vec2<T> { }
+
+pub trait Matrix<T,V>: Dimensional<V> {
+ fn identity(t:T) -> Self;
+}
+
+impl<T> Matrix<T, Vec2<T>> for Mat2<T> {
+ fn identity(t:T) -> Mat2<T> { Mat2{ x: t } }
+}
--- /dev/null
+// run-pass
+#![allow(unused)]
+
+fn main() {
+}
+
+fn foo() {
+ let b = mk::<
+ Forward<(Box<dyn Future<Error = u32>>,)>,
+ >();
+ b.map_err(|_| ()).join();
+}
+
+fn mk<T>() -> T {
+ loop {}
+}
+
+impl<I: Future<Error = E>, E> Future for (I,) {
+ type Error = E;
+}
+
+struct Forward<T: Future> {
+ _a: T,
+}
+
+impl<T: Future> Future for Forward<T>
+where
+ T::Error: From<u32>,
+{
+ type Error = T::Error;
+}
+
+trait Future {
+ type Error;
+
+ fn map_err<F, E>(self, _: F) -> (Self, F)
+ where
+ F: FnOnce(Self::Error) -> E,
+ Self: Sized,
+ {
+ loop {}
+ }
+
+ fn join(self) -> (MaybeDone<Self>, ())
+ where
+ Self: Sized,
+ {
+ loop {}
+ }
+}
+
+impl<S: ?Sized + Future> Future for Box<S> {
+ type Error = S::Error;
+}
+
+enum MaybeDone<A: Future> {
+ _Done(A::Error),
+}
+
+impl<U, A: Future, F> Future for (A, F)
+where
+ F: FnOnce(A::Error) -> U,
+{
+ type Error = U;
+}
= help: items from traits can only be used if the type parameter is bounded by the trait
help: the following trait defines an item `foo`, perhaps you need to restrict type parameter `T` with it:
|
-LL | fn do_stuff<T: Foo + Bar>(t : T) {
- | ~~~~~~~~
+LL | fn do_stuff<T : Bar + Foo>(t : T) {
+ | +++++
error: aborting due to previous error
--- /dev/null
+#![feature(unsize, dispatch_from_dyn)]
+
+use std::marker::Unsize;
+use std::ops::DispatchFromDyn;
+
+#[allow(unused)]
+struct Foo<'a, T: ?Sized> {
+ _inner: &'a &'a T,
+}
+
+impl<'a, T: ?Sized + Unsize<U>, U: ?Sized> DispatchFromDyn<Foo<'a, U>> for Foo<'a, T> {}
+//~^ ERROR the trait bound `&'a T: Unsize<&'a U>` is not satisfied
+//~| NOTE the trait `Unsize<&'a U>` is not implemented for `&'a T`
+//~| NOTE all implementations of `Unsize` are provided automatically by the compiler
+//~| NOTE required because of the requirements on the impl
+
+fn main() {}
--- /dev/null
+error[E0277]: the trait bound `&'a T: Unsize<&'a U>` is not satisfied
+ --> $DIR/issue-71036.rs:11:1
+ |
+LL | impl<'a, T: ?Sized + Unsize<U>, U: ?Sized> DispatchFromDyn<Foo<'a, U>> for Foo<'a, T> {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `Unsize<&'a U>` is not implemented for `&'a T`
+ |
+ = note: all implementations of `Unsize` are provided automatically by the compiler, see <https://doc.rust-lang.org/stable/std/marker/trait.Unsize.html> for more information
+ = note: required because of the requirements on the impl of `DispatchFromDyn<&'a &'a U>` for `&'a &'a T`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
help: use the fully qualified path for the potential candidates
|
LL | opts.get(<String as AsRef<OsStr>>::as_ref(opt));
- | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ | +++++++++++++++++++++++++++++++++ ~
LL | opts.get(<String as AsRef<Path>>::as_ref(opt));
- | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ | ++++++++++++++++++++++++++++++++ ~
LL | opts.get(<String as AsRef<[u8]>>::as_ref(opt));
- | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ | ++++++++++++++++++++++++++++++++ ~
LL | opts.get(<String as AsRef<str>>::as_ref(opt));
- | ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ | +++++++++++++++++++++++++++++++ ~
and 4 other candidates
error[E0283]: type annotations needed
help: the following trait defines an item `clone`, perhaps you need to restrict type parameter `T` with it:
|
LL | fn foo<T: Clone>(t: T) {
- | ~~~~~~~~
+ | +++++
error: aborting due to previous error
| |
| &T
|
-help: consider further restricting this bound
+help: consider introducing a `where` bound, but there might be an alternative better way to express this requirement
|
-LL | fn foo<T: MyMul<f64, f64> + std::ops::Mul<Output = f64>>(a: &T, b: f64) -> f64 {
- | +++++++++++++++++++++++++++++
+LL | fn foo<T: MyMul<f64, f64>>(a: &T, b: f64) -> f64 where &T: Mul<f64> {
+ | ++++++++++++++++++
error: aborting due to previous error
| |_- this function returns a `Result`
|
= help: the trait `FromResidual<Option<Infallible>>` is not implemented for `Result<u64, String>`
- = help: the trait `FromResidual<Result<Infallible, E>>` is implemented for `Result<T, F>`
+ = help: the following other types implement trait `FromResidual<R>`:
+ <Result<T, F> as FromResidual<Result<Infallible, E>>>
+ <Result<T, F> as FromResidual<Yeet<E>>>
error[E0277]: the `?` operator can only be used on `Result`s in a function that returns `Result`
--> $DIR/bad-interconversion.rs:17:31
| |_- this function returns a `Result`
|
= help: the trait `FromResidual<ControlFlow<{integer}, Infallible>>` is not implemented for `Result<u64, String>`
- = help: the trait `FromResidual<Result<Infallible, E>>` is implemented for `Result<T, F>`
+ = help: the following other types implement trait `FromResidual<R>`:
+ <Result<T, F> as FromResidual<Result<Infallible, E>>>
+ <Result<T, F> as FromResidual<Yeet<E>>>
error[E0277]: the `?` operator can only be used on `Option`s, not `Result`s, in a function that returns `Option`
--> $DIR/bad-interconversion.rs:22:22
| |_- this function returns an `Option`
|
= help: the trait `FromResidual<Result<Infallible, &str>>` is not implemented for `Option<u16>`
- = help: the trait `FromResidual` is implemented for `Option<T>`
+ = help: the following other types implement trait `FromResidual<R>`:
+ <Option<T> as FromResidual<Yeet<()>>>
+ <Option<T> as FromResidual>
error[E0277]: the `?` operator can only be used on `Option`s in a function that returns `Option`
--> $DIR/bad-interconversion.rs:27:33
| |_- this function returns an `Option`
|
= help: the trait `FromResidual<ControlFlow<{integer}, Infallible>>` is not implemented for `Option<u64>`
- = help: the trait `FromResidual` is implemented for `Option<T>`
+ = help: the following other types implement trait `FromResidual<R>`:
+ <Option<T> as FromResidual<Yeet<()>>>
+ <Option<T> as FromResidual>
error[E0277]: the `?` operator can only be used on `ControlFlow`s in a function that returns `ControlFlow`
--> $DIR/bad-interconversion.rs:32:39
| |_- this function returns a `Result`
|
= help: the trait `FromResidual<Option<Infallible>>` is not implemented for `Result<(), ()>`
- = help: the trait `FromResidual<Result<Infallible, E>>` is implemented for `Result<T, F>`
+ = help: the following other types implement trait `FromResidual<R>`:
+ <Result<T, F> as FromResidual<Result<Infallible, E>>>
+ <Result<T, F> as FromResidual<Yeet<E>>>
error[E0277]: the `?` operator can only be used on `Option`s, not `Result`s, in a function that returns `Option`
--> $DIR/option-to-result.rs:11:6
| |_- this function returns an `Option`
|
= help: the trait `FromResidual<Result<Infallible, i32>>` is not implemented for `Option<i32>`
- = help: the trait `FromResidual` is implemented for `Option<T>`
+ = help: the following other types implement trait `FromResidual<R>`:
+ <Option<T> as FromResidual<Yeet<()>>>
+ <Option<T> as FromResidual>
error: aborting due to 2 previous errors
| |_- this function returns a `Result`
|
= help: the trait `FromResidual<Option<Infallible>>` is not implemented for `Result<u32, ()>`
- = help: the trait `FromResidual<Result<Infallible, E>>` is implemented for `Result<T, F>`
+ = help: the following other types implement trait `FromResidual<R>`:
+ <Result<T, F> as FromResidual<Result<Infallible, E>>>
+ <Result<T, F> as FromResidual<Yeet<E>>>
error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `FromResidual`)
--> $DIR/try-on-option.rs:11:6
--- /dev/null
+// run-pass
+
+#![feature(yeet_expr)]
+
+fn always_yeet() -> Option<String> {
+ do yeet;
+}
+
+fn main() {
+ assert_eq!(always_yeet(), None);
+}
--- /dev/null
+// run-pass
+
+#![feature(yeet_expr)]
+
+fn always_yeet() -> Result<i32, String> {
+ do yeet "hello";
+}
+
+fn main() {
+ assert_eq!(always_yeet(), Err("hello".to_string()));
+}
--> $DIR/bounds-are-checked.rs:8:6
|
LL | fn f<'a: 'static>(t: &'a str) -> X<'a> {
- | ^^^^^^^^^^^
+ | ^^
|
= help: you can use the `'static` lifetime directly, in place of `'a`
error[E0310]: the parameter type `T` may not live long enough
--> $DIR/generic_type_does_not_live_long_enough.rs:18:5
|
-LL | fn wrong_generic<T>(t: T) -> WrongGeneric<T> {
- | - help: consider adding an explicit lifetime bound...: `T: 'static`
LL | t
| ^ ...so that the type `T` will meet its required lifetime bounds
+ |
+help: consider adding an explicit lifetime bound...
+ |
+LL | fn wrong_generic<T: 'static>(t: T) -> WrongGeneric<T> {
+ | +++++++++
error: aborting due to 3 previous errors
--> $DIR/generic_type_does_not_live_long_enough.rs:18:5
|
LL | t
- | ^
+ | ^ ...so that the type `T` will meet its required lifetime bounds
|
- = help: consider adding an explicit lifetime bound `T: 'static`...
+help: consider adding an explicit lifetime bound...
+ |
+LL | fn wrong_generic<T: 'static>(t: T) -> WrongGeneric<T> {
+ | +++++++++
error: aborting due to 3 previous errors
trait Animal { }
fn main() {
- pub type ServeFut = /*impl Trait*/;
+ type ServeFut = /*impl Trait*/;
}
fn foo() -> impl Foo<FooX> {
//~^ ERROR: the trait bound `(): Foo<FooX>` is not satisfied
- //~| ERROR: the trait bound `(): Foo<FooX>` is not satisfied
// FIXME(type-alias-impl-trait): We could probably make this work.
()
}
|
= help: the trait `Foo<()>` is implemented for `()`
-error[E0277]: the trait bound `(): Foo<FooX>` is not satisfied
- --> $DIR/nested-tait-inference.rs:12:28
- |
-LL | fn foo() -> impl Foo<FooX> {
- | ____________________________^
-LL | |
-LL | |
-LL | | // FIXME(type-alias-impl-trait): We could probably make this work.
-LL | | ()
-LL | | }
- | |_^ the trait `Foo<FooX>` is not implemented for `()`
- |
- = help: the trait `Foo<()>` is implemented for `()`
-
-error: aborting due to 2 previous errors
+error: aborting due to previous error
For more information about this error, try `rustc --explain E0277`.
fn foo() -> impl Foo<FooX> {
//~^ ERROR: the trait bound `(): Foo<FooX>` is not satisfied
- //~| ERROR: the trait bound `(): Foo<FooX>` is not satisfied
()
}
<() as Foo<()>>
<() as Foo<u32>>
-error[E0277]: the trait bound `(): Foo<FooX>` is not satisfied
- --> $DIR/nested-tait-inference2.rs:13:28
- |
-LL | fn foo() -> impl Foo<FooX> {
- | ____________________________^
-LL | |
-LL | |
-LL | | ()
-LL | | }
- | |_^ the trait `Foo<FooX>` is not implemented for `()`
- |
- = help: the following other types implement trait `Foo<A>`:
- <() as Foo<()>>
- <() as Foo<u32>>
-
-error: aborting due to 2 previous errors
+error: aborting due to previous error
For more information about this error, try `rustc --explain E0277`.
|
help: consider restricting type parameter `T`
|
-LL | fn foo<T: std::ops::Add<Output = T>>(x: T, y: T) {
- | +++++++++++++++++++++++++++
+LL | fn foo<T: std::ops::Add>(x: T, y: T) {
+ | +++++++++++++++
error[E0368]: binary assignment operation `+=` cannot be applied to type `T`
--> $DIR/missing_trait_impl.rs:9:5
|
help: consider restricting type parameter `T`
|
-LL | fn baz<T: std::ops::Neg<Output = T>>(x: T) {
- | +++++++++++++++++++++++++++
+LL | fn baz<T: std::ops::Neg>(x: T) {
+ | +++++++++++++++
error[E0600]: cannot apply unary operator `!` to type `T`
--> $DIR/missing_trait_impl.rs:14:13
|
help: consider restricting type parameter `T`
|
-LL | fn baz<T: std::ops::Not<Output = T>>(x: T) {
- | +++++++++++++++++++++++++++
+LL | fn baz<T: std::ops::Not>(x: T) {
+ | +++++++++++++++
error[E0614]: type `T` cannot be dereferenced
--> $DIR/missing_trait_impl.rs:15:13
--- /dev/null
+struct Bar<T> {
+ bar: T
+}
+
+struct Foo();
+impl Foo {
+ fn foo() { }
+}
+
+fn main() {
+ let thing = Bar { bar: Foo };
+ thing.bar.foo();
+ //~^ ERROR no method named `foo` found for fn item `fn() -> Foo {Foo}` in the current scope [E0599]
+}
--- /dev/null
+error[E0599]: no method named `foo` found for fn item `fn() -> Foo {Foo}` in the current scope
+ --> $DIR/empty-tuple-method.rs:12:15
+ |
+LL | thing.bar.foo();
+ | --------- ^^^ method not found in `fn() -> Foo {Foo}`
+ | |
+ | this is the constructor of a struct
+ |
+help: call the constructor
+ |
+LL | (thing.bar)().foo();
+ | + +++
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0599`.
--- /dev/null
+struct Bar<T> {
+ bar: T
+}
+
+enum Foo{
+ Tup()
+}
+impl Foo {
+ fn foo() { }
+}
+
+fn main() {
+ let thing = Bar { bar: Foo::Tup };
+ thing.bar.foo();
+ //~^ ERROR no method named `foo` found for fn item `fn() -> Foo {Foo::Tup}` in the current scope [E0599]
+}
--- /dev/null
+error[E0599]: no method named `foo` found for fn item `fn() -> Foo {Foo::Tup}` in the current scope
+ --> $DIR/enum-variant.rs:14:15
+ |
+LL | thing.bar.foo();
+ | --------- ^^^ method not found in `fn() -> Foo {Foo::Tup}`
+ | |
+ | this is the constructor of an enum variant
+ |
+help: call the constructor
+ |
+LL | (thing.bar)().foo();
+ | + +++
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0599`.
--- /dev/null
+struct Bar<T> {
+ bar: T
+}
+
+struct Foo(char, u16);
+impl Foo {
+ fn foo() { }
+}
+
+fn main() {
+ let thing = Bar { bar: Foo };
+ thing.bar.0;
+ //~^ ERROR no field `0` on type `fn(char, u16) -> Foo {Foo}` [E0609]
+}
--- /dev/null
+error[E0609]: no field `0` on type `fn(char, u16) -> Foo {Foo}`
+ --> $DIR/tuple-field.rs:12:15
+ |
+LL | thing.bar.0;
+ | --------- ^
+ | |
+ | this is the constructor of a struct
+ |
+help: call the constructor
+ |
+LL | (thing.bar)(_, _).0;
+ | + +++++++
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0609`.
--- /dev/null
+struct Bar<T> {
+ bar: T
+}
+
+struct Foo(u8, i32);
+impl Foo {
+ fn foo() { }
+}
+
+fn main() {
+ let thing = Bar { bar: Foo };
+ thing.bar.foo();
+ //~^ ERROR no method named `foo` found for fn item `fn(u8, i32) -> Foo {Foo}` in the current scope [E0599]
+}
--- /dev/null
+error[E0599]: no method named `foo` found for fn item `fn(u8, i32) -> Foo {Foo}` in the current scope
+ --> $DIR/tuple-method.rs:12:15
+ |
+LL | thing.bar.foo();
+ | --------- ^^^ method not found in `fn(u8, i32) -> Foo {Foo}`
+ | |
+ | this is the constructor of a struct
+ |
+help: call the constructor
+ |
+LL | (thing.bar)(_, _).foo();
+ | + +++++++
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0599`.
--- /dev/null
+// run-rustfix
+// Check that the HELP suggestion is `l(vec![])` instead of `l($crate::vec::Vec::new())`
+fn l(_a: Vec<u8>) {}
+
+fn main() {
+ l(vec![])
+ //~^ ERROR this function takes 1 argument but 2 arguments were supplied
+ //~| HELP remove the extra argument
+}
--- /dev/null
+// run-rustfix
+// Check that the HELP suggestion is `l(vec![])` instead of `l($crate::vec::Vec::new())`
+fn l(_a: Vec<u8>) {}
+
+fn main() {
+ l(vec![], vec![])
+ //~^ ERROR this function takes 1 argument but 2 arguments were supplied
+ //~| HELP remove the extra argument
+}
--- /dev/null
+error[E0061]: this function takes 1 argument but 2 arguments were supplied
+ --> $DIR/remove-extra-argument.rs:6:5
+ |
+LL | l(vec![], vec![])
+ | ^ ------ argument unexpected
+ |
+note: function defined here
+ --> $DIR/remove-extra-argument.rs:3:4
+ |
+LL | fn l(_a: Vec<u8>) {}
+ | ^ -----------
+help: remove the extra argument
+ |
+LL | l(vec![])
+ |
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0061`.
LL | impl DefaultedTrait for Box<C> { }
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can't implement cross-crate trait for type in another crate
-error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
+error[E0117]: only traits defined in the current crate can be implemented for types defined outside of the crate
--> $DIR/typeck-default-trait-impl-cross-crate-coherence.rs:22:1
|
LL | impl DefaultedTrait for lib::Something<C> { }
impl Clone for Test9 {
fn clone(&self) -> _ { Test9 }
- //~^ ERROR the placeholder `_` is not allowed within types on item signatures for return types
+ //~^ ERROR the placeholder `_` is not allowed within types on item signatures for functions
fn clone_from(&mut self, other: _) { *self = Test9; }
//~^ ERROR the placeholder `_` is not allowed within types on item signatures for functions
impl Clone for FnTest9 {
fn clone(&self) -> _ { FnTest9 }
- //~^ ERROR the placeholder `_` is not allowed within types on item signatures for return types
+ //~^ ERROR the placeholder `_` is not allowed within types on item signatures for functions
fn clone_from(&mut self, other: _) { *self = FnTest9; }
//~^ ERROR the placeholder `_` is not allowed within types on item signatures for functions
LL | fn test10<T>(&self, _x : T) { }
| +++ ~
-error[E0121]: the placeholder `_` is not allowed within types on item signatures for return types
+error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions
--> $DIR/typeck_type_placeholder_item.rs:59:24
|
LL | fn clone(&self) -> _ { Test9 }
- | ^
- | |
- | not allowed in type signatures
- | help: replace with the correct return type: `Test9`
+ | ^ not allowed in type signatures
+ |
+help: try replacing `_` with the type in the corresponding trait method signature
+ |
+LL | fn clone(&self) -> Test9 { Test9 }
+ | ~~~~~
error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions
--> $DIR/typeck_type_placeholder_item.rs:62:37
LL | fn clone_from(&mut self, other: _) { *self = Test9; }
| ^ not allowed in type signatures
|
-help: use type parameters instead
+help: try replacing `_` with the type in the corresponding trait method signature
|
-LL | fn clone_from<T>(&mut self, other: T) { *self = Test9; }
- | +++ ~
+LL | fn clone_from(&mut self, other: &Test9) { *self = Test9; }
+ | ~~~~~~
error[E0121]: the placeholder `_` is not allowed within types on item signatures for return types
--> $DIR/typeck_type_placeholder_item.rs:107:31
LL | fn fn_test10<T>(&self, _x : T) { }
| +++ ~
-error[E0121]: the placeholder `_` is not allowed within types on item signatures for return types
+error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions
--> $DIR/typeck_type_placeholder_item.rs:115:28
|
LL | fn clone(&self) -> _ { FnTest9 }
- | ^
- | |
- | not allowed in type signatures
- | help: replace with the correct return type: `FnTest9`
+ | ^ not allowed in type signatures
+ |
+help: try replacing `_` with the type in the corresponding trait method signature
+ |
+LL | fn clone(&self) -> FnTest9 { FnTest9 }
+ | ~~~~~~~
error[E0121]: the placeholder `_` is not allowed within types on item signatures for functions
--> $DIR/typeck_type_placeholder_item.rs:118:41
LL | fn clone_from(&mut self, other: _) { *self = FnTest9; }
| ^ not allowed in type signatures
|
-help: use type parameters instead
+help: try replacing `_` with the type in the corresponding trait method signature
|
-LL | fn clone_from<T>(&mut self, other: T) { *self = FnTest9; }
- | +++ ~
+LL | fn clone_from(&mut self, other: &FnTest9) { *self = FnTest9; }
+ | ~~~~~~~~
error[E0121]: the placeholder `_` is not allowed within types on item signatures for associated types
--> $DIR/typeck_type_placeholder_item.rs:201:14
--> $DIR/unboxed-closures-static-call-wrong-trait.rs:7:10
|
LL | mut_.call((0, ));
- | ^^^^ method not found in `[closure@$DIR/unboxed-closures-static-call-wrong-trait.rs:6:26: 6:31]`
- |
- = note: `mut_` is a function, perhaps you wish to call it
+ | ---- ^^^^ method not found in `[closure@$DIR/unboxed-closures-static-call-wrong-trait.rs:6:26: 6:31]`
+ | |
+ | this is a function, perhaps you wish to call it
error: aborting due to previous error
LL | // ^^^^^^^^^^^^^^^^^^^^^ bound *here* defaults to `'static`
LL | Box::new(items.iter())
| ^^^^^^^^^^^^^^^^^^^^^^ returning this value requires that `'1` must outlive `'static`
+ |
+help: to declare that the trait object captures data from argument `items`, you can add an explicit `'_` lifetime bound
+ |
+LL | fn a<T>(items: &[T]) -> Box<dyn Iterator<Item=&T> + '_> {
+ | ++++
error: aborting due to previous error
| | |
| | let's call the lifetime of this reference `'1`
| let's call the lifetime of this reference `'2`
+ |
+help: consider introducing a named lifetime parameter
+ |
+LL | fn foo<'a>(x: &mut Vec<&'a u8>, y: &'a u8) { x.push(y); }
+ | ++++ ~~ ~~
error: aborting due to previous error
= note: consult the function's documentation for information on how to avoid undefined behavior
error: dereference of raw pointer is unsafe and requires unsafe block (error E0133)
- --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:14:5
+ --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:15:5
|
LL | *PTR;
| ^^^^ dereference of raw pointer
= note: raw pointers may be null, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior
error: use of mutable static is unsafe and requires unsafe block (error E0133)
- --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:16:5
+ --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:17:5
|
LL | VOID = ();
| ^^^^^^^^^ use of mutable static
= note: mutable statics can be mutated by multiple threads: aliasing violations or data races will cause undefined behavior
error: unnecessary `unsafe` block
- --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:19:5
+ --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:20:5
|
LL | unsafe {}
| ^^^^^^ unnecessary `unsafe` block
| ^^^^^^^^^^^^^
error: call to unsafe function is unsafe and requires unsafe block (error E0133)
- --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:27:5
+ --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:28:5
|
LL | unsf();
| ^^^^^^ call to unsafe function
|
note: the lint level is defined here
- --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:25:8
+ --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:26:8
|
LL | #[deny(warnings)]
| ^^^^^^^^
= note: consult the function's documentation for information on how to avoid undefined behavior
error: dereference of raw pointer is unsafe and requires unsafe block (error E0133)
- --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:29:5
+ --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:31:5
|
LL | *PTR;
| ^^^^ dereference of raw pointer
= note: raw pointers may be null, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior
error: use of mutable static is unsafe and requires unsafe block (error E0133)
- --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:31:5
+ --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:33:5
|
LL | VOID = ();
| ^^^^^^^^^ use of mutable static
= note: mutable statics can be mutated by multiple threads: aliasing violations or data races will cause undefined behavior
error: unnecessary `unsafe` block
- --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:33:5
+ --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:35:5
|
LL | unsafe {}
| ^^^^^^ unnecessary `unsafe` block
error: unnecessary `unsafe` block
- --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:47:5
+ --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:49:5
|
LL | unsafe { unsafe { unsf() } }
| ^^^^^^ unnecessary `unsafe` block
error: unnecessary `unsafe` block
- --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:58:5
+ --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:60:5
|
LL | unsafe fn allow_level() {
| ----------------------- because it's nested under this `unsafe` fn
|
= note: this `unsafe` block does contain unsafe operations, but those are already allowed in an `unsafe fn`
note: the lint level is defined here
- --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:51:9
+ --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:53:9
|
LL | #[allow(unsafe_op_in_unsafe_fn)]
| ^^^^^^^^^^^^^^^^^^^^^^
error: unnecessary `unsafe` block
- --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:70:9
+ --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:72:9
|
LL | unsafe fn nested_allow_level() {
| ------------------------------ because it's nested under this `unsafe` fn
|
= note: this `unsafe` block does contain unsafe operations, but those are already allowed in an `unsafe fn`
note: the lint level is defined here
- --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:63:13
+ --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:65:13
|
LL | #[allow(unsafe_op_in_unsafe_fn)]
| ^^^^^^^^^^^^^^^^^^^^^^
error[E0133]: call to unsafe function is unsafe and requires unsafe block
- --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:76:5
+ --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:78:5
|
LL | unsf();
| ^^^^^^ call to unsafe function
= note: consult the function's documentation for information on how to avoid undefined behavior
error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
- --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:80:9
+ --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:83:9
|
LL | unsf();
| ^^^^^^ call to unsafe function
unsafe fn deny_level() {
unsf();
- //~^ ERROR call to unsafe function is unsafe and requires unsafe block
+ //[mir]~^ ERROR call to unsafe function is unsafe and requires unsafe block
+ //[thir]~^^ ERROR call to unsafe function `unsf` is unsafe and requires unsafe block
*PTR;
//~^ ERROR dereference of raw pointer is unsafe and requires unsafe block
VOID = ();
#[deny(warnings)]
unsafe fn warning_level() {
unsf();
- //~^ ERROR call to unsafe function is unsafe and requires unsafe block
+ //[mir]~^ ERROR call to unsafe function is unsafe and requires unsafe block
+ //[thir]~^^ ERROR call to unsafe function `unsf` is unsafe and requires unsafe block
*PTR;
//~^ ERROR dereference of raw pointer is unsafe and requires unsafe block
VOID = ();
fn main() {
unsf();
- //~^ ERROR call to unsafe function is unsafe and requires unsafe block
+ //[mir]~^ ERROR call to unsafe function is unsafe and requires unsafe block
+ //[thir]~^^ ERROR call to unsafe function `unsf` is unsafe and requires unsafe block
#[allow(unsafe_op_in_unsafe_fn)]
{
unsf();
- //~^ ERROR call to unsafe function is unsafe and requires unsafe function or block
+ //[mir]~^ ERROR call to unsafe function is unsafe and requires unsafe function or block
+ //[thir]~^^ ERROR call to unsafe function `unsf` is unsafe and requires unsafe function or block
}
}
-error: call to unsafe function is unsafe and requires unsafe block (error E0133)
+error: call to unsafe function `unsf` is unsafe and requires unsafe block (error E0133)
--> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:12:5
|
LL | unsf();
= note: consult the function's documentation for information on how to avoid undefined behavior
error: dereference of raw pointer is unsafe and requires unsafe block (error E0133)
- --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:14:5
+ --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:15:5
|
LL | *PTR;
| ^^^^ dereference of raw pointer
= note: raw pointers may be null, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior
error: use of mutable static is unsafe and requires unsafe block (error E0133)
- --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:16:5
+ --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:17:5
|
LL | VOID = ();
| ^^^^ use of mutable static
= note: mutable statics can be mutated by multiple threads: aliasing violations or data races will cause undefined behavior
error: unnecessary `unsafe` block
- --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:19:5
+ --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:20:5
|
LL | unsafe {}
| ^^^^^^ unnecessary `unsafe` block
LL | #![deny(unused_unsafe)]
| ^^^^^^^^^^^^^
-error: call to unsafe function is unsafe and requires unsafe block (error E0133)
- --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:27:5
+error: call to unsafe function `unsf` is unsafe and requires unsafe block (error E0133)
+ --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:28:5
|
LL | unsf();
| ^^^^^^ call to unsafe function
|
note: the lint level is defined here
- --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:25:8
+ --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:26:8
|
LL | #[deny(warnings)]
| ^^^^^^^^
= note: consult the function's documentation for information on how to avoid undefined behavior
error: dereference of raw pointer is unsafe and requires unsafe block (error E0133)
- --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:29:5
+ --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:31:5
|
LL | *PTR;
| ^^^^ dereference of raw pointer
= note: raw pointers may be null, dangling or unaligned; they can violate aliasing rules and cause data races: all of these are undefined behavior
error: use of mutable static is unsafe and requires unsafe block (error E0133)
- --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:31:5
+ --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:33:5
|
LL | VOID = ();
| ^^^^ use of mutable static
= note: mutable statics can be mutated by multiple threads: aliasing violations or data races will cause undefined behavior
error: unnecessary `unsafe` block
- --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:33:5
+ --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:35:5
|
LL | unsafe {}
| ^^^^^^ unnecessary `unsafe` block
error: unnecessary `unsafe` block
- --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:47:14
+ --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:49:14
|
LL | unsafe { unsafe { unsf() } }
| ------ ^^^^^^ unnecessary `unsafe` block
| because it's nested under this `unsafe` block
error: unnecessary `unsafe` block
- --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:58:5
+ --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:60:5
|
LL | unsafe fn allow_level() {
| ----------------------- because it's nested under this `unsafe` fn
| ^^^^^^ unnecessary `unsafe` block
error: unnecessary `unsafe` block
- --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:70:9
+ --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:72:9
|
LL | unsafe fn nested_allow_level() {
| ------------------------------ because it's nested under this `unsafe` fn
LL | unsafe { unsf() }
| ^^^^^^ unnecessary `unsafe` block
-error[E0133]: call to unsafe function is unsafe and requires unsafe block
- --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:76:5
+error[E0133]: call to unsafe function `unsf` is unsafe and requires unsafe block
+ --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:78:5
|
LL | unsf();
| ^^^^^^ call to unsafe function
|
= note: consult the function's documentation for information on how to avoid undefined behavior
-error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
- --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:80:9
+error[E0133]: call to unsafe function `unsf` is unsafe and requires unsafe function or block
+ --> $DIR/rfc-2585-unsafe_op_in_unsafe_fn.rs:83:9
|
LL | unsf();
| ^^^^^^ call to unsafe function
-error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
+error[E0133]: call to unsafe function `dummy` is unsafe and requires unsafe function or block
--> $DIR/unsafe-const-fn.rs:10:18
|
LL | const VAL: u32 = dummy(0xFFFF);
unsafe fn f() { return; }
fn main() {
- f(); //~ ERROR call to unsafe function is unsafe
+ f();
+ //[mir]~^ ERROR call to unsafe function is unsafe
+ //[thir]~^^ ERROR call to unsafe function `f` is unsafe
}
-error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
+error[E0133]: call to unsafe function `f` is unsafe and requires unsafe function or block
--> $DIR/unsafe-fn-called-from-safe.rs:7:5
|
LL | f();
fn main() {
let x = f;
- x(); //~ ERROR call to unsafe function is unsafe
+ x();
+ //[mir]~^ ERROR call to unsafe function is unsafe
+ //[thir]~^^ ERROR call to unsafe function `f` is unsafe
}
-error[E0133]: call to unsafe function is unsafe and requires unsafe function or block
+error[E0133]: call to unsafe function `f` is unsafe and requires unsafe function or block
--> $DIR/unsafe-fn-used-as-value.rs:8:5
|
LL | x();
--- /dev/null
+// Check for unused crate dep, no path
+
+// edition:2018
+// aux-crate:bar=bar.rs
+
+#![deny(unused_crate_dependencies)]
+//~^ ERROR external crate `bar` unused in
+
+fn main() {}
--- /dev/null
+error: external crate `bar` unused in `deny_attr`: remove the dependency or add `use bar as _;`
+ --> $DIR/deny-attr.rs:6:1
+ |
+LL | #![deny(unused_crate_dependencies)]
+ | ^
+ |
+note: the lint level is defined here
+ --> $DIR/deny-attr.rs:6:9
+ |
+LL | #![deny(unused_crate_dependencies)]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^
+
+error: aborting due to previous error
+
--- /dev/null
+// Check for unused crate dep, json event, deny but we're not reporting that in exit status
+
+// edition:2018
+// check-pass
+// compile-flags: -Dunused-crate-dependencies -Zunstable-options --json unused-externs-silent --error-format=json
+// aux-crate:bar=bar.rs
+
+fn main() {}
--- /dev/null
+{"lint_level":"deny","unused_extern_names":["bar"]}
--- /dev/null
+// Check for unused crate dep, json event, deny, expect compile failure
+
+// edition:2018
+// compile-flags: -Dunused-crate-dependencies -Zunstable-options --json unused-externs --error-format=json
+// aux-crate:bar=bar.rs
+
+fn main() {}
--- /dev/null
+{"lint_level":"deny","unused_extern_names":["bar"]}
--- /dev/null
+// Check for unused crate dep, deny, expect failure
+
+// edition:2018
+// compile-flags: -Dunused-crate-dependencies
+// aux-crate:bar=bar.rs
+
+fn main() {}
+//~^ ERROR external crate `bar` unused in
--- /dev/null
+error: external crate `bar` unused in `deny_cmdline`: remove the dependency or add `use bar as _;`
+ --> $DIR/deny-cmdline.rs:7:1
+ |
+LL | fn main() {}
+ | ^
+ |
+ = note: requested on the command line with `-D unused-crate-dependencies`
+
+error: aborting due to previous error
+
--- /dev/null
+// Check for unused crate dep, warn, json event, expect pass
+
+// edition:2018
+// check-pass
+// compile-flags: -Wunused-crate-dependencies -Zunstable-options --json unused-externs --error-format=json
+// aux-crate:bar=bar.rs
+
+fn main() {}
--- /dev/null
+{"lint_level":"warn","unused_extern_names":["bar"]}
assert_eq!(val, ());
}
+fn bathroom_stall() {
+ let mut i = 1;
+ matches!(2, _|_|_|_|_|_ if (i+=1) != (i+=1));
+ assert_eq!(i, 13);
+}
+
pub fn main() {
strange();
funny();
i_yield();
match_nested_if();
monkey_barrel();
+ bathroom_stall();
}
error[E0309]: the parameter type `T` may not live long enough
--> $DIR/wf-impl-associated-type-region.rs:10:16
|
-LL | impl<'a, T> Foo<'a> for T {
- | - help: consider adding an explicit lifetime bound...: `T: 'a`
LL | type Bar = &'a T;
| ^^^^^ ...so that the reference type `&'a T` does not outlive the data it points at
+ |
+help: consider adding an explicit lifetime bound...
+ |
+LL | impl<'a, T: 'a> Foo<'a> for T {
+ | ++++
error: aborting due to previous error
error[E0310]: the parameter type `T` may not live long enough
--> $DIR/wf-in-fn-type-static.rs:13:8
|
-LL | struct Foo<T> {
- | - help: consider adding an explicit lifetime bound...: `T: 'static`
-LL | // needs T: 'static
LL | x: fn() -> &'static T
| ^^^^^^^^^^^^^^^^^^ ...so that the reference type `&'static T` does not outlive the data it points at
+ |
+help: consider adding an explicit lifetime bound...
+ |
+LL | struct Foo<T: 'static> {
+ | +++++++++
error[E0310]: the parameter type `T` may not live long enough
--> $DIR/wf-in-fn-type-static.rs:18:8
|
-LL | struct Bar<T> {
- | - help: consider adding an explicit lifetime bound...: `T: 'static`
-LL | // needs T: Copy
LL | x: fn(&'static T)
| ^^^^^^^^^^^^^^ ...so that the reference type `&'static T` does not outlive the data it points at
+ |
+help: consider adding an explicit lifetime bound...
+ |
+LL | struct Bar<T: 'static> {
+ | +++++++++
error: aborting due to 2 previous errors
error[E0310]: the parameter type `T` may not live long enough
--> $DIR/wf-in-obj-type-static.rs:14:8
|
-LL | struct Foo<T> {
- | - help: consider adding an explicit lifetime bound...: `T: 'static`
-LL | // needs T: 'static
LL | x: dyn Object<&'static T>
| ^^^^^^^^^^^^^^^^^^^^^^ ...so that the reference type `&'static T` does not outlive the data it points at
+ |
+help: consider adding an explicit lifetime bound...
+ |
+LL | struct Foo<T: 'static> {
+ | +++++++++
error: aborting due to previous error
error[E0309]: the parameter type `T` may not live long enough
--> $DIR/wf-outlives-ty-in-fn-or-trait.rs:9:16
|
-LL | impl<'a, T> Trait<'a, T> for usize {
- | - help: consider adding an explicit lifetime bound...: `T: 'a`
LL | type Out = &'a fn(T);
| ^^^^^^^^^ ...so that the reference type `&'a fn(T)` does not outlive the data it points at
+ |
+help: consider adding an explicit lifetime bound...
+ |
+LL | impl<'a, T: 'a> Trait<'a, T> for usize {
+ | ++++
error[E0309]: the parameter type `T` may not live long enough
--> $DIR/wf-outlives-ty-in-fn-or-trait.rs:19:16
|
-LL | impl<'a, T> Trait<'a, T> for u32 {
- | - help: consider adding an explicit lifetime bound...: `T: 'a`
LL | type Out = &'a dyn Baz<T>;
| ^^^^^^^^^^^^^^ ...so that the reference type `&'a (dyn Baz<T> + 'a)` does not outlive the data it points at
+ |
+help: consider adding an explicit lifetime bound...
+ |
+LL | impl<'a, T: 'a> Trait<'a, T> for u32 {
+ | ++++
error: aborting due to 2 previous errors
-Subproject commit edffc4ada3d77799e5a04eeafd9b2f843d29fc23
+Subproject commit f63f23ff1f1a12ede8585bbd1bbf0c536e50293d
if rust_cc > self.limit.limit() {
let fn_span = match kind {
- FnKind::ItemFn(ident, _, _, _) | FnKind::Method(ident, _, _) => ident.span,
+ FnKind::ItemFn(ident, _, _) | FnKind::Method(ident, _) => ident.span,
FnKind::Closure => {
let header_span = body_span.with_hi(decl.output.span().lo());
let pos = snippet_opt(cx, header_span).and_then(|snip| {
}
// The `module_name_repetitions` lint should only trigger if the item has the module in its
// name. Having the same name is accepted.
- if item.vis.node.is_pub() && item_camel.len() > mod_camel.len() {
+ if cx.tcx.visibility(item.def_id).is_public() && item_camel.len() > mod_camel.len() {
let matching = count_match_start(mod_camel, &item_camel);
let rmatching = count_match_end(mod_camel, &item_camel);
let nchars = mod_camel.chars().count();
if !attrs.iter().any(|a| a.has_name(sym::non_exhaustive));
then {
let (lint, msg) = if let ItemKind::Struct(ref v, ..) = item.kind {
- if v.fields().iter().any(|f| !f.vis.node.is_pub()) {
+ if v.fields().iter().any(|f| {
+ let def_id = cx.tcx.hir().local_def_id(f.hir_id);
+ !cx.tcx.visibility(def_id).is_public()
+ }) {
// skip structs with private fields
return;
}
diag.span_suggestion(
attr.span,
"remove the attribute",
- "".into(),
+ "",
Applicability::MachineApplicable,
);
},
hir_id: hir::HirId,
) {
let unsafety = match kind {
- intravisit::FnKind::ItemFn(_, _, hir::FnHeader { unsafety, .. }, _) => unsafety,
- intravisit::FnKind::Method(_, sig, _) => sig.header.unsafety,
+ intravisit::FnKind::ItemFn(_, _, hir::FnHeader { unsafety, .. }) => unsafety,
+ intravisit::FnKind::Method(_, sig) => sig.header.unsafety,
intravisit::FnKind::Closure => return,
};
header: hir::FnHeader { abi: Abi::Rust, .. },
..
},
- _,
)
- | intravisit::FnKind::ItemFn(_, _, hir::FnHeader { abi: Abi::Rust, .. }, _) => check_arg_number(
+ | intravisit::FnKind::ItemFn(_, _, hir::FnHeader { abi: Abi::Rust, .. }) => check_arg_number(
cx,
decl,
span.with_hi(decl.output.span().hi()),
use rustc_hir::{
BareFnTy, BodyId, FnDecl, GenericArg, GenericBound, GenericParam, GenericParamKind, Generics, ImplItem,
ImplItemKind, Item, ItemKind, LangItem, Lifetime, LifetimeName, ParamName, PolyTraitRef, TraitBoundModifier,
- TraitFn, TraitItem, TraitItemKind, Ty, TyKind, WhereClause, WherePredicate,
+ TraitFn, TraitItem, TraitItemKind, Ty, TyKind, WherePredicate,
};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_lint_pass, declare_tool_lint};
span: Span,
report_extra_lifetimes: bool,
) {
- if span.from_expansion() || has_where_lifetimes(cx, &generics.where_clause) {
+ if span.from_expansion() || has_where_lifetimes(cx, generics) {
return;
}
.iter()
.filter(|param| matches!(param.kind, GenericParamKind::Type { .. }));
for typ in types {
- for bound in typ.bounds {
- let mut visitor = RefVisitor::new(cx);
- walk_param_bound(&mut visitor, bound);
- if visitor.lts.iter().any(|lt| matches!(lt, RefLt::Named(_))) {
- return;
+ for pred in generics.bounds_for_param(cx.tcx.hir().local_def_id(typ.hir_id)) {
+ if pred.in_where_clause {
+ // has_where_lifetimes checked that this predicate contains no lifetime.
+ continue;
}
- if let GenericBound::Trait(ref trait_ref, _) = *bound {
- let params = &trait_ref
- .trait_ref
- .path
- .segments
- .last()
- .expect("a path must have at least one segment")
- .args;
- if let Some(params) = *params {
- let lifetimes = params.args.iter().filter_map(|arg| match arg {
- GenericArg::Lifetime(lt) => Some(lt),
- _ => None,
- });
- for bound in lifetimes {
- if bound.name != LifetimeName::Static && !bound.is_elided() {
- return;
+
+ for bound in pred.bounds {
+ let mut visitor = RefVisitor::new(cx);
+ walk_param_bound(&mut visitor, bound);
+ if visitor.lts.iter().any(|lt| matches!(lt, RefLt::Named(_))) {
+ return;
+ }
+ if let GenericBound::Trait(ref trait_ref, _) = *bound {
+ let params = &trait_ref
+ .trait_ref
+ .path
+ .segments
+ .last()
+ .expect("a path must have at least one segment")
+ .args;
+ if let Some(params) = *params {
+ let lifetimes = params.args.iter().filter_map(|arg| match arg {
+ GenericArg::Lifetime(lt) => Some(lt),
+ _ => None,
+ });
+ for bound in lifetimes {
+ if bound.name != LifetimeName::Static && !bound.is_elided() {
+ return;
+ }
}
}
}
let mut allowed_lts = FxHashSet::default();
for par in named_generics.iter() {
if let GenericParamKind::Lifetime { .. } = par.kind {
- if par.bounds.is_empty() {
- allowed_lts.insert(RefLt::Named(par.name.ident().name));
- }
+ allowed_lts.insert(RefLt::Named(par.name.ident().name));
}
}
allowed_lts.insert(RefLt::Unnamed);
/// Are any lifetimes mentioned in the `where` clause? If so, we don't try to
/// reason about elision.
-fn has_where_lifetimes<'tcx>(cx: &LateContext<'tcx>, where_clause: &'tcx WhereClause<'_>) -> bool {
- for predicate in where_clause.predicates {
+fn has_where_lifetimes<'tcx>(cx: &LateContext<'tcx>, generics: &'tcx Generics<'_>) -> bool {
+ for predicate in generics.predicates {
match *predicate {
WherePredicate::RegionPredicate(..) => return true,
WherePredicate::BoundPredicate(ref pred) => {
PatKind::TupleStruct(ref qpath, [sub_pat], _) => {
if let PatKind::Wild = sub_pat.kind {
let res = cx.typeck_results().qpath_res(qpath, check_pat.hir_id);
- let Some(id) = res.opt_def_id().and_then(|ctor_id| cx.tcx.parent(ctor_id)) else { return };
+ let Some(id) = res.opt_def_id().map(|ctor_id| cx.tcx.parent(ctor_id)) else { return };
let lang_items = cx.tcx.lang_items();
if Some(id) == lang_items.result_ok_variant() {
("is_ok()", try_get_generic_ty(op_ty, 0).unwrap_or(op_ty))
fn no_op_msg(cx: &LateContext<'_>) -> Option<String> {
let variant_id = cx.tcx.lang_items().require(Self::VARIANT_LANG_ITEM).ok()?;
- let item_id = cx.tcx.parent(variant_id)?;
+ let item_id = cx.tcx.parent(variant_id);
Some(format!(
"using `{}.{}({})`, which is a no-op",
cx.tcx.item_name(item_id),
fn lint_msg(cx: &LateContext<'_>) -> Option<String> {
let variant_id = cx.tcx.lang_items().require(Self::VARIANT_LANG_ITEM).ok()?;
- let item_id = cx.tcx.parent(variant_id)?;
+ let item_id = cx.tcx.parent(variant_id);
Some(format!(
"using `{}.{}(|x| {}(y))`, which is more succinctly expressed as `{}(|x| y)`",
cx.tcx.item_name(item_id),
if_chain! {
if let Some(adt) = cx.typeck_results().expr_ty(recv).ty_adt_def();
if let Ok(vid) = cx.tcx.lang_items().require(Self::VARIANT_LANG_ITEM);
- if Some(adt.did()) == cx.tcx.parent(vid);
+ if adt.did() == cx.tcx.parent(vid);
then {} else { return false; }
}
fn is_variant(cx: &LateContext<'_>, res: Res) -> bool {
if let Res::Def(DefKind::Ctor(CtorOf::Variant, CtorKind::Fn), id) = res {
if let Ok(variant_id) = cx.tcx.lang_items().require(Self::VARIANT_LANG_ITEM) {
- return cx.tcx.parent(id) == Some(variant_id);
+ return cx.tcx.parent(id) == variant_id;
}
}
false
if_chain! {
if let Some(args) = method_chain_args(info.chain, chain_methods);
if let hir::ExprKind::Call(fun, [arg_char]) = info.other.kind;
- if let Some(id) = path_def_id(cx, fun).and_then(|ctor_id| cx.tcx.parent(ctor_id));
+ if let Some(id) = path_def_id(cx, fun).map(|ctor_id| cx.tcx.parent(ctor_id));
if Some(id) == cx.tcx.lang_items().option_some_variant();
then {
let mut applicability = Applicability::MachineApplicable;
let arg_snippet = snippet(cx, span, "..");
let body = cx.tcx.hir().body(id);
if let Some((func, [arg_char])) = reduce_unit_expression(&body.value);
- if let Some(id) = path_def_id(cx, func).and_then(|ctor_id| cx.tcx.parent(ctor_id));
+ if let Some(id) = path_def_id(cx, func).map(|ctor_id| cx.tcx.parent(ctor_id));
if Some(id) == cx.tcx.lang_items().option_some_variant();
then {
let func_snippet = snippet(cx, arg_char.span, "..");
hir::ItemKind::Fn(..) => {
// ignore main()
if it.ident.name == sym::main {
- let at_root = cx.tcx.local_parent(it.def_id) == Some(CRATE_DEF_ID);
+ let at_root = cx.tcx.local_parent(it.def_id) == CRATE_DEF_ID;
if at_root {
return;
}
}
match kind {
- FnKind::ItemFn(.., header, _) => {
+ FnKind::ItemFn(.., header) => {
let attrs = cx.tcx.hir().attrs(hir_id);
if header.abi != Abi::Rust || requires_exact_signature(attrs) {
return;
|x| Cow::from(format!("change `{}` to", x)),
)
.as_ref(),
- suggestion.into(),
+ suggestion,
Applicability::Unspecified,
);
}
|x| Cow::from(format!("change `{}` to", x))
)
.as_ref(),
- suggestion.into(),
+ suggestion,
Applicability::Unspecified,
);
}
..
}) = item.kind
{
- for assoc_item in items {
+ for assoc_item in *items {
if assoc_item.kind == (hir::AssocItemKind::Fn { has_self: false }) {
let impl_item = cx.tcx.hir().impl_item(assoc_item.id);
if in_external_macro(cx.sess(), impl_item.span) {
if let Some(eq_trait) = cx.tcx.lang_items().eq_trait();
if trait_ref.path.res.def_id() == eq_trait;
then {
- for impl_item in impl_items {
+ for impl_item in *impl_items {
if impl_item.ident.name == sym::ne {
span_lint_hir(
cx,
}
match kind {
- FnKind::ItemFn(.., header, _) => {
+ FnKind::ItemFn(.., header) => {
if header.abi != Abi::Rust {
return;
}
use clippy_utils::diagnostics::span_lint_and_then;
use rustc_errors::Applicability;
-use rustc_hir::{Item, ItemKind, VisibilityKind};
+use rustc_hir::{Item, ItemKind};
use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::ty;
use rustc_session::{declare_tool_lint, impl_lint_pass};
+use rustc_span::def_id::CRATE_DEF_ID;
declare_clippy_lint! {
/// ### What it does
impl<'tcx> LateLintPass<'tcx> for RedundantPubCrate {
fn check_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx Item<'tcx>) {
- if let VisibilityKind::Crate { .. } = item.vis.node {
+ if cx.tcx.visibility(item.def_id) == ty::Visibility::Restricted(CRATE_DEF_ID.to_def_id()) {
if !cx.access_levels.is_exported(item.def_id) && self.is_exported.last() == Some(&false) {
let span = item.span.with_hi(item.ident.span.hi());
let descr = cx.tcx.def_kind(item.def_id).descr(item.def_id.to_def_id());
&format!("pub(crate) {} inside private module", descr),
|diag| {
diag.span_suggestion(
- item.vis.span,
+ item.vis_span,
"consider using",
"pub".to_string(),
Applicability::MachineApplicable,
) {
if_chain! {
// We are only interested in methods, not in functions or associated functions.
- if matches!(kind, FnKind::Method(_, _, _));
+ if matches!(kind, FnKind::Method(_, _));
if let Some(fn_def) = cx.tcx.hir().opt_local_def_id(hir_id);
if let Some(impl_def) = cx.tcx.impl_of_method(fn_def.to_def_id());
// We don't want this method to be te implementation of a trait because the
if did == visit_did {
let mut seen_str = None;
let mut seen_string = None;
- for item in items {
+ for item in *items {
match item.ident.as_str() {
"visit_str" => seen_str = Some(item.span),
"visit_string" => seen_string = Some(item.span),
use rustc_errors::Applicability;
use rustc_hir::def::Res;
use rustc_hir::{
- GenericBound, Generics, Item, ItemKind, Node, ParamName, Path, PathSegment, QPath, TraitItem, Ty, TyKind,
- WherePredicate,
+ GenericBound, Generics, Item, ItemKind, Node, Path, PathSegment, QPath, TraitItem, Ty, TyKind, WherePredicate,
};
use rustc_lint::{LateContext, LateLintPass};
use rustc_session::{declare_tool_lint, impl_lint_pass};
}
fn check_trait_item(&mut self, cx: &LateContext<'tcx>, item: &'tcx TraitItem<'tcx>) {
- let Generics { where_clause, .. } = &item.generics;
let mut self_bounds_map = FxHashMap::default();
- for predicate in where_clause.predicates {
+ for predicate in item.generics.predicates {
if_chain! {
if let WherePredicate::BoundPredicate(ref bound_predicate) = predicate;
if !bound_predicate.span.from_expansion();
}
let mut map: UnhashMap<SpanlessTy<'_, '_>, Vec<&GenericBound<'_>>> = UnhashMap::default();
let mut applicability = Applicability::MaybeIncorrect;
- for bound in gen.where_clause.predicates {
+ for bound in gen.predicates {
if_chain! {
if let WherePredicate::BoundPredicate(ref p) = bound;
if p.bounds.len() as u64 <= self.max_trait_bounds;
}
fn check_trait_bound_duplication(cx: &LateContext<'_>, gen: &'_ Generics<'_>) {
- if gen.span.from_expansion() || gen.params.is_empty() || gen.where_clause.predicates.is_empty() {
+ if gen.span.from_expansion() || gen.params.is_empty() || gen.predicates.is_empty() {
return;
}
- let mut map = FxHashMap::default();
- for param in gen.params {
- if let ParamName::Plain(ref ident) = param.name {
- let res = param
- .bounds
- .iter()
- .filter_map(get_trait_info_from_bound)
- .collect::<Vec<_>>();
- map.insert(*ident, res);
- }
- }
-
- for predicate in gen.where_clause.predicates {
+ let mut map = FxHashMap::<_, Vec<_>>::default();
+ for predicate in gen.predicates {
if_chain! {
if let WherePredicate::BoundPredicate(ref bound_predicate) = predicate;
if !bound_predicate.span.from_expansion();
if let TyKind::Path(QPath::Resolved(_, Path { segments, .. })) = bound_predicate.bounded_ty.kind;
if let Some(segment) = segments.first();
- if let Some(trait_resolutions_direct) = map.get(&segment.ident);
then {
- for (res_where, _, _) in bound_predicate.bounds.iter().filter_map(get_trait_info_from_bound) {
- if let Some((_, _, span_direct)) = trait_resolutions_direct
+ for (res_where, _, span_where) in bound_predicate.bounds.iter().filter_map(get_trait_info_from_bound) {
+ let trait_resolutions_direct = map.entry(segment.ident).or_default();
+ if let Some((_, span_direct)) = trait_resolutions_direct
.iter()
- .find(|(res_direct, _, _)| *res_direct == res_where) {
+ .find(|(res_direct, _)| *res_direct == res_where) {
span_lint_and_help(
cx,
TRAIT_DUPLICATION_IN_BOUNDS,
"consider removing this trait bound",
);
}
+ else {
+ trait_resolutions_direct.push((res_where, span_where))
+ }
}
}
}
if let Some(Node::GenericParam(generic_param)) = cx.tcx.hir().get_if_local(did);
if let GenericParamKind::Type { synthetic, .. } = generic_param.kind;
if synthetic;
+ if let Some(generics) = cx.tcx.hir().get_generics(id.owner);
+ if let Some(pred) = generics.bounds_for_param(did.expect_local()).next();
then {
- Some(generic_param.bounds)
+ Some(pred.bounds)
} else {
None
}
span: Span,
hir_id: HirId,
) {
- if let FnKind::ItemFn(_, _, FnHeader { asyncness, .. }, _) = &fn_kind {
+ if let FnKind::ItemFn(_, _, FnHeader { asyncness, .. }) = &fn_kind {
if matches!(asyncness, IsAsync::Async) {
let mut visitor = AsyncFnVisitor { cx, found_await: false };
walk_fn(&mut visitor, fn_kind, fn_decl, body.id(), span, hir_id);
use rustc_ast::ast::{Attribute, InlineAsmTemplatePiece};
use rustc_hir as hir;
use rustc_lint::{LateContext, LateLintPass, LintContext};
+use rustc_middle::ty;
use rustc_session::Session;
use rustc_session::{declare_lint_pass, declare_tool_lint};
return;
}
println!("impl item `{}`", item.ident.name);
- match item.vis.node {
- hir::VisibilityKind::Public => println!("public"),
- hir::VisibilityKind::Crate(_) => println!("visible crate wide"),
- hir::VisibilityKind::Restricted { path, .. } => println!(
- "visible in module `{}`",
- rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_path(path, false))
- ),
- hir::VisibilityKind::Inherited => println!("visibility inherited from outer item"),
+ match cx.tcx.visibility(item.def_id) {
+ ty::Visibility::Public => println!("public"),
+ ty::Visibility::Restricted(def_id) => {
+ if def_id.is_top_level_module() {
+ println!("visible crate wide")
+ } else {
+ println!("visible in module `{}`", cx.tcx.def_path_str(def_id))
+ }
+ },
+ ty::Visibility::Invisible => println!("invisible"),
}
match item.kind {
hir::ImplItemKind::Const(_, body_id) => {
fn print_item(cx: &LateContext<'_>, item: &hir::Item<'_>) {
let did = item.def_id;
println!("item `{}`", item.ident.name);
- match item.vis.node {
- hir::VisibilityKind::Public => println!("public"),
- hir::VisibilityKind::Crate(_) => println!("visible crate wide"),
- hir::VisibilityKind::Restricted { path, .. } => println!(
- "visible in module `{}`",
- rustc_hir_pretty::to_string(rustc_hir_pretty::NO_ANN, |s| s.print_path(path, false))
- ),
- hir::VisibilityKind::Inherited => println!("visibility inherited from outer item"),
+ match cx.tcx.visibility(item.def_id) {
+ ty::Visibility::Public => println!("public"),
+ ty::Visibility::Restricted(def_id) => {
+ if def_id.is_top_level_module() {
+ println!("visible crate wide")
+ } else {
+ println!("visible in module `{}`", cx.tcx.def_path_str(def_id))
+ }
+ },
+ ty::Visibility::Invisible => println!("invisible"),
}
match item.kind {
hir::ItemKind::ExternCrate(ref _renamed_from) => {
Item, ItemKind, PathSegment, UseKind,
};
use rustc_lint::{LateContext, LateLintPass};
+use rustc_middle::ty;
use rustc_session::{declare_tool_lint, impl_lint_pass};
use rustc_span::symbol::kw;
use rustc_span::{sym, BytePos};
if is_test_module_or_function(cx.tcx, item) {
self.test_modules_deep = self.test_modules_deep.saturating_add(1);
}
- if item.vis.node.is_pub() || item.vis.node.is_pub_restricted() {
+ let module = cx.tcx.parent_module_from_def_id(item.def_id);
+ if cx.tcx.visibility(item.def_id) != ty::Visibility::Restricted(module.to_def_id()) {
return;
}
if_chain! {
if let QPath::Resolved(_, path) = qpath {
if let Res::Def(DefKind::Ctor(..), ctor_id) = path.res {
if let Ok(item_id) = cx.tcx.lang_items().require(lang_item) {
- return cx.tcx.parent(ctor_id) == Some(item_id);
+ return cx.tcx.parent(ctor_id) == item_id;
}
}
}
/// Checks if the given function kind is an async function.
pub fn is_async_fn(kind: FnKind<'_>) -> bool {
- matches!(kind, FnKind::ItemFn(_, _, header, _) if header.asyncness == IsAsync::Async)
+ matches!(kind, FnKind::ItemFn(_, _, header) if header.asyncness == IsAsync::Async)
}
/// Peels away all the compiler generated code surrounding the body of an async function,
| ast::ExprKind::Path(..)
| ast::ExprKind::Repeat(..)
| ast::ExprKind::Ret(..)
+ | ast::ExprKind::Yeet(..)
| ast::ExprKind::Struct(..)
| ast::ExprKind::Try(..)
| ast::ExprKind::TryBlock(..)
|
= note: `-D clippy::extra-unused-lifetimes` implied by `-D warnings`
-error: this lifetime isn't used in the function definition
- --> $DIR/extra_unused_lifetimes.rs:16:25
- |
-LL | fn unused_lt_transitive<'a, 'b: 'a>(x: &'b u8) {
- | ^^
-
error: this lifetime isn't used in the function definition
--> $DIR/extra_unused_lifetimes.rs:41:10
|
LL | fn unused_lt<'a>(x: u8) {}
| ^^
-error: aborting due to 4 previous errors
+error: aborting due to 3 previous errors
LL | fn baz<'a>(&'a self) -> impl Foo + 'a {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
- --> $DIR/needless_lifetimes.rs:307:5
- |
-LL | fn impl_trait_elidable_nested_named_lifetimes<'a>(i: &'a i32, f: impl for<'b> Fn(&'b i32) -> &'b i32) -> &'a i32 {
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-
error: explicit lifetimes given in parameter types where they could be elided (or replaced with `'_` if needed by type declaration)
--> $DIR/needless_lifetimes.rs:310:5
|
LL | fn lifetime_elsewhere_provided<'a>(self: Box<Self>, here: &'a ()) -> &'a () {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
-error: aborting due to 32 previous errors
+error: aborting due to 31 previous errors
/// The rust-demangler executable.
pub rust_demangler_path: Option<PathBuf>,
- /// The Python executable to use for LLDB.
- pub lldb_python: String,
-
- /// The Python executable to use for htmldocck.
- pub docck_python: String,
+ /// The Python executable to use for LLDB and htmldocck.
+ pub python: String,
/// The jsondocck executable.
pub jsondocck_path: Option<String>,
pub should_ice: bool,
// If true, the stderr is expected to be different across bit-widths.
pub stderr_per_bitwidth: bool,
+ // The MIR opt to unit test, if any
+ pub mir_unit_test: Option<String>,
}
mod directives {
pub const STDERR_PER_BITWIDTH: &'static str = "stderr-per-bitwidth";
pub const INCREMENTAL: &'static str = "incremental";
pub const KNOWN_BUG: &'static str = "known-bug";
+ pub const MIR_UNIT_TEST: &'static str = "unit-test";
// This isn't a real directive, just one that is probably mistyped often
pub const INCORRECT_COMPILER_FLAGS: &'static str = "compiler-flags";
}
assembly_output: None,
should_ice: false,
stderr_per_bitwidth: false,
+ mir_unit_test: None,
}
}
config.set_name_directive(ln, STDERR_PER_BITWIDTH, &mut self.stderr_per_bitwidth);
config.set_name_directive(ln, INCREMENTAL, &mut self.incremental);
config.set_name_directive(ln, KNOWN_BUG, &mut self.known_bug);
+ config.set_name_value_directive(ln, MIR_UNIT_TEST, &mut self.mir_unit_test, |s| {
+ s.trim().to_string()
+ });
});
}
"--compile-lib-path=",
"--run-lib-path=",
"--rustc-path=",
- "--lldb-python=",
- "--docck-python=",
+ "--python=",
"--jsondocck-path=",
"--src-base=",
"--build-base=",
artifact: PathBuf,
}
+#[derive(Deserialize)]
+struct UnusedExternNotification {
+ #[allow(dead_code)]
+ lint_level: String,
+ #[allow(dead_code)]
+ unused_extern_names: Vec<String>,
+}
+
#[derive(Deserialize, Clone)]
struct DiagnosticSpan {
file_name: String,
} else if serde_json::from_str::<ArtifactNotification>(line).is_ok() {
// Ignore the notification.
None
+ } else if serde_json::from_str::<UnusedExternNotification>(line).is_ok() {
+ // Ignore the notification.
+ None
} else {
print!(
"failed to decode compiler output as json: line: {}\noutput: {}",
.reqopt("", "rustc-path", "path to rustc to use for compiling", "PATH")
.optopt("", "rustdoc-path", "path to rustdoc to use for compiling", "PATH")
.optopt("", "rust-demangler-path", "path to rust-demangler to use in tests", "PATH")
- .reqopt("", "lldb-python", "path to python to use for doc tests", "PATH")
- .reqopt("", "docck-python", "path to python to use for doc tests", "PATH")
+ .reqopt("", "python", "path to python to use for doc tests", "PATH")
.optopt("", "jsondocck-path", "path to jsondocck to use for doc tests", "PATH")
.optopt("", "valgrind-path", "path to Valgrind executable for Valgrind tests", "PROGRAM")
.optflag("", "force-valgrind", "fail if Valgrind tests cannot be run under Valgrind")
rustc_path: opt_path(matches, "rustc-path"),
rustdoc_path: matches.opt_str("rustdoc-path").map(PathBuf::from),
rust_demangler_path: matches.opt_str("rust-demangler-path").map(PathBuf::from),
- lldb_python: matches.opt_str("lldb-python").unwrap(),
- docck_python: matches.opt_str("docck-python").unwrap(),
+ python: matches.opt_str("python").unwrap(),
jsondocck_path: matches.opt_str("jsondocck-path"),
valgrind_path: matches.opt_str("valgrind-path"),
force_valgrind: matches.opt_present("force-valgrind"),
output_base_dir(config, testpaths, revision).join("stamp")
}
+fn files_related_to_test(
+ config: &Config,
+ testpaths: &TestPaths,
+ props: &EarlyProps,
+ revision: Option<&str>,
+) -> Vec<PathBuf> {
+ let mut related = vec![];
+
+ if testpaths.file.is_dir() {
+ // run-make tests use their individual directory
+ for entry in WalkDir::new(&testpaths.file) {
+ let path = entry.unwrap().into_path();
+ if path.is_file() {
+ related.push(path);
+ }
+ }
+ } else {
+ related.push(testpaths.file.clone());
+ }
+
+ for aux in &props.aux {
+ let path = testpaths.file.parent().unwrap().join("auxiliary").join(aux);
+ related.push(path);
+ }
+
+ // UI test files.
+ for extension in UI_EXTENSIONS {
+ let path = expected_output_path(testpaths, revision, &config.compare_mode, extension);
+ related.push(path);
+ }
+
+ related
+}
+
fn is_up_to_date(
config: &Config,
testpaths: &TestPaths,
// Check timestamps.
let mut inputs = inputs.clone();
- // Use `add_dir` to account for run-make tests, which use their individual directory
- inputs.add_dir(&testpaths.file);
-
- for aux in &props.aux {
- let path = testpaths.file.parent().unwrap().join("auxiliary").join(aux);
+ for path in files_related_to_test(config, testpaths, props, revision) {
inputs.add_path(&path);
}
- // UI test files.
- for extension in UI_EXTENSIONS {
- let path = &expected_output_path(testpaths, revision, &config.compare_mode, extension);
- inputs.add_path(path);
- }
-
inputs < Stamp::from_path(&stamp_name)
}
}
Some(Debugger::Lldb) => {
- config.lldb_python.hash(&mut hash);
+ config.python.hash(&mut hash);
config.lldb_python_dir.hash(&mut hash);
env::var_os("PATH").hash(&mut hash);
env::var_os("PYTHONPATH").hash(&mut hash);
// Prepare the lldb_batchmode which executes the debugger script
let lldb_script_path = rust_src_root.join("src/etc/lldb_batchmode.py");
self.cmd2procres(
- Command::new(&self.config.lldb_python)
+ Command::new(&self.config.python)
.arg(&lldb_script_path)
.arg(test_executable)
.arg(debugger_script)
rustc.args(&[
"-Copt-level=1",
"-Zdump-mir=all",
- "-Zmir-opt-level=4",
"-Zvalidate-mir",
"-Zdump-mir-exclude-pass-number",
]);
+ if let Some(pass) = &self.props.mir_unit_test {
+ rustc.args(&["-Zmir-opt-level=0", &format!("-Zmir-enable-passes=+{}", pass)]);
+ } else {
+ rustc.arg("-Zmir-opt-level=4");
+ }
let mir_dump_dir = self.get_mir_dump_dir();
let _ = fs::remove_dir_all(&mir_dump_dir);
self.check_rustdoc_test_option(proc_res);
} else {
let root = self.config.find_rust_src_root().unwrap();
- let mut cmd = Command::new(&self.config.docck_python);
+ let mut cmd = Command::new(&self.config.python);
cmd.arg(root.join("src/etc/htmldocck.py")).arg(&out_dir).arg(&self.testpaths.file);
if self.config.bless {
cmd.arg("--bless");
let mut json_out = out_dir.join(self.testpaths.file.file_stem().unwrap());
json_out.set_extension("json");
let res = self.cmd2procres(
- Command::new(&self.config.docck_python)
+ Command::new(&self.config.python)
.arg(root.join("src/etc/check_missing_items.py"))
.arg(&json_out),
);
.stdout(Stdio::piped())
.stderr(Stdio::piped())
.env("TARGET", &self.config.target)
- .env("PYTHON", &self.config.docck_python)
+ .env("PYTHON", &self.config.python)
.env("S", src_root)
.env("RUST_BUILD_STAGE", &self.config.stage_id)
.env("RUSTC", cwd.join(&self.config.rustc_path))
-Subproject commit edd4858846003dc96020a0de07a1499e3224e633
+Subproject commit a71a0083937671d79e16bfac4c7b8cab9c8ab9bb
-Subproject commit 24cf957627d5ede1b395f92ff871fd7a281d49a4
+Subproject commit 5dce1ff0212e467271c9e895478670c74d847ee9
try {
// This is more convenient that setting fields one by one.
let args = [
- "--no-screenshot-comparison",
"--variable", "DOC_PATH", opts["doc_folder"],
];
if (opts["debug"]) {
}
// Stupid function extractor for array.
-function extractArrayVariable(content, arrayName) {
- var splitter = "var " + arrayName;
+function extractArrayVariable(content, arrayName, kind) {
+ if (typeof kind === "undefined") {
+ kind = "let ";
+ }
+ var splitter = kind + arrayName;
while (true) {
var start = content.indexOf(splitter);
if (start === -1) {
}
content = content.slice(start + 1);
}
+ if (kind === "let ") {
+ return extractArrayVariable(content, arrayName, "const ");
+ }
return null;
}
// Stupid function extractor for variable.
-function extractVariable(content, varName) {
- var splitter = "var " + varName;
+function extractVariable(content, varName, kind) {
+ if (typeof kind === "undefined") {
+ kind = "let ";
+ }
+ var splitter = kind + varName;
while (true) {
var start = content.indexOf(splitter);
if (start === -1) {
}
content = content.slice(start + 1);
}
+ if (kind === "let ") {
+ return extractVariable(content, varName, "const ");
+ }
return null;
}
use std::cmp::min;
use itertools::Itertools;
-use rustc_ast::token::{DelimToken, LitKind};
+use rustc_ast::token::{Delimiter, LitKind};
use rustc_ast::{ast, ptr};
use rustc_span::{BytePos, Span};
ast::ExprKind::Ret(Some(ref expr)) => {
rewrite_unary_prefix(context, "return ", &**expr, shape)
}
+ ast::ExprKind::Yeet(None) => Some("do yeet".to_owned()),
+ ast::ExprKind::Yeet(Some(ref expr)) => {
+ rewrite_unary_prefix(context, "do yeet ", &**expr, shape)
+ }
ast::ExprKind::Box(ref expr) => rewrite_unary_prefix(context, "box ", &**expr, shape),
ast::ExprKind::AddrOf(borrow_kind, mutability, ref expr) => {
rewrite_expr_addrof(context, borrow_kind, mutability, expr, shape)
context: &'a RewriteContext<'_>,
shape: Shape,
force_separator_tactic: Option<SeparatorTactic>,
- delim_token: Option<DelimToken>,
+ delim_token: Option<Delimiter>,
) -> Option<String> {
overflow::rewrite_with_square_brackets(
context,
}
ast::ExprKind::MacCall(ref mac) => {
match (
- rustc_ast::ast::MacDelimiter::from_token(mac.args.delim()),
+ rustc_ast::ast::MacDelimiter::from_token(mac.args.delim().unwrap()),
context.config.overflow_delimited_expr(),
) {
(Some(ast::MacDelimiter::Bracket), true)
use std::collections::HashMap;
use std::panic::{catch_unwind, AssertUnwindSafe};
-use rustc_ast::token::{BinOpToken, DelimToken, Token, TokenKind};
+use rustc_ast::token::{BinOpToken, Delimiter, Token, TokenKind};
use rustc_ast::tokenstream::{Cursor, Spacing, TokenStream, TokenTree};
use rustc_ast::{ast, ptr};
use rustc_ast_pretty::pprust;
let is_forced_bracket = FORCED_BRACKET_MACROS.contains(&¯o_name[..]);
let style = if is_forced_bracket && !is_nested_macro {
- DelimToken::Bracket
+ Delimiter::Bracket
} else {
original_style
};
let has_comment = contains_comment(context.snippet(mac.span()));
if ts.is_empty() && !has_comment {
return match style {
- DelimToken::Paren if position == MacroPosition::Item => {
+ Delimiter::Parenthesis if position == MacroPosition::Item => {
Some(format!("{}();", macro_name))
}
- DelimToken::Bracket if position == MacroPosition::Item => {
+ Delimiter::Bracket if position == MacroPosition::Item => {
Some(format!("{}[];", macro_name))
}
- DelimToken::Paren => Some(format!("{}()", macro_name)),
- DelimToken::Bracket => Some(format!("{}[]", macro_name)),
- DelimToken::Brace => Some(format!("{} {{}}", macro_name)),
+ Delimiter::Parenthesis => Some(format!("{}()", macro_name)),
+ Delimiter::Bracket => Some(format!("{}[]", macro_name)),
+ Delimiter::Brace => Some(format!("{} {{}}", macro_name)),
_ => unreachable!(),
};
}
}
match style {
- DelimToken::Paren => {
+ Delimiter::Parenthesis => {
// Handle special case: `vec!(expr; expr)`
if vec_with_semi {
handle_vec_semi(context, shape, arg_vec, macro_name, style)
})
}
}
- DelimToken::Bracket => {
+ Delimiter::Bracket => {
// Handle special case: `vec![expr; expr]`
if vec_with_semi {
handle_vec_semi(context, shape, arg_vec, macro_name, style)
Some(format!("{}{}", rewrite, comma))
}
}
- DelimToken::Brace => {
+ Delimiter::Brace => {
// For macro invocations with braces, always put a space between
// the `macro_name!` and `{ /* macro_body */ }` but skip modifying
// anything in between the braces (for now).
shape: Shape,
arg_vec: Vec<MacroArg>,
macro_name: String,
- delim_token: DelimToken,
+ delim_token: Delimiter,
) -> Option<String> {
let (left, right) = match delim_token {
- DelimToken::Paren => ("(", ")"),
- DelimToken::Bracket => ("[", "]"),
+ Delimiter::Parenthesis => ("(", ")"),
+ Delimiter::Bracket => ("[", "]"),
_ => unreachable!(),
};
/// e.g., `$($foo: expr),*`
Repeat(
/// `()`, `[]` or `{}`.
- DelimToken,
+ Delimiter,
/// Inner arguments inside delimiters.
Vec<ParsedMacroArg>,
/// Something after the closing delimiter and the repeat token, if available.
Token,
),
/// e.g., `[derive(Debug)]`
- Delimited(DelimToken, Vec<ParsedMacroArg>),
+ Delimited(Delimiter, Vec<ParsedMacroArg>),
/// A possible separator. e.g., `,` or `;`.
Separator(String, String),
/// Other random stuff that does not fit to other kinds.
fn delim_token_to_str(
context: &RewriteContext<'_>,
- delim_token: DelimToken,
+ delim_token: Delimiter,
shape: Shape,
use_multiple_lines: bool,
inner_is_empty: bool,
) -> (String, String) {
let (lhs, rhs) = match delim_token {
- DelimToken::Paren => ("(", ")"),
- DelimToken::Bracket => ("[", "]"),
- DelimToken::Brace => {
+ Delimiter::Parenthesis => ("(", ")"),
+ Delimiter::Bracket => ("[", "]"),
+ Delimiter::Brace => {
if inner_is_empty || use_multiple_lines {
("{", "}")
} else {
("{ ", " }")
}
}
- DelimToken::NoDelim => ("", ""),
+ Delimiter::Invisible => unreachable!(),
};
if use_multiple_lines {
let indent_str = shape.indent.to_string_with_newline(context.config);
fn starts_with_brace(&self) -> bool {
matches!(
*self,
- MacroArgKind::Repeat(DelimToken::Brace, _, _, _)
- | MacroArgKind::Delimited(DelimToken::Brace, _)
+ MacroArgKind::Repeat(Delimiter::Brace, _, _, _)
+ | MacroArgKind::Delimited(Delimiter::Brace, _)
)
}
}
}
- fn add_delimited(&mut self, inner: Vec<ParsedMacroArg>, delim: DelimToken) {
+ fn add_delimited(&mut self, inner: Vec<ParsedMacroArg>, delim: Delimiter) {
self.result.push(ParsedMacroArg {
kind: MacroArgKind::Delimited(delim, inner),
});
fn add_repeat(
&mut self,
inner: Vec<ParsedMacroArg>,
- delim: DelimToken,
+ delim: Delimiter,
iter: &mut Cursor,
) -> Option<()> {
let mut buffer = String::new();
}
}
-pub(crate) fn macro_style(mac: &ast::MacCall, context: &RewriteContext<'_>) -> DelimToken {
+pub(crate) fn macro_style(mac: &ast::MacCall, context: &RewriteContext<'_>) -> Delimiter {
let snippet = context.snippet(mac.span());
let paren_pos = snippet.find_uncommented("(").unwrap_or(usize::max_value());
let bracket_pos = snippet.find_uncommented("[").unwrap_or(usize::max_value());
let brace_pos = snippet.find_uncommented("{").unwrap_or(usize::max_value());
if paren_pos < bracket_pos && paren_pos < brace_pos {
- DelimToken::Paren
+ Delimiter::Parenthesis
} else if bracket_pos < brace_pos {
- DelimToken::Bracket
+ Delimiter::Bracket
} else {
- DelimToken::Brace
+ Delimiter::Brace
}
}
// rather than clone them, if we can make the borrowing work out.
struct MacroBranch {
span: Span,
- args_paren_kind: DelimToken,
+ args_paren_kind: Delimiter,
args: TokenStream,
body: Span,
whole_body: Span,
multi_branch_style: bool,
) -> Option<String> {
// Only attempt to format function-like macros.
- if self.args_paren_kind != DelimToken::Paren {
+ if self.args_paren_kind != Delimiter::Parenthesis {
// FIXME(#1539): implement for non-sugared macros.
return None;
}
items: &[MacroArg],
macro_name: &str,
shape: Shape,
- style: DelimToken,
+ style: Delimiter,
position: MacroPosition,
span: Span,
) -> Option<String> {
let (opener, closer) = match style {
- DelimToken::Paren => ("(", ")"),
- DelimToken::Bracket => ("[", "]"),
- DelimToken::Brace => (" {", "}"),
+ Delimiter::Parenthesis => ("(", ")"),
+ Delimiter::Bracket => ("[", "]"),
+ Delimiter::Brace => (" {", "}"),
_ => return None,
};
let trailing_semicolon = match style {
- DelimToken::Paren | DelimToken::Bracket if position == MacroPosition::Item => ";",
+ Delimiter::Parenthesis | Delimiter::Bracket if position == MacroPosition::Item => ";",
_ => "",
};
use std::cmp::min;
use itertools::Itertools;
-use rustc_ast::token::DelimToken;
+use rustc_ast::token::Delimiter;
use rustc_ast::{ast, ptr};
use rustc_span::Span;
shape: Shape,
span: Span,
force_separator_tactic: Option<SeparatorTactic>,
- delim_token: Option<DelimToken>,
+ delim_token: Option<Delimiter>,
) -> Option<String> {
let (lhs, rhs) = match delim_token {
- Some(DelimToken::Paren) => ("(", ")"),
- Some(DelimToken::Brace) => ("{", "}"),
+ Some(Delimiter::Parenthesis) => ("(", ")"),
+ Some(Delimiter::Brace) => ("{", "}"),
_ => ("[", "]"),
};
Context::new(
use std::panic::{catch_unwind, AssertUnwindSafe};
use rustc_ast::ast;
-use rustc_ast::token::{DelimToken, TokenKind};
+use rustc_ast::token::{Delimiter, TokenKind};
use rustc_parse::parser::ForceCollect;
use rustc_span::symbol::kw;
.map_err(|_| "Failed to parse attributes")?;
}
- if !parser.eat(&TokenKind::OpenDelim(DelimToken::Brace)) {
+ if !parser.eat(&TokenKind::OpenDelim(Delimiter::Brace)) {
return Err("Expected an opening brace");
}
- while parser.token != TokenKind::CloseDelim(DelimToken::Brace)
+ while parser.token != TokenKind::CloseDelim(Delimiter::Brace)
&& parser.token.kind != TokenKind::Eof
{
let item = match parser.parse_item(ForceCollect::No) {
}
}
- if !parser.eat(&TokenKind::CloseDelim(DelimToken::Brace)) {
+ if !parser.eat(&TokenKind::CloseDelim(Delimiter::Brace)) {
return Err("Expected a closing brace");
}
-use rustc_ast::token::{DelimToken, TokenKind};
+use rustc_ast::token::{Delimiter, TokenKind};
use rustc_ast::tokenstream::TokenStream;
use rustc_ast::{ast, ptr};
use rustc_parse::parser::{ForceCollect, Parser};
&& parser.look_ahead(1, |t| {
t.kind == TokenKind::Eof
|| t.kind == TokenKind::Comma
- || t.kind == TokenKind::CloseDelim(DelimToken::NoDelim)
+ || t.kind == TokenKind::CloseDelim(Delimiter::Invisible)
})
{
parser.bump();
pub(crate) fn parse_macro_args(
context: &RewriteContext<'_>,
tokens: TokenStream,
- style: DelimToken,
+ style: Delimiter,
forced_bracket: bool,
) -> Option<ParsedMacroArgs> {
let mut parser = build_parser(context, tokens);
let mut vec_with_semi = false;
let mut trailing_comma = false;
- if DelimToken::Brace != style {
+ if Delimiter::Brace != style {
loop {
if let Some(arg) = check_keyword(&mut parser) {
args.push(arg);
| ast::ExprKind::Range(..)
| ast::ExprKind::Repeat(..)
| ast::ExprKind::Ret(..)
+ | ast::ExprKind::Yeet(..)
| ast::ExprKind::Tup(..)
| ast::ExprKind::Type(..)
| ast::ExprKind::Yield(None)
use std::cell::{Cell, RefCell};
use std::rc::Rc;
-use rustc_ast::{ast, token::DelimToken, visit, AstLike};
+use rustc_ast::{ast, token::Delimiter, visit, AstLike};
use rustc_data_structures::sync::Lrc;
use rustc_span::{symbol, BytePos, Pos, Span};
// with whitespace between the delimiters and trailing semi (i.e. `foo!(abc) ;`)
// are formatted correctly.
let (span, rewrite) = match macro_style(mac, &self.get_context()) {
- DelimToken::Bracket | DelimToken::Paren if MacroPosition::Item == pos => {
+ Delimiter::Bracket | Delimiter::Parenthesis if MacroPosition::Item == pos => {
let search_span = mk_sp(mac.span().hi(), self.snippet_provider.end_pos());
let hi = self.snippet_provider.span_before(search_span, ";");
let target_span = mk_sp(mac.span().lo(), hi + BytePos(1));
"rand_chacha",
"rand_core",
"rand_hc",
- "rand_pcg",
"rand_xorshift",
"rand_xoshiro",
"redox_syscall",
const ENTRY_LIMIT: usize = 1000;
// FIXME: The following limits should be reduced eventually.
-const ROOT_ENTRY_LIMIT: usize = 986;
-const ISSUES_ENTRY_LIMIT: usize = 2310;
+const ROOT_ENTRY_LIMIT: usize = 977;
+const ISSUES_ENTRY_LIMIT: usize = 2278;
fn check_entries(path: &Path, bad: &mut bool) {
let dirs = walkdir::WalkDir::new(&path.join("test/ui"))