name: PR
env:
CI_JOB_NAME: "${{ matrix.name }}"
+ CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse
SCCACHE_BUCKET: rust-lang-ci-sccache2
TOOLSTATE_REPO: "https://github.com/rust-lang-nursery/rust-toolstate"
CACHE_DOMAIN: ci-caches.rust-lang.org
name: auto
env:
CI_JOB_NAME: "${{ matrix.name }}"
+ CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse
SCCACHE_BUCKET: rust-lang-ci-sccache2
DEPLOY_BUCKET: rust-lang-ci2
TOOLSTATE_REPO: "https://github.com/rust-lang-nursery/rust-toolstate"
name: try
env:
CI_JOB_NAME: "${{ matrix.name }}"
+ CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse
SCCACHE_BUCKET: rust-lang-ci-sccache2
DEPLOY_BUCKET: rust-lang-ci2
TOOLSTATE_REPO: "https://github.com/rust-lang-nursery/rust-toolstate"
Eduardo Bautista <me@eduardobautista.com> <=>
Eduardo Bautista <me@eduardobautista.com> <mail@eduardobautista.com>
Eduardo Broto <ebroto@tutanota.com>
+Edward Shen <code@eddie.sh> <xes@meta.com>
Elliott Slaughter <elliottslaughter@gmail.com> <eslaughter@mozilla.com>
Elly Fong-Jones <elly@leptoquark.net>
Eric Holk <eric.holk@gmail.com> <eholk@cs.indiana.edu>
"jemalloc-sys",
"rustc_codegen_ssa",
"rustc_driver",
+ "rustc_driver_impl",
"rustc_smir",
]
name = "rustc_ast_lowering"
version = "0.0.0"
dependencies = [
- "rustc_arena",
"rustc_ast",
"rustc_ast_pretty",
"rustc_data_structures",
"rustc_index",
"rustc_macros",
"rustc_middle",
- "rustc_query_system",
"rustc_session",
"rustc_span",
"rustc_target",
version = "0.0.0"
dependencies = [
"rustc_ast",
- "rustc_parse_format",
"rustc_span",
]
"rustc_metadata",
"rustc_middle",
"rustc_query_system",
- "rustc_serialize",
"rustc_session",
"rustc_span",
"rustc_symbol_mangling",
"rustc_arena",
"rustc_ast",
"rustc_attr",
- "rustc_const_eval",
"rustc_data_structures",
"rustc_errors",
"rustc_fs_util",
"rustc_macros",
"rustc_middle",
"rustc_mir_dataflow",
- "rustc_query_system",
"rustc_session",
"rustc_span",
"rustc_target",
[[package]]
name = "rustc_driver"
version = "0.0.0"
+dependencies = [
+ "rustc_driver_impl",
+]
+
+[[package]]
+name = "rustc_driver_impl"
+version = "0.0.0"
dependencies = [
"libc",
"rustc_ast",
"rustc_data_structures",
"rustc_errors",
"rustc_feature",
- "rustc_graphviz",
"rustc_hir",
- "rustc_hir_pretty",
"rustc_index",
"rustc_infer",
"rustc_lint",
"rustc_macros",
"rustc_middle",
- "rustc_serialize",
"rustc_session",
"rustc_span",
"rustc_target",
"rustc_macros",
"rustc_middle",
"rustc_serialize",
- "rustc_session",
"rustc_span",
"rustc_target",
"smallvec",
"rustc_privacy",
"rustc_query_impl",
"rustc_resolve",
- "rustc_serialize",
"rustc_session",
"rustc_span",
"rustc_symbol_mangling",
"rustc_apfloat",
"rustc_arena",
"rustc_ast",
- "rustc_attr",
"rustc_data_structures",
"rustc_errors",
"rustc_hir",
"rustc_macros",
"rustc_middle",
"rustc_serialize",
- "rustc_session",
"rustc_span",
"rustc_target",
"smallvec",
"rustc_middle",
"rustc_session",
"rustc_span",
- "rustc_trait_selection",
"tracing",
]
"rustc_serialize",
"rustc_session",
"rustc_span",
- "rustc_target",
"thin-vec",
"tracing",
]
"rustc_hir",
"rustc_index",
"rustc_infer",
- "rustc_lint_defs",
"rustc_macros",
"rustc_middle",
"rustc_parse_format",
"chalk-ir",
"chalk-solve",
"rustc_ast",
- "rustc_attr",
"rustc_data_structures",
"rustc_hir",
"rustc_index",
[dependencies]
rustc_driver = { path = "../rustc_driver" }
+rustc_driver_impl = { path = "../rustc_driver_impl" }
# Make sure rustc_codegen_ssa ends up in the sysroot, because this
# crate is intended to be used by codegen backends, which may not be in-tree.
[features]
jemalloc = ['jemalloc-sys']
-llvm = ['rustc_driver/llvm']
-max_level_info = ['rustc_driver/max_level_info']
-rustc_use_parallel_compiler = ['rustc_driver/rustc_use_parallel_compiler']
+llvm = ['rustc_driver_impl/llvm']
+max_level_info = ['rustc_driver_impl/max_level_info']
+rustc_use_parallel_compiler = ['rustc_driver_impl/rustc_use_parallel_compiler']
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum PointerKind {
- /// Most general case, we know no restrictions to tell LLVM.
- SharedMutable,
-
- /// `&T` where `T` contains no `UnsafeCell`, is `dereferenceable`, `noalias` and `readonly`.
- Frozen,
-
- /// `&mut T` which is `dereferenceable` and `noalias` but not `readonly`.
- UniqueBorrowed,
-
- /// `&mut !Unpin`, which is `dereferenceable` but neither `noalias` nor `readonly`.
- UniqueBorrowedPinned,
-
- /// `Box<T>`, which is `noalias` (even on return types, unlike the above) but neither `readonly`
- /// nor `dereferenceable`.
- UniqueOwned,
+ /// Shared reference. `frozen` indicates the absence of any `UnsafeCell`.
+ SharedRef { frozen: bool },
+ /// Mutable reference. `unpin` indicates the absence of any pinned data.
+ MutableRef { unpin: bool },
+ /// Box. `unpin` indicates the absence of any pinned data.
+ Box { unpin: bool },
}
/// Note that this information is advisory only, and backends are free to ignore it.
}
impl LitKind {
+ pub fn str(&self) -> Option<Symbol> {
+ match *self {
+ LitKind::Str(s, _) => Some(s),
+ _ => None,
+ }
+ }
+
/// Returns `true` if this literal is a string.
pub fn is_str(&self) -> bool {
matches!(self, LitKind::Str(..))
pub fn value_str(&self) -> Option<Symbol> {
match &self.kind {
- AttrKind::Normal(normal) => normal.item.meta_kind().and_then(|kind| kind.value_str()),
+ AttrKind::Normal(normal) => normal.item.value_str(),
AttrKind::DocComment(..) => None,
}
}
pub fn meta_item_list(&self) -> Option<Vec<NestedMetaItem>> {
match &self.kind {
- AttrKind::Normal(normal) => match normal.item.meta_kind() {
- Some(MetaItemKind::List(list)) => Some(list),
- _ => None,
- },
+ AttrKind::Normal(normal) => normal.item.meta_item_list(),
AttrKind::DocComment(..) => None,
}
}
}
}
+impl AttrArgsEq {
+ fn value_str(&self) -> Option<Symbol> {
+ match self {
+ AttrArgsEq::Ast(expr) => match expr.kind {
+ ExprKind::Lit(token_lit) => {
+ LitKind::from_token_lit(token_lit).ok().and_then(|lit| lit.str())
+ }
+ _ => None,
+ },
+ AttrArgsEq::Hir(lit) => lit.kind.str(),
+ }
+ }
+}
+
impl AttrItem {
pub fn span(&self) -> Span {
self.args.span().map_or(self.path.span, |args_span| self.path.span.to(args_span))
pub fn meta_kind(&self) -> Option<MetaItemKind> {
MetaItemKind::from_attr_args(&self.args)
}
+
+ fn meta_item_list(&self) -> Option<Vec<NestedMetaItem>> {
+ match &self.args {
+ AttrArgs::Delimited(args) if args.delim == MacDelimiter::Parenthesis => {
+ MetaItemKind::list_from_tokens(args.tokens.clone())
+ }
+ AttrArgs::Delimited(_) | AttrArgs::Eq(..) | AttrArgs::Empty => None,
+ }
+ }
+
+ fn value_str(&self) -> Option<Symbol> {
+ match &self.args {
+ AttrArgs::Eq(_, args) => args.value_str(),
+ AttrArgs::Delimited(_) | AttrArgs::Empty => None,
+ }
+ }
}
impl Attribute {
/// * `#[doc = "doc"]` returns `Some(("doc", CommentKind::Line))`.
/// * `#[doc(...)]` returns `None`.
pub fn doc_str_and_comment_kind(&self) -> Option<(Symbol, CommentKind)> {
- match self.kind {
- AttrKind::DocComment(kind, data) => Some((data, kind)),
- AttrKind::Normal(ref normal) if normal.item.path == sym::doc => normal
- .item
- .meta_kind()
- .and_then(|kind| kind.value_str())
- .map(|data| (data, CommentKind::Line)),
+ match &self.kind {
+ AttrKind::DocComment(kind, data) => Some((*data, *kind)),
+ AttrKind::Normal(normal) if normal.item.path == sym::doc => {
+ normal.item.value_str().map(|s| (s, CommentKind::Line))
+ }
_ => None,
}
}
pub fn doc_str(&self) -> Option<Symbol> {
match &self.kind {
AttrKind::DocComment(.., data) => Some(*data),
- AttrKind::Normal(normal) if normal.item.path == sym::doc => {
- normal.item.meta_kind().and_then(|kind| kind.value_str())
- }
+ AttrKind::Normal(normal) if normal.item.path == sym::doc => normal.item.value_str(),
_ => None,
}
}
impl MetaItemKind {
pub fn value_str(&self) -> Option<Symbol> {
match self {
- MetaItemKind::NameValue(v) => match v.kind {
- LitKind::Str(s, _) => Some(s),
- _ => None,
- },
+ MetaItemKind::NameValue(v) => v.kind.str(),
_ => None,
}
}
- fn list_from_tokens(tokens: TokenStream) -> Option<MetaItemKind> {
+ fn list_from_tokens(tokens: TokenStream) -> Option<Vec<NestedMetaItem>> {
let mut tokens = tokens.into_trees().peekable();
let mut result = Vec::new();
while tokens.peek().is_some() {
_ => return None,
}
}
- Some(MetaItemKind::List(result))
+ Some(result)
}
fn name_value_from_tokens(
dspan: _,
delim: MacDelimiter::Parenthesis,
tokens,
- }) => MetaItemKind::list_from_tokens(tokens.clone()),
+ }) => MetaItemKind::list_from_tokens(tokens.clone()).map(MetaItemKind::List),
AttrArgs::Delimited(..) => None,
AttrArgs::Eq(_, AttrArgsEq::Ast(expr)) => match expr.kind {
ExprKind::Lit(token_lit) => {
Some(TokenTree::Delimited(_, Delimiter::Parenthesis, inner_tokens)) => {
let inner_tokens = inner_tokens.clone();
tokens.next();
- MetaItemKind::list_from_tokens(inner_tokens)
+ MetaItemKind::list_from_tokens(inner_tokens).map(MetaItemKind::List)
}
Some(TokenTree::Delimited(..)) => None,
Some(TokenTree::Token(Token { kind: token::Eq, .. }, _)) => {
/// Nothing special happens to misnamed or misplaced `SubstNt`s.
#[derive(Debug, Clone, PartialEq, Encodable, Decodable, HashStable_Generic)]
pub enum TokenTree {
- /// A single token.
+ /// A single token. Should never be `OpenDelim` or `CloseDelim`, because
+ /// delimiters are implicitly represented by `Delimited`.
Token(Token, Spacing),
/// A delimited sequence of token trees.
Delimited(DelimSpan, Delimiter, TokenStream),
self.0.len()
}
- pub fn trees(&self) -> CursorRef<'_> {
- CursorRef::new(self)
+ pub fn trees(&self) -> RefTokenTreeCursor<'_> {
+ RefTokenTreeCursor::new(self)
}
- pub fn into_trees(self) -> Cursor {
- Cursor::new(self)
+ pub fn into_trees(self) -> TokenTreeCursor {
+ TokenTreeCursor::new(self)
}
/// Compares two `TokenStream`s, checking equality without regarding span information.
}
}
-/// By-reference iterator over a [`TokenStream`].
+/// By-reference iterator over a [`TokenStream`], that produces `&TokenTree`
+/// items.
#[derive(Clone)]
-pub struct CursorRef<'t> {
+pub struct RefTokenTreeCursor<'t> {
stream: &'t TokenStream,
index: usize,
}
-impl<'t> CursorRef<'t> {
+impl<'t> RefTokenTreeCursor<'t> {
fn new(stream: &'t TokenStream) -> Self {
- CursorRef { stream, index: 0 }
+ RefTokenTreeCursor { stream, index: 0 }
}
pub fn look_ahead(&self, n: usize) -> Option<&TokenTree> {
}
}
-impl<'t> Iterator for CursorRef<'t> {
+impl<'t> Iterator for RefTokenTreeCursor<'t> {
type Item = &'t TokenTree;
fn next(&mut self) -> Option<&'t TokenTree> {
}
}
-/// Owning by-value iterator over a [`TokenStream`].
+/// Owning by-value iterator over a [`TokenStream`], that produces `TokenTree`
+/// items.
// FIXME: Many uses of this can be replaced with by-reference iterator to avoid clones.
#[derive(Clone)]
-pub struct Cursor {
+pub struct TokenTreeCursor {
pub stream: TokenStream,
index: usize,
}
-impl Iterator for Cursor {
+impl Iterator for TokenTreeCursor {
type Item = TokenTree;
fn next(&mut self) -> Option<TokenTree> {
}
}
-impl Cursor {
+impl TokenTreeCursor {
fn new(stream: TokenStream) -> Self {
- Cursor { stream, index: 0 }
+ TokenTreeCursor { stream, index: 0 }
}
#[inline]
pub fn look_ahead(&self, n: usize) -> Option<&TokenTree> {
self.stream.0.get(self.index + n)
}
+
+ // Replace the previously obtained token tree with `tts`, and rewind to
+ // just before them.
+ pub fn replace_prev_and_rewind(&mut self, tts: Vec<TokenTree>) {
+ assert!(self.index > 0);
+ self.index -= 1;
+ let stream = Lrc::make_mut(&mut self.stream.0);
+ stream.splice(self.index..self.index + 1, tts);
+ }
}
#[derive(Debug, Copy, Clone, PartialEq, Encodable, Decodable, HashStable_Generic)]
walk_list!(visitor, visit_lifetime, opt_lifetime, LifetimeCtxt::Ref);
visitor.visit_ty(&mutable_type.ty)
}
- TyKind::Tup(tys) => {
- walk_list!(visitor, visit_ty, tys);
+ TyKind::Tup(tuple_element_types) => {
+ walk_list!(visitor, visit_ty, tuple_element_types);
}
TyKind::BareFn(function_declaration) => {
walk_list!(visitor, visit_generic_param, &function_declaration.generic_params);
doctest = false
[dependencies]
-rustc_arena = { path = "../rustc_arena" }
rustc_ast = { path = "../rustc_ast" }
rustc_ast_pretty = { path = "../rustc_ast_pretty" }
rustc_data_structures = { path = "../rustc_data_structures" }
rustc_index = { path = "../rustc_index" }
rustc_middle = { path = "../rustc_middle" }
rustc_macros = { path = "../rustc_macros" }
-rustc_query_system = { path = "../rustc_query_system" }
rustc_session = { path = "../rustc_session" }
rustc_span = { path = "../rustc_span" }
rustc_target = { path = "../rustc_target" }
[dependencies]
rustc_ast = { path = "../rustc_ast" }
-rustc_parse_format = { path = "../rustc_parse_format" }
rustc_span = { path = "../rustc_span" }
// Currently, in Rust 2018 we don't have `extern crate std;` at the crate
// root, so this is not needed, and actually breaks things.
- if edition.rust_2015() {
+ if edition.is_rust_2015() {
// `#![no_std]`
let fake_attr = attr::mk_attr_word(g, ast::AttrStyle::Inner, sym::no_std, DUMMY_SP);
s.print_attribute(&fake_attr);
operands,
) = rvalue
{
+ let def_id = def_id.expect_local();
for operand in operands {
let (Operand::Copy(assigned_from) | Operand::Move(assigned_from)) = operand else {
continue;
// into a place then we should annotate the closure in
// case it ends up being assigned into the return place.
annotated_closure =
- self.annotate_fn_sig(*def_id, substs.as_closure().sig());
+ self.annotate_fn_sig(def_id, substs.as_closure().sig());
debug!(
"annotate_argument_and_return_for_borrow: \
annotated_closure={:?} assigned_from_local={:?} \
&& let AggregateKind::Closure(def_id, _) | AggregateKind::Generator(def_id, _, _) = **kind
{
debug!("move_spans: def_id={:?} places={:?}", def_id, places);
+ let def_id = def_id.expect_local();
if let Some((args_span, generator_kind, capture_kind_span, path_span)) =
self.closure_span(def_id, moved_place, places)
{
box AggregateKind::Generator(def_id, _, _) => (def_id, true),
_ => continue,
};
+ let def_id = def_id.expect_local();
debug!(
"borrow_spans: def_id={:?} is_generator={:?} places={:?}",
}
}
Some((false, err_label_span, message)) => {
- err.span_label(
- err_label_span,
- &format!(
- "consider changing this binding's type to be: `{message}`"
- ),
- );
+ struct BindingFinder {
+ span: Span,
+ hir_id: Option<hir::HirId>,
+ }
+
+ impl<'tcx> Visitor<'tcx> for BindingFinder {
+ fn visit_stmt(&mut self, s: &'tcx hir::Stmt<'tcx>) {
+ if let hir::StmtKind::Local(local) = s.kind {
+ if local.pat.span == self.span {
+ self.hir_id = Some(local.hir_id);
+ }
+ }
+ hir::intravisit::walk_stmt(self, s);
+ }
+ }
+ let hir_map = self.infcx.tcx.hir();
+ let def_id = self.body.source.def_id();
+ let hir_id = hir_map.local_def_id_to_hir_id(def_id.expect_local());
+ let node = hir_map.find(hir_id);
+ let hir_id = if let Some(hir::Node::Item(item)) = node
+ && let hir::ItemKind::Fn(.., body_id) = item.kind
+ {
+ let body = hir_map.body(body_id);
+ let mut v = BindingFinder {
+ span: err_label_span,
+ hir_id: None,
+ };
+ v.visit_body(body);
+ v.hir_id
+ } else {
+ None
+ };
+ if let Some(hir_id) = hir_id
+ && let Some(hir::Node::Local(local)) = hir_map.find(hir_id)
+ {
+ let (changing, span, sugg) = match local.ty {
+ Some(ty) => ("changing", ty.span, message),
+ None => (
+ "specifying",
+ local.pat.span.shrink_to_hi(),
+ format!(": {message}"),
+ ),
+ };
+ err.span_suggestion_verbose(
+ span,
+ &format!("consider {changing} this binding's type"),
+ sugg,
+ Applicability::HasPlaceholders,
+ );
+ } else {
+ err.span_label(
+ err_label_span,
+ &format!(
+ "consider changing this binding's type to be: `{message}`"
+ ),
+ );
+ }
}
None => {}
}
let err = FnMutError {
span: *span,
ty_err: match output_ty.kind() {
- ty::Closure(_, _) => FnMutReturnTypeErr::ReturnClosure { span: *span },
ty::Generator(def, ..) if self.infcx.tcx.generator_is_async(*def) => {
FnMutReturnTypeErr::ReturnAsyncBlock { span: *span }
}
+ _ if output_ty.contains_closure() => {
+ FnMutReturnTypeErr::ReturnClosure { span: *span }
+ }
_ => FnMutReturnTypeErr::ReturnRef { span: *span },
},
};
fn suggest_move_on_borrowing_closure(&self, diag: &mut Diagnostic) {
let map = self.infcx.tcx.hir();
let body_id = map.body_owned_by(self.mir_def_id());
- let expr = &map.body(body_id).value;
+ let expr = &map.body(body_id).value.peel_blocks();
let mut closure_span = None::<rustc_span::Span>;
match expr.kind {
hir::ExprKind::MethodCall(.., args, _) => {
}
}
}
- hir::ExprKind::Block(blk, _) => {
- if let Some(expr) = blk.expr {
- // only when the block is a closure
- if let hir::ExprKind::Closure(hir::Closure {
- capture_clause: hir::CaptureBy::Ref,
- body,
- ..
- }) = expr.kind
- {
- let body = map.body(*body);
- if !matches!(body.generator_kind, Some(hir::GeneratorKind::Async(..))) {
- closure_span = Some(expr.span.shrink_to_lo());
- }
- }
+ hir::ExprKind::Closure(hir::Closure {
+ capture_clause: hir::CaptureBy::Ref,
+ body,
+ ..
+ }) => {
+ let body = map.body(*body);
+ if !matches!(body.generator_kind, Some(hir::GeneratorKind::Async(..))) {
+ closure_span = Some(expr.span.shrink_to_lo());
}
}
_ => {}
// in order to populate our used_mut set.
match **aggregate_kind {
AggregateKind::Closure(def_id, _) | AggregateKind::Generator(def_id, _, _) => {
+ let def_id = def_id.expect_local();
let BorrowCheckResult { used_mut_upvars, .. } =
self.infcx.tcx.mir_borrowck(def_id);
debug!("{:?} used_mut_upvars={:?}", def_id, used_mut_upvars);
}
}
None => {
- if !sig.output().is_privately_uninhabited(self.tcx(), self.param_env) {
+ // The signature in this call can reference region variables,
+ // so erase them before calling a query.
+ let output_ty = self.tcx().erase_regions(sig.output());
+ if !output_ty.is_privately_uninhabited(self.tcx(), self.param_env) {
span_mirbug!(self, term, "call to converging function {:?} w/o dest", sig);
}
}
// clauses on the struct.
AggregateKind::Closure(def_id, substs)
| AggregateKind::Generator(def_id, substs, _) => {
- (def_id.to_def_id(), self.prove_closure_bounds(tcx, def_id, substs, location))
+ (def_id, self.prove_closure_bounds(tcx, def_id.expect_local(), substs, location))
}
AggregateKind::Array(_) | AggregateKind::Tuple => {
cx.span_bug(field.span, "not exactly 2 arguments in `derive(PartialEq)`");
};
- // We received `&T` arguments. Convert them to `T` by
- // stripping `&` or adding `*`. This isn't necessary for
- // type checking, but it results in much better error
- // messages if something goes wrong.
+ // We received arguments of type `&T`. Convert them to type `T` by stripping
+ // any leading `&` or adding `*`. This isn't necessary for type checking, but
+ // it results in better error messages if something goes wrong.
+ //
+ // Note: for arguments that look like `&{ x }`, which occur with packed
+ // structs, this would cause expressions like `{ self.x } == { other.x }`,
+ // which isn't valid Rust syntax. This wouldn't break compilation because these
+ // AST nodes are constructed within the compiler. But it would mean that code
+ // printed by `-Zunpretty=expanded` (or `cargo expand`) would have invalid
+ // syntax, which would be suboptimal. So we wrap these in parens, giving
+ // `({ self.x }) == ({ other.x })`, which is valid syntax.
let convert = |expr: &P<Expr>| {
if let ExprKind::AddrOf(BorrowKind::Ref, Mutability::Not, inner) =
&expr.kind
{
- inner.clone()
+ if let ExprKind::Block(..) = &inner.kind {
+ // `&{ x }` form: remove the `&`, add parens.
+ cx.expr_paren(field.span, inner.clone())
+ } else {
+ // `&x` form: remove the `&`.
+ inner.clone()
+ }
} else {
+ // No leading `&`: add a leading `*`.
cx.expr_deref(field.span, expr.clone())
}
};
// The number of fields that can be handled without an array.
const CUTOFF: usize = 5;
+ fn expr_for_field(
+ cx: &ExtCtxt<'_>,
+ field: &FieldInfo,
+ index: usize,
+ len: usize,
+ ) -> ast::ptr::P<ast::Expr> {
+ if index < len - 1 {
+ field.self_expr.clone()
+ } else {
+ // Unsized types need an extra indirection, but only the last field
+ // may be unsized.
+ cx.expr_addr_of(field.span, field.self_expr.clone())
+ }
+ }
+
if fields.is_empty() {
// Special case for no fields.
let fn_path_write_str = cx.std_path(&[sym::fmt, sym::Formatter, sym::write_str]);
let name = cx.expr_str(field.span, field.name.unwrap().name);
args.push(name);
}
- // Use an extra indirection to make sure this works for unsized types.
- let field = cx.expr_addr_of(field.span, field.self_expr.clone());
+
+ let field = expr_for_field(cx, field, i, fields.len());
args.push(field);
}
let expr = cx.expr_call_global(span, fn_path_debug, args);
let mut name_exprs = Vec::with_capacity(fields.len());
let mut value_exprs = Vec::with_capacity(fields.len());
- for field in fields {
+ for i in 0..fields.len() {
+ let field = &fields[i];
if is_struct {
name_exprs.push(cx.expr_str(field.span, field.name.unwrap().name));
}
- // Use an extra indirection to make sure this works for unsized types.
- let field = cx.expr_addr_of(field.span, field.self_expr.clone());
+ let field = expr_for_field(cx, field, i, fields.len());
value_exprs.push(field);
}
rustc_metadata = { path = "../rustc_metadata" }
rustc_query_system = { path = "../rustc_query_system" }
rustc_session = { path = "../rustc_session" }
-rustc_serialize = { path = "../rustc_serialize" }
rustc_symbol_mangling = { path = "../rustc_symbol_mangling" }
rustc_target = { path = "../rustc_target" }
smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
rustc_query_system = { path = "../rustc_query_system" }
rustc_target = { path = "../rustc_target" }
rustc_session = { path = "../rustc_session" }
-rustc_const_eval = { path = "../rustc_const_eval" }
[dependencies.object]
version = "0.30.1"
}
}
} else if attr.has_name(sym::instruction_set) {
- codegen_fn_attrs.instruction_set = match attr.meta_kind() {
- Some(MetaItemKind::List(ref items)) => match items.as_slice() {
- [NestedMetaItem::MetaItem(set)] => {
- let segments =
- set.path.segments.iter().map(|x| x.ident.name).collect::<Vec<_>>();
- match segments.as_slice() {
- [sym::arm, sym::a32] | [sym::arm, sym::t32] => {
- if !tcx.sess.target.has_thumb_interworking {
- struct_span_err!(
- tcx.sess.diagnostic(),
- attr.span,
- E0779,
- "target does not support `#[instruction_set]`"
- )
- .emit();
- None
- } else if segments[1] == sym::a32 {
- Some(InstructionSetAttr::ArmA32)
- } else if segments[1] == sym::t32 {
- Some(InstructionSetAttr::ArmT32)
- } else {
- unreachable!()
- }
- }
- _ => {
+ codegen_fn_attrs.instruction_set = attr.meta_item_list().and_then(|l| match &l[..] {
+ [NestedMetaItem::MetaItem(set)] => {
+ let segments =
+ set.path.segments.iter().map(|x| x.ident.name).collect::<Vec<_>>();
+ match segments.as_slice() {
+ [sym::arm, sym::a32] | [sym::arm, sym::t32] => {
+ if !tcx.sess.target.has_thumb_interworking {
struct_span_err!(
tcx.sess.diagnostic(),
attr.span,
E0779,
- "invalid instruction set specified",
+ "target does not support `#[instruction_set]`"
)
.emit();
None
+ } else if segments[1] == sym::a32 {
+ Some(InstructionSetAttr::ArmA32)
+ } else if segments[1] == sym::t32 {
+ Some(InstructionSetAttr::ArmT32)
+ } else {
+ unreachable!()
}
}
+ _ => {
+ struct_span_err!(
+ tcx.sess.diagnostic(),
+ attr.span,
+ E0779,
+ "invalid instruction set specified",
+ )
+ .emit();
+ None
+ }
}
- [] => {
- struct_span_err!(
- tcx.sess.diagnostic(),
- attr.span,
- E0778,
- "`#[instruction_set]` requires an argument"
- )
- .emit();
- None
- }
- _ => {
- struct_span_err!(
- tcx.sess.diagnostic(),
- attr.span,
- E0779,
- "cannot specify more than one instruction set"
- )
- .emit();
- None
- }
- },
- _ => {
+ }
+ [] => {
struct_span_err!(
tcx.sess.diagnostic(),
attr.span,
E0778,
- "must specify an instruction set"
+ "`#[instruction_set]` requires an argument"
)
.emit();
None
}
- };
+ _ => {
+ struct_span_err!(
+ tcx.sess.diagnostic(),
+ attr.span,
+ E0779,
+ "cannot specify more than one instruction set"
+ )
+ .emit();
+ None
+ }
+ })
} else if attr.has_name(sym::repr) {
codegen_fn_attrs.alignment = match attr.meta_item_list() {
Some(items) => match items.as_slice() {
use rustc_middle::ty::layout::{HasTyCtxt, LayoutOf};
use rustc_middle::ty::{self, adjustment::PointerCast, Instance, Ty, TyCtxt};
use rustc_span::source_map::{Span, DUMMY_SP};
+use rustc_target::abi::VariantIdx;
impl<'a, 'tcx, Bx: BuilderMethods<'a, 'tcx>> FunctionCx<'a, 'tcx, Bx> {
#[instrument(level = "trace", skip(self, bx))]
}
mir::Rvalue::Aggregate(ref kind, ref operands) => {
- let (dest, active_field_index) = match **kind {
- mir::AggregateKind::Adt(adt_did, variant_index, _, _, active_field_index) => {
- dest.codegen_set_discr(bx, variant_index);
- if bx.tcx().adt_def(adt_did).is_enum() {
- (dest.project_downcast(bx, variant_index), active_field_index)
- } else {
- (dest, active_field_index)
- }
+ let (variant_index, variant_dest, active_field_index) = match **kind {
+ mir::AggregateKind::Adt(_, variant_index, _, _, active_field_index) => {
+ let variant_dest = dest.project_downcast(bx, variant_index);
+ (variant_index, variant_dest, active_field_index)
}
- _ => (dest, None),
+ _ => (VariantIdx::from_u32(0), dest, None),
};
+ if active_field_index.is_some() {
+ assert_eq!(operands.len(), 1);
+ }
for (i, operand) in operands.iter().enumerate() {
let op = self.codegen_operand(bx, operand);
// Do not generate stores and GEPis for zero-sized fields.
let field_index = active_field_index.unwrap_or(i);
let field = if let mir::AggregateKind::Array(_) = **kind {
let llindex = bx.cx().const_usize(field_index as u64);
- dest.project_index(bx, llindex)
+ variant_dest.project_index(bx, llindex)
} else {
- dest.project_field(bx, field_index)
+ variant_dest.project_field(bx, field_index)
};
op.val.store(bx, field);
}
}
+ dest.codegen_set_discr(bx, variant_index);
}
_ => {
rustc_macros = { path = "../rustc_macros" }
rustc_middle = { path = "../rustc_middle" }
rustc_mir_dataflow = { path = "../rustc_mir_dataflow" }
-rustc_query_system = { path = "../rustc_query_system" }
rustc_session = { path = "../rustc_session" }
rustc_target = { path = "../rustc_target" }
rustc_trait_selection = { path = "../rustc_trait_selection" }
--- /dev/null
+//! Functions for reading and writing discriminants of multi-variant layouts (enums and generators).
+
+use rustc_middle::ty::layout::{LayoutOf, PrimitiveExt};
+use rustc_middle::{mir, ty};
+use rustc_target::abi::{self, TagEncoding};
+use rustc_target::abi::{VariantIdx, Variants};
+
+use super::{ImmTy, InterpCx, InterpResult, Machine, OpTy, PlaceTy, Scalar};
+
+impl<'mir, 'tcx: 'mir, M: Machine<'mir, 'tcx>> InterpCx<'mir, 'tcx, M> {
+ /// Writes the discriminant of the given variant.
+ #[instrument(skip(self), level = "trace")]
+ pub fn write_discriminant(
+ &mut self,
+ variant_index: VariantIdx,
+ dest: &PlaceTy<'tcx, M::Provenance>,
+ ) -> InterpResult<'tcx> {
+ // Layout computation excludes uninhabited variants from consideration
+ // therefore there's no way to represent those variants in the given layout.
+ // Essentially, uninhabited variants do not have a tag that corresponds to their
+ // discriminant, so we cannot do anything here.
+ // When evaluating we will always error before even getting here, but ConstProp 'executes'
+ // dead code, so we cannot ICE here.
+ if dest.layout.for_variant(self, variant_index).abi.is_uninhabited() {
+ throw_ub!(UninhabitedEnumVariantWritten)
+ }
+
+ match dest.layout.variants {
+ abi::Variants::Single { index } => {
+ assert_eq!(index, variant_index);
+ }
+ abi::Variants::Multiple {
+ tag_encoding: TagEncoding::Direct,
+ tag: tag_layout,
+ tag_field,
+ ..
+ } => {
+ // No need to validate that the discriminant here because the
+ // `TyAndLayout::for_variant()` call earlier already checks the variant is valid.
+
+ let discr_val =
+ dest.layout.ty.discriminant_for_variant(*self.tcx, variant_index).unwrap().val;
+
+ // raw discriminants for enums are isize or bigger during
+ // their computation, but the in-memory tag is the smallest possible
+ // representation
+ let size = tag_layout.size(self);
+ let tag_val = size.truncate(discr_val);
+
+ let tag_dest = self.place_field(dest, tag_field)?;
+ self.write_scalar(Scalar::from_uint(tag_val, size), &tag_dest)?;
+ }
+ abi::Variants::Multiple {
+ tag_encoding:
+ TagEncoding::Niche { untagged_variant, ref niche_variants, niche_start },
+ tag: tag_layout,
+ tag_field,
+ ..
+ } => {
+ // No need to validate that the discriminant here because the
+ // `TyAndLayout::for_variant()` call earlier already checks the variant is valid.
+
+ if variant_index != untagged_variant {
+ let variants_start = niche_variants.start().as_u32();
+ let variant_index_relative = variant_index
+ .as_u32()
+ .checked_sub(variants_start)
+ .expect("overflow computing relative variant idx");
+ // We need to use machine arithmetic when taking into account `niche_start`:
+ // tag_val = variant_index_relative + niche_start_val
+ let tag_layout = self.layout_of(tag_layout.primitive().to_int_ty(*self.tcx))?;
+ let niche_start_val = ImmTy::from_uint(niche_start, tag_layout);
+ let variant_index_relative_val =
+ ImmTy::from_uint(variant_index_relative, tag_layout);
+ let tag_val = self.binary_op(
+ mir::BinOp::Add,
+ &variant_index_relative_val,
+ &niche_start_val,
+ )?;
+ // Write result.
+ let niche_dest = self.place_field(dest, tag_field)?;
+ self.write_immediate(*tag_val, &niche_dest)?;
+ }
+ }
+ }
+
+ Ok(())
+ }
+
+ /// Read discriminant, return the runtime value as well as the variant index.
+ /// Can also legally be called on non-enums (e.g. through the discriminant_value intrinsic)!
+ #[instrument(skip(self), level = "trace")]
+ pub fn read_discriminant(
+ &self,
+ op: &OpTy<'tcx, M::Provenance>,
+ ) -> InterpResult<'tcx, (Scalar<M::Provenance>, VariantIdx)> {
+ trace!("read_discriminant_value {:#?}", op.layout);
+ // Get type and layout of the discriminant.
+ let discr_layout = self.layout_of(op.layout.ty.discriminant_ty(*self.tcx))?;
+ trace!("discriminant type: {:?}", discr_layout.ty);
+
+ // We use "discriminant" to refer to the value associated with a particular enum variant.
+ // This is not to be confused with its "variant index", which is just determining its position in the
+ // declared list of variants -- they can differ with explicitly assigned discriminants.
+ // We use "tag" to refer to how the discriminant is encoded in memory, which can be either
+ // straight-forward (`TagEncoding::Direct`) or with a niche (`TagEncoding::Niche`).
+ let (tag_scalar_layout, tag_encoding, tag_field) = match op.layout.variants {
+ Variants::Single { index } => {
+ let discr = match op.layout.ty.discriminant_for_variant(*self.tcx, index) {
+ Some(discr) => {
+ // This type actually has discriminants.
+ assert_eq!(discr.ty, discr_layout.ty);
+ Scalar::from_uint(discr.val, discr_layout.size)
+ }
+ None => {
+ // On a type without actual discriminants, variant is 0.
+ assert_eq!(index.as_u32(), 0);
+ Scalar::from_uint(index.as_u32(), discr_layout.size)
+ }
+ };
+ return Ok((discr, index));
+ }
+ Variants::Multiple { tag, ref tag_encoding, tag_field, .. } => {
+ (tag, tag_encoding, tag_field)
+ }
+ };
+
+ // There are *three* layouts that come into play here:
+ // - The discriminant has a type for typechecking. This is `discr_layout`, and is used for
+ // the `Scalar` we return.
+ // - The tag (encoded discriminant) has layout `tag_layout`. This is always an integer type,
+ // and used to interpret the value we read from the tag field.
+ // For the return value, a cast to `discr_layout` is performed.
+ // - The field storing the tag has a layout, which is very similar to `tag_layout` but
+ // may be a pointer. This is `tag_val.layout`; we just use it for sanity checks.
+
+ // Get layout for tag.
+ let tag_layout = self.layout_of(tag_scalar_layout.primitive().to_int_ty(*self.tcx))?;
+
+ // Read tag and sanity-check `tag_layout`.
+ let tag_val = self.read_immediate(&self.operand_field(op, tag_field)?)?;
+ assert_eq!(tag_layout.size, tag_val.layout.size);
+ assert_eq!(tag_layout.abi.is_signed(), tag_val.layout.abi.is_signed());
+ trace!("tag value: {}", tag_val);
+
+ // Figure out which discriminant and variant this corresponds to.
+ Ok(match *tag_encoding {
+ TagEncoding::Direct => {
+ let scalar = tag_val.to_scalar();
+ // Generate a specific error if `tag_val` is not an integer.
+ // (`tag_bits` itself is only used for error messages below.)
+ let tag_bits = scalar
+ .try_to_int()
+ .map_err(|dbg_val| err_ub!(InvalidTag(dbg_val)))?
+ .assert_bits(tag_layout.size);
+ // Cast bits from tag layout to discriminant layout.
+ // After the checks we did above, this cannot fail, as
+ // discriminants are int-like.
+ let discr_val =
+ self.cast_from_int_like(scalar, tag_val.layout, discr_layout.ty).unwrap();
+ let discr_bits = discr_val.assert_bits(discr_layout.size);
+ // Convert discriminant to variant index, and catch invalid discriminants.
+ let index = match *op.layout.ty.kind() {
+ ty::Adt(adt, _) => {
+ adt.discriminants(*self.tcx).find(|(_, var)| var.val == discr_bits)
+ }
+ ty::Generator(def_id, substs, _) => {
+ let substs = substs.as_generator();
+ substs
+ .discriminants(def_id, *self.tcx)
+ .find(|(_, var)| var.val == discr_bits)
+ }
+ _ => span_bug!(self.cur_span(), "tagged layout for non-adt non-generator"),
+ }
+ .ok_or_else(|| err_ub!(InvalidTag(Scalar::from_uint(tag_bits, tag_layout.size))))?;
+ // Return the cast value, and the index.
+ (discr_val, index.0)
+ }
+ TagEncoding::Niche { untagged_variant, ref niche_variants, niche_start } => {
+ let tag_val = tag_val.to_scalar();
+ // Compute the variant this niche value/"tag" corresponds to. With niche layout,
+ // discriminant (encoded in niche/tag) and variant index are the same.
+ let variants_start = niche_variants.start().as_u32();
+ let variants_end = niche_variants.end().as_u32();
+ let variant = match tag_val.try_to_int() {
+ Err(dbg_val) => {
+ // So this is a pointer then, and casting to an int failed.
+ // Can only happen during CTFE.
+ // The niche must be just 0, and the ptr not null, then we know this is
+ // okay. Everything else, we conservatively reject.
+ let ptr_valid = niche_start == 0
+ && variants_start == variants_end
+ && !self.scalar_may_be_null(tag_val)?;
+ if !ptr_valid {
+ throw_ub!(InvalidTag(dbg_val))
+ }
+ untagged_variant
+ }
+ Ok(tag_bits) => {
+ let tag_bits = tag_bits.assert_bits(tag_layout.size);
+ // We need to use machine arithmetic to get the relative variant idx:
+ // variant_index_relative = tag_val - niche_start_val
+ let tag_val = ImmTy::from_uint(tag_bits, tag_layout);
+ let niche_start_val = ImmTy::from_uint(niche_start, tag_layout);
+ let variant_index_relative_val =
+ self.binary_op(mir::BinOp::Sub, &tag_val, &niche_start_val)?;
+ let variant_index_relative =
+ variant_index_relative_val.to_scalar().assert_bits(tag_val.layout.size);
+ // Check if this is in the range that indicates an actual discriminant.
+ if variant_index_relative <= u128::from(variants_end - variants_start) {
+ let variant_index_relative = u32::try_from(variant_index_relative)
+ .expect("we checked that this fits into a u32");
+ // Then computing the absolute variant idx should not overflow any more.
+ let variant_index = variants_start
+ .checked_add(variant_index_relative)
+ .expect("overflow computing absolute variant idx");
+ let variants_len = op
+ .layout
+ .ty
+ .ty_adt_def()
+ .expect("tagged layout for non adt")
+ .variants()
+ .len();
+ assert!(usize::try_from(variant_index).unwrap() < variants_len);
+ VariantIdx::from_u32(variant_index)
+ } else {
+ untagged_variant
+ }
+ }
+ };
+ // Compute the size of the scalar we need to return.
+ // No need to cast, because the variant index directly serves as discriminant and is
+ // encoded in the tag.
+ (Scalar::from_uint(variant.as_u32(), discr_layout.size), variant)
+ }
+ })
+ }
+}
//! An interpreter for MIR used in CTFE and by miri
mod cast;
+mod discriminant;
mod eval_context;
mod intern;
mod intrinsics;
use either::{Either, Left, Right};
use rustc_hir::def::Namespace;
-use rustc_middle::ty::layout::{LayoutOf, PrimitiveExt, TyAndLayout};
+use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
use rustc_middle::ty::print::{FmtPrinter, PrettyPrinter};
use rustc_middle::ty::{ConstInt, Ty, ValTree};
use rustc_middle::{mir, ty};
use rustc_span::Span;
-use rustc_target::abi::{self, Abi, Align, HasDataLayout, Size, TagEncoding};
-use rustc_target::abi::{VariantIdx, Variants};
+use rustc_target::abi::{self, Abi, Align, HasDataLayout, Size};
use super::{
alloc_range, from_known_layout, mir_assign_valid_types, AllocId, ConstValue, Frame, GlobalId,
};
Ok(OpTy { op, layout, align: Some(layout.align.abi) })
}
-
- /// Read discriminant, return the runtime value as well as the variant index.
- /// Can also legally be called on non-enums (e.g. through the discriminant_value intrinsic)!
- pub fn read_discriminant(
- &self,
- op: &OpTy<'tcx, M::Provenance>,
- ) -> InterpResult<'tcx, (Scalar<M::Provenance>, VariantIdx)> {
- trace!("read_discriminant_value {:#?}", op.layout);
- // Get type and layout of the discriminant.
- let discr_layout = self.layout_of(op.layout.ty.discriminant_ty(*self.tcx))?;
- trace!("discriminant type: {:?}", discr_layout.ty);
-
- // We use "discriminant" to refer to the value associated with a particular enum variant.
- // This is not to be confused with its "variant index", which is just determining its position in the
- // declared list of variants -- they can differ with explicitly assigned discriminants.
- // We use "tag" to refer to how the discriminant is encoded in memory, which can be either
- // straight-forward (`TagEncoding::Direct`) or with a niche (`TagEncoding::Niche`).
- let (tag_scalar_layout, tag_encoding, tag_field) = match op.layout.variants {
- Variants::Single { index } => {
- let discr = match op.layout.ty.discriminant_for_variant(*self.tcx, index) {
- Some(discr) => {
- // This type actually has discriminants.
- assert_eq!(discr.ty, discr_layout.ty);
- Scalar::from_uint(discr.val, discr_layout.size)
- }
- None => {
- // On a type without actual discriminants, variant is 0.
- assert_eq!(index.as_u32(), 0);
- Scalar::from_uint(index.as_u32(), discr_layout.size)
- }
- };
- return Ok((discr, index));
- }
- Variants::Multiple { tag, ref tag_encoding, tag_field, .. } => {
- (tag, tag_encoding, tag_field)
- }
- };
-
- // There are *three* layouts that come into play here:
- // - The discriminant has a type for typechecking. This is `discr_layout`, and is used for
- // the `Scalar` we return.
- // - The tag (encoded discriminant) has layout `tag_layout`. This is always an integer type,
- // and used to interpret the value we read from the tag field.
- // For the return value, a cast to `discr_layout` is performed.
- // - The field storing the tag has a layout, which is very similar to `tag_layout` but
- // may be a pointer. This is `tag_val.layout`; we just use it for sanity checks.
-
- // Get layout for tag.
- let tag_layout = self.layout_of(tag_scalar_layout.primitive().to_int_ty(*self.tcx))?;
-
- // Read tag and sanity-check `tag_layout`.
- let tag_val = self.read_immediate(&self.operand_field(op, tag_field)?)?;
- assert_eq!(tag_layout.size, tag_val.layout.size);
- assert_eq!(tag_layout.abi.is_signed(), tag_val.layout.abi.is_signed());
- trace!("tag value: {}", tag_val);
-
- // Figure out which discriminant and variant this corresponds to.
- Ok(match *tag_encoding {
- TagEncoding::Direct => {
- let scalar = tag_val.to_scalar();
- // Generate a specific error if `tag_val` is not an integer.
- // (`tag_bits` itself is only used for error messages below.)
- let tag_bits = scalar
- .try_to_int()
- .map_err(|dbg_val| err_ub!(InvalidTag(dbg_val)))?
- .assert_bits(tag_layout.size);
- // Cast bits from tag layout to discriminant layout.
- // After the checks we did above, this cannot fail, as
- // discriminants are int-like.
- let discr_val =
- self.cast_from_int_like(scalar, tag_val.layout, discr_layout.ty).unwrap();
- let discr_bits = discr_val.assert_bits(discr_layout.size);
- // Convert discriminant to variant index, and catch invalid discriminants.
- let index = match *op.layout.ty.kind() {
- ty::Adt(adt, _) => {
- adt.discriminants(*self.tcx).find(|(_, var)| var.val == discr_bits)
- }
- ty::Generator(def_id, substs, _) => {
- let substs = substs.as_generator();
- substs
- .discriminants(def_id, *self.tcx)
- .find(|(_, var)| var.val == discr_bits)
- }
- _ => span_bug!(self.cur_span(), "tagged layout for non-adt non-generator"),
- }
- .ok_or_else(|| err_ub!(InvalidTag(Scalar::from_uint(tag_bits, tag_layout.size))))?;
- // Return the cast value, and the index.
- (discr_val, index.0)
- }
- TagEncoding::Niche { untagged_variant, ref niche_variants, niche_start } => {
- let tag_val = tag_val.to_scalar();
- // Compute the variant this niche value/"tag" corresponds to. With niche layout,
- // discriminant (encoded in niche/tag) and variant index are the same.
- let variants_start = niche_variants.start().as_u32();
- let variants_end = niche_variants.end().as_u32();
- let variant = match tag_val.try_to_int() {
- Err(dbg_val) => {
- // So this is a pointer then, and casting to an int failed.
- // Can only happen during CTFE.
- // The niche must be just 0, and the ptr not null, then we know this is
- // okay. Everything else, we conservatively reject.
- let ptr_valid = niche_start == 0
- && variants_start == variants_end
- && !self.scalar_may_be_null(tag_val)?;
- if !ptr_valid {
- throw_ub!(InvalidTag(dbg_val))
- }
- untagged_variant
- }
- Ok(tag_bits) => {
- let tag_bits = tag_bits.assert_bits(tag_layout.size);
- // We need to use machine arithmetic to get the relative variant idx:
- // variant_index_relative = tag_val - niche_start_val
- let tag_val = ImmTy::from_uint(tag_bits, tag_layout);
- let niche_start_val = ImmTy::from_uint(niche_start, tag_layout);
- let variant_index_relative_val =
- self.binary_op(mir::BinOp::Sub, &tag_val, &niche_start_val)?;
- let variant_index_relative =
- variant_index_relative_val.to_scalar().assert_bits(tag_val.layout.size);
- // Check if this is in the range that indicates an actual discriminant.
- if variant_index_relative <= u128::from(variants_end - variants_start) {
- let variant_index_relative = u32::try_from(variant_index_relative)
- .expect("we checked that this fits into a u32");
- // Then computing the absolute variant idx should not overflow any more.
- let variant_index = variants_start
- .checked_add(variant_index_relative)
- .expect("overflow computing absolute variant idx");
- let variants_len = op
- .layout
- .ty
- .ty_adt_def()
- .expect("tagged layout for non adt")
- .variants()
- .len();
- assert!(usize::try_from(variant_index).unwrap() < variants_len);
- VariantIdx::from_u32(variant_index)
- } else {
- untagged_variant
- }
- }
- };
- // Compute the size of the scalar we need to return.
- // No need to cast, because the variant index directly serves as discriminant and is
- // encoded in the tag.
- (Scalar::from_uint(variant.as_u32(), discr_layout.size), variant)
- }
- })
- }
}
// Some nodes are used a lot. Make sure they don't unintentionally get bigger.
use rustc_ast::Mutability;
use rustc_middle::mir;
use rustc_middle::ty;
-use rustc_middle::ty::layout::{LayoutOf, PrimitiveExt, TyAndLayout};
-use rustc_target::abi::{self, Abi, Align, HasDataLayout, Size, TagEncoding, VariantIdx};
+use rustc_middle::ty::layout::{LayoutOf, TyAndLayout};
+use rustc_target::abi::{self, Abi, Align, HasDataLayout, Size, VariantIdx};
use super::{
alloc_range, mir_assign_valid_types, AllocId, AllocRef, AllocRefMut, CheckInAllocMsg,
MPlaceTy { mplace, layout, align: layout.align.abi }
}
- /// Writes the discriminant of the given variant.
- #[instrument(skip(self), level = "debug")]
- pub fn write_discriminant(
+ /// Writes the aggregate to the destination.
+ #[instrument(skip(self), level = "trace")]
+ pub fn write_aggregate(
&mut self,
- variant_index: VariantIdx,
+ kind: &mir::AggregateKind<'tcx>,
+ operands: &[mir::Operand<'tcx>],
dest: &PlaceTy<'tcx, M::Provenance>,
) -> InterpResult<'tcx> {
- // This must be an enum or generator.
- match dest.layout.ty.kind() {
- ty::Adt(adt, _) => assert!(adt.is_enum()),
- ty::Generator(..) => {}
- _ => span_bug!(
- self.cur_span(),
- "write_discriminant called on non-variant-type (neither enum nor generator)"
- ),
- }
- // Layout computation excludes uninhabited variants from consideration
- // therefore there's no way to represent those variants in the given layout.
- // Essentially, uninhabited variants do not have a tag that corresponds to their
- // discriminant, so we cannot do anything here.
- // When evaluating we will always error before even getting here, but ConstProp 'executes'
- // dead code, so we cannot ICE here.
- if dest.layout.for_variant(self, variant_index).abi.is_uninhabited() {
- throw_ub!(UninhabitedEnumVariantWritten)
- }
-
- match dest.layout.variants {
- abi::Variants::Single { index } => {
- assert_eq!(index, variant_index);
- }
- abi::Variants::Multiple {
- tag_encoding: TagEncoding::Direct,
- tag: tag_layout,
- tag_field,
- ..
- } => {
- // No need to validate that the discriminant here because the
- // `TyAndLayout::for_variant()` call earlier already checks the variant is valid.
-
- let discr_val =
- dest.layout.ty.discriminant_for_variant(*self.tcx, variant_index).unwrap().val;
-
- // raw discriminants for enums are isize or bigger during
- // their computation, but the in-memory tag is the smallest possible
- // representation
- let size = tag_layout.size(self);
- let tag_val = size.truncate(discr_val);
-
- let tag_dest = self.place_field(dest, tag_field)?;
- self.write_scalar(Scalar::from_uint(tag_val, size), &tag_dest)?;
- }
- abi::Variants::Multiple {
- tag_encoding:
- TagEncoding::Niche { untagged_variant, ref niche_variants, niche_start },
- tag: tag_layout,
- tag_field,
- ..
- } => {
- // No need to validate that the discriminant here because the
- // `TyAndLayout::for_variant()` call earlier already checks the variant is valid.
-
- if variant_index != untagged_variant {
- let variants_start = niche_variants.start().as_u32();
- let variant_index_relative = variant_index
- .as_u32()
- .checked_sub(variants_start)
- .expect("overflow computing relative variant idx");
- // We need to use machine arithmetic when taking into account `niche_start`:
- // tag_val = variant_index_relative + niche_start_val
- let tag_layout = self.layout_of(tag_layout.primitive().to_int_ty(*self.tcx))?;
- let niche_start_val = ImmTy::from_uint(niche_start, tag_layout);
- let variant_index_relative_val =
- ImmTy::from_uint(variant_index_relative, tag_layout);
- let tag_val = self.binary_op(
- mir::BinOp::Add,
- &variant_index_relative_val,
- &niche_start_val,
- )?;
- // Write result.
- let niche_dest = self.place_field(dest, tag_field)?;
- self.write_immediate(*tag_val, &niche_dest)?;
- }
+ self.write_uninit(&dest)?;
+ let (variant_index, variant_dest, active_field_index) = match *kind {
+ mir::AggregateKind::Adt(_, variant_index, _, _, active_field_index) => {
+ let variant_dest = self.place_downcast(&dest, variant_index)?;
+ (variant_index, variant_dest, active_field_index)
}
+ _ => (VariantIdx::from_u32(0), dest.clone(), None),
+ };
+ if active_field_index.is_some() {
+ assert_eq!(operands.len(), 1);
}
-
- Ok(())
+ for (field_index, operand) in operands.iter().enumerate() {
+ let field_index = active_field_index.unwrap_or(field_index);
+ let field_dest = self.place_field(&variant_dest, field_index)?;
+ let op = self.eval_operand(operand, Some(field_dest.layout))?;
+ self.copy_op(&op, &field_dest, /*allow_transmute*/ false)?;
+ }
+ self.write_discriminant(variant_index, &dest)
}
pub fn raw_const_to_mplace(
}
Aggregate(box ref kind, ref operands) => {
- assert!(matches!(kind, mir::AggregateKind::Array(..)));
-
- for (field_index, operand) in operands.iter().enumerate() {
- let op = self.eval_operand(operand, None)?;
- let field_dest = self.place_field(&dest, field_index)?;
- self.copy_op(&op, &field_dest, /*allow_transmute*/ false)?;
- }
+ self.write_aggregate(kind, operands, &dest)?;
}
Repeat(ref operand, _) => {
Rvalue::Aggregate(kind, ..) => {
if let AggregateKind::Generator(def_id, ..) = kind.as_ref()
- && let Some(generator_kind @ hir::GeneratorKind::Async(..)) = self.tcx.generator_kind(def_id.to_def_id())
+ && let Some(generator_kind @ hir::GeneratorKind::Async(..)) = self.tcx.generator_kind(def_id)
{
self.check_op(ops::Generator(generator_kind));
}
use rustc_trait_selection::traits::SelectionContext;
use super::ConstCx;
-use crate::errors::{
- InteriorMutabilityBorrow, InteriorMutableDataRefer, MutDerefErr, NonConstFmtMacroCall,
- NonConstFnCall, NonConstOpErr, PanicNonStrErr, RawPtrToIntErr, StaticAccessErr,
- TransientMutBorrowErr, TransientMutBorrowErrRaw, UnallowedFnPointerCall,
- UnallowedHeapAllocations, UnallowedInlineAsm, UnallowedMutableRefs, UnallowedMutableRefsRaw,
- UnallowedOpInConstContext, UnstableConstFn,
-};
+use crate::errors;
use crate::util::{call_kind, CallDesugaringKind, CallKind};
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
ccx: &ConstCx<'_, 'tcx>,
span: Span,
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
- ccx.tcx.sess.create_err(UnallowedFnPointerCall { span, kind: ccx.const_kind() })
+ ccx.tcx.sess.create_err(errors::UnallowedFnPointerCall { span, kind: ccx.const_kind() })
}
}
diag_trait(&mut err, self_ty, tcx.require_lang_item(LangItem::Deref, Some(span)));
err
}
- _ if tcx.opt_parent(callee) == tcx.get_diagnostic_item(sym::ArgumentV1Methods) => {
- ccx.tcx.sess.create_err(NonConstFmtMacroCall { span, kind: ccx.const_kind() })
- }
- _ => ccx.tcx.sess.create_err(NonConstFnCall {
+ _ if tcx.opt_parent(callee) == tcx.get_diagnostic_item(sym::ArgumentV1Methods) => ccx
+ .tcx
+ .sess
+ .create_err(errors::NonConstFmtMacroCall { span, kind: ccx.const_kind() }),
+ _ => ccx.tcx.sess.create_err(errors::NonConstFnCall {
span,
def_path_str: ccx.tcx.def_path_str_with_substs(callee, substs),
kind: ccx.const_kind(),
let mut err = ccx
.tcx
.sess
- .create_err(UnstableConstFn { span, def_path: ccx.tcx.def_path_str(def_id) });
+ .create_err(errors::UnstableConstFn { span, def_path: ccx.tcx.def_path_str(def_id) });
if ccx.is_const_stable_const_fn() {
err.help("const-stable functions can only call other const-stable functions");
let msg = format!("{}s are not allowed in {}s", self.0.descr(), ccx.const_kind());
if let hir::GeneratorKind::Async(hir::AsyncGeneratorKind::Block) = self.0 {
ccx.tcx.sess.create_feature_err(
- UnallowedOpInConstContext { span, msg },
+ errors::UnallowedOpInConstContext { span, msg },
sym::const_async_blocks,
)
} else {
- ccx.tcx.sess.create_err(UnallowedOpInConstContext { span, msg })
+ ccx.tcx.sess.create_err(errors::UnallowedOpInConstContext { span, msg })
}
}
}
ccx: &ConstCx<'_, 'tcx>,
span: Span,
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
- ccx.tcx.sess.create_err(UnallowedHeapAllocations {
+ ccx.tcx.sess.create_err(errors::UnallowedHeapAllocations {
span,
kind: ccx.const_kind(),
teach: ccx.tcx.sess.teach(&error_code!(E0010)).then_some(()),
ccx: &ConstCx<'_, 'tcx>,
span: Span,
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
- ccx.tcx.sess.create_err(UnallowedInlineAsm { span, kind: ccx.const_kind() })
+ ccx.tcx.sess.create_err(errors::UnallowedInlineAsm { span, kind: ccx.const_kind() })
}
}
ccx: &ConstCx<'_, 'tcx>,
span: Span,
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
- ccx.tcx.sess.create_feature_err(InteriorMutabilityBorrow { span }, sym::const_refs_to_cell)
+ ccx.tcx
+ .sess
+ .create_feature_err(errors::InteriorMutabilityBorrow { span }, sym::const_refs_to_cell)
}
}
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
// FIXME: Maybe a more elegant solution to this if else case
if let hir::ConstContext::Static(_) = ccx.const_kind() {
- ccx.tcx.sess.create_err(InteriorMutableDataRefer {
+ ccx.tcx.sess.create_err(errors::InteriorMutableDataRefer {
span,
opt_help: Some(()),
kind: ccx.const_kind(),
teach: ccx.tcx.sess.teach(&error_code!(E0492)).then_some(()),
})
} else {
- ccx.tcx.sess.create_err(InteriorMutableDataRefer {
+ ccx.tcx.sess.create_err(errors::InteriorMutableDataRefer {
span,
opt_help: None,
kind: ccx.const_kind(),
span: Span,
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
match self.0 {
- hir::BorrowKind::Raw => ccx.tcx.sess.create_err(UnallowedMutableRefsRaw {
+ hir::BorrowKind::Raw => ccx.tcx.sess.create_err(errors::UnallowedMutableRefsRaw {
span,
kind: ccx.const_kind(),
teach: ccx.tcx.sess.teach(&error_code!(E0764)).then_some(()),
}),
- hir::BorrowKind::Ref => ccx.tcx.sess.create_err(UnallowedMutableRefs {
+ hir::BorrowKind::Ref => ccx.tcx.sess.create_err(errors::UnallowedMutableRefs {
span,
kind: ccx.const_kind(),
teach: ccx.tcx.sess.teach(&error_code!(E0764)).then_some(()),
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
let kind = ccx.const_kind();
match self.0 {
- hir::BorrowKind::Raw => ccx
- .tcx
- .sess
- .create_feature_err(TransientMutBorrowErrRaw { span, kind }, sym::const_mut_refs),
- hir::BorrowKind::Ref => ccx
- .tcx
- .sess
- .create_feature_err(TransientMutBorrowErr { span, kind }, sym::const_mut_refs),
+ hir::BorrowKind::Raw => ccx.tcx.sess.create_feature_err(
+ errors::TransientMutBorrowErrRaw { span, kind },
+ sym::const_mut_refs,
+ ),
+ hir::BorrowKind::Ref => ccx.tcx.sess.create_feature_err(
+ errors::TransientMutBorrowErr { span, kind },
+ sym::const_mut_refs,
+ ),
}
}
}
ccx: &ConstCx<'_, 'tcx>,
span: Span,
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
- ccx.tcx
- .sess
- .create_feature_err(MutDerefErr { span, kind: ccx.const_kind() }, sym::const_mut_refs)
+ ccx.tcx.sess.create_feature_err(
+ errors::MutDerefErr { span, kind: ccx.const_kind() },
+ sym::const_mut_refs,
+ )
}
}
ccx: &ConstCx<'_, 'tcx>,
span: Span,
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
- ccx.tcx.sess.create_err(PanicNonStrErr { span })
+ ccx.tcx.sess.create_err(errors::PanicNonStrErr { span })
}
}
ccx: &ConstCx<'_, 'tcx>,
span: Span,
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
- ccx.tcx.sess.create_err(RawPtrToIntErr { span })
+ ccx.tcx.sess.create_err(errors::RawPtrToIntErr { span })
}
}
ccx: &ConstCx<'_, 'tcx>,
span: Span,
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
- ccx.tcx.sess.create_err(StaticAccessErr {
+ ccx.tcx.sess.create_err(errors::StaticAccessErr {
span,
kind: ccx.const_kind(),
teach: ccx.tcx.sess.teach(&error_code!(E0013)).then_some(()),
ccx: &ConstCx<'_, 'tcx>,
span: Span,
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
- ccx.tcx.sess.create_err(NonConstOpErr { span })
+ ccx.tcx.sess.create_err(errors::NonConstOpErr { span })
}
}
use rustc_middle::mir::visit::NonUseContext::VarDebugInfo;
use rustc_middle::mir::visit::{PlaceContext, Visitor};
use rustc_middle::mir::{
- traversal, AggregateKind, BasicBlock, BinOp, Body, BorrowKind, CastKind, CopyNonOverlapping,
- Local, Location, MirPass, MirPhase, NonDivergingIntrinsic, Operand, Place, PlaceElem, PlaceRef,
- ProjectionElem, RetagKind, RuntimePhase, Rvalue, SourceScope, Statement, StatementKind,
- Terminator, TerminatorKind, UnOp, START_BLOCK,
+ traversal, BasicBlock, BinOp, Body, BorrowKind, CastKind, CopyNonOverlapping, Local, Location,
+ MirPass, MirPhase, NonDivergingIntrinsic, Operand, Place, PlaceElem, PlaceRef, ProjectionElem,
+ RetagKind, RuntimePhase, Rvalue, SourceScope, Statement, StatementKind, Terminator,
+ TerminatorKind, UnOp, START_BLOCK,
};
use rustc_middle::ty::{self, InstanceDef, ParamEnv, Ty, TyCtxt};
use rustc_mir_dataflow::impls::MaybeStorageLive;
};
}
match rvalue {
- Rvalue::Use(_) | Rvalue::CopyForDeref(_) => {}
- Rvalue::Aggregate(agg_kind, _) => {
- let disallowed = match **agg_kind {
- AggregateKind::Array(..) => false,
- _ => self.mir_phase >= MirPhase::Runtime(RuntimePhase::PostCleanup),
- };
- if disallowed {
- self.fail(
- location,
- format!("{:?} have been lowered to field assignments", rvalue),
- )
- }
- }
+ Rvalue::Use(_) | Rvalue::CopyForDeref(_) | Rvalue::Aggregate(..) => {}
Rvalue::Ref(_, BorrowKind::Shallow, _) => {
if self.mir_phase >= MirPhase::Runtime(RuntimePhase::Initial) {
self.fail(
+++ /dev/null
-use rustc_index::vec::Idx;
-use rustc_middle::mir::*;
-use rustc_middle::ty::{Ty, TyCtxt};
-use rustc_target::abi::VariantIdx;
-
-use std::iter::TrustedLen;
-
-/// Expand `lhs = Rvalue::Aggregate(kind, operands)` into assignments to the fields.
-///
-/// Produces something like
-/// ```ignore (ilustrative)
-/// (lhs as Variant).field0 = arg0; // We only have a downcast if this is an enum
-/// (lhs as Variant).field1 = arg1;
-/// discriminant(lhs) = variant_index; // If lhs is an enum or generator.
-/// ```
-pub fn expand_aggregate<'tcx>(
- orig_lhs: Place<'tcx>,
- operands: impl Iterator<Item = (Operand<'tcx>, Ty<'tcx>)> + TrustedLen,
- kind: AggregateKind<'tcx>,
- source_info: SourceInfo,
- tcx: TyCtxt<'tcx>,
-) -> impl Iterator<Item = Statement<'tcx>> + TrustedLen {
- let mut lhs = orig_lhs;
- let mut set_discriminant = None;
- let active_field_index = match kind {
- AggregateKind::Adt(adt_did, variant_index, _, _, active_field_index) => {
- let adt_def = tcx.adt_def(adt_did);
- if adt_def.is_enum() {
- set_discriminant = Some(Statement {
- kind: StatementKind::SetDiscriminant {
- place: Box::new(orig_lhs),
- variant_index,
- },
- source_info,
- });
- lhs = tcx.mk_place_downcast(orig_lhs, adt_def, variant_index);
- }
- active_field_index
- }
- AggregateKind::Generator(..) => {
- // Right now we only support initializing generators to
- // variant 0 (Unresumed).
- let variant_index = VariantIdx::new(0);
- set_discriminant = Some(Statement {
- kind: StatementKind::SetDiscriminant { place: Box::new(orig_lhs), variant_index },
- source_info,
- });
-
- // Operands are upvars stored on the base place, so no
- // downcast is necessary.
-
- None
- }
- _ => None,
- };
-
- let operands = operands.enumerate().map(move |(i, (op, ty))| {
- let lhs_field = if let AggregateKind::Array(_) = kind {
- let offset = u64::try_from(i).unwrap();
- tcx.mk_place_elem(
- lhs,
- ProjectionElem::ConstantIndex { offset, min_length: offset + 1, from_end: false },
- )
- } else {
- let field = Field::new(active_field_index.unwrap_or(i));
- tcx.mk_place_field(lhs, field, ty)
- };
- Statement {
- source_info,
- kind: StatementKind::Assign(Box::new((lhs_field, Rvalue::Use(op)))),
- }
- });
- [Statement { source_info, kind: StatementKind::Deinit(Box::new(orig_lhs)) }]
- .into_iter()
- .chain(operands)
- .chain(set_discriminant)
-}
-pub mod aggregate;
mod alignment;
mod call_kind;
pub mod collect_writes;
mod might_permit_raw_init;
mod type_name;
-pub use self::aggregate::expand_aggregate;
pub use self::alignment::is_disaligned;
pub use self::call_kind::{call_kind, CallDesugaringKind, CallKind};
pub use self::compare_types::{is_equal_up_to_subtyping, is_subtype};
pub auto trait Send {}
pub auto trait Sync {}
- impl<T: ?Sized> Send for T {}
- impl<T: ?Sized> Sync for T {}
+ impl<T> Send for T {}
+ impl<T> Sync for T {}
#[macro_export]
macro_rules! rustc_erase_owner {
crate-type = ["dylib"]
[dependencies]
-tracing = { version = "0.1.35" }
-serde_json = "1.0.59"
-rustc_log = { path = "../rustc_log" }
-rustc_middle = { path = "../rustc_middle" }
-rustc_ast_pretty = { path = "../rustc_ast_pretty" }
-rustc_target = { path = "../rustc_target" }
-rustc_lint = { path = "../rustc_lint" }
-rustc_data_structures = { path = "../rustc_data_structures" }
-rustc_errors = { path = "../rustc_errors" }
-rustc_feature = { path = "../rustc_feature" }
-rustc_hir = { path = "../rustc_hir" }
-rustc_hir_pretty = { path = "../rustc_hir_pretty" }
-rustc_macros = { path = "../rustc_macros" }
-rustc_metadata = { path = "../rustc_metadata" }
-rustc_parse = { path = "../rustc_parse" }
-rustc_plugin_impl = { path = "../rustc_plugin_impl" }
-rustc_save_analysis = { path = "../rustc_save_analysis" }
-rustc_codegen_ssa = { path = "../rustc_codegen_ssa" }
-rustc_session = { path = "../rustc_session" }
-rustc_error_codes = { path = "../rustc_error_codes" }
-rustc_interface = { path = "../rustc_interface" }
-rustc_ast = { path = "../rustc_ast" }
-rustc_span = { path = "../rustc_span" }
-rustc_hir_analysis = { path = "../rustc_hir_analysis" }
-
-[target.'cfg(unix)'.dependencies]
-libc = "0.2"
-
-[target.'cfg(windows)'.dependencies]
-winapi = { version = "0.3", features = ["consoleapi", "debugapi", "processenv"] }
-
-[features]
-llvm = ['rustc_interface/llvm']
-max_level_info = ['rustc_log/max_level_info']
-rustc_use_parallel_compiler = ['rustc_data_structures/rustc_use_parallel_compiler', 'rustc_interface/rustc_use_parallel_compiler',
- 'rustc_middle/rustc_use_parallel_compiler']
+rustc_driver_impl = { path = "../rustc_driver_impl" }
+++ /dev/null
-The `driver` crate is effectively the "main" function for the rust
-compiler. It orchestrates the compilation process and "knits together"
-the code from the other crates within rustc. This crate itself does
-not contain any of the "main logic" of the compiler (though it does
-have some code related to pretty printing or other minor compiler
-options).
-
-For more information about how the driver works, see the [rustc dev guide].
-
-[rustc dev guide]: https://rustc-dev-guide.rust-lang.org/rustc-driver.html
+++ /dev/null
-use std::error;
-use std::fmt;
-use std::fs;
-use std::io;
-
-fn arg_expand(arg: String) -> Result<Vec<String>, Error> {
- if let Some(path) = arg.strip_prefix('@') {
- let file = match fs::read_to_string(path) {
- Ok(file) => file,
- Err(ref err) if err.kind() == io::ErrorKind::InvalidData => {
- return Err(Error::Utf8Error(Some(path.to_string())));
- }
- Err(err) => return Err(Error::IOError(path.to_string(), err)),
- };
- Ok(file.lines().map(ToString::to_string).collect())
- } else {
- Ok(vec![arg])
- }
-}
-
-pub fn arg_expand_all(at_args: &[String]) -> Vec<String> {
- let mut args = Vec::new();
- for arg in at_args {
- match arg_expand(arg.clone()) {
- Ok(arg) => args.extend(arg),
- Err(err) => rustc_session::early_error(
- rustc_session::config::ErrorOutputType::default(),
- &format!("Failed to load argument file: {err}"),
- ),
- }
- }
- args
-}
-
-#[derive(Debug)]
-pub enum Error {
- Utf8Error(Option<String>),
- IOError(String, io::Error),
-}
-
-impl fmt::Display for Error {
- fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
- match self {
- Error::Utf8Error(None) => write!(fmt, "Utf8 error"),
- Error::Utf8Error(Some(path)) => write!(fmt, "Utf8 error in {path}"),
- Error::IOError(path, err) => write!(fmt, "IO Error: {path}: {err}"),
- }
- }
-}
-
-impl error::Error for Error {}
-//! The Rust compiler.
-//!
-//! # Note
-//!
-//! This API is completely unstable and subject to change.
+// This crate is intentionally empty and a rexport of `rustc_driver_impl` to allow the code in
+// `rustc_driver_impl` to be compiled in parallel with other crates.
-#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
-#![feature(is_terminal)]
-#![feature(once_cell)]
-#![feature(decl_macro)]
-#![recursion_limit = "256"]
-#![allow(rustc::potential_query_instability)]
-#![deny(rustc::untranslatable_diagnostic)]
-#![deny(rustc::diagnostic_outside_of_impl)]
-
-#[macro_use]
-extern crate tracing;
-
-pub extern crate rustc_plugin_impl as plugin;
-
-use rustc_ast as ast;
-use rustc_codegen_ssa::{traits::CodegenBackend, CodegenErrors, CodegenResults};
-use rustc_data_structures::profiling::{get_resident_set_size, print_time_passes_entry};
-use rustc_data_structures::sync::SeqCst;
-use rustc_errors::registry::{InvalidErrorCode, Registry};
-use rustc_errors::{ErrorGuaranteed, PResult};
-use rustc_feature::find_gated_cfg;
-use rustc_hir::def_id::LOCAL_CRATE;
-use rustc_interface::util::{self, collect_crate_types, get_codegen_backend};
-use rustc_interface::{interface, Queries};
-use rustc_lint::LintStore;
-use rustc_metadata::locator;
-use rustc_save_analysis as save;
-use rustc_save_analysis::DumpHandler;
-use rustc_session::config::{nightly_options, CG_OPTIONS, Z_OPTIONS};
-use rustc_session::config::{ErrorOutputType, Input, OutputType, PrintRequest, TrimmedDefPaths};
-use rustc_session::cstore::MetadataLoader;
-use rustc_session::getopts;
-use rustc_session::lint::{Lint, LintId};
-use rustc_session::{config, Session};
-use rustc_session::{early_error, early_error_no_abort, early_warn};
-use rustc_span::source_map::{FileLoader, FileName};
-use rustc_span::symbol::sym;
-use rustc_target::json::ToJson;
-
-use std::cmp::max;
-use std::env;
-use std::ffi::OsString;
-use std::fs;
-use std::io::{self, IsTerminal, Read, Write};
-use std::panic::{self, catch_unwind};
-use std::path::PathBuf;
-use std::process::{self, Command, Stdio};
-use std::str;
-use std::sync::LazyLock;
-use std::time::Instant;
-
-pub mod args;
-pub mod pretty;
-mod session_diagnostics;
-
-use crate::session_diagnostics::{
- RLinkEmptyVersionNumber, RLinkEncodingVersionMismatch, RLinkRustcVersionMismatch,
- RLinkWrongFileType, RlinkNotAFile, RlinkUnableToRead,
-};
-
-/// Exit status code used for successful compilation and help output.
-pub const EXIT_SUCCESS: i32 = 0;
-
-/// Exit status code used for compilation failures and invalid flags.
-pub const EXIT_FAILURE: i32 = 1;
-
-const BUG_REPORT_URL: &str = "https://github.com/rust-lang/rust/issues/new\
- ?labels=C-bug%2C+I-ICE%2C+T-compiler&template=ice.md";
-
-const ICE_REPORT_COMPILER_FLAGS: &[&str] = &["-Z", "-C", "--crate-type"];
-
-const ICE_REPORT_COMPILER_FLAGS_EXCLUDE: &[&str] = &["metadata", "extra-filename"];
-
-const ICE_REPORT_COMPILER_FLAGS_STRIP_VALUE: &[&str] = &["incremental"];
-
-pub fn abort_on_err<T>(result: Result<T, ErrorGuaranteed>, sess: &Session) -> T {
- match result {
- Err(..) => {
- sess.abort_if_errors();
- panic!("error reported but abort_if_errors didn't abort???");
- }
- Ok(x) => x,
- }
-}
-
-pub trait Callbacks {
- /// Called before creating the compiler instance
- fn config(&mut self, _config: &mut interface::Config) {}
- /// Called after parsing. Return value instructs the compiler whether to
- /// continue the compilation afterwards (defaults to `Compilation::Continue`)
- fn after_parsing<'tcx>(
- &mut self,
- _compiler: &interface::Compiler,
- _queries: &'tcx Queries<'tcx>,
- ) -> Compilation {
- Compilation::Continue
- }
- /// Called after expansion. Return value instructs the compiler whether to
- /// continue the compilation afterwards (defaults to `Compilation::Continue`)
- fn after_expansion<'tcx>(
- &mut self,
- _compiler: &interface::Compiler,
- _queries: &'tcx Queries<'tcx>,
- ) -> Compilation {
- Compilation::Continue
- }
- /// Called after analysis. Return value instructs the compiler whether to
- /// continue the compilation afterwards (defaults to `Compilation::Continue`)
- fn after_analysis<'tcx>(
- &mut self,
- _compiler: &interface::Compiler,
- _queries: &'tcx Queries<'tcx>,
- ) -> Compilation {
- Compilation::Continue
- }
-}
-
-#[derive(Default)]
-pub struct TimePassesCallbacks {
- time_passes: bool,
-}
-
-impl Callbacks for TimePassesCallbacks {
- // JUSTIFICATION: the session doesn't exist at this point.
- #[allow(rustc::bad_opt_access)]
- fn config(&mut self, config: &mut interface::Config) {
- // If a --print=... option has been given, we don't print the "total"
- // time because it will mess up the --print output. See #64339.
- //
- self.time_passes = config.opts.prints.is_empty() && config.opts.unstable_opts.time_passes;
- config.opts.trimmed_def_paths = TrimmedDefPaths::GoodPath;
- }
-}
-
-pub fn diagnostics_registry() -> Registry {
- Registry::new(rustc_error_codes::DIAGNOSTICS)
-}
-
-/// This is the primary entry point for rustc.
-pub struct RunCompiler<'a, 'b> {
- at_args: &'a [String],
- callbacks: &'b mut (dyn Callbacks + Send),
- file_loader: Option<Box<dyn FileLoader + Send + Sync>>,
- make_codegen_backend:
- Option<Box<dyn FnOnce(&config::Options) -> Box<dyn CodegenBackend> + Send>>,
-}
-
-impl<'a, 'b> RunCompiler<'a, 'b> {
- pub fn new(at_args: &'a [String], callbacks: &'b mut (dyn Callbacks + Send)) -> Self {
- Self { at_args, callbacks, file_loader: None, make_codegen_backend: None }
- }
-
- /// Set a custom codegen backend.
- ///
- /// Has no uses within this repository, but is used by bjorn3 for "the
- /// hotswapping branch of cg_clif" for "setting the codegen backend from a
- /// custom driver where the custom codegen backend has arbitrary data."
- /// (See #102759.)
- pub fn set_make_codegen_backend(
- &mut self,
- make_codegen_backend: Option<
- Box<dyn FnOnce(&config::Options) -> Box<dyn CodegenBackend> + Send>,
- >,
- ) -> &mut Self {
- self.make_codegen_backend = make_codegen_backend;
- self
- }
-
- /// Load files from sources other than the file system.
- ///
- /// Has no uses within this repository, but may be used in the future by
- /// bjorn3 for "hooking rust-analyzer's VFS into rustc at some point for
- /// running rustc without having to save". (See #102759.)
- pub fn set_file_loader(
- &mut self,
- file_loader: Option<Box<dyn FileLoader + Send + Sync>>,
- ) -> &mut Self {
- self.file_loader = file_loader;
- self
- }
-
- /// Parse args and run the compiler.
- pub fn run(self) -> interface::Result<()> {
- run_compiler(self.at_args, self.callbacks, self.file_loader, self.make_codegen_backend)
- }
-}
-
-fn run_compiler(
- at_args: &[String],
- callbacks: &mut (dyn Callbacks + Send),
- file_loader: Option<Box<dyn FileLoader + Send + Sync>>,
- make_codegen_backend: Option<
- Box<dyn FnOnce(&config::Options) -> Box<dyn CodegenBackend> + Send>,
- >,
-) -> interface::Result<()> {
- let args = args::arg_expand_all(at_args);
-
- let Some(matches) = handle_options(&args) else { return Ok(()) };
-
- let sopts = config::build_session_options(&matches);
-
- if let Some(ref code) = matches.opt_str("explain") {
- handle_explain(diagnostics_registry(), code, sopts.error_format);
- return Ok(());
- }
-
- let cfg = interface::parse_cfgspecs(matches.opt_strs("cfg"));
- let check_cfg = interface::parse_check_cfg(matches.opt_strs("check-cfg"));
- let (odir, ofile) = make_output(&matches);
- let mut config = interface::Config {
- opts: sopts,
- crate_cfg: cfg,
- crate_check_cfg: check_cfg,
- input: Input::File(PathBuf::new()),
- output_file: ofile,
- output_dir: odir,
- file_loader,
- lint_caps: Default::default(),
- parse_sess_created: None,
- register_lints: None,
- override_queries: None,
- make_codegen_backend,
- registry: diagnostics_registry(),
- };
-
- if !tracing::dispatcher::has_been_set() {
- init_rustc_env_logger_with_backtrace_option(&config.opts.unstable_opts.log_backtrace);
- }
-
- match make_input(config.opts.error_format, &matches.free) {
- Err(reported) => return Err(reported),
- Ok(Some(input)) => {
- config.input = input;
-
- callbacks.config(&mut config);
- }
- Ok(None) => match matches.free.len() {
- 0 => {
- callbacks.config(&mut config);
- interface::run_compiler(config, |compiler| {
- let sopts = &compiler.session().opts;
- if sopts.describe_lints {
- let mut lint_store =
- rustc_lint::new_lint_store(compiler.session().enable_internal_lints());
- let registered_lints =
- if let Some(register_lints) = compiler.register_lints() {
- register_lints(compiler.session(), &mut lint_store);
- true
- } else {
- false
- };
- describe_lints(compiler.session(), &lint_store, registered_lints);
- return;
- }
- let should_stop =
- print_crate_info(&***compiler.codegen_backend(), compiler.session(), false);
-
- if should_stop == Compilation::Stop {
- return;
- }
- early_error(sopts.error_format, "no input filename given")
- });
- return Ok(());
- }
- 1 => panic!("make_input should have provided valid inputs"),
- _ => early_error(
- config.opts.error_format,
- &format!(
- "multiple input filenames provided (first two filenames are `{}` and `{}`)",
- matches.free[0], matches.free[1],
- ),
- ),
- },
- };
-
- interface::run_compiler(config, |compiler| {
- let sess = compiler.session();
- let should_stop = print_crate_info(&***compiler.codegen_backend(), sess, true)
- .and_then(|| list_metadata(sess, &*compiler.codegen_backend().metadata_loader()))
- .and_then(|| try_process_rlink(sess, compiler));
-
- if should_stop == Compilation::Stop {
- return sess.compile_status();
- }
-
- let linker = compiler.enter(|queries| {
- let early_exit = || sess.compile_status().map(|_| None);
- queries.parse()?;
-
- if let Some(ppm) = &sess.opts.pretty {
- if ppm.needs_ast_map() {
- queries.global_ctxt()?.enter(|tcx| {
- pretty::print_after_hir_lowering(tcx, *ppm);
- Ok(())
- })?;
- } else {
- let krate = queries.parse()?.steal();
- pretty::print_after_parsing(sess, &krate, *ppm);
- }
- trace!("finished pretty-printing");
- return early_exit();
- }
-
- if callbacks.after_parsing(compiler, queries) == Compilation::Stop {
- return early_exit();
- }
-
- if sess.opts.unstable_opts.parse_only || sess.opts.unstable_opts.show_span.is_some() {
- return early_exit();
- }
-
- {
- let plugins = queries.register_plugins()?;
- let (_, lint_store) = &*plugins.borrow();
-
- // Lint plugins are registered; now we can process command line flags.
- if sess.opts.describe_lints {
- describe_lints(sess, lint_store, true);
- return early_exit();
- }
- }
-
- let mut gctxt = queries.global_ctxt()?;
- if callbacks.after_expansion(compiler, queries) == Compilation::Stop {
- return early_exit();
- }
-
- // Make sure the `output_filenames` query is run for its side
- // effects of writing the dep-info and reporting errors.
- gctxt.enter(|tcx| tcx.output_filenames(()));
-
- if sess.opts.output_types.contains_key(&OutputType::DepInfo)
- && sess.opts.output_types.len() == 1
- {
- return early_exit();
- }
-
- if sess.opts.unstable_opts.no_analysis {
- return early_exit();
- }
-
- gctxt.enter(|tcx| {
- let result = tcx.analysis(());
- if sess.opts.unstable_opts.save_analysis {
- let crate_name = tcx.crate_name(LOCAL_CRATE);
- sess.time("save_analysis", || {
- save::process_crate(
- tcx,
- crate_name,
- &sess.io.input,
- None,
- DumpHandler::new(sess.io.output_dir.as_deref(), crate_name),
- )
- });
- }
- result
- })?;
-
- drop(gctxt);
-
- if callbacks.after_analysis(compiler, queries) == Compilation::Stop {
- return early_exit();
- }
-
- queries.ongoing_codegen()?;
-
- if sess.opts.unstable_opts.print_type_sizes {
- sess.code_stats.print_type_sizes();
- }
-
- let linker = queries.linker()?;
- Ok(Some(linker))
- })?;
-
- if let Some(linker) = linker {
- let _timer = sess.timer("link");
- linker.link()?
- }
-
- if sess.opts.unstable_opts.perf_stats {
- sess.print_perf_stats();
- }
-
- if sess.opts.unstable_opts.print_fuel.is_some() {
- eprintln!(
- "Fuel used by {}: {}",
- sess.opts.unstable_opts.print_fuel.as_ref().unwrap(),
- sess.print_fuel.load(SeqCst)
- );
- }
-
- Ok(())
- })
-}
-
-// Extract output directory and file from matches.
-fn make_output(matches: &getopts::Matches) -> (Option<PathBuf>, Option<PathBuf>) {
- let odir = matches.opt_str("out-dir").map(|o| PathBuf::from(&o));
- let ofile = matches.opt_str("o").map(|o| PathBuf::from(&o));
- (odir, ofile)
-}
-
-// Extract input (string or file and optional path) from matches.
-fn make_input(
- error_format: ErrorOutputType,
- free_matches: &[String],
-) -> Result<Option<Input>, ErrorGuaranteed> {
- if free_matches.len() == 1 {
- let ifile = &free_matches[0];
- if ifile == "-" {
- let mut src = String::new();
- if io::stdin().read_to_string(&mut src).is_err() {
- // Immediately stop compilation if there was an issue reading
- // the input (for example if the input stream is not UTF-8).
- let reported = early_error_no_abort(
- error_format,
- "couldn't read from stdin, as it did not contain valid UTF-8",
- );
- return Err(reported);
- }
- if let Ok(path) = env::var("UNSTABLE_RUSTDOC_TEST_PATH") {
- let line = env::var("UNSTABLE_RUSTDOC_TEST_LINE").expect(
- "when UNSTABLE_RUSTDOC_TEST_PATH is set \
- UNSTABLE_RUSTDOC_TEST_LINE also needs to be set",
- );
- let line = isize::from_str_radix(&line, 10)
- .expect("UNSTABLE_RUSTDOC_TEST_LINE needs to be an number");
- let file_name = FileName::doc_test_source_code(PathBuf::from(path), line);
- Ok(Some(Input::Str { name: file_name, input: src }))
- } else {
- Ok(Some(Input::Str { name: FileName::anon_source_code(&src), input: src }))
- }
- } else {
- Ok(Some(Input::File(PathBuf::from(ifile))))
- }
- } else {
- Ok(None)
- }
-}
-
-/// Whether to stop or continue compilation.
-#[derive(Copy, Clone, Debug, Eq, PartialEq)]
-pub enum Compilation {
- Stop,
- Continue,
-}
-
-impl Compilation {
- pub fn and_then<F: FnOnce() -> Compilation>(self, next: F) -> Compilation {
- match self {
- Compilation::Stop => Compilation::Stop,
- Compilation::Continue => next(),
- }
- }
-}
-
-fn handle_explain(registry: Registry, code: &str, output: ErrorOutputType) {
- let upper_cased_code = code.to_ascii_uppercase();
- let normalised =
- if upper_cased_code.starts_with('E') { upper_cased_code } else { format!("E{code:0>4}") };
- match registry.try_find_description(&normalised) {
- Ok(Some(description)) => {
- let mut is_in_code_block = false;
- let mut text = String::new();
- // Slice off the leading newline and print.
- for line in description.lines() {
- let indent_level =
- line.find(|c: char| !c.is_whitespace()).unwrap_or_else(|| line.len());
- let dedented_line = &line[indent_level..];
- if dedented_line.starts_with("```") {
- is_in_code_block = !is_in_code_block;
- text.push_str(&line[..(indent_level + 3)]);
- } else if is_in_code_block && dedented_line.starts_with("# ") {
- continue;
- } else {
- text.push_str(line);
- }
- text.push('\n');
- }
- if io::stdout().is_terminal() {
- show_content_with_pager(&text);
- } else {
- print!("{text}");
- }
- }
- Ok(None) => {
- early_error(output, &format!("no extended information for {code}"));
- }
- Err(InvalidErrorCode) => {
- early_error(output, &format!("{code} is not a valid error code"));
- }
- }
-}
-
-fn show_content_with_pager(content: &str) {
- let pager_name = env::var_os("PAGER").unwrap_or_else(|| {
- if cfg!(windows) { OsString::from("more.com") } else { OsString::from("less") }
- });
-
- let mut fallback_to_println = false;
-
- match Command::new(pager_name).stdin(Stdio::piped()).spawn() {
- Ok(mut pager) => {
- if let Some(pipe) = pager.stdin.as_mut() {
- if pipe.write_all(content.as_bytes()).is_err() {
- fallback_to_println = true;
- }
- }
-
- if pager.wait().is_err() {
- fallback_to_println = true;
- }
- }
- Err(_) => {
- fallback_to_println = true;
- }
- }
-
- // If pager fails for whatever reason, we should still print the content
- // to standard output
- if fallback_to_println {
- print!("{content}");
- }
-}
-
-pub fn try_process_rlink(sess: &Session, compiler: &interface::Compiler) -> Compilation {
- if sess.opts.unstable_opts.link_only {
- if let Input::File(file) = &sess.io.input {
- // FIXME: #![crate_type] and #![crate_name] support not implemented yet
- sess.init_crate_types(collect_crate_types(sess, &[]));
- let outputs = compiler.build_output_filenames(sess, &[]);
- let rlink_data = fs::read(file).unwrap_or_else(|err| {
- sess.emit_fatal(RlinkUnableToRead { err });
- });
- let codegen_results = match CodegenResults::deserialize_rlink(rlink_data) {
- Ok(codegen) => codegen,
- Err(err) => {
- match err {
- CodegenErrors::WrongFileType => sess.emit_fatal(RLinkWrongFileType),
- CodegenErrors::EmptyVersionNumber => {
- sess.emit_fatal(RLinkEmptyVersionNumber)
- }
- CodegenErrors::EncodingVersionMismatch { version_array, rlink_version } => {
- sess.emit_fatal(RLinkEncodingVersionMismatch {
- version_array,
- rlink_version,
- })
- }
- CodegenErrors::RustcVersionMismatch { rustc_version, current_version } => {
- sess.emit_fatal(RLinkRustcVersionMismatch {
- rustc_version,
- current_version,
- })
- }
- };
- }
- };
- let result = compiler.codegen_backend().link(sess, codegen_results, &outputs);
- abort_on_err(result, sess);
- } else {
- sess.emit_fatal(RlinkNotAFile {})
- }
- Compilation::Stop
- } else {
- Compilation::Continue
- }
-}
-
-pub fn list_metadata(sess: &Session, metadata_loader: &dyn MetadataLoader) -> Compilation {
- if sess.opts.unstable_opts.ls {
- match sess.io.input {
- Input::File(ref ifile) => {
- let path = &(*ifile);
- let mut v = Vec::new();
- locator::list_file_metadata(&sess.target, path, metadata_loader, &mut v).unwrap();
- println!("{}", String::from_utf8(v).unwrap());
- }
- Input::Str { .. } => {
- early_error(ErrorOutputType::default(), "cannot list metadata for stdin");
- }
- }
- return Compilation::Stop;
- }
-
- Compilation::Continue
-}
-
-fn print_crate_info(
- codegen_backend: &dyn CodegenBackend,
- sess: &Session,
- parse_attrs: bool,
-) -> Compilation {
- use rustc_session::config::PrintRequest::*;
- // NativeStaticLibs and LinkArgs are special - printed during linking
- // (empty iterator returns true)
- if sess.opts.prints.iter().all(|&p| p == NativeStaticLibs || p == LinkArgs) {
- return Compilation::Continue;
- }
-
- let attrs = if parse_attrs {
- let result = parse_crate_attrs(sess);
- match result {
- Ok(attrs) => Some(attrs),
- Err(mut parse_error) => {
- parse_error.emit();
- return Compilation::Stop;
- }
- }
- } else {
- None
- };
- for req in &sess.opts.prints {
- match *req {
- TargetList => {
- let mut targets = rustc_target::spec::TARGETS.to_vec();
- targets.sort_unstable();
- println!("{}", targets.join("\n"));
- }
- Sysroot => println!("{}", sess.sysroot.display()),
- TargetLibdir => println!("{}", sess.target_tlib_path.dir.display()),
- TargetSpec => {
- println!("{}", serde_json::to_string_pretty(&sess.target.to_json()).unwrap());
- }
- FileNames | CrateName => {
- let attrs = attrs.as_ref().unwrap();
- let t_outputs = rustc_interface::util::build_output_filenames(attrs, sess);
- let id = rustc_session::output::find_crate_name(sess, attrs);
- if *req == PrintRequest::CrateName {
- println!("{id}");
- continue;
- }
- let crate_types = collect_crate_types(sess, attrs);
- for &style in &crate_types {
- let fname =
- rustc_session::output::filename_for_input(sess, style, id, &t_outputs);
- println!("{}", fname.file_name().unwrap().to_string_lossy());
- }
- }
- Cfg => {
- let mut cfgs = sess
- .parse_sess
- .config
- .iter()
- .filter_map(|&(name, value)| {
- // Note that crt-static is a specially recognized cfg
- // directive that's printed out here as part of
- // rust-lang/rust#37406, but in general the
- // `target_feature` cfg is gated under
- // rust-lang/rust#29717. For now this is just
- // specifically allowing the crt-static cfg and that's
- // it, this is intended to get into Cargo and then go
- // through to build scripts.
- if (name != sym::target_feature || value != Some(sym::crt_dash_static))
- && !sess.is_nightly_build()
- && find_gated_cfg(|cfg_sym| cfg_sym == name).is_some()
- {
- return None;
- }
-
- if let Some(value) = value {
- Some(format!("{name}=\"{value}\""))
- } else {
- Some(name.to_string())
- }
- })
- .collect::<Vec<String>>();
-
- cfgs.sort();
- for cfg in cfgs {
- println!("{cfg}");
- }
- }
- CallingConventions => {
- let mut calling_conventions = rustc_target::spec::abi::all_names();
- calling_conventions.sort_unstable();
- println!("{}", calling_conventions.join("\n"));
- }
- RelocationModels
- | CodeModels
- | TlsModels
- | TargetCPUs
- | StackProtectorStrategies
- | TargetFeatures => {
- codegen_backend.print(*req, sess);
- }
- // Any output here interferes with Cargo's parsing of other printed output
- NativeStaticLibs => {}
- LinkArgs => {}
- SplitDebuginfo => {
- use rustc_target::spec::SplitDebuginfo::{Off, Packed, Unpacked};
-
- for split in &[Off, Packed, Unpacked] {
- let stable = sess.target.options.supported_split_debuginfo.contains(split);
- let unstable_ok = sess.unstable_options();
- if stable || unstable_ok {
- println!("{split}");
- }
- }
- }
- }
- }
- Compilation::Stop
-}
-
-/// Prints version information
-///
-/// NOTE: this is a macro to support drivers built at a different time than the main `rustc_driver` crate.
-pub macro version($binary: literal, $matches: expr) {
- fn unw(x: Option<&str>) -> &str {
- x.unwrap_or("unknown")
- }
- $crate::version_at_macro_invocation(
- $binary,
- $matches,
- unw(option_env!("CFG_VERSION")),
- unw(option_env!("CFG_VER_HASH")),
- unw(option_env!("CFG_VER_DATE")),
- unw(option_env!("CFG_RELEASE")),
- )
-}
-
-#[doc(hidden)] // use the macro instead
-pub fn version_at_macro_invocation(
- binary: &str,
- matches: &getopts::Matches,
- version: &str,
- commit_hash: &str,
- commit_date: &str,
- release: &str,
-) {
- let verbose = matches.opt_present("verbose");
-
- println!("{binary} {version}");
-
- if verbose {
- println!("binary: {binary}");
- println!("commit-hash: {commit_hash}");
- println!("commit-date: {commit_date}");
- println!("host: {}", config::host_triple());
- println!("release: {release}");
-
- let debug_flags = matches.opt_strs("Z");
- let backend_name = debug_flags.iter().find_map(|x| x.strip_prefix("codegen-backend="));
- get_codegen_backend(&None, backend_name).print_version();
- }
-}
-
-fn usage(verbose: bool, include_unstable_options: bool, nightly_build: bool) {
- let groups = if verbose { config::rustc_optgroups() } else { config::rustc_short_optgroups() };
- let mut options = getopts::Options::new();
- for option in groups.iter().filter(|x| include_unstable_options || x.is_stable()) {
- (option.apply)(&mut options);
- }
- let message = "Usage: rustc [OPTIONS] INPUT";
- let nightly_help = if nightly_build {
- "\n -Z help Print unstable compiler options"
- } else {
- ""
- };
- let verbose_help = if verbose {
- ""
- } else {
- "\n --help -v Print the full set of options rustc accepts"
- };
- let at_path = if verbose {
- " @path Read newline separated options from `path`\n"
- } else {
- ""
- };
- println!(
- "{options}{at_path}\nAdditional help:
- -C help Print codegen options
- -W help \
- Print 'lint' options and default settings{nightly}{verbose}\n",
- options = options.usage(message),
- at_path = at_path,
- nightly = nightly_help,
- verbose = verbose_help
- );
-}
-
-fn print_wall_help() {
- println!(
- "
-The flag `-Wall` does not exist in `rustc`. Most useful lints are enabled by
-default. Use `rustc -W help` to see all available lints. It's more common to put
-warning settings in the crate root using `#![warn(LINT_NAME)]` instead of using
-the command line flag directly.
-"
- );
-}
-
-/// Write to stdout lint command options, together with a list of all available lints
-pub fn describe_lints(sess: &Session, lint_store: &LintStore, loaded_plugins: bool) {
- println!(
- "
-Available lint options:
- -W <foo> Warn about <foo>
- -A <foo> \
- Allow <foo>
- -D <foo> Deny <foo>
- -F <foo> Forbid <foo> \
- (deny <foo> and all attempts to override)
-
-"
- );
-
- fn sort_lints(sess: &Session, mut lints: Vec<&'static Lint>) -> Vec<&'static Lint> {
- // The sort doesn't case-fold but it's doubtful we care.
- lints.sort_by_cached_key(|x: &&Lint| (x.default_level(sess.edition()), x.name));
- lints
- }
-
- fn sort_lint_groups(
- lints: Vec<(&'static str, Vec<LintId>, bool)>,
- ) -> Vec<(&'static str, Vec<LintId>)> {
- let mut lints: Vec<_> = lints.into_iter().map(|(x, y, _)| (x, y)).collect();
- lints.sort_by_key(|l| l.0);
- lints
- }
-
- let (plugin, builtin): (Vec<_>, _) =
- lint_store.get_lints().iter().cloned().partition(|&lint| lint.is_plugin);
- let plugin = sort_lints(sess, plugin);
- let builtin = sort_lints(sess, builtin);
-
- let (plugin_groups, builtin_groups): (Vec<_>, _) =
- lint_store.get_lint_groups().partition(|&(.., p)| p);
- let plugin_groups = sort_lint_groups(plugin_groups);
- let builtin_groups = sort_lint_groups(builtin_groups);
-
- let max_name_len =
- plugin.iter().chain(&builtin).map(|&s| s.name.chars().count()).max().unwrap_or(0);
- let padded = |x: &str| {
- let mut s = " ".repeat(max_name_len - x.chars().count());
- s.push_str(x);
- s
- };
-
- println!("Lint checks provided by rustc:\n");
-
- let print_lints = |lints: Vec<&Lint>| {
- println!(" {} {:7.7} {}", padded("name"), "default", "meaning");
- println!(" {} {:7.7} {}", padded("----"), "-------", "-------");
- for lint in lints {
- let name = lint.name_lower().replace('_', "-");
- println!(
- " {} {:7.7} {}",
- padded(&name),
- lint.default_level(sess.edition()).as_str(),
- lint.desc
- );
- }
- println!("\n");
- };
-
- print_lints(builtin);
-
- let max_name_len = max(
- "warnings".len(),
- plugin_groups
- .iter()
- .chain(&builtin_groups)
- .map(|&(s, _)| s.chars().count())
- .max()
- .unwrap_or(0),
- );
-
- let padded = |x: &str| {
- let mut s = " ".repeat(max_name_len - x.chars().count());
- s.push_str(x);
- s
- };
-
- println!("Lint groups provided by rustc:\n");
-
- let print_lint_groups = |lints: Vec<(&'static str, Vec<LintId>)>, all_warnings| {
- println!(" {} sub-lints", padded("name"));
- println!(" {} ---------", padded("----"));
-
- if all_warnings {
- println!(" {} all lints that are set to issue warnings", padded("warnings"));
- }
-
- for (name, to) in lints {
- let name = name.to_lowercase().replace('_', "-");
- let desc = to
- .into_iter()
- .map(|x| x.to_string().replace('_', "-"))
- .collect::<Vec<String>>()
- .join(", ");
- println!(" {} {}", padded(&name), desc);
- }
- println!("\n");
- };
-
- print_lint_groups(builtin_groups, true);
-
- match (loaded_plugins, plugin.len(), plugin_groups.len()) {
- (false, 0, _) | (false, _, 0) => {
- println!("Lint tools like Clippy can provide additional lints and lint groups.");
- }
- (false, ..) => panic!("didn't load lint plugins but got them anyway!"),
- (true, 0, 0) => println!("This crate does not load any lint plugins or lint groups."),
- (true, l, g) => {
- if l > 0 {
- println!("Lint checks provided by plugins loaded by this crate:\n");
- print_lints(plugin);
- }
- if g > 0 {
- println!("Lint groups provided by plugins loaded by this crate:\n");
- print_lint_groups(plugin_groups, false);
- }
- }
- }
-}
-
-fn describe_debug_flags() {
- println!("\nAvailable options:\n");
- print_flag_list("-Z", config::Z_OPTIONS);
-}
-
-fn describe_codegen_flags() {
- println!("\nAvailable codegen options:\n");
- print_flag_list("-C", config::CG_OPTIONS);
-}
-
-pub fn print_flag_list<T>(
- cmdline_opt: &str,
- flag_list: &[(&'static str, T, &'static str, &'static str)],
-) {
- let max_len = flag_list.iter().map(|&(name, _, _, _)| name.chars().count()).max().unwrap_or(0);
-
- for &(name, _, _, desc) in flag_list {
- println!(
- " {} {:>width$}=val -- {}",
- cmdline_opt,
- name.replace('_', "-"),
- desc,
- width = max_len
- );
- }
-}
-
-/// Process command line options. Emits messages as appropriate. If compilation
-/// should continue, returns a getopts::Matches object parsed from args,
-/// otherwise returns `None`.
-///
-/// The compiler's handling of options is a little complicated as it ties into
-/// our stability story. The current intention of each compiler option is to
-/// have one of two modes:
-///
-/// 1. An option is stable and can be used everywhere.
-/// 2. An option is unstable, and can only be used on nightly.
-///
-/// Like unstable library and language features, however, unstable options have
-/// always required a form of "opt in" to indicate that you're using them. This
-/// provides the easy ability to scan a code base to check to see if anything
-/// unstable is being used. Currently, this "opt in" is the `-Z` "zed" flag.
-///
-/// All options behind `-Z` are considered unstable by default. Other top-level
-/// options can also be considered unstable, and they were unlocked through the
-/// `-Z unstable-options` flag. Note that `-Z` remains to be the root of
-/// instability in both cases, though.
-///
-/// So with all that in mind, the comments below have some more detail about the
-/// contortions done here to get things to work out correctly.
-pub fn handle_options(args: &[String]) -> Option<getopts::Matches> {
- // Throw away the first argument, the name of the binary
- let args = &args[1..];
-
- if args.is_empty() {
- // user did not write `-v` nor `-Z unstable-options`, so do not
- // include that extra information.
- let nightly_build =
- rustc_feature::UnstableFeatures::from_environment(None).is_nightly_build();
- usage(false, false, nightly_build);
- return None;
- }
-
- // Parse with *all* options defined in the compiler, we don't worry about
- // option stability here we just want to parse as much as possible.
- let mut options = getopts::Options::new();
- for option in config::rustc_optgroups() {
- (option.apply)(&mut options);
- }
- let matches = options.parse(args).unwrap_or_else(|e| {
- let msg = match e {
- getopts::Fail::UnrecognizedOption(ref opt) => CG_OPTIONS
- .iter()
- .map(|&(name, ..)| ('C', name))
- .chain(Z_OPTIONS.iter().map(|&(name, ..)| ('Z', name)))
- .find(|&(_, name)| *opt == name.replace('_', "-"))
- .map(|(flag, _)| format!("{e}. Did you mean `-{flag} {opt}`?")),
- _ => None,
- };
- early_error(ErrorOutputType::default(), &msg.unwrap_or_else(|| e.to_string()));
- });
-
- // For all options we just parsed, we check a few aspects:
- //
- // * If the option is stable, we're all good
- // * If the option wasn't passed, we're all good
- // * If `-Z unstable-options` wasn't passed (and we're not a -Z option
- // ourselves), then we require the `-Z unstable-options` flag to unlock
- // this option that was passed.
- // * If we're a nightly compiler, then unstable options are now unlocked, so
- // we're good to go.
- // * Otherwise, if we're an unstable option then we generate an error
- // (unstable option being used on stable)
- nightly_options::check_nightly_options(&matches, &config::rustc_optgroups());
-
- if matches.opt_present("h") || matches.opt_present("help") {
- // Only show unstable options in --help if we accept unstable options.
- let unstable_enabled = nightly_options::is_unstable_enabled(&matches);
- let nightly_build = nightly_options::match_is_nightly_build(&matches);
- usage(matches.opt_present("verbose"), unstable_enabled, nightly_build);
- return None;
- }
-
- // Handle the special case of -Wall.
- let wall = matches.opt_strs("W");
- if wall.iter().any(|x| *x == "all") {
- print_wall_help();
- rustc_errors::FatalError.raise();
- }
-
- // Don't handle -W help here, because we might first load plugins.
- let debug_flags = matches.opt_strs("Z");
- if debug_flags.iter().any(|x| *x == "help") {
- describe_debug_flags();
- return None;
- }
-
- let cg_flags = matches.opt_strs("C");
-
- if cg_flags.iter().any(|x| *x == "help") {
- describe_codegen_flags();
- return None;
- }
-
- if cg_flags.iter().any(|x| *x == "no-stack-check") {
- early_warn(
- ErrorOutputType::default(),
- "the --no-stack-check flag is deprecated and does nothing",
- );
- }
-
- if cg_flags.iter().any(|x| *x == "passes=list") {
- let backend_name = debug_flags.iter().find_map(|x| x.strip_prefix("codegen-backend="));
- get_codegen_backend(&None, backend_name).print_passes();
- return None;
- }
-
- if matches.opt_present("version") {
- version!("rustc", &matches);
- return None;
- }
-
- Some(matches)
-}
-
-fn parse_crate_attrs<'a>(sess: &'a Session) -> PResult<'a, ast::AttrVec> {
- match &sess.io.input {
- Input::File(ifile) => rustc_parse::parse_crate_attrs_from_file(ifile, &sess.parse_sess),
- Input::Str { name, input } => rustc_parse::parse_crate_attrs_from_source_str(
- name.clone(),
- input.clone(),
- &sess.parse_sess,
- ),
- }
-}
-
-/// Gets a list of extra command-line flags provided by the user, as strings.
-///
-/// This function is used during ICEs to show more information useful for
-/// debugging, since some ICEs only happens with non-default compiler flags
-/// (and the users don't always report them).
-fn extra_compiler_flags() -> Option<(Vec<String>, bool)> {
- let mut args = env::args_os().map(|arg| arg.to_string_lossy().to_string()).peekable();
-
- let mut result = Vec::new();
- let mut excluded_cargo_defaults = false;
- while let Some(arg) = args.next() {
- if let Some(a) = ICE_REPORT_COMPILER_FLAGS.iter().find(|a| arg.starts_with(*a)) {
- let content = if arg.len() == a.len() {
- // A space-separated option, like `-C incremental=foo` or `--crate-type rlib`
- match args.next() {
- Some(arg) => arg.to_string(),
- None => continue,
- }
- } else if arg.get(a.len()..a.len() + 1) == Some("=") {
- // An equals option, like `--crate-type=rlib`
- arg[a.len() + 1..].to_string()
- } else {
- // A non-space option, like `-Cincremental=foo`
- arg[a.len()..].to_string()
- };
- let option = content.split_once('=').map(|s| s.0).unwrap_or(&content);
- if ICE_REPORT_COMPILER_FLAGS_EXCLUDE.iter().any(|exc| option == *exc) {
- excluded_cargo_defaults = true;
- } else {
- result.push(a.to_string());
- match ICE_REPORT_COMPILER_FLAGS_STRIP_VALUE.iter().find(|s| option == **s) {
- Some(s) => result.push(format!("{s}=[REDACTED]")),
- None => result.push(content),
- }
- }
- }
- }
-
- if !result.is_empty() { Some((result, excluded_cargo_defaults)) } else { None }
-}
-
-/// Runs a closure and catches unwinds triggered by fatal errors.
-///
-/// The compiler currently unwinds with a special sentinel value to abort
-/// compilation on fatal errors. This function catches that sentinel and turns
-/// the panic into a `Result` instead.
-pub fn catch_fatal_errors<F: FnOnce() -> R, R>(f: F) -> Result<R, ErrorGuaranteed> {
- catch_unwind(panic::AssertUnwindSafe(f)).map_err(|value| {
- if value.is::<rustc_errors::FatalErrorMarker>() {
- ErrorGuaranteed::unchecked_claim_error_was_emitted()
- } else {
- panic::resume_unwind(value);
- }
- })
-}
-
-/// Variant of `catch_fatal_errors` for the `interface::Result` return type
-/// that also computes the exit code.
-pub fn catch_with_exit_code(f: impl FnOnce() -> interface::Result<()>) -> i32 {
- let result = catch_fatal_errors(f).and_then(|result| result);
- match result {
- Ok(()) => EXIT_SUCCESS,
- Err(_) => EXIT_FAILURE,
- }
-}
-
-static DEFAULT_HOOK: LazyLock<Box<dyn Fn(&panic::PanicInfo<'_>) + Sync + Send + 'static>> =
- LazyLock::new(|| {
- let hook = panic::take_hook();
- panic::set_hook(Box::new(|info| {
- // If the error was caused by a broken pipe then this is not a bug.
- // Write the error and return immediately. See #98700.
- #[cfg(windows)]
- if let Some(msg) = info.payload().downcast_ref::<String>() {
- if msg.starts_with("failed printing to stdout: ") && msg.ends_with("(os error 232)")
- {
- early_error_no_abort(ErrorOutputType::default(), &msg);
- return;
- }
- };
-
- // Invoke the default handler, which prints the actual panic message and optionally a backtrace
- // Don't do this for delayed bugs, which already emit their own more useful backtrace.
- if !info.payload().is::<rustc_errors::DelayedBugPanic>() {
- (*DEFAULT_HOOK)(info);
-
- // Separate the output with an empty line
- eprintln!();
- }
-
- // Print the ICE message
- report_ice(info, BUG_REPORT_URL);
- }));
- hook
- });
-
-/// Prints the ICE message, including query stack, but without backtrace.
-///
-/// The message will point the user at `bug_report_url` to report the ICE.
-///
-/// When `install_ice_hook` is called, this function will be called as the panic
-/// hook.
-pub fn report_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) {
- let fallback_bundle =
- rustc_errors::fallback_fluent_bundle(rustc_errors::DEFAULT_LOCALE_RESOURCES, false);
- let emitter = Box::new(rustc_errors::emitter::EmitterWriter::stderr(
- rustc_errors::ColorConfig::Auto,
- None,
- None,
- fallback_bundle,
- false,
- false,
- None,
- false,
- false,
- ));
- let handler = rustc_errors::Handler::with_emitter(true, None, emitter);
-
- // a .span_bug or .bug call has already printed what
- // it wants to print.
- if !info.payload().is::<rustc_errors::ExplicitBug>()
- && !info.payload().is::<rustc_errors::DelayedBugPanic>()
- {
- let mut d = rustc_errors::Diagnostic::new(rustc_errors::Level::Bug, "unexpected panic");
- handler.emit_diagnostic(&mut d);
- }
-
- handler.emit_note(session_diagnostics::Ice);
- handler.emit_note(session_diagnostics::IceBugReport { bug_report_url });
- handler.emit_note(session_diagnostics::IceVersion {
- version: util::version_str!().unwrap_or("unknown_version"),
- triple: config::host_triple(),
- });
-
- if let Some((flags, excluded_cargo_defaults)) = extra_compiler_flags() {
- handler.emit_note(session_diagnostics::IceFlags { flags: flags.join(" ") });
- if excluded_cargo_defaults {
- handler.emit_note(session_diagnostics::IceExcludeCargoDefaults);
- }
- }
-
- // If backtraces are enabled, also print the query stack
- let backtrace = env::var_os("RUST_BACKTRACE").map_or(false, |x| &x != "0");
-
- let num_frames = if backtrace { None } else { Some(2) };
-
- interface::try_print_query_stack(&handler, num_frames);
-
- #[cfg(windows)]
- unsafe {
- if env::var("RUSTC_BREAK_ON_ICE").is_ok() {
- // Trigger a debugger if we crashed during bootstrap
- winapi::um::debugapi::DebugBreak();
- }
- }
-}
-
-/// Installs a panic hook that will print the ICE message on unexpected panics.
-///
-/// A custom rustc driver can skip calling this to set up a custom ICE hook.
-pub fn install_ice_hook() {
- // If the user has not explicitly overridden "RUST_BACKTRACE", then produce
- // full backtraces. When a compiler ICE happens, we want to gather
- // as much information as possible to present in the issue opened
- // by the user. Compiler developers and other rustc users can
- // opt in to less-verbose backtraces by manually setting "RUST_BACKTRACE"
- // (e.g. `RUST_BACKTRACE=1`)
- if std::env::var("RUST_BACKTRACE").is_err() {
- std::env::set_var("RUST_BACKTRACE", "full");
- }
- LazyLock::force(&DEFAULT_HOOK);
-}
-
-/// This allows tools to enable rust logging without having to magically match rustc's
-/// tracing crate version.
-pub fn init_rustc_env_logger() {
- init_rustc_env_logger_with_backtrace_option(&None);
-}
-
-/// This allows tools to enable rust logging without having to magically match rustc's
-/// tracing crate version. In contrast to `init_rustc_env_logger` it allows you to
-/// choose a target module you wish to show backtraces along with its logging.
-pub fn init_rustc_env_logger_with_backtrace_option(backtrace_target: &Option<String>) {
- if let Err(error) = rustc_log::init_rustc_env_logger_with_backtrace_option(backtrace_target) {
- early_error(ErrorOutputType::default(), &error.to_string());
- }
-}
-
-/// This allows tools to enable rust logging without having to magically match rustc's
-/// tracing crate version. In contrast to `init_rustc_env_logger` it allows you to choose an env var
-/// other than `RUSTC_LOG`.
-pub fn init_env_logger(env: &str) {
- if let Err(error) = rustc_log::init_env_logger(env) {
- early_error(ErrorOutputType::default(), &error.to_string());
- }
-}
-
-#[cfg(all(unix, any(target_env = "gnu", target_os = "macos")))]
-mod signal_handler {
- extern "C" {
- fn backtrace_symbols_fd(
- buffer: *const *mut libc::c_void,
- size: libc::c_int,
- fd: libc::c_int,
- );
- }
-
- extern "C" fn print_stack_trace(_: libc::c_int) {
- const MAX_FRAMES: usize = 256;
- static mut STACK_TRACE: [*mut libc::c_void; MAX_FRAMES] =
- [std::ptr::null_mut(); MAX_FRAMES];
- unsafe {
- let depth = libc::backtrace(STACK_TRACE.as_mut_ptr(), MAX_FRAMES as i32);
- if depth == 0 {
- return;
- }
- backtrace_symbols_fd(STACK_TRACE.as_ptr(), depth, 2);
- }
- }
-
- /// When an error signal (such as SIGABRT or SIGSEGV) is delivered to the
- /// process, print a stack trace and then exit.
- pub(super) fn install() {
- unsafe {
- const ALT_STACK_SIZE: usize = libc::MINSIGSTKSZ + 64 * 1024;
- let mut alt_stack: libc::stack_t = std::mem::zeroed();
- alt_stack.ss_sp =
- std::alloc::alloc(std::alloc::Layout::from_size_align(ALT_STACK_SIZE, 1).unwrap())
- as *mut libc::c_void;
- alt_stack.ss_size = ALT_STACK_SIZE;
- libc::sigaltstack(&alt_stack, std::ptr::null_mut());
-
- let mut sa: libc::sigaction = std::mem::zeroed();
- sa.sa_sigaction = print_stack_trace as libc::sighandler_t;
- sa.sa_flags = libc::SA_NODEFER | libc::SA_RESETHAND | libc::SA_ONSTACK;
- libc::sigemptyset(&mut sa.sa_mask);
- libc::sigaction(libc::SIGSEGV, &sa, std::ptr::null_mut());
- }
- }
-}
-
-#[cfg(not(all(unix, any(target_env = "gnu", target_os = "macos"))))]
-mod signal_handler {
- pub(super) fn install() {}
-}
-
-pub fn main() -> ! {
- let start_time = Instant::now();
- let start_rss = get_resident_set_size();
- signal_handler::install();
- let mut callbacks = TimePassesCallbacks::default();
- install_ice_hook();
- let exit_code = catch_with_exit_code(|| {
- let args = env::args_os()
- .enumerate()
- .map(|(i, arg)| {
- arg.into_string().unwrap_or_else(|arg| {
- early_error(
- ErrorOutputType::default(),
- &format!("argument {i} is not valid Unicode: {arg:?}"),
- )
- })
- })
- .collect::<Vec<_>>();
- RunCompiler::new(&args, &mut callbacks).run()
- });
-
- if callbacks.time_passes {
- let end_rss = get_resident_set_size();
- print_time_passes_entry("total", start_time.elapsed(), start_rss, end_rss);
- }
-
- process::exit(exit_code)
-}
+pub use rustc_driver_impl::*;
+++ /dev/null
-//! The various pretty-printing routines.
-
-use crate::session_diagnostics::UnprettyDumpFail;
-use rustc_ast as ast;
-use rustc_ast_pretty::pprust;
-use rustc_errors::ErrorGuaranteed;
-use rustc_hir as hir;
-use rustc_hir_pretty as pprust_hir;
-use rustc_middle::hir::map as hir_map;
-use rustc_middle::mir::{write_mir_graphviz, write_mir_pretty};
-use rustc_middle::ty::{self, TyCtxt};
-use rustc_session::config::{PpAstTreeMode, PpHirMode, PpMode, PpSourceMode};
-use rustc_session::Session;
-use rustc_span::symbol::Ident;
-use rustc_span::FileName;
-
-use std::cell::Cell;
-use std::fmt::Write;
-
-pub use self::PpMode::*;
-pub use self::PpSourceMode::*;
-use crate::abort_on_err;
-
-// This slightly awkward construction is to allow for each PpMode to
-// choose whether it needs to do analyses (which can consume the
-// Session) and then pass through the session (now attached to the
-// analysis results) on to the chosen pretty-printer, along with the
-// `&PpAnn` object.
-//
-// Note that since the `&PrinterSupport` is freshly constructed on each
-// call, it would not make sense to try to attach the lifetime of `self`
-// to the lifetime of the `&PrinterObject`.
-
-/// Constructs a `PrinterSupport` object and passes it to `f`.
-fn call_with_pp_support<'tcx, A, F>(
- ppmode: &PpSourceMode,
- sess: &'tcx Session,
- tcx: Option<TyCtxt<'tcx>>,
- f: F,
-) -> A
-where
- F: FnOnce(&dyn PrinterSupport) -> A,
-{
- match *ppmode {
- Normal | Expanded => {
- let annotation = NoAnn { sess, tcx };
- f(&annotation)
- }
-
- Identified | ExpandedIdentified => {
- let annotation = IdentifiedAnnotation { sess, tcx };
- f(&annotation)
- }
- ExpandedHygiene => {
- let annotation = HygieneAnnotation { sess };
- f(&annotation)
- }
- }
-}
-fn call_with_pp_support_hir<A, F>(ppmode: &PpHirMode, tcx: TyCtxt<'_>, f: F) -> A
-where
- F: FnOnce(&dyn HirPrinterSupport<'_>, hir_map::Map<'_>) -> A,
-{
- match *ppmode {
- PpHirMode::Normal => {
- let annotation = NoAnn { sess: tcx.sess, tcx: Some(tcx) };
- f(&annotation, tcx.hir())
- }
-
- PpHirMode::Identified => {
- let annotation = IdentifiedAnnotation { sess: tcx.sess, tcx: Some(tcx) };
- f(&annotation, tcx.hir())
- }
- PpHirMode::Typed => {
- abort_on_err(tcx.analysis(()), tcx.sess);
-
- let annotation = TypedAnnotation { tcx, maybe_typeck_results: Cell::new(None) };
- tcx.dep_graph.with_ignore(|| f(&annotation, tcx.hir()))
- }
- }
-}
-
-trait PrinterSupport: pprust::PpAnn {
- /// Provides a uniform interface for re-extracting a reference to a
- /// `Session` from a value that now owns it.
- fn sess(&self) -> &Session;
-
- /// Produces the pretty-print annotation object.
- ///
- /// (Rust does not yet support upcasting from a trait object to
- /// an object for one of its supertraits.)
- fn pp_ann(&self) -> &dyn pprust::PpAnn;
-}
-
-trait HirPrinterSupport<'hir>: pprust_hir::PpAnn {
- /// Provides a uniform interface for re-extracting a reference to a
- /// `Session` from a value that now owns it.
- fn sess(&self) -> &Session;
-
- /// Provides a uniform interface for re-extracting a reference to an
- /// `hir_map::Map` from a value that now owns it.
- fn hir_map(&self) -> Option<hir_map::Map<'hir>>;
-
- /// Produces the pretty-print annotation object.
- ///
- /// (Rust does not yet support upcasting from a trait object to
- /// an object for one of its supertraits.)
- fn pp_ann(&self) -> &dyn pprust_hir::PpAnn;
-}
-
-struct NoAnn<'hir> {
- sess: &'hir Session,
- tcx: Option<TyCtxt<'hir>>,
-}
-
-impl<'hir> PrinterSupport for NoAnn<'hir> {
- fn sess(&self) -> &Session {
- self.sess
- }
-
- fn pp_ann(&self) -> &dyn pprust::PpAnn {
- self
- }
-}
-
-impl<'hir> HirPrinterSupport<'hir> for NoAnn<'hir> {
- fn sess(&self) -> &Session {
- self.sess
- }
-
- fn hir_map(&self) -> Option<hir_map::Map<'hir>> {
- self.tcx.map(|tcx| tcx.hir())
- }
-
- fn pp_ann(&self) -> &dyn pprust_hir::PpAnn {
- self
- }
-}
-
-impl<'hir> pprust::PpAnn for NoAnn<'hir> {}
-impl<'hir> pprust_hir::PpAnn for NoAnn<'hir> {
- fn nested(&self, state: &mut pprust_hir::State<'_>, nested: pprust_hir::Nested) {
- if let Some(tcx) = self.tcx {
- pprust_hir::PpAnn::nested(&(&tcx.hir() as &dyn hir::intravisit::Map<'_>), state, nested)
- }
- }
-}
-
-struct IdentifiedAnnotation<'hir> {
- sess: &'hir Session,
- tcx: Option<TyCtxt<'hir>>,
-}
-
-impl<'hir> PrinterSupport for IdentifiedAnnotation<'hir> {
- fn sess(&self) -> &Session {
- self.sess
- }
-
- fn pp_ann(&self) -> &dyn pprust::PpAnn {
- self
- }
-}
-
-impl<'hir> pprust::PpAnn for IdentifiedAnnotation<'hir> {
- fn pre(&self, s: &mut pprust::State<'_>, node: pprust::AnnNode<'_>) {
- if let pprust::AnnNode::Expr(_) = node {
- s.popen();
- }
- }
- fn post(&self, s: &mut pprust::State<'_>, node: pprust::AnnNode<'_>) {
- match node {
- pprust::AnnNode::Crate(_) | pprust::AnnNode::Ident(_) | pprust::AnnNode::Name(_) => {}
-
- pprust::AnnNode::Item(item) => {
- s.s.space();
- s.synth_comment(item.id.to_string())
- }
- pprust::AnnNode::SubItem(id) => {
- s.s.space();
- s.synth_comment(id.to_string())
- }
- pprust::AnnNode::Block(blk) => {
- s.s.space();
- s.synth_comment(format!("block {}", blk.id))
- }
- pprust::AnnNode::Expr(expr) => {
- s.s.space();
- s.synth_comment(expr.id.to_string());
- s.pclose()
- }
- pprust::AnnNode::Pat(pat) => {
- s.s.space();
- s.synth_comment(format!("pat {}", pat.id));
- }
- }
- }
-}
-
-impl<'hir> HirPrinterSupport<'hir> for IdentifiedAnnotation<'hir> {
- fn sess(&self) -> &Session {
- self.sess
- }
-
- fn hir_map(&self) -> Option<hir_map::Map<'hir>> {
- self.tcx.map(|tcx| tcx.hir())
- }
-
- fn pp_ann(&self) -> &dyn pprust_hir::PpAnn {
- self
- }
-}
-
-impl<'hir> pprust_hir::PpAnn for IdentifiedAnnotation<'hir> {
- fn nested(&self, state: &mut pprust_hir::State<'_>, nested: pprust_hir::Nested) {
- if let Some(ref tcx) = self.tcx {
- pprust_hir::PpAnn::nested(&(&tcx.hir() as &dyn hir::intravisit::Map<'_>), state, nested)
- }
- }
- fn pre(&self, s: &mut pprust_hir::State<'_>, node: pprust_hir::AnnNode<'_>) {
- if let pprust_hir::AnnNode::Expr(_) = node {
- s.popen();
- }
- }
- fn post(&self, s: &mut pprust_hir::State<'_>, node: pprust_hir::AnnNode<'_>) {
- match node {
- pprust_hir::AnnNode::Name(_) => {}
- pprust_hir::AnnNode::Item(item) => {
- s.s.space();
- s.synth_comment(format!("hir_id: {}", item.hir_id()));
- }
- pprust_hir::AnnNode::SubItem(id) => {
- s.s.space();
- s.synth_comment(id.to_string());
- }
- pprust_hir::AnnNode::Block(blk) => {
- s.s.space();
- s.synth_comment(format!("block hir_id: {}", blk.hir_id));
- }
- pprust_hir::AnnNode::Expr(expr) => {
- s.s.space();
- s.synth_comment(format!("expr hir_id: {}", expr.hir_id));
- s.pclose();
- }
- pprust_hir::AnnNode::Pat(pat) => {
- s.s.space();
- s.synth_comment(format!("pat hir_id: {}", pat.hir_id));
- }
- pprust_hir::AnnNode::Arm(arm) => {
- s.s.space();
- s.synth_comment(format!("arm hir_id: {}", arm.hir_id));
- }
- }
- }
-}
-
-struct HygieneAnnotation<'a> {
- sess: &'a Session,
-}
-
-impl<'a> PrinterSupport for HygieneAnnotation<'a> {
- fn sess(&self) -> &Session {
- self.sess
- }
-
- fn pp_ann(&self) -> &dyn pprust::PpAnn {
- self
- }
-}
-
-impl<'a> pprust::PpAnn for HygieneAnnotation<'a> {
- fn post(&self, s: &mut pprust::State<'_>, node: pprust::AnnNode<'_>) {
- match node {
- pprust::AnnNode::Ident(&Ident { name, span }) => {
- s.s.space();
- s.synth_comment(format!("{}{:?}", name.as_u32(), span.ctxt()))
- }
- pprust::AnnNode::Name(&name) => {
- s.s.space();
- s.synth_comment(name.as_u32().to_string())
- }
- pprust::AnnNode::Crate(_) => {
- s.s.hardbreak();
- let verbose = self.sess.verbose();
- s.synth_comment(rustc_span::hygiene::debug_hygiene_data(verbose));
- s.s.hardbreak_if_not_bol();
- }
- _ => {}
- }
- }
-}
-
-struct TypedAnnotation<'tcx> {
- tcx: TyCtxt<'tcx>,
- maybe_typeck_results: Cell<Option<&'tcx ty::TypeckResults<'tcx>>>,
-}
-
-impl<'tcx> HirPrinterSupport<'tcx> for TypedAnnotation<'tcx> {
- fn sess(&self) -> &Session {
- self.tcx.sess
- }
-
- fn hir_map(&self) -> Option<hir_map::Map<'tcx>> {
- Some(self.tcx.hir())
- }
-
- fn pp_ann(&self) -> &dyn pprust_hir::PpAnn {
- self
- }
-}
-
-impl<'tcx> pprust_hir::PpAnn for TypedAnnotation<'tcx> {
- fn nested(&self, state: &mut pprust_hir::State<'_>, nested: pprust_hir::Nested) {
- let old_maybe_typeck_results = self.maybe_typeck_results.get();
- if let pprust_hir::Nested::Body(id) = nested {
- self.maybe_typeck_results.set(Some(self.tcx.typeck_body(id)));
- }
- let pp_ann = &(&self.tcx.hir() as &dyn hir::intravisit::Map<'_>);
- pprust_hir::PpAnn::nested(pp_ann, state, nested);
- self.maybe_typeck_results.set(old_maybe_typeck_results);
- }
- fn pre(&self, s: &mut pprust_hir::State<'_>, node: pprust_hir::AnnNode<'_>) {
- if let pprust_hir::AnnNode::Expr(_) = node {
- s.popen();
- }
- }
- fn post(&self, s: &mut pprust_hir::State<'_>, node: pprust_hir::AnnNode<'_>) {
- if let pprust_hir::AnnNode::Expr(expr) = node {
- let typeck_results = self.maybe_typeck_results.get().or_else(|| {
- self.tcx
- .hir()
- .maybe_body_owned_by(expr.hir_id.owner.def_id)
- .map(|body_id| self.tcx.typeck_body(body_id))
- });
-
- if let Some(typeck_results) = typeck_results {
- s.s.space();
- s.s.word("as");
- s.s.space();
- s.s.word(typeck_results.expr_ty(expr).to_string());
- }
-
- s.pclose();
- }
- }
-}
-
-fn get_source(sess: &Session) -> (String, FileName) {
- let src_name = sess.io.input.source_name();
- let src = String::clone(
- sess.source_map()
- .get_source_file(&src_name)
- .expect("get_source_file")
- .src
- .as_ref()
- .expect("src"),
- );
- (src, src_name)
-}
-
-fn write_or_print(out: &str, sess: &Session) {
- match &sess.io.output_file {
- None => print!("{out}"),
- Some(p) => {
- if let Err(e) = std::fs::write(p, out) {
- sess.emit_fatal(UnprettyDumpFail {
- path: p.display().to_string(),
- err: e.to_string(),
- });
- }
- }
- }
-}
-
-pub fn print_after_parsing(sess: &Session, krate: &ast::Crate, ppm: PpMode) {
- let (src, src_name) = get_source(sess);
-
- let out = match ppm {
- Source(s) => {
- // Silently ignores an identified node.
- call_with_pp_support(&s, sess, None, move |annotation| {
- debug!("pretty printing source code {:?}", s);
- let sess = annotation.sess();
- let parse = &sess.parse_sess;
- pprust::print_crate(
- sess.source_map(),
- krate,
- src_name,
- src,
- annotation.pp_ann(),
- false,
- parse.edition,
- &sess.parse_sess.attr_id_generator,
- )
- })
- }
- AstTree(PpAstTreeMode::Normal) => {
- debug!("pretty printing AST tree");
- format!("{krate:#?}")
- }
- _ => unreachable!(),
- };
-
- write_or_print(&out, sess);
-}
-
-pub fn print_after_hir_lowering<'tcx>(tcx: TyCtxt<'tcx>, ppm: PpMode) {
- if ppm.needs_analysis() {
- abort_on_err(print_with_analysis(tcx, ppm), tcx.sess);
- return;
- }
-
- let (src, src_name) = get_source(tcx.sess);
-
- let out = match ppm {
- Source(s) => {
- // Silently ignores an identified node.
- call_with_pp_support(&s, tcx.sess, Some(tcx), move |annotation| {
- debug!("pretty printing source code {:?}", s);
- let sess = annotation.sess();
- let parse = &sess.parse_sess;
- pprust::print_crate(
- sess.source_map(),
- &tcx.resolver_for_lowering(()).borrow().1,
- src_name,
- src,
- annotation.pp_ann(),
- true,
- parse.edition,
- &sess.parse_sess.attr_id_generator,
- )
- })
- }
-
- AstTree(PpAstTreeMode::Expanded) => {
- debug!("pretty-printing expanded AST");
- format!("{:#?}", tcx.resolver_for_lowering(()).borrow().1)
- }
-
- Hir(s) => call_with_pp_support_hir(&s, tcx, move |annotation, hir_map| {
- debug!("pretty printing HIR {:?}", s);
- let sess = annotation.sess();
- let sm = sess.source_map();
- let attrs = |id| hir_map.attrs(id);
- pprust_hir::print_crate(
- sm,
- hir_map.root_module(),
- src_name,
- src,
- &attrs,
- annotation.pp_ann(),
- )
- }),
-
- HirTree => {
- call_with_pp_support_hir(&PpHirMode::Normal, tcx, move |_annotation, hir_map| {
- debug!("pretty printing HIR tree");
- format!("{:#?}", hir_map.krate())
- })
- }
-
- _ => unreachable!(),
- };
-
- write_or_print(&out, tcx.sess);
-}
-
-// In an ideal world, this would be a public function called by the driver after
-// analysis is performed. However, we want to call `phase_3_run_analysis_passes`
-// with a different callback than the standard driver, so that isn't easy.
-// Instead, we call that function ourselves.
-fn print_with_analysis(tcx: TyCtxt<'_>, ppm: PpMode) -> Result<(), ErrorGuaranteed> {
- tcx.analysis(())?;
- let out = match ppm {
- Mir => {
- let mut out = Vec::new();
- write_mir_pretty(tcx, None, &mut out).unwrap();
- String::from_utf8(out).unwrap()
- }
-
- MirCFG => {
- let mut out = Vec::new();
- write_mir_graphviz(tcx, None, &mut out).unwrap();
- String::from_utf8(out).unwrap()
- }
-
- ThirTree => {
- let mut out = String::new();
- abort_on_err(rustc_hir_analysis::check_crate(tcx), tcx.sess);
- debug!("pretty printing THIR tree");
- for did in tcx.hir().body_owners() {
- let _ = writeln!(
- out,
- "{:?}:\n{}\n",
- did,
- tcx.thir_tree(ty::WithOptConstParam::unknown(did))
- );
- }
- out
- }
-
- ThirFlat => {
- let mut out = String::new();
- abort_on_err(rustc_hir_analysis::check_crate(tcx), tcx.sess);
- debug!("pretty printing THIR flat");
- for did in tcx.hir().body_owners() {
- let _ = writeln!(
- out,
- "{:?}:\n{}\n",
- did,
- tcx.thir_flat(ty::WithOptConstParam::unknown(did))
- );
- }
- out
- }
-
- _ => unreachable!(),
- };
-
- write_or_print(&out, tcx.sess);
-
- Ok(())
-}
+++ /dev/null
-use rustc_macros::Diagnostic;
-
-#[derive(Diagnostic)]
-#[diag(driver_rlink_unable_to_read)]
-pub(crate) struct RlinkUnableToRead {
- pub err: std::io::Error,
-}
-
-#[derive(Diagnostic)]
-#[diag(driver_rlink_wrong_file_type)]
-pub(crate) struct RLinkWrongFileType;
-
-#[derive(Diagnostic)]
-#[diag(driver_rlink_empty_version_number)]
-pub(crate) struct RLinkEmptyVersionNumber;
-
-#[derive(Diagnostic)]
-#[diag(driver_rlink_encoding_version_mismatch)]
-pub(crate) struct RLinkEncodingVersionMismatch {
- pub version_array: String,
- pub rlink_version: u32,
-}
-
-#[derive(Diagnostic)]
-#[diag(driver_rlink_rustc_version_mismatch)]
-pub(crate) struct RLinkRustcVersionMismatch<'a> {
- pub rustc_version: String,
- pub current_version: &'a str,
-}
-
-#[derive(Diagnostic)]
-#[diag(driver_rlink_no_a_file)]
-pub(crate) struct RlinkNotAFile;
-
-#[derive(Diagnostic)]
-#[diag(driver_unpretty_dump_fail)]
-pub(crate) struct UnprettyDumpFail {
- pub path: String,
- pub err: String,
-}
-
-#[derive(Diagnostic)]
-#[diag(driver_ice)]
-pub(crate) struct Ice;
-
-#[derive(Diagnostic)]
-#[diag(driver_ice_bug_report)]
-pub(crate) struct IceBugReport<'a> {
- pub bug_report_url: &'a str,
-}
-
-#[derive(Diagnostic)]
-#[diag(driver_ice_version)]
-pub(crate) struct IceVersion<'a> {
- pub version: &'a str,
- pub triple: &'a str,
-}
-
-#[derive(Diagnostic)]
-#[diag(driver_ice_flags)]
-pub(crate) struct IceFlags {
- pub flags: String,
-}
-
-#[derive(Diagnostic)]
-#[diag(driver_ice_exclude_cargo_defaults)]
-pub(crate) struct IceExcludeCargoDefaults;
--- /dev/null
+[package]
+name = "rustc_driver_impl"
+version = "0.0.0"
+edition = "2021"
+
+[lib]
+
+[dependencies]
+tracing = { version = "0.1.35" }
+serde_json = "1.0.59"
+rustc_log = { path = "../rustc_log" }
+rustc_middle = { path = "../rustc_middle" }
+rustc_ast_pretty = { path = "../rustc_ast_pretty" }
+rustc_target = { path = "../rustc_target" }
+rustc_lint = { path = "../rustc_lint" }
+rustc_data_structures = { path = "../rustc_data_structures" }
+rustc_errors = { path = "../rustc_errors" }
+rustc_feature = { path = "../rustc_feature" }
+rustc_hir = { path = "../rustc_hir" }
+rustc_hir_pretty = { path = "../rustc_hir_pretty" }
+rustc_macros = { path = "../rustc_macros" }
+rustc_metadata = { path = "../rustc_metadata" }
+rustc_parse = { path = "../rustc_parse" }
+rustc_plugin_impl = { path = "../rustc_plugin_impl" }
+rustc_save_analysis = { path = "../rustc_save_analysis" }
+rustc_codegen_ssa = { path = "../rustc_codegen_ssa" }
+rustc_session = { path = "../rustc_session" }
+rustc_error_codes = { path = "../rustc_error_codes" }
+rustc_interface = { path = "../rustc_interface" }
+rustc_ast = { path = "../rustc_ast" }
+rustc_span = { path = "../rustc_span" }
+rustc_hir_analysis = { path = "../rustc_hir_analysis" }
+
+[target.'cfg(unix)'.dependencies]
+libc = "0.2"
+
+[target.'cfg(windows)'.dependencies]
+winapi = { version = "0.3", features = ["consoleapi", "debugapi", "processenv"] }
+
+[features]
+llvm = ['rustc_interface/llvm']
+max_level_info = ['rustc_log/max_level_info']
+rustc_use_parallel_compiler = ['rustc_data_structures/rustc_use_parallel_compiler', 'rustc_interface/rustc_use_parallel_compiler',
+ 'rustc_middle/rustc_use_parallel_compiler']
--- /dev/null
+The `driver` crate is effectively the "main" function for the rust
+compiler. It orchestrates the compilation process and "knits together"
+the code from the other crates within rustc. This crate itself does
+not contain any of the "main logic" of the compiler (though it does
+have some code related to pretty printing or other minor compiler
+options).
+
+For more information about how the driver works, see the [rustc dev guide].
+
+[rustc dev guide]: https://rustc-dev-guide.rust-lang.org/rustc-driver.html
--- /dev/null
+use std::error;
+use std::fmt;
+use std::fs;
+use std::io;
+
+fn arg_expand(arg: String) -> Result<Vec<String>, Error> {
+ if let Some(path) = arg.strip_prefix('@') {
+ let file = match fs::read_to_string(path) {
+ Ok(file) => file,
+ Err(ref err) if err.kind() == io::ErrorKind::InvalidData => {
+ return Err(Error::Utf8Error(Some(path.to_string())));
+ }
+ Err(err) => return Err(Error::IOError(path.to_string(), err)),
+ };
+ Ok(file.lines().map(ToString::to_string).collect())
+ } else {
+ Ok(vec![arg])
+ }
+}
+
+pub fn arg_expand_all(at_args: &[String]) -> Vec<String> {
+ let mut args = Vec::new();
+ for arg in at_args {
+ match arg_expand(arg.clone()) {
+ Ok(arg) => args.extend(arg),
+ Err(err) => rustc_session::early_error(
+ rustc_session::config::ErrorOutputType::default(),
+ &format!("Failed to load argument file: {err}"),
+ ),
+ }
+ }
+ args
+}
+
+#[derive(Debug)]
+pub enum Error {
+ Utf8Error(Option<String>),
+ IOError(String, io::Error),
+}
+
+impl fmt::Display for Error {
+ fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
+ match self {
+ Error::Utf8Error(None) => write!(fmt, "Utf8 error"),
+ Error::Utf8Error(Some(path)) => write!(fmt, "Utf8 error in {path}"),
+ Error::IOError(path, err) => write!(fmt, "IO Error: {path}: {err}"),
+ }
+ }
+}
+
+impl error::Error for Error {}
--- /dev/null
+//! The Rust compiler.
+//!
+//! # Note
+//!
+//! This API is completely unstable and subject to change.
+
+#![doc(html_root_url = "https://doc.rust-lang.org/nightly/nightly-rustc/")]
+#![feature(is_terminal)]
+#![feature(once_cell)]
+#![feature(decl_macro)]
+#![recursion_limit = "256"]
+#![allow(rustc::potential_query_instability)]
+#![deny(rustc::untranslatable_diagnostic)]
+#![deny(rustc::diagnostic_outside_of_impl)]
+
+#[macro_use]
+extern crate tracing;
+
+pub extern crate rustc_plugin_impl as plugin;
+
+use rustc_ast as ast;
+use rustc_codegen_ssa::{traits::CodegenBackend, CodegenErrors, CodegenResults};
+use rustc_data_structures::profiling::{get_resident_set_size, print_time_passes_entry};
+use rustc_data_structures::sync::SeqCst;
+use rustc_errors::registry::{InvalidErrorCode, Registry};
+use rustc_errors::{ErrorGuaranteed, PResult};
+use rustc_feature::find_gated_cfg;
+use rustc_hir::def_id::LOCAL_CRATE;
+use rustc_interface::util::{self, collect_crate_types, get_codegen_backend};
+use rustc_interface::{interface, Queries};
+use rustc_lint::LintStore;
+use rustc_metadata::locator;
+use rustc_save_analysis as save;
+use rustc_save_analysis::DumpHandler;
+use rustc_session::config::{nightly_options, CG_OPTIONS, Z_OPTIONS};
+use rustc_session::config::{ErrorOutputType, Input, OutputType, PrintRequest, TrimmedDefPaths};
+use rustc_session::cstore::MetadataLoader;
+use rustc_session::getopts;
+use rustc_session::lint::{Lint, LintId};
+use rustc_session::{config, Session};
+use rustc_session::{early_error, early_error_no_abort, early_warn};
+use rustc_span::source_map::{FileLoader, FileName};
+use rustc_span::symbol::sym;
+use rustc_target::json::ToJson;
+
+use std::cmp::max;
+use std::env;
+use std::ffi::OsString;
+use std::fs;
+use std::io::{self, IsTerminal, Read, Write};
+use std::panic::{self, catch_unwind};
+use std::path::PathBuf;
+use std::process::{self, Command, Stdio};
+use std::str;
+use std::sync::LazyLock;
+use std::time::Instant;
+
+pub mod args;
+pub mod pretty;
+mod session_diagnostics;
+
+use crate::session_diagnostics::{
+ RLinkEmptyVersionNumber, RLinkEncodingVersionMismatch, RLinkRustcVersionMismatch,
+ RLinkWrongFileType, RlinkNotAFile, RlinkUnableToRead,
+};
+
+/// Exit status code used for successful compilation and help output.
+pub const EXIT_SUCCESS: i32 = 0;
+
+/// Exit status code used for compilation failures and invalid flags.
+pub const EXIT_FAILURE: i32 = 1;
+
+const BUG_REPORT_URL: &str = "https://github.com/rust-lang/rust/issues/new\
+ ?labels=C-bug%2C+I-ICE%2C+T-compiler&template=ice.md";
+
+const ICE_REPORT_COMPILER_FLAGS: &[&str] = &["-Z", "-C", "--crate-type"];
+
+const ICE_REPORT_COMPILER_FLAGS_EXCLUDE: &[&str] = &["metadata", "extra-filename"];
+
+const ICE_REPORT_COMPILER_FLAGS_STRIP_VALUE: &[&str] = &["incremental"];
+
+pub fn abort_on_err<T>(result: Result<T, ErrorGuaranteed>, sess: &Session) -> T {
+ match result {
+ Err(..) => {
+ sess.abort_if_errors();
+ panic!("error reported but abort_if_errors didn't abort???");
+ }
+ Ok(x) => x,
+ }
+}
+
+pub trait Callbacks {
+ /// Called before creating the compiler instance
+ fn config(&mut self, _config: &mut interface::Config) {}
+ /// Called after parsing. Return value instructs the compiler whether to
+ /// continue the compilation afterwards (defaults to `Compilation::Continue`)
+ fn after_parsing<'tcx>(
+ &mut self,
+ _compiler: &interface::Compiler,
+ _queries: &'tcx Queries<'tcx>,
+ ) -> Compilation {
+ Compilation::Continue
+ }
+ /// Called after expansion. Return value instructs the compiler whether to
+ /// continue the compilation afterwards (defaults to `Compilation::Continue`)
+ fn after_expansion<'tcx>(
+ &mut self,
+ _compiler: &interface::Compiler,
+ _queries: &'tcx Queries<'tcx>,
+ ) -> Compilation {
+ Compilation::Continue
+ }
+ /// Called after analysis. Return value instructs the compiler whether to
+ /// continue the compilation afterwards (defaults to `Compilation::Continue`)
+ fn after_analysis<'tcx>(
+ &mut self,
+ _compiler: &interface::Compiler,
+ _queries: &'tcx Queries<'tcx>,
+ ) -> Compilation {
+ Compilation::Continue
+ }
+}
+
+#[derive(Default)]
+pub struct TimePassesCallbacks {
+ time_passes: bool,
+}
+
+impl Callbacks for TimePassesCallbacks {
+ // JUSTIFICATION: the session doesn't exist at this point.
+ #[allow(rustc::bad_opt_access)]
+ fn config(&mut self, config: &mut interface::Config) {
+ // If a --print=... option has been given, we don't print the "total"
+ // time because it will mess up the --print output. See #64339.
+ //
+ self.time_passes = config.opts.prints.is_empty() && config.opts.unstable_opts.time_passes;
+ config.opts.trimmed_def_paths = TrimmedDefPaths::GoodPath;
+ }
+}
+
+pub fn diagnostics_registry() -> Registry {
+ Registry::new(rustc_error_codes::DIAGNOSTICS)
+}
+
+/// This is the primary entry point for rustc.
+pub struct RunCompiler<'a, 'b> {
+ at_args: &'a [String],
+ callbacks: &'b mut (dyn Callbacks + Send),
+ file_loader: Option<Box<dyn FileLoader + Send + Sync>>,
+ make_codegen_backend:
+ Option<Box<dyn FnOnce(&config::Options) -> Box<dyn CodegenBackend> + Send>>,
+}
+
+impl<'a, 'b> RunCompiler<'a, 'b> {
+ pub fn new(at_args: &'a [String], callbacks: &'b mut (dyn Callbacks + Send)) -> Self {
+ Self { at_args, callbacks, file_loader: None, make_codegen_backend: None }
+ }
+
+ /// Set a custom codegen backend.
+ ///
+ /// Has no uses within this repository, but is used by bjorn3 for "the
+ /// hotswapping branch of cg_clif" for "setting the codegen backend from a
+ /// custom driver where the custom codegen backend has arbitrary data."
+ /// (See #102759.)
+ pub fn set_make_codegen_backend(
+ &mut self,
+ make_codegen_backend: Option<
+ Box<dyn FnOnce(&config::Options) -> Box<dyn CodegenBackend> + Send>,
+ >,
+ ) -> &mut Self {
+ self.make_codegen_backend = make_codegen_backend;
+ self
+ }
+
+ /// Load files from sources other than the file system.
+ ///
+ /// Has no uses within this repository, but may be used in the future by
+ /// bjorn3 for "hooking rust-analyzer's VFS into rustc at some point for
+ /// running rustc without having to save". (See #102759.)
+ pub fn set_file_loader(
+ &mut self,
+ file_loader: Option<Box<dyn FileLoader + Send + Sync>>,
+ ) -> &mut Self {
+ self.file_loader = file_loader;
+ self
+ }
+
+ /// Parse args and run the compiler.
+ pub fn run(self) -> interface::Result<()> {
+ run_compiler(self.at_args, self.callbacks, self.file_loader, self.make_codegen_backend)
+ }
+}
+
+fn run_compiler(
+ at_args: &[String],
+ callbacks: &mut (dyn Callbacks + Send),
+ file_loader: Option<Box<dyn FileLoader + Send + Sync>>,
+ make_codegen_backend: Option<
+ Box<dyn FnOnce(&config::Options) -> Box<dyn CodegenBackend> + Send>,
+ >,
+) -> interface::Result<()> {
+ let args = args::arg_expand_all(at_args);
+
+ let Some(matches) = handle_options(&args) else { return Ok(()) };
+
+ let sopts = config::build_session_options(&matches);
+
+ if let Some(ref code) = matches.opt_str("explain") {
+ handle_explain(diagnostics_registry(), code, sopts.error_format);
+ return Ok(());
+ }
+
+ let cfg = interface::parse_cfgspecs(matches.opt_strs("cfg"));
+ let check_cfg = interface::parse_check_cfg(matches.opt_strs("check-cfg"));
+ let (odir, ofile) = make_output(&matches);
+ let mut config = interface::Config {
+ opts: sopts,
+ crate_cfg: cfg,
+ crate_check_cfg: check_cfg,
+ input: Input::File(PathBuf::new()),
+ output_file: ofile,
+ output_dir: odir,
+ file_loader,
+ lint_caps: Default::default(),
+ parse_sess_created: None,
+ register_lints: None,
+ override_queries: None,
+ make_codegen_backend,
+ registry: diagnostics_registry(),
+ };
+
+ if !tracing::dispatcher::has_been_set() {
+ init_rustc_env_logger_with_backtrace_option(&config.opts.unstable_opts.log_backtrace);
+ }
+
+ match make_input(config.opts.error_format, &matches.free) {
+ Err(reported) => return Err(reported),
+ Ok(Some(input)) => {
+ config.input = input;
+
+ callbacks.config(&mut config);
+ }
+ Ok(None) => match matches.free.len() {
+ 0 => {
+ callbacks.config(&mut config);
+ interface::run_compiler(config, |compiler| {
+ let sopts = &compiler.session().opts;
+ if sopts.describe_lints {
+ let mut lint_store =
+ rustc_lint::new_lint_store(compiler.session().enable_internal_lints());
+ let registered_lints =
+ if let Some(register_lints) = compiler.register_lints() {
+ register_lints(compiler.session(), &mut lint_store);
+ true
+ } else {
+ false
+ };
+ describe_lints(compiler.session(), &lint_store, registered_lints);
+ return;
+ }
+ let should_stop =
+ print_crate_info(&***compiler.codegen_backend(), compiler.session(), false);
+
+ if should_stop == Compilation::Stop {
+ return;
+ }
+ early_error(sopts.error_format, "no input filename given")
+ });
+ return Ok(());
+ }
+ 1 => panic!("make_input should have provided valid inputs"),
+ _ => early_error(
+ config.opts.error_format,
+ &format!(
+ "multiple input filenames provided (first two filenames are `{}` and `{}`)",
+ matches.free[0], matches.free[1],
+ ),
+ ),
+ },
+ };
+
+ interface::run_compiler(config, |compiler| {
+ let sess = compiler.session();
+ let should_stop = print_crate_info(&***compiler.codegen_backend(), sess, true)
+ .and_then(|| list_metadata(sess, &*compiler.codegen_backend().metadata_loader()))
+ .and_then(|| try_process_rlink(sess, compiler));
+
+ if should_stop == Compilation::Stop {
+ return sess.compile_status();
+ }
+
+ let linker = compiler.enter(|queries| {
+ let early_exit = || sess.compile_status().map(|_| None);
+ queries.parse()?;
+
+ if let Some(ppm) = &sess.opts.pretty {
+ if ppm.needs_ast_map() {
+ queries.global_ctxt()?.enter(|tcx| {
+ pretty::print_after_hir_lowering(tcx, *ppm);
+ Ok(())
+ })?;
+ } else {
+ let krate = queries.parse()?.steal();
+ pretty::print_after_parsing(sess, &krate, *ppm);
+ }
+ trace!("finished pretty-printing");
+ return early_exit();
+ }
+
+ if callbacks.after_parsing(compiler, queries) == Compilation::Stop {
+ return early_exit();
+ }
+
+ if sess.opts.unstable_opts.parse_only || sess.opts.unstable_opts.show_span.is_some() {
+ return early_exit();
+ }
+
+ {
+ let plugins = queries.register_plugins()?;
+ let (_, lint_store) = &*plugins.borrow();
+
+ // Lint plugins are registered; now we can process command line flags.
+ if sess.opts.describe_lints {
+ describe_lints(sess, lint_store, true);
+ return early_exit();
+ }
+ }
+
+ let mut gctxt = queries.global_ctxt()?;
+ if callbacks.after_expansion(compiler, queries) == Compilation::Stop {
+ return early_exit();
+ }
+
+ // Make sure the `output_filenames` query is run for its side
+ // effects of writing the dep-info and reporting errors.
+ gctxt.enter(|tcx| tcx.output_filenames(()));
+
+ if sess.opts.output_types.contains_key(&OutputType::DepInfo)
+ && sess.opts.output_types.len() == 1
+ {
+ return early_exit();
+ }
+
+ if sess.opts.unstable_opts.no_analysis {
+ return early_exit();
+ }
+
+ gctxt.enter(|tcx| {
+ let result = tcx.analysis(());
+ if sess.opts.unstable_opts.save_analysis {
+ let crate_name = tcx.crate_name(LOCAL_CRATE);
+ sess.time("save_analysis", || {
+ save::process_crate(
+ tcx,
+ crate_name,
+ &sess.io.input,
+ None,
+ DumpHandler::new(sess.io.output_dir.as_deref(), crate_name),
+ )
+ });
+ }
+ result
+ })?;
+
+ drop(gctxt);
+
+ if callbacks.after_analysis(compiler, queries) == Compilation::Stop {
+ return early_exit();
+ }
+
+ queries.ongoing_codegen()?;
+
+ if sess.opts.unstable_opts.print_type_sizes {
+ sess.code_stats.print_type_sizes();
+ }
+
+ let linker = queries.linker()?;
+ Ok(Some(linker))
+ })?;
+
+ if let Some(linker) = linker {
+ let _timer = sess.timer("link");
+ linker.link()?
+ }
+
+ if sess.opts.unstable_opts.perf_stats {
+ sess.print_perf_stats();
+ }
+
+ if sess.opts.unstable_opts.print_fuel.is_some() {
+ eprintln!(
+ "Fuel used by {}: {}",
+ sess.opts.unstable_opts.print_fuel.as_ref().unwrap(),
+ sess.print_fuel.load(SeqCst)
+ );
+ }
+
+ Ok(())
+ })
+}
+
+// Extract output directory and file from matches.
+fn make_output(matches: &getopts::Matches) -> (Option<PathBuf>, Option<PathBuf>) {
+ let odir = matches.opt_str("out-dir").map(|o| PathBuf::from(&o));
+ let ofile = matches.opt_str("o").map(|o| PathBuf::from(&o));
+ (odir, ofile)
+}
+
+// Extract input (string or file and optional path) from matches.
+fn make_input(
+ error_format: ErrorOutputType,
+ free_matches: &[String],
+) -> Result<Option<Input>, ErrorGuaranteed> {
+ if free_matches.len() == 1 {
+ let ifile = &free_matches[0];
+ if ifile == "-" {
+ let mut src = String::new();
+ if io::stdin().read_to_string(&mut src).is_err() {
+ // Immediately stop compilation if there was an issue reading
+ // the input (for example if the input stream is not UTF-8).
+ let reported = early_error_no_abort(
+ error_format,
+ "couldn't read from stdin, as it did not contain valid UTF-8",
+ );
+ return Err(reported);
+ }
+ if let Ok(path) = env::var("UNSTABLE_RUSTDOC_TEST_PATH") {
+ let line = env::var("UNSTABLE_RUSTDOC_TEST_LINE").expect(
+ "when UNSTABLE_RUSTDOC_TEST_PATH is set \
+ UNSTABLE_RUSTDOC_TEST_LINE also needs to be set",
+ );
+ let line = isize::from_str_radix(&line, 10)
+ .expect("UNSTABLE_RUSTDOC_TEST_LINE needs to be an number");
+ let file_name = FileName::doc_test_source_code(PathBuf::from(path), line);
+ Ok(Some(Input::Str { name: file_name, input: src }))
+ } else {
+ Ok(Some(Input::Str { name: FileName::anon_source_code(&src), input: src }))
+ }
+ } else {
+ Ok(Some(Input::File(PathBuf::from(ifile))))
+ }
+ } else {
+ Ok(None)
+ }
+}
+
+/// Whether to stop or continue compilation.
+#[derive(Copy, Clone, Debug, Eq, PartialEq)]
+pub enum Compilation {
+ Stop,
+ Continue,
+}
+
+impl Compilation {
+ pub fn and_then<F: FnOnce() -> Compilation>(self, next: F) -> Compilation {
+ match self {
+ Compilation::Stop => Compilation::Stop,
+ Compilation::Continue => next(),
+ }
+ }
+}
+
+fn handle_explain(registry: Registry, code: &str, output: ErrorOutputType) {
+ let upper_cased_code = code.to_ascii_uppercase();
+ let normalised =
+ if upper_cased_code.starts_with('E') { upper_cased_code } else { format!("E{code:0>4}") };
+ match registry.try_find_description(&normalised) {
+ Ok(Some(description)) => {
+ let mut is_in_code_block = false;
+ let mut text = String::new();
+ // Slice off the leading newline and print.
+ for line in description.lines() {
+ let indent_level =
+ line.find(|c: char| !c.is_whitespace()).unwrap_or_else(|| line.len());
+ let dedented_line = &line[indent_level..];
+ if dedented_line.starts_with("```") {
+ is_in_code_block = !is_in_code_block;
+ text.push_str(&line[..(indent_level + 3)]);
+ } else if is_in_code_block && dedented_line.starts_with("# ") {
+ continue;
+ } else {
+ text.push_str(line);
+ }
+ text.push('\n');
+ }
+ if io::stdout().is_terminal() {
+ show_content_with_pager(&text);
+ } else {
+ print!("{text}");
+ }
+ }
+ Ok(None) => {
+ early_error(output, &format!("no extended information for {code}"));
+ }
+ Err(InvalidErrorCode) => {
+ early_error(output, &format!("{code} is not a valid error code"));
+ }
+ }
+}
+
+fn show_content_with_pager(content: &str) {
+ let pager_name = env::var_os("PAGER").unwrap_or_else(|| {
+ if cfg!(windows) { OsString::from("more.com") } else { OsString::from("less") }
+ });
+
+ let mut fallback_to_println = false;
+
+ match Command::new(pager_name).stdin(Stdio::piped()).spawn() {
+ Ok(mut pager) => {
+ if let Some(pipe) = pager.stdin.as_mut() {
+ if pipe.write_all(content.as_bytes()).is_err() {
+ fallback_to_println = true;
+ }
+ }
+
+ if pager.wait().is_err() {
+ fallback_to_println = true;
+ }
+ }
+ Err(_) => {
+ fallback_to_println = true;
+ }
+ }
+
+ // If pager fails for whatever reason, we should still print the content
+ // to standard output
+ if fallback_to_println {
+ print!("{content}");
+ }
+}
+
+pub fn try_process_rlink(sess: &Session, compiler: &interface::Compiler) -> Compilation {
+ if sess.opts.unstable_opts.link_only {
+ if let Input::File(file) = &sess.io.input {
+ // FIXME: #![crate_type] and #![crate_name] support not implemented yet
+ sess.init_crate_types(collect_crate_types(sess, &[]));
+ let outputs = compiler.build_output_filenames(sess, &[]);
+ let rlink_data = fs::read(file).unwrap_or_else(|err| {
+ sess.emit_fatal(RlinkUnableToRead { err });
+ });
+ let codegen_results = match CodegenResults::deserialize_rlink(rlink_data) {
+ Ok(codegen) => codegen,
+ Err(err) => {
+ match err {
+ CodegenErrors::WrongFileType => sess.emit_fatal(RLinkWrongFileType),
+ CodegenErrors::EmptyVersionNumber => {
+ sess.emit_fatal(RLinkEmptyVersionNumber)
+ }
+ CodegenErrors::EncodingVersionMismatch { version_array, rlink_version } => {
+ sess.emit_fatal(RLinkEncodingVersionMismatch {
+ version_array,
+ rlink_version,
+ })
+ }
+ CodegenErrors::RustcVersionMismatch { rustc_version, current_version } => {
+ sess.emit_fatal(RLinkRustcVersionMismatch {
+ rustc_version,
+ current_version,
+ })
+ }
+ };
+ }
+ };
+ let result = compiler.codegen_backend().link(sess, codegen_results, &outputs);
+ abort_on_err(result, sess);
+ } else {
+ sess.emit_fatal(RlinkNotAFile {})
+ }
+ Compilation::Stop
+ } else {
+ Compilation::Continue
+ }
+}
+
+pub fn list_metadata(sess: &Session, metadata_loader: &dyn MetadataLoader) -> Compilation {
+ if sess.opts.unstable_opts.ls {
+ match sess.io.input {
+ Input::File(ref ifile) => {
+ let path = &(*ifile);
+ let mut v = Vec::new();
+ locator::list_file_metadata(&sess.target, path, metadata_loader, &mut v).unwrap();
+ println!("{}", String::from_utf8(v).unwrap());
+ }
+ Input::Str { .. } => {
+ early_error(ErrorOutputType::default(), "cannot list metadata for stdin");
+ }
+ }
+ return Compilation::Stop;
+ }
+
+ Compilation::Continue
+}
+
+fn print_crate_info(
+ codegen_backend: &dyn CodegenBackend,
+ sess: &Session,
+ parse_attrs: bool,
+) -> Compilation {
+ use rustc_session::config::PrintRequest::*;
+ // NativeStaticLibs and LinkArgs are special - printed during linking
+ // (empty iterator returns true)
+ if sess.opts.prints.iter().all(|&p| p == NativeStaticLibs || p == LinkArgs) {
+ return Compilation::Continue;
+ }
+
+ let attrs = if parse_attrs {
+ let result = parse_crate_attrs(sess);
+ match result {
+ Ok(attrs) => Some(attrs),
+ Err(mut parse_error) => {
+ parse_error.emit();
+ return Compilation::Stop;
+ }
+ }
+ } else {
+ None
+ };
+ for req in &sess.opts.prints {
+ match *req {
+ TargetList => {
+ let mut targets = rustc_target::spec::TARGETS.to_vec();
+ targets.sort_unstable();
+ println!("{}", targets.join("\n"));
+ }
+ Sysroot => println!("{}", sess.sysroot.display()),
+ TargetLibdir => println!("{}", sess.target_tlib_path.dir.display()),
+ TargetSpec => {
+ println!("{}", serde_json::to_string_pretty(&sess.target.to_json()).unwrap());
+ }
+ FileNames | CrateName => {
+ let attrs = attrs.as_ref().unwrap();
+ let t_outputs = rustc_interface::util::build_output_filenames(attrs, sess);
+ let id = rustc_session::output::find_crate_name(sess, attrs);
+ if *req == PrintRequest::CrateName {
+ println!("{id}");
+ continue;
+ }
+ let crate_types = collect_crate_types(sess, attrs);
+ for &style in &crate_types {
+ let fname =
+ rustc_session::output::filename_for_input(sess, style, id, &t_outputs);
+ println!("{}", fname.file_name().unwrap().to_string_lossy());
+ }
+ }
+ Cfg => {
+ let mut cfgs = sess
+ .parse_sess
+ .config
+ .iter()
+ .filter_map(|&(name, value)| {
+ // Note that crt-static is a specially recognized cfg
+ // directive that's printed out here as part of
+ // rust-lang/rust#37406, but in general the
+ // `target_feature` cfg is gated under
+ // rust-lang/rust#29717. For now this is just
+ // specifically allowing the crt-static cfg and that's
+ // it, this is intended to get into Cargo and then go
+ // through to build scripts.
+ if (name != sym::target_feature || value != Some(sym::crt_dash_static))
+ && !sess.is_nightly_build()
+ && find_gated_cfg(|cfg_sym| cfg_sym == name).is_some()
+ {
+ return None;
+ }
+
+ if let Some(value) = value {
+ Some(format!("{name}=\"{value}\""))
+ } else {
+ Some(name.to_string())
+ }
+ })
+ .collect::<Vec<String>>();
+
+ cfgs.sort();
+ for cfg in cfgs {
+ println!("{cfg}");
+ }
+ }
+ CallingConventions => {
+ let mut calling_conventions = rustc_target::spec::abi::all_names();
+ calling_conventions.sort_unstable();
+ println!("{}", calling_conventions.join("\n"));
+ }
+ RelocationModels
+ | CodeModels
+ | TlsModels
+ | TargetCPUs
+ | StackProtectorStrategies
+ | TargetFeatures => {
+ codegen_backend.print(*req, sess);
+ }
+ // Any output here interferes with Cargo's parsing of other printed output
+ NativeStaticLibs => {}
+ LinkArgs => {}
+ SplitDebuginfo => {
+ use rustc_target::spec::SplitDebuginfo::{Off, Packed, Unpacked};
+
+ for split in &[Off, Packed, Unpacked] {
+ let stable = sess.target.options.supported_split_debuginfo.contains(split);
+ let unstable_ok = sess.unstable_options();
+ if stable || unstable_ok {
+ println!("{split}");
+ }
+ }
+ }
+ }
+ }
+ Compilation::Stop
+}
+
+/// Prints version information
+///
+/// NOTE: this is a macro to support drivers built at a different time than the main `rustc_driver` crate.
+pub macro version($binary: literal, $matches: expr) {
+ fn unw(x: Option<&str>) -> &str {
+ x.unwrap_or("unknown")
+ }
+ $crate::version_at_macro_invocation(
+ $binary,
+ $matches,
+ unw(option_env!("CFG_VERSION")),
+ unw(option_env!("CFG_VER_HASH")),
+ unw(option_env!("CFG_VER_DATE")),
+ unw(option_env!("CFG_RELEASE")),
+ )
+}
+
+#[doc(hidden)] // use the macro instead
+pub fn version_at_macro_invocation(
+ binary: &str,
+ matches: &getopts::Matches,
+ version: &str,
+ commit_hash: &str,
+ commit_date: &str,
+ release: &str,
+) {
+ let verbose = matches.opt_present("verbose");
+
+ println!("{binary} {version}");
+
+ if verbose {
+ println!("binary: {binary}");
+ println!("commit-hash: {commit_hash}");
+ println!("commit-date: {commit_date}");
+ println!("host: {}", config::host_triple());
+ println!("release: {release}");
+
+ let debug_flags = matches.opt_strs("Z");
+ let backend_name = debug_flags.iter().find_map(|x| x.strip_prefix("codegen-backend="));
+ get_codegen_backend(&None, backend_name).print_version();
+ }
+}
+
+fn usage(verbose: bool, include_unstable_options: bool, nightly_build: bool) {
+ let groups = if verbose { config::rustc_optgroups() } else { config::rustc_short_optgroups() };
+ let mut options = getopts::Options::new();
+ for option in groups.iter().filter(|x| include_unstable_options || x.is_stable()) {
+ (option.apply)(&mut options);
+ }
+ let message = "Usage: rustc [OPTIONS] INPUT";
+ let nightly_help = if nightly_build {
+ "\n -Z help Print unstable compiler options"
+ } else {
+ ""
+ };
+ let verbose_help = if verbose {
+ ""
+ } else {
+ "\n --help -v Print the full set of options rustc accepts"
+ };
+ let at_path = if verbose {
+ " @path Read newline separated options from `path`\n"
+ } else {
+ ""
+ };
+ println!(
+ "{options}{at_path}\nAdditional help:
+ -C help Print codegen options
+ -W help \
+ Print 'lint' options and default settings{nightly}{verbose}\n",
+ options = options.usage(message),
+ at_path = at_path,
+ nightly = nightly_help,
+ verbose = verbose_help
+ );
+}
+
+fn print_wall_help() {
+ println!(
+ "
+The flag `-Wall` does not exist in `rustc`. Most useful lints are enabled by
+default. Use `rustc -W help` to see all available lints. It's more common to put
+warning settings in the crate root using `#![warn(LINT_NAME)]` instead of using
+the command line flag directly.
+"
+ );
+}
+
+/// Write to stdout lint command options, together with a list of all available lints
+pub fn describe_lints(sess: &Session, lint_store: &LintStore, loaded_plugins: bool) {
+ println!(
+ "
+Available lint options:
+ -W <foo> Warn about <foo>
+ -A <foo> \
+ Allow <foo>
+ -D <foo> Deny <foo>
+ -F <foo> Forbid <foo> \
+ (deny <foo> and all attempts to override)
+
+"
+ );
+
+ fn sort_lints(sess: &Session, mut lints: Vec<&'static Lint>) -> Vec<&'static Lint> {
+ // The sort doesn't case-fold but it's doubtful we care.
+ lints.sort_by_cached_key(|x: &&Lint| (x.default_level(sess.edition()), x.name));
+ lints
+ }
+
+ fn sort_lint_groups(
+ lints: Vec<(&'static str, Vec<LintId>, bool)>,
+ ) -> Vec<(&'static str, Vec<LintId>)> {
+ let mut lints: Vec<_> = lints.into_iter().map(|(x, y, _)| (x, y)).collect();
+ lints.sort_by_key(|l| l.0);
+ lints
+ }
+
+ let (plugin, builtin): (Vec<_>, _) =
+ lint_store.get_lints().iter().cloned().partition(|&lint| lint.is_plugin);
+ let plugin = sort_lints(sess, plugin);
+ let builtin = sort_lints(sess, builtin);
+
+ let (plugin_groups, builtin_groups): (Vec<_>, _) =
+ lint_store.get_lint_groups().partition(|&(.., p)| p);
+ let plugin_groups = sort_lint_groups(plugin_groups);
+ let builtin_groups = sort_lint_groups(builtin_groups);
+
+ let max_name_len =
+ plugin.iter().chain(&builtin).map(|&s| s.name.chars().count()).max().unwrap_or(0);
+ let padded = |x: &str| {
+ let mut s = " ".repeat(max_name_len - x.chars().count());
+ s.push_str(x);
+ s
+ };
+
+ println!("Lint checks provided by rustc:\n");
+
+ let print_lints = |lints: Vec<&Lint>| {
+ println!(" {} {:7.7} {}", padded("name"), "default", "meaning");
+ println!(" {} {:7.7} {}", padded("----"), "-------", "-------");
+ for lint in lints {
+ let name = lint.name_lower().replace('_', "-");
+ println!(
+ " {} {:7.7} {}",
+ padded(&name),
+ lint.default_level(sess.edition()).as_str(),
+ lint.desc
+ );
+ }
+ println!("\n");
+ };
+
+ print_lints(builtin);
+
+ let max_name_len = max(
+ "warnings".len(),
+ plugin_groups
+ .iter()
+ .chain(&builtin_groups)
+ .map(|&(s, _)| s.chars().count())
+ .max()
+ .unwrap_or(0),
+ );
+
+ let padded = |x: &str| {
+ let mut s = " ".repeat(max_name_len - x.chars().count());
+ s.push_str(x);
+ s
+ };
+
+ println!("Lint groups provided by rustc:\n");
+
+ let print_lint_groups = |lints: Vec<(&'static str, Vec<LintId>)>, all_warnings| {
+ println!(" {} sub-lints", padded("name"));
+ println!(" {} ---------", padded("----"));
+
+ if all_warnings {
+ println!(" {} all lints that are set to issue warnings", padded("warnings"));
+ }
+
+ for (name, to) in lints {
+ let name = name.to_lowercase().replace('_', "-");
+ let desc = to
+ .into_iter()
+ .map(|x| x.to_string().replace('_', "-"))
+ .collect::<Vec<String>>()
+ .join(", ");
+ println!(" {} {}", padded(&name), desc);
+ }
+ println!("\n");
+ };
+
+ print_lint_groups(builtin_groups, true);
+
+ match (loaded_plugins, plugin.len(), plugin_groups.len()) {
+ (false, 0, _) | (false, _, 0) => {
+ println!("Lint tools like Clippy can provide additional lints and lint groups.");
+ }
+ (false, ..) => panic!("didn't load lint plugins but got them anyway!"),
+ (true, 0, 0) => println!("This crate does not load any lint plugins or lint groups."),
+ (true, l, g) => {
+ if l > 0 {
+ println!("Lint checks provided by plugins loaded by this crate:\n");
+ print_lints(plugin);
+ }
+ if g > 0 {
+ println!("Lint groups provided by plugins loaded by this crate:\n");
+ print_lint_groups(plugin_groups, false);
+ }
+ }
+ }
+}
+
+fn describe_debug_flags() {
+ println!("\nAvailable options:\n");
+ print_flag_list("-Z", config::Z_OPTIONS);
+}
+
+fn describe_codegen_flags() {
+ println!("\nAvailable codegen options:\n");
+ print_flag_list("-C", config::CG_OPTIONS);
+}
+
+pub fn print_flag_list<T>(
+ cmdline_opt: &str,
+ flag_list: &[(&'static str, T, &'static str, &'static str)],
+) {
+ let max_len = flag_list.iter().map(|&(name, _, _, _)| name.chars().count()).max().unwrap_or(0);
+
+ for &(name, _, _, desc) in flag_list {
+ println!(
+ " {} {:>width$}=val -- {}",
+ cmdline_opt,
+ name.replace('_', "-"),
+ desc,
+ width = max_len
+ );
+ }
+}
+
+/// Process command line options. Emits messages as appropriate. If compilation
+/// should continue, returns a getopts::Matches object parsed from args,
+/// otherwise returns `None`.
+///
+/// The compiler's handling of options is a little complicated as it ties into
+/// our stability story. The current intention of each compiler option is to
+/// have one of two modes:
+///
+/// 1. An option is stable and can be used everywhere.
+/// 2. An option is unstable, and can only be used on nightly.
+///
+/// Like unstable library and language features, however, unstable options have
+/// always required a form of "opt in" to indicate that you're using them. This
+/// provides the easy ability to scan a code base to check to see if anything
+/// unstable is being used. Currently, this "opt in" is the `-Z` "zed" flag.
+///
+/// All options behind `-Z` are considered unstable by default. Other top-level
+/// options can also be considered unstable, and they were unlocked through the
+/// `-Z unstable-options` flag. Note that `-Z` remains to be the root of
+/// instability in both cases, though.
+///
+/// So with all that in mind, the comments below have some more detail about the
+/// contortions done here to get things to work out correctly.
+pub fn handle_options(args: &[String]) -> Option<getopts::Matches> {
+ // Throw away the first argument, the name of the binary
+ let args = &args[1..];
+
+ if args.is_empty() {
+ // user did not write `-v` nor `-Z unstable-options`, so do not
+ // include that extra information.
+ let nightly_build =
+ rustc_feature::UnstableFeatures::from_environment(None).is_nightly_build();
+ usage(false, false, nightly_build);
+ return None;
+ }
+
+ // Parse with *all* options defined in the compiler, we don't worry about
+ // option stability here we just want to parse as much as possible.
+ let mut options = getopts::Options::new();
+ for option in config::rustc_optgroups() {
+ (option.apply)(&mut options);
+ }
+ let matches = options.parse(args).unwrap_or_else(|e| {
+ let msg = match e {
+ getopts::Fail::UnrecognizedOption(ref opt) => CG_OPTIONS
+ .iter()
+ .map(|&(name, ..)| ('C', name))
+ .chain(Z_OPTIONS.iter().map(|&(name, ..)| ('Z', name)))
+ .find(|&(_, name)| *opt == name.replace('_', "-"))
+ .map(|(flag, _)| format!("{e}. Did you mean `-{flag} {opt}`?")),
+ _ => None,
+ };
+ early_error(ErrorOutputType::default(), &msg.unwrap_or_else(|| e.to_string()));
+ });
+
+ // For all options we just parsed, we check a few aspects:
+ //
+ // * If the option is stable, we're all good
+ // * If the option wasn't passed, we're all good
+ // * If `-Z unstable-options` wasn't passed (and we're not a -Z option
+ // ourselves), then we require the `-Z unstable-options` flag to unlock
+ // this option that was passed.
+ // * If we're a nightly compiler, then unstable options are now unlocked, so
+ // we're good to go.
+ // * Otherwise, if we're an unstable option then we generate an error
+ // (unstable option being used on stable)
+ nightly_options::check_nightly_options(&matches, &config::rustc_optgroups());
+
+ if matches.opt_present("h") || matches.opt_present("help") {
+ // Only show unstable options in --help if we accept unstable options.
+ let unstable_enabled = nightly_options::is_unstable_enabled(&matches);
+ let nightly_build = nightly_options::match_is_nightly_build(&matches);
+ usage(matches.opt_present("verbose"), unstable_enabled, nightly_build);
+ return None;
+ }
+
+ // Handle the special case of -Wall.
+ let wall = matches.opt_strs("W");
+ if wall.iter().any(|x| *x == "all") {
+ print_wall_help();
+ rustc_errors::FatalError.raise();
+ }
+
+ // Don't handle -W help here, because we might first load plugins.
+ let debug_flags = matches.opt_strs("Z");
+ if debug_flags.iter().any(|x| *x == "help") {
+ describe_debug_flags();
+ return None;
+ }
+
+ let cg_flags = matches.opt_strs("C");
+
+ if cg_flags.iter().any(|x| *x == "help") {
+ describe_codegen_flags();
+ return None;
+ }
+
+ if cg_flags.iter().any(|x| *x == "no-stack-check") {
+ early_warn(
+ ErrorOutputType::default(),
+ "the --no-stack-check flag is deprecated and does nothing",
+ );
+ }
+
+ if cg_flags.iter().any(|x| *x == "passes=list") {
+ let backend_name = debug_flags.iter().find_map(|x| x.strip_prefix("codegen-backend="));
+ get_codegen_backend(&None, backend_name).print_passes();
+ return None;
+ }
+
+ if matches.opt_present("version") {
+ version!("rustc", &matches);
+ return None;
+ }
+
+ Some(matches)
+}
+
+fn parse_crate_attrs<'a>(sess: &'a Session) -> PResult<'a, ast::AttrVec> {
+ match &sess.io.input {
+ Input::File(ifile) => rustc_parse::parse_crate_attrs_from_file(ifile, &sess.parse_sess),
+ Input::Str { name, input } => rustc_parse::parse_crate_attrs_from_source_str(
+ name.clone(),
+ input.clone(),
+ &sess.parse_sess,
+ ),
+ }
+}
+
+/// Gets a list of extra command-line flags provided by the user, as strings.
+///
+/// This function is used during ICEs to show more information useful for
+/// debugging, since some ICEs only happens with non-default compiler flags
+/// (and the users don't always report them).
+fn extra_compiler_flags() -> Option<(Vec<String>, bool)> {
+ let mut args = env::args_os().map(|arg| arg.to_string_lossy().to_string()).peekable();
+
+ let mut result = Vec::new();
+ let mut excluded_cargo_defaults = false;
+ while let Some(arg) = args.next() {
+ if let Some(a) = ICE_REPORT_COMPILER_FLAGS.iter().find(|a| arg.starts_with(*a)) {
+ let content = if arg.len() == a.len() {
+ // A space-separated option, like `-C incremental=foo` or `--crate-type rlib`
+ match args.next() {
+ Some(arg) => arg.to_string(),
+ None => continue,
+ }
+ } else if arg.get(a.len()..a.len() + 1) == Some("=") {
+ // An equals option, like `--crate-type=rlib`
+ arg[a.len() + 1..].to_string()
+ } else {
+ // A non-space option, like `-Cincremental=foo`
+ arg[a.len()..].to_string()
+ };
+ let option = content.split_once('=').map(|s| s.0).unwrap_or(&content);
+ if ICE_REPORT_COMPILER_FLAGS_EXCLUDE.iter().any(|exc| option == *exc) {
+ excluded_cargo_defaults = true;
+ } else {
+ result.push(a.to_string());
+ match ICE_REPORT_COMPILER_FLAGS_STRIP_VALUE.iter().find(|s| option == **s) {
+ Some(s) => result.push(format!("{s}=[REDACTED]")),
+ None => result.push(content),
+ }
+ }
+ }
+ }
+
+ if !result.is_empty() { Some((result, excluded_cargo_defaults)) } else { None }
+}
+
+/// Runs a closure and catches unwinds triggered by fatal errors.
+///
+/// The compiler currently unwinds with a special sentinel value to abort
+/// compilation on fatal errors. This function catches that sentinel and turns
+/// the panic into a `Result` instead.
+pub fn catch_fatal_errors<F: FnOnce() -> R, R>(f: F) -> Result<R, ErrorGuaranteed> {
+ catch_unwind(panic::AssertUnwindSafe(f)).map_err(|value| {
+ if value.is::<rustc_errors::FatalErrorMarker>() {
+ ErrorGuaranteed::unchecked_claim_error_was_emitted()
+ } else {
+ panic::resume_unwind(value);
+ }
+ })
+}
+
+/// Variant of `catch_fatal_errors` for the `interface::Result` return type
+/// that also computes the exit code.
+pub fn catch_with_exit_code(f: impl FnOnce() -> interface::Result<()>) -> i32 {
+ let result = catch_fatal_errors(f).and_then(|result| result);
+ match result {
+ Ok(()) => EXIT_SUCCESS,
+ Err(_) => EXIT_FAILURE,
+ }
+}
+
+static DEFAULT_HOOK: LazyLock<Box<dyn Fn(&panic::PanicInfo<'_>) + Sync + Send + 'static>> =
+ LazyLock::new(|| {
+ let hook = panic::take_hook();
+ panic::set_hook(Box::new(|info| {
+ // If the error was caused by a broken pipe then this is not a bug.
+ // Write the error and return immediately. See #98700.
+ #[cfg(windows)]
+ if let Some(msg) = info.payload().downcast_ref::<String>() {
+ if msg.starts_with("failed printing to stdout: ") && msg.ends_with("(os error 232)")
+ {
+ early_error_no_abort(ErrorOutputType::default(), &msg);
+ return;
+ }
+ };
+
+ // Invoke the default handler, which prints the actual panic message and optionally a backtrace
+ // Don't do this for delayed bugs, which already emit their own more useful backtrace.
+ if !info.payload().is::<rustc_errors::DelayedBugPanic>() {
+ (*DEFAULT_HOOK)(info);
+
+ // Separate the output with an empty line
+ eprintln!();
+ }
+
+ // Print the ICE message
+ report_ice(info, BUG_REPORT_URL);
+ }));
+ hook
+ });
+
+/// Prints the ICE message, including query stack, but without backtrace.
+///
+/// The message will point the user at `bug_report_url` to report the ICE.
+///
+/// When `install_ice_hook` is called, this function will be called as the panic
+/// hook.
+pub fn report_ice(info: &panic::PanicInfo<'_>, bug_report_url: &str) {
+ let fallback_bundle =
+ rustc_errors::fallback_fluent_bundle(rustc_errors::DEFAULT_LOCALE_RESOURCES, false);
+ let emitter = Box::new(rustc_errors::emitter::EmitterWriter::stderr(
+ rustc_errors::ColorConfig::Auto,
+ None,
+ None,
+ fallback_bundle,
+ false,
+ false,
+ None,
+ false,
+ false,
+ ));
+ let handler = rustc_errors::Handler::with_emitter(true, None, emitter);
+
+ // a .span_bug or .bug call has already printed what
+ // it wants to print.
+ if !info.payload().is::<rustc_errors::ExplicitBug>()
+ && !info.payload().is::<rustc_errors::DelayedBugPanic>()
+ {
+ let mut d = rustc_errors::Diagnostic::new(rustc_errors::Level::Bug, "unexpected panic");
+ handler.emit_diagnostic(&mut d);
+ }
+
+ handler.emit_note(session_diagnostics::Ice);
+ handler.emit_note(session_diagnostics::IceBugReport { bug_report_url });
+ handler.emit_note(session_diagnostics::IceVersion {
+ version: util::version_str!().unwrap_or("unknown_version"),
+ triple: config::host_triple(),
+ });
+
+ if let Some((flags, excluded_cargo_defaults)) = extra_compiler_flags() {
+ handler.emit_note(session_diagnostics::IceFlags { flags: flags.join(" ") });
+ if excluded_cargo_defaults {
+ handler.emit_note(session_diagnostics::IceExcludeCargoDefaults);
+ }
+ }
+
+ // If backtraces are enabled, also print the query stack
+ let backtrace = env::var_os("RUST_BACKTRACE").map_or(false, |x| &x != "0");
+
+ let num_frames = if backtrace { None } else { Some(2) };
+
+ interface::try_print_query_stack(&handler, num_frames);
+
+ #[cfg(windows)]
+ unsafe {
+ if env::var("RUSTC_BREAK_ON_ICE").is_ok() {
+ // Trigger a debugger if we crashed during bootstrap
+ winapi::um::debugapi::DebugBreak();
+ }
+ }
+}
+
+/// Installs a panic hook that will print the ICE message on unexpected panics.
+///
+/// A custom rustc driver can skip calling this to set up a custom ICE hook.
+pub fn install_ice_hook() {
+ // If the user has not explicitly overridden "RUST_BACKTRACE", then produce
+ // full backtraces. When a compiler ICE happens, we want to gather
+ // as much information as possible to present in the issue opened
+ // by the user. Compiler developers and other rustc users can
+ // opt in to less-verbose backtraces by manually setting "RUST_BACKTRACE"
+ // (e.g. `RUST_BACKTRACE=1`)
+ if std::env::var("RUST_BACKTRACE").is_err() {
+ std::env::set_var("RUST_BACKTRACE", "full");
+ }
+ LazyLock::force(&DEFAULT_HOOK);
+}
+
+/// This allows tools to enable rust logging without having to magically match rustc's
+/// tracing crate version.
+pub fn init_rustc_env_logger() {
+ init_rustc_env_logger_with_backtrace_option(&None);
+}
+
+/// This allows tools to enable rust logging without having to magically match rustc's
+/// tracing crate version. In contrast to `init_rustc_env_logger` it allows you to
+/// choose a target module you wish to show backtraces along with its logging.
+pub fn init_rustc_env_logger_with_backtrace_option(backtrace_target: &Option<String>) {
+ if let Err(error) = rustc_log::init_rustc_env_logger_with_backtrace_option(backtrace_target) {
+ early_error(ErrorOutputType::default(), &error.to_string());
+ }
+}
+
+/// This allows tools to enable rust logging without having to magically match rustc's
+/// tracing crate version. In contrast to `init_rustc_env_logger` it allows you to choose an env var
+/// other than `RUSTC_LOG`.
+pub fn init_env_logger(env: &str) {
+ if let Err(error) = rustc_log::init_env_logger(env) {
+ early_error(ErrorOutputType::default(), &error.to_string());
+ }
+}
+
+#[cfg(all(unix, any(target_env = "gnu", target_os = "macos")))]
+mod signal_handler {
+ extern "C" {
+ fn backtrace_symbols_fd(
+ buffer: *const *mut libc::c_void,
+ size: libc::c_int,
+ fd: libc::c_int,
+ );
+ }
+
+ extern "C" fn print_stack_trace(_: libc::c_int) {
+ const MAX_FRAMES: usize = 256;
+ static mut STACK_TRACE: [*mut libc::c_void; MAX_FRAMES] =
+ [std::ptr::null_mut(); MAX_FRAMES];
+ unsafe {
+ let depth = libc::backtrace(STACK_TRACE.as_mut_ptr(), MAX_FRAMES as i32);
+ if depth == 0 {
+ return;
+ }
+ backtrace_symbols_fd(STACK_TRACE.as_ptr(), depth, 2);
+ }
+ }
+
+ /// When an error signal (such as SIGABRT or SIGSEGV) is delivered to the
+ /// process, print a stack trace and then exit.
+ pub(super) fn install() {
+ unsafe {
+ const ALT_STACK_SIZE: usize = libc::MINSIGSTKSZ + 64 * 1024;
+ let mut alt_stack: libc::stack_t = std::mem::zeroed();
+ alt_stack.ss_sp =
+ std::alloc::alloc(std::alloc::Layout::from_size_align(ALT_STACK_SIZE, 1).unwrap())
+ as *mut libc::c_void;
+ alt_stack.ss_size = ALT_STACK_SIZE;
+ libc::sigaltstack(&alt_stack, std::ptr::null_mut());
+
+ let mut sa: libc::sigaction = std::mem::zeroed();
+ sa.sa_sigaction = print_stack_trace as libc::sighandler_t;
+ sa.sa_flags = libc::SA_NODEFER | libc::SA_RESETHAND | libc::SA_ONSTACK;
+ libc::sigemptyset(&mut sa.sa_mask);
+ libc::sigaction(libc::SIGSEGV, &sa, std::ptr::null_mut());
+ }
+ }
+}
+
+#[cfg(not(all(unix, any(target_env = "gnu", target_os = "macos"))))]
+mod signal_handler {
+ pub(super) fn install() {}
+}
+
+pub fn main() -> ! {
+ let start_time = Instant::now();
+ let start_rss = get_resident_set_size();
+ signal_handler::install();
+ let mut callbacks = TimePassesCallbacks::default();
+ install_ice_hook();
+ let exit_code = catch_with_exit_code(|| {
+ let args = env::args_os()
+ .enumerate()
+ .map(|(i, arg)| {
+ arg.into_string().unwrap_or_else(|arg| {
+ early_error(
+ ErrorOutputType::default(),
+ &format!("argument {i} is not valid Unicode: {arg:?}"),
+ )
+ })
+ })
+ .collect::<Vec<_>>();
+ RunCompiler::new(&args, &mut callbacks).run()
+ });
+
+ if callbacks.time_passes {
+ let end_rss = get_resident_set_size();
+ print_time_passes_entry("total", start_time.elapsed(), start_rss, end_rss);
+ }
+
+ process::exit(exit_code)
+}
--- /dev/null
+//! The various pretty-printing routines.
+
+use crate::session_diagnostics::UnprettyDumpFail;
+use rustc_ast as ast;
+use rustc_ast_pretty::pprust;
+use rustc_errors::ErrorGuaranteed;
+use rustc_hir as hir;
+use rustc_hir_pretty as pprust_hir;
+use rustc_middle::hir::map as hir_map;
+use rustc_middle::mir::{write_mir_graphviz, write_mir_pretty};
+use rustc_middle::ty::{self, TyCtxt};
+use rustc_session::config::{PpAstTreeMode, PpHirMode, PpMode, PpSourceMode};
+use rustc_session::Session;
+use rustc_span::symbol::Ident;
+use rustc_span::FileName;
+
+use std::cell::Cell;
+use std::fmt::Write;
+
+pub use self::PpMode::*;
+pub use self::PpSourceMode::*;
+use crate::abort_on_err;
+
+// This slightly awkward construction is to allow for each PpMode to
+// choose whether it needs to do analyses (which can consume the
+// Session) and then pass through the session (now attached to the
+// analysis results) on to the chosen pretty-printer, along with the
+// `&PpAnn` object.
+//
+// Note that since the `&PrinterSupport` is freshly constructed on each
+// call, it would not make sense to try to attach the lifetime of `self`
+// to the lifetime of the `&PrinterObject`.
+
+/// Constructs a `PrinterSupport` object and passes it to `f`.
+fn call_with_pp_support<'tcx, A, F>(
+ ppmode: &PpSourceMode,
+ sess: &'tcx Session,
+ tcx: Option<TyCtxt<'tcx>>,
+ f: F,
+) -> A
+where
+ F: FnOnce(&dyn PrinterSupport) -> A,
+{
+ match *ppmode {
+ Normal | Expanded => {
+ let annotation = NoAnn { sess, tcx };
+ f(&annotation)
+ }
+
+ Identified | ExpandedIdentified => {
+ let annotation = IdentifiedAnnotation { sess, tcx };
+ f(&annotation)
+ }
+ ExpandedHygiene => {
+ let annotation = HygieneAnnotation { sess };
+ f(&annotation)
+ }
+ }
+}
+fn call_with_pp_support_hir<A, F>(ppmode: &PpHirMode, tcx: TyCtxt<'_>, f: F) -> A
+where
+ F: FnOnce(&dyn HirPrinterSupport<'_>, hir_map::Map<'_>) -> A,
+{
+ match *ppmode {
+ PpHirMode::Normal => {
+ let annotation = NoAnn { sess: tcx.sess, tcx: Some(tcx) };
+ f(&annotation, tcx.hir())
+ }
+
+ PpHirMode::Identified => {
+ let annotation = IdentifiedAnnotation { sess: tcx.sess, tcx: Some(tcx) };
+ f(&annotation, tcx.hir())
+ }
+ PpHirMode::Typed => {
+ abort_on_err(tcx.analysis(()), tcx.sess);
+
+ let annotation = TypedAnnotation { tcx, maybe_typeck_results: Cell::new(None) };
+ tcx.dep_graph.with_ignore(|| f(&annotation, tcx.hir()))
+ }
+ }
+}
+
+trait PrinterSupport: pprust::PpAnn {
+ /// Provides a uniform interface for re-extracting a reference to a
+ /// `Session` from a value that now owns it.
+ fn sess(&self) -> &Session;
+
+ /// Produces the pretty-print annotation object.
+ ///
+ /// (Rust does not yet support upcasting from a trait object to
+ /// an object for one of its supertraits.)
+ fn pp_ann(&self) -> &dyn pprust::PpAnn;
+}
+
+trait HirPrinterSupport<'hir>: pprust_hir::PpAnn {
+ /// Provides a uniform interface for re-extracting a reference to a
+ /// `Session` from a value that now owns it.
+ fn sess(&self) -> &Session;
+
+ /// Provides a uniform interface for re-extracting a reference to an
+ /// `hir_map::Map` from a value that now owns it.
+ fn hir_map(&self) -> Option<hir_map::Map<'hir>>;
+
+ /// Produces the pretty-print annotation object.
+ ///
+ /// (Rust does not yet support upcasting from a trait object to
+ /// an object for one of its supertraits.)
+ fn pp_ann(&self) -> &dyn pprust_hir::PpAnn;
+}
+
+struct NoAnn<'hir> {
+ sess: &'hir Session,
+ tcx: Option<TyCtxt<'hir>>,
+}
+
+impl<'hir> PrinterSupport for NoAnn<'hir> {
+ fn sess(&self) -> &Session {
+ self.sess
+ }
+
+ fn pp_ann(&self) -> &dyn pprust::PpAnn {
+ self
+ }
+}
+
+impl<'hir> HirPrinterSupport<'hir> for NoAnn<'hir> {
+ fn sess(&self) -> &Session {
+ self.sess
+ }
+
+ fn hir_map(&self) -> Option<hir_map::Map<'hir>> {
+ self.tcx.map(|tcx| tcx.hir())
+ }
+
+ fn pp_ann(&self) -> &dyn pprust_hir::PpAnn {
+ self
+ }
+}
+
+impl<'hir> pprust::PpAnn for NoAnn<'hir> {}
+impl<'hir> pprust_hir::PpAnn for NoAnn<'hir> {
+ fn nested(&self, state: &mut pprust_hir::State<'_>, nested: pprust_hir::Nested) {
+ if let Some(tcx) = self.tcx {
+ pprust_hir::PpAnn::nested(&(&tcx.hir() as &dyn hir::intravisit::Map<'_>), state, nested)
+ }
+ }
+}
+
+struct IdentifiedAnnotation<'hir> {
+ sess: &'hir Session,
+ tcx: Option<TyCtxt<'hir>>,
+}
+
+impl<'hir> PrinterSupport for IdentifiedAnnotation<'hir> {
+ fn sess(&self) -> &Session {
+ self.sess
+ }
+
+ fn pp_ann(&self) -> &dyn pprust::PpAnn {
+ self
+ }
+}
+
+impl<'hir> pprust::PpAnn for IdentifiedAnnotation<'hir> {
+ fn pre(&self, s: &mut pprust::State<'_>, node: pprust::AnnNode<'_>) {
+ if let pprust::AnnNode::Expr(_) = node {
+ s.popen();
+ }
+ }
+ fn post(&self, s: &mut pprust::State<'_>, node: pprust::AnnNode<'_>) {
+ match node {
+ pprust::AnnNode::Crate(_) | pprust::AnnNode::Ident(_) | pprust::AnnNode::Name(_) => {}
+
+ pprust::AnnNode::Item(item) => {
+ s.s.space();
+ s.synth_comment(item.id.to_string())
+ }
+ pprust::AnnNode::SubItem(id) => {
+ s.s.space();
+ s.synth_comment(id.to_string())
+ }
+ pprust::AnnNode::Block(blk) => {
+ s.s.space();
+ s.synth_comment(format!("block {}", blk.id))
+ }
+ pprust::AnnNode::Expr(expr) => {
+ s.s.space();
+ s.synth_comment(expr.id.to_string());
+ s.pclose()
+ }
+ pprust::AnnNode::Pat(pat) => {
+ s.s.space();
+ s.synth_comment(format!("pat {}", pat.id));
+ }
+ }
+ }
+}
+
+impl<'hir> HirPrinterSupport<'hir> for IdentifiedAnnotation<'hir> {
+ fn sess(&self) -> &Session {
+ self.sess
+ }
+
+ fn hir_map(&self) -> Option<hir_map::Map<'hir>> {
+ self.tcx.map(|tcx| tcx.hir())
+ }
+
+ fn pp_ann(&self) -> &dyn pprust_hir::PpAnn {
+ self
+ }
+}
+
+impl<'hir> pprust_hir::PpAnn for IdentifiedAnnotation<'hir> {
+ fn nested(&self, state: &mut pprust_hir::State<'_>, nested: pprust_hir::Nested) {
+ if let Some(ref tcx) = self.tcx {
+ pprust_hir::PpAnn::nested(&(&tcx.hir() as &dyn hir::intravisit::Map<'_>), state, nested)
+ }
+ }
+ fn pre(&self, s: &mut pprust_hir::State<'_>, node: pprust_hir::AnnNode<'_>) {
+ if let pprust_hir::AnnNode::Expr(_) = node {
+ s.popen();
+ }
+ }
+ fn post(&self, s: &mut pprust_hir::State<'_>, node: pprust_hir::AnnNode<'_>) {
+ match node {
+ pprust_hir::AnnNode::Name(_) => {}
+ pprust_hir::AnnNode::Item(item) => {
+ s.s.space();
+ s.synth_comment(format!("hir_id: {}", item.hir_id()));
+ }
+ pprust_hir::AnnNode::SubItem(id) => {
+ s.s.space();
+ s.synth_comment(id.to_string());
+ }
+ pprust_hir::AnnNode::Block(blk) => {
+ s.s.space();
+ s.synth_comment(format!("block hir_id: {}", blk.hir_id));
+ }
+ pprust_hir::AnnNode::Expr(expr) => {
+ s.s.space();
+ s.synth_comment(format!("expr hir_id: {}", expr.hir_id));
+ s.pclose();
+ }
+ pprust_hir::AnnNode::Pat(pat) => {
+ s.s.space();
+ s.synth_comment(format!("pat hir_id: {}", pat.hir_id));
+ }
+ pprust_hir::AnnNode::Arm(arm) => {
+ s.s.space();
+ s.synth_comment(format!("arm hir_id: {}", arm.hir_id));
+ }
+ }
+ }
+}
+
+struct HygieneAnnotation<'a> {
+ sess: &'a Session,
+}
+
+impl<'a> PrinterSupport for HygieneAnnotation<'a> {
+ fn sess(&self) -> &Session {
+ self.sess
+ }
+
+ fn pp_ann(&self) -> &dyn pprust::PpAnn {
+ self
+ }
+}
+
+impl<'a> pprust::PpAnn for HygieneAnnotation<'a> {
+ fn post(&self, s: &mut pprust::State<'_>, node: pprust::AnnNode<'_>) {
+ match node {
+ pprust::AnnNode::Ident(&Ident { name, span }) => {
+ s.s.space();
+ s.synth_comment(format!("{}{:?}", name.as_u32(), span.ctxt()))
+ }
+ pprust::AnnNode::Name(&name) => {
+ s.s.space();
+ s.synth_comment(name.as_u32().to_string())
+ }
+ pprust::AnnNode::Crate(_) => {
+ s.s.hardbreak();
+ let verbose = self.sess.verbose();
+ s.synth_comment(rustc_span::hygiene::debug_hygiene_data(verbose));
+ s.s.hardbreak_if_not_bol();
+ }
+ _ => {}
+ }
+ }
+}
+
+struct TypedAnnotation<'tcx> {
+ tcx: TyCtxt<'tcx>,
+ maybe_typeck_results: Cell<Option<&'tcx ty::TypeckResults<'tcx>>>,
+}
+
+impl<'tcx> HirPrinterSupport<'tcx> for TypedAnnotation<'tcx> {
+ fn sess(&self) -> &Session {
+ self.tcx.sess
+ }
+
+ fn hir_map(&self) -> Option<hir_map::Map<'tcx>> {
+ Some(self.tcx.hir())
+ }
+
+ fn pp_ann(&self) -> &dyn pprust_hir::PpAnn {
+ self
+ }
+}
+
+impl<'tcx> pprust_hir::PpAnn for TypedAnnotation<'tcx> {
+ fn nested(&self, state: &mut pprust_hir::State<'_>, nested: pprust_hir::Nested) {
+ let old_maybe_typeck_results = self.maybe_typeck_results.get();
+ if let pprust_hir::Nested::Body(id) = nested {
+ self.maybe_typeck_results.set(Some(self.tcx.typeck_body(id)));
+ }
+ let pp_ann = &(&self.tcx.hir() as &dyn hir::intravisit::Map<'_>);
+ pprust_hir::PpAnn::nested(pp_ann, state, nested);
+ self.maybe_typeck_results.set(old_maybe_typeck_results);
+ }
+ fn pre(&self, s: &mut pprust_hir::State<'_>, node: pprust_hir::AnnNode<'_>) {
+ if let pprust_hir::AnnNode::Expr(_) = node {
+ s.popen();
+ }
+ }
+ fn post(&self, s: &mut pprust_hir::State<'_>, node: pprust_hir::AnnNode<'_>) {
+ if let pprust_hir::AnnNode::Expr(expr) = node {
+ let typeck_results = self.maybe_typeck_results.get().or_else(|| {
+ self.tcx
+ .hir()
+ .maybe_body_owned_by(expr.hir_id.owner.def_id)
+ .map(|body_id| self.tcx.typeck_body(body_id))
+ });
+
+ if let Some(typeck_results) = typeck_results {
+ s.s.space();
+ s.s.word("as");
+ s.s.space();
+ s.s.word(typeck_results.expr_ty(expr).to_string());
+ }
+
+ s.pclose();
+ }
+ }
+}
+
+fn get_source(sess: &Session) -> (String, FileName) {
+ let src_name = sess.io.input.source_name();
+ let src = String::clone(
+ sess.source_map()
+ .get_source_file(&src_name)
+ .expect("get_source_file")
+ .src
+ .as_ref()
+ .expect("src"),
+ );
+ (src, src_name)
+}
+
+fn write_or_print(out: &str, sess: &Session) {
+ match &sess.io.output_file {
+ None => print!("{out}"),
+ Some(p) => {
+ if let Err(e) = std::fs::write(p, out) {
+ sess.emit_fatal(UnprettyDumpFail {
+ path: p.display().to_string(),
+ err: e.to_string(),
+ });
+ }
+ }
+ }
+}
+
+pub fn print_after_parsing(sess: &Session, krate: &ast::Crate, ppm: PpMode) {
+ let (src, src_name) = get_source(sess);
+
+ let out = match ppm {
+ Source(s) => {
+ // Silently ignores an identified node.
+ call_with_pp_support(&s, sess, None, move |annotation| {
+ debug!("pretty printing source code {:?}", s);
+ let sess = annotation.sess();
+ let parse = &sess.parse_sess;
+ pprust::print_crate(
+ sess.source_map(),
+ krate,
+ src_name,
+ src,
+ annotation.pp_ann(),
+ false,
+ parse.edition,
+ &sess.parse_sess.attr_id_generator,
+ )
+ })
+ }
+ AstTree(PpAstTreeMode::Normal) => {
+ debug!("pretty printing AST tree");
+ format!("{krate:#?}")
+ }
+ _ => unreachable!(),
+ };
+
+ write_or_print(&out, sess);
+}
+
+pub fn print_after_hir_lowering<'tcx>(tcx: TyCtxt<'tcx>, ppm: PpMode) {
+ if ppm.needs_analysis() {
+ abort_on_err(print_with_analysis(tcx, ppm), tcx.sess);
+ return;
+ }
+
+ let (src, src_name) = get_source(tcx.sess);
+
+ let out = match ppm {
+ Source(s) => {
+ // Silently ignores an identified node.
+ call_with_pp_support(&s, tcx.sess, Some(tcx), move |annotation| {
+ debug!("pretty printing source code {:?}", s);
+ let sess = annotation.sess();
+ let parse = &sess.parse_sess;
+ pprust::print_crate(
+ sess.source_map(),
+ &tcx.resolver_for_lowering(()).borrow().1,
+ src_name,
+ src,
+ annotation.pp_ann(),
+ true,
+ parse.edition,
+ &sess.parse_sess.attr_id_generator,
+ )
+ })
+ }
+
+ AstTree(PpAstTreeMode::Expanded) => {
+ debug!("pretty-printing expanded AST");
+ format!("{:#?}", tcx.resolver_for_lowering(()).borrow().1)
+ }
+
+ Hir(s) => call_with_pp_support_hir(&s, tcx, move |annotation, hir_map| {
+ debug!("pretty printing HIR {:?}", s);
+ let sess = annotation.sess();
+ let sm = sess.source_map();
+ let attrs = |id| hir_map.attrs(id);
+ pprust_hir::print_crate(
+ sm,
+ hir_map.root_module(),
+ src_name,
+ src,
+ &attrs,
+ annotation.pp_ann(),
+ )
+ }),
+
+ HirTree => {
+ call_with_pp_support_hir(&PpHirMode::Normal, tcx, move |_annotation, hir_map| {
+ debug!("pretty printing HIR tree");
+ format!("{:#?}", hir_map.krate())
+ })
+ }
+
+ _ => unreachable!(),
+ };
+
+ write_or_print(&out, tcx.sess);
+}
+
+// In an ideal world, this would be a public function called by the driver after
+// analysis is performed. However, we want to call `phase_3_run_analysis_passes`
+// with a different callback than the standard driver, so that isn't easy.
+// Instead, we call that function ourselves.
+fn print_with_analysis(tcx: TyCtxt<'_>, ppm: PpMode) -> Result<(), ErrorGuaranteed> {
+ tcx.analysis(())?;
+ let out = match ppm {
+ Mir => {
+ let mut out = Vec::new();
+ write_mir_pretty(tcx, None, &mut out).unwrap();
+ String::from_utf8(out).unwrap()
+ }
+
+ MirCFG => {
+ let mut out = Vec::new();
+ write_mir_graphviz(tcx, None, &mut out).unwrap();
+ String::from_utf8(out).unwrap()
+ }
+
+ ThirTree => {
+ let mut out = String::new();
+ abort_on_err(rustc_hir_analysis::check_crate(tcx), tcx.sess);
+ debug!("pretty printing THIR tree");
+ for did in tcx.hir().body_owners() {
+ let _ = writeln!(
+ out,
+ "{:?}:\n{}\n",
+ did,
+ tcx.thir_tree(ty::WithOptConstParam::unknown(did))
+ );
+ }
+ out
+ }
+
+ ThirFlat => {
+ let mut out = String::new();
+ abort_on_err(rustc_hir_analysis::check_crate(tcx), tcx.sess);
+ debug!("pretty printing THIR flat");
+ for did in tcx.hir().body_owners() {
+ let _ = writeln!(
+ out,
+ "{:?}:\n{}\n",
+ did,
+ tcx.thir_flat(ty::WithOptConstParam::unknown(did))
+ );
+ }
+ out
+ }
+
+ _ => unreachable!(),
+ };
+
+ write_or_print(&out, tcx.sess);
+
+ Ok(())
+}
--- /dev/null
+use rustc_macros::Diagnostic;
+
+#[derive(Diagnostic)]
+#[diag(driver_impl_rlink_unable_to_read)]
+pub(crate) struct RlinkUnableToRead {
+ pub err: std::io::Error,
+}
+
+#[derive(Diagnostic)]
+#[diag(driver_impl_rlink_wrong_file_type)]
+pub(crate) struct RLinkWrongFileType;
+
+#[derive(Diagnostic)]
+#[diag(driver_impl_rlink_empty_version_number)]
+pub(crate) struct RLinkEmptyVersionNumber;
+
+#[derive(Diagnostic)]
+#[diag(driver_impl_rlink_encoding_version_mismatch)]
+pub(crate) struct RLinkEncodingVersionMismatch {
+ pub version_array: String,
+ pub rlink_version: u32,
+}
+
+#[derive(Diagnostic)]
+#[diag(driver_impl_rlink_rustc_version_mismatch)]
+pub(crate) struct RLinkRustcVersionMismatch<'a> {
+ pub rustc_version: String,
+ pub current_version: &'a str,
+}
+
+#[derive(Diagnostic)]
+#[diag(driver_impl_rlink_no_a_file)]
+pub(crate) struct RlinkNotAFile;
+
+#[derive(Diagnostic)]
+#[diag(driver_impl_unpretty_dump_fail)]
+pub(crate) struct UnprettyDumpFail {
+ pub path: String,
+ pub err: String,
+}
+
+#[derive(Diagnostic)]
+#[diag(driver_impl_ice)]
+pub(crate) struct Ice;
+
+#[derive(Diagnostic)]
+#[diag(driver_impl_ice_bug_report)]
+pub(crate) struct IceBugReport<'a> {
+ pub bug_report_url: &'a str,
+}
+
+#[derive(Diagnostic)]
+#[diag(driver_impl_ice_version)]
+pub(crate) struct IceVersion<'a> {
+ pub version: &'a str,
+ pub triple: &'a str,
+}
+
+#[derive(Diagnostic)]
+#[diag(driver_impl_ice_flags)]
+pub(crate) struct IceFlags {
+ pub flags: String,
+}
+
+#[derive(Diagnostic)]
+#[diag(driver_impl_ice_exclude_cargo_defaults)]
+pub(crate) struct IceExcludeCargoDefaults;
borrowck_var_here_captured = variable captured here
-borrowck_closure_inferred_mut = inferred to be a `FnMut` closure
+borrowck_closure_inferred_mut = inferred to be a `FnMut` closure
borrowck_returned_closure_escaped =
returns a closure that contains a reference to a captured variable, which then escapes the closure body
-driver_rlink_unable_to_read = failed to read rlink file: `{$err}`
+driver_impl_rlink_unable_to_read = failed to read rlink file: `{$err}`
-driver_rlink_wrong_file_type = The input does not look like a .rlink file
+driver_impl_rlink_wrong_file_type = The input does not look like a .rlink file
-driver_rlink_empty_version_number = The input does not contain version number
+driver_impl_rlink_empty_version_number = The input does not contain version number
-driver_rlink_encoding_version_mismatch = .rlink file was produced with encoding version `{$version_array}`, but the current version is `{$rlink_version}`
+driver_impl_rlink_encoding_version_mismatch = .rlink file was produced with encoding version `{$version_array}`, but the current version is `{$rlink_version}`
-driver_rlink_rustc_version_mismatch = .rlink file was produced by rustc version `{$rustc_version}`, but the current version is `{$current_version}`
+driver_impl_rlink_rustc_version_mismatch = .rlink file was produced by rustc version `{$rustc_version}`, but the current version is `{$current_version}`
-driver_rlink_no_a_file = rlink must be a file
+driver_impl_rlink_no_a_file = rlink must be a file
-driver_unpretty_dump_fail = pretty-print failed to write `{$path}` due to error `{$err}`
+driver_impl_unpretty_dump_fail = pretty-print failed to write `{$path}` due to error `{$err}`
-driver_ice = the compiler unexpectedly panicked. this is a bug.
-driver_ice_bug_report = we would appreciate a bug report: {$bug_report_url}
-driver_ice_version = rustc {$version} running on {$triple}
-driver_ice_flags = compiler flags: {$flags}
-driver_ice_exclude_cargo_defaults = some of the compiler flags provided by cargo are hidden
+driver_impl_ice = the compiler unexpectedly panicked. this is a bug.
+driver_impl_ice_bug_report = we would appreciate a bug report: {$bug_report_url}
+driver_impl_ice_version = rustc {$version} running on {$triple}
+driver_impl_ice_flags = compiler flags: {$flags}
+driver_impl_ice_exclude_cargo_defaults = some of the compiler flags provided by cargo are hidden
hir_typeck_help_set_edition_cargo = set `edition = "{$edition}"` in `Cargo.toml`
hir_typeck_help_set_edition_standalone = pass `--edition {$edition}` to `rustc`
hir_typeck_note_edition_guide = for more on editions, read https://doc.rust-lang.org/edition-guide
+
+hir_typeck_convert_to_str = try converting the passed type into a `&str`
.note = generators are lazy and do nothing unless resumed
lint_unused_def = unused {$pre}`{$def}`{$post} that must be used
+ .suggestion = use `let _ = ...` to ignore the resulting value
lint_path_statement_drop = path statement drops value
.suggestion = use `drop` to clarify the intent
.use_in_not_of = try using `in` here instead
.add_in = try adding `in` here
+parse_missing_expression_in_for_loop = missing expression to iterate on in `for` loop
+ .suggestion = try adding an expression to the `for` loop
+
parse_missing_comma_after_match_arm = expected `,` following `match` arm
.suggestion = missing a comma here to end this `match` arm
.suggestion_remove_eq = use `..=` instead
.note = inclusive ranges end with a single equals sign (`..=`)
-parse_inclusive_range_match_arrow = unexpected `=>` after open range
- .suggestion_add_space = add a space between the pattern and `=>`
+parse_inclusive_range_match_arrow = unexpected `>` after inclusive range
+ .label = this is parsed as an inclusive range `..=`
+ .suggestion = add a space between the pattern and `=>`
parse_inclusive_range_no_end = inclusive range with no end
.suggestion_open_range = use `..` instead
parse_unexpected_self_in_generic_parameters = unexpected keyword `Self` in generic parameters
.note = you cannot use `Self` as a generic parameter because it is reserved for associated items
+parse_unexpected_default_value_for_lifetime_in_generic_parameters = unexpected default lifetime parameter
+ .label = lifetime parameters cannot have default values
+
parse_multiple_where_clauses = cannot define duplicate `where` clauses on an item
.label = previous `where` clause starts here
.suggestion = consider joining the two `where` clauses into one
parse_enum_pattern_instead_of_identifier = expected identifier, found enum pattern
-parse_dot_dot_dot_for_remaining_fields = expected field pattern, found `...`
- .suggestion = to omit remaining fields, use one fewer `.`
+parse_dot_dot_dot_for_remaining_fields = expected field pattern, found `{$token_str}`
+ .suggestion = to omit remaining fields, use `..`
parse_expected_comma_after_pattern_field = expected `,`
#![deny(rustc::untranslatable_diagnostic)]
-use crate::errors::{
- ArgumentNotAttributes, AttrNoArguments, AttributeMetaItem, AttributeSingleWord,
- AttributesWrongForm, CannotBeNameOfMacro, ExpectedCommaInList, HelperAttributeNameInvalid,
- MacroBodyStability, MacroConstStability, NotAMetaItem, OnlyOneArgument, OnlyOneWord,
- ResolveRelativePath, TakesNoArguments, TraceMacro,
-};
+use crate::errors;
use crate::expand::{self, AstFragment, Invocation};
use crate::module::DirOwnership;
.unwrap_or_else(|| (None, helper_attrs));
let (stability, const_stability, body_stability) = attr::find_stability(&sess, attrs, span);
if let Some((_, sp)) = const_stability {
- sess.emit_err(MacroConstStability {
+ sess.emit_err(errors::MacroConstStability {
span: sp,
head_span: sess.source_map().guess_head_span(span),
});
}
if let Some((_, sp)) = body_stability {
- sess.emit_err(MacroBodyStability {
+ sess.emit_err(errors::MacroBodyStability {
span: sp,
head_span: sess.source_map().guess_head_span(span),
});
}
pub fn trace_macros_diag(&mut self) {
for (span, notes) in self.expansions.iter() {
- let mut db = self.sess.parse_sess.create_note(TraceMacro { span: *span });
+ let mut db = self.sess.parse_sess.create_note(errors::TraceMacro { span: *span });
for note in notes {
db.note(note);
}
.expect("attempting to resolve a file path in an external file"),
FileName::DocTest(path, _) => path,
other => {
- return Err(ResolveRelativePath {
+ return Err(errors::ResolveRelativePath {
span,
path: parse_sess.source_map().filename_for_diagnostics(&other).to_string(),
}
/// done as rarely as possible).
pub fn check_zero_tts(cx: &ExtCtxt<'_>, span: Span, tts: TokenStream, name: &str) {
if !tts.is_empty() {
- cx.emit_err(TakesNoArguments { span, name });
+ cx.emit_err(errors::TakesNoArguments { span, name });
}
}
) -> Option<Symbol> {
let mut p = cx.new_parser_from_tts(tts);
if p.token == token::Eof {
- cx.emit_err(OnlyOneArgument { span, name });
+ cx.emit_err(errors::OnlyOneArgument { span, name });
return None;
}
let ret = parse_expr(&mut p)?;
let _ = p.eat(&token::Comma);
if p.token != token::Eof {
- cx.emit_err(OnlyOneArgument { span, name });
+ cx.emit_err(errors::OnlyOneArgument { span, name });
}
expr_to_string(cx, ret, "argument must be a string literal").map(|(s, _)| s)
}
continue;
}
if p.token != token::Eof {
- cx.emit_err(ExpectedCommaInList { span: p.token.span });
+ cx.emit_err(errors::ExpectedCommaInList { span: p.token.span });
return None;
}
}
// `#[proc_macro_derive(Foo, attributes(A, ..))]`
let list = attr.meta_item_list()?;
if list.len() != 1 && list.len() != 2 {
- diag.emit_err(AttrNoArguments { span: attr.span });
+ diag.emit_err(errors::AttrNoArguments { span: attr.span });
return None;
}
let Some(trait_attr) = list[0].meta_item() else {
- diag.emit_err(NotAMetaItem {span: list[0].span()});
+ diag.emit_err(errors::NotAMetaItem {span: list[0].span()});
return None;
};
let trait_ident = match trait_attr.ident() {
Some(trait_ident) if trait_attr.is_word() => trait_ident,
_ => {
- diag.emit_err(OnlyOneWord { span: trait_attr.span });
+ diag.emit_err(errors::OnlyOneWord { span: trait_attr.span });
return None;
}
};
if !trait_ident.name.can_be_raw() {
- diag.emit_err(CannotBeNameOfMacro { span: trait_attr.span, trait_ident, macro_type });
+ diag.emit_err(errors::CannotBeNameOfMacro {
+ span: trait_attr.span,
+ trait_ident,
+ macro_type,
+ });
}
let attributes_attr = list.get(1);
let proc_attrs: Vec<_> = if let Some(attr) = attributes_attr {
if !attr.has_name(sym::attributes) {
- diag.emit_err(ArgumentNotAttributes { span: attr.span() });
+ diag.emit_err(errors::ArgumentNotAttributes { span: attr.span() });
}
attr.meta_item_list()
.unwrap_or_else(|| {
- diag.emit_err(AttributesWrongForm { span: attr.span() });
+ diag.emit_err(errors::AttributesWrongForm { span: attr.span() });
&[]
})
.iter()
.filter_map(|attr| {
let Some(attr) = attr.meta_item() else {
- diag.emit_err(AttributeMetaItem { span: attr.span() });
+ diag.emit_err(errors::AttributeMetaItem { span: attr.span() });
return None;
};
let ident = match attr.ident() {
Some(ident) if attr.is_word() => ident,
_ => {
- diag.emit_err(AttributeSingleWord { span: attr.span });
+ diag.emit_err(errors::AttributeSingleWord { span: attr.span });
return None;
}
};
if !ident.name.can_be_raw() {
- diag.emit_err(HelperAttributeNameInvalid { span: attr.span, name: ident });
+ diag.emit_err(errors::HelperAttributeNameInvalid {
+ span: attr.span,
+ name: ident,
+ });
}
Some(ident.name)
self.expr(sp, ast::ExprKind::AddrOf(ast::BorrowKind::Ref, ast::Mutability::Not, e))
}
+ pub fn expr_paren(&self, sp: Span, e: P<ast::Expr>) -> P<ast::Expr> {
+ self.expr(sp, ast::ExprKind::Paren(e))
+ }
+
pub fn expr_call(
&self,
span: Span,
use rustc_ast::token::{self, Delimiter};
-use rustc_ast::tokenstream::{CursorRef, TokenStream, TokenTree};
+use rustc_ast::tokenstream::{RefTokenTreeCursor, TokenStream, TokenTree};
use rustc_ast::{LitIntType, LitKind};
use rustc_ast_pretty::pprust;
use rustc_errors::{Applicability, PResult};
// Checks if there are any remaining tokens. For example, `${ignore(ident ... a b c ...)}`
fn check_trailing_token<'sess>(
- iter: &mut CursorRef<'_>,
+ iter: &mut RefTokenTreeCursor<'_>,
sess: &'sess ParseSess,
) -> PResult<'sess, ()> {
if let Some(tt) = iter.next() {
/// Parse a meta-variable `count` expression: `count(ident[, depth])`
fn parse_count<'sess>(
- iter: &mut CursorRef<'_>,
+ iter: &mut RefTokenTreeCursor<'_>,
sess: &'sess ParseSess,
span: Span,
) -> PResult<'sess, MetaVarExpr> {
/// Parses the depth used by index(depth) and length(depth).
fn parse_depth<'sess>(
- iter: &mut CursorRef<'_>,
+ iter: &mut RefTokenTreeCursor<'_>,
sess: &'sess ParseSess,
span: Span,
) -> PResult<'sess, usize> {
/// Parses an generic ident
fn parse_ident<'sess>(
- iter: &mut CursorRef<'_>,
+ iter: &mut RefTokenTreeCursor<'_>,
sess: &'sess ParseSess,
span: Span,
) -> PResult<'sess, Ident> {
/// Tries to move the iterator forward returning `true` if there is a comma. If not, then the
/// iterator is not modified and the result is `false`.
-fn try_eat_comma(iter: &mut CursorRef<'_>) -> bool {
+fn try_eat_comma(iter: &mut RefTokenTreeCursor<'_>) -> bool {
if let Some(TokenTree::Token(token::Token { kind: token::Comma, .. }, _)) = iter.look_ahead(0) {
let _ = iter.next();
return true;
rustc_attr = { path = "../rustc_attr" }
rustc_data_structures = { path = "../rustc_data_structures" }
rustc_errors = { path = "../rustc_errors" }
-rustc_graphviz = { path = "../rustc_graphviz" }
rustc_hir = { path = "../rustc_hir" }
-rustc_hir_pretty = { path = "../rustc_hir_pretty" }
rustc_target = { path = "../rustc_target" }
rustc_session = { path = "../rustc_session" }
smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
rustc_infer = { path = "../rustc_infer" }
rustc_trait_selection = { path = "../rustc_trait_selection" }
rustc_lint = { path = "../rustc_lint" }
-rustc_serialize = { path = "../rustc_serialize" }
rustc_type_ir = { path = "../rustc_type_ir" }
rustc_feature = { path = "../rustc_feature" }
if r.is_erased() { tcx.lifetimes.re_static } else { r }
});
let span = ast_ty.span;
- tcx.sess.emit_err(TypeofReservedKeywordUsed {
- span,
- ty,
- opt_sugg: Some((span, Applicability::MachineApplicable))
- .filter(|_| ty.is_suggestable(tcx, false)),
- });
+ let (ty, opt_sugg) = if let Some(ty) = ty.make_suggestable(tcx, false) {
+ (ty, Some((span, Applicability::MachineApplicable)))
+ } else {
+ (ty, None)
+ };
+ tcx.sess.emit_err(TypeofReservedKeywordUsed { span, ty, opt_sugg });
ty
}
use rustc_middle::ty::subst::InternalSubsts;
use rustc_middle::ty::util::IgnoreRegions;
use rustc_middle::ty::{
- self, ImplPolarity, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor,
+ self, AliasKind, ImplPolarity, Ty, TyCtxt, TypeSuperVisitable, TypeVisitable, TypeVisitor,
};
use rustc_session::lint;
use rustc_span::def_id::{DefId, LocalDefId};
// struct B { }
// impl Foo for A { }
// impl Foo for B { }
- // impl !Send for (A, B) { }
+ // impl !Foo for (A, B) { }
// ```
//
// This final impl is legal according to the orphan
tcx.trait_is_auto(trait_def_id)
);
- if tcx.trait_is_auto(trait_def_id) && !trait_def_id.is_local() {
+ if tcx.trait_is_auto(trait_def_id) {
let self_ty = trait_ref.self_ty();
- let opt_self_def_id = match *self_ty.kind() {
- ty::Adt(self_def, _) => Some(self_def.did()),
- ty::Foreign(did) => Some(did),
- _ => None,
- };
- let msg = match opt_self_def_id {
- // We only want to permit nominal types, but not *all* nominal types.
- // They must be local to the current crate, so that people
- // can't do `unsafe impl Send for Rc<SomethingLocal>` or
- // `impl !Send for Box<SomethingLocalAndSend>`.
- Some(self_def_id) => {
- if self_def_id.is_local() {
- None
+ // If the impl is in the same crate as the auto-trait, almost anything
+ // goes.
+ //
+ // impl MyAuto for Rc<Something> {} // okay
+ // impl<T> !MyAuto for *const T {} // okay
+ // impl<T> MyAuto for T {} // okay
+ //
+ // But there is one important exception: implementing for a trait object
+ // is not allowed.
+ //
+ // impl MyAuto for dyn Trait {} // NOT OKAY
+ // impl<T: ?Sized> MyAuto for T {} // NOT OKAY
+ //
+ // With this restriction, it's guaranteed that an auto-trait is
+ // implemented for a trait object if and only if the auto-trait is one
+ // of the trait object's trait bounds (or a supertrait of a bound). In
+ // other words `dyn Trait + AutoTrait` always implements AutoTrait,
+ // while `dyn Trait` never implements AutoTrait.
+ //
+ // This is necessary in order for autotrait bounds on methods of trait
+ // objects to be sound.
+ //
+ // auto trait AutoTrait {}
+ //
+ // trait ObjectSafeTrait {
+ // fn f(&self) where Self: AutoTrait;
+ // }
+ //
+ // We can allow f to be called on `dyn ObjectSafeTrait + AutoTrait`.
+ //
+ // If we didn't deny `impl AutoTrait for dyn Trait`, it would be unsound
+ // for the ObjectSafeTrait shown above to be object safe because someone
+ // could take some type implementing ObjectSafeTrait but not AutoTrait,
+ // unsize it to `dyn ObjectSafeTrait`, and call .f() which has no
+ // concrete implementation (issue #50781).
+ enum LocalImpl {
+ Allow,
+ Disallow { problematic_kind: &'static str },
+ }
+
+ // If the auto-trait is from a dependency, it must only be getting
+ // implemented for a nominal type, and specifically one local to the
+ // current crate.
+ //
+ // impl<T> Sync for MyStruct<T> {} // okay
+ //
+ // impl Sync for Rc<MyStruct> {} // NOT OKAY
+ enum NonlocalImpl {
+ Allow,
+ DisallowBecauseNonlocal,
+ DisallowOther,
+ }
+
+ // Exhaustive match considering that this logic is essential for
+ // soundness.
+ let (local_impl, nonlocal_impl) = match self_ty.kind() {
+ // struct Struct<T>;
+ // impl AutoTrait for Struct<Foo> {}
+ ty::Adt(self_def, _) => (
+ LocalImpl::Allow,
+ if self_def.did().is_local() {
+ NonlocalImpl::Allow
+ } else {
+ NonlocalImpl::DisallowBecauseNonlocal
+ },
+ ),
+
+ // extern { type OpaqueType; }
+ // impl AutoTrait for OpaqueType {}
+ ty::Foreign(did) => (
+ LocalImpl::Allow,
+ if did.is_local() {
+ NonlocalImpl::Allow
} else {
- Some((
- format!(
- "cross-crate traits with a default impl, like `{}`, \
- can only be implemented for a struct/enum type \
- defined in the current crate",
- tcx.def_path_str(trait_def_id)
- ),
- "can't implement cross-crate trait for type in another crate",
- ))
+ NonlocalImpl::DisallowBecauseNonlocal
+ },
+ ),
+
+ // impl AutoTrait for dyn Trait {}
+ ty::Dynamic(..) => (
+ LocalImpl::Disallow { problematic_kind: "trait object" },
+ NonlocalImpl::DisallowOther,
+ ),
+
+ // impl<T> AutoTrait for T {}
+ // impl<T: ?Sized> AutoTrait for T {}
+ ty::Param(..) => (
+ if self_ty.is_sized(tcx, tcx.param_env(def_id)) {
+ LocalImpl::Allow
+ } else {
+ LocalImpl::Disallow { problematic_kind: "generic type" }
+ },
+ NonlocalImpl::DisallowOther,
+ ),
+
+ // trait Id { type This: ?Sized; }
+ // impl<T: ?Sized> Id for T {
+ // type This = T;
+ // }
+ // impl<T: ?Sized> AutoTrait for <T as Id>::This {}
+ ty::Alias(AliasKind::Projection, _) => (
+ LocalImpl::Disallow { problematic_kind: "associated type" },
+ NonlocalImpl::DisallowOther,
+ ),
+
+ // type Opaque = impl Trait;
+ // impl AutoTrait for Opaque {}
+ ty::Alias(AliasKind::Opaque, _) => (
+ LocalImpl::Disallow { problematic_kind: "opaque type" },
+ NonlocalImpl::DisallowOther,
+ ),
+
+ ty::Bool
+ | ty::Char
+ | ty::Int(..)
+ | ty::Uint(..)
+ | ty::Float(..)
+ | ty::Str
+ | ty::Array(..)
+ | ty::Slice(..)
+ | ty::RawPtr(..)
+ | ty::Ref(..)
+ | ty::FnDef(..)
+ | ty::FnPtr(..)
+ | ty::Never
+ | ty::Tuple(..) => (LocalImpl::Allow, NonlocalImpl::DisallowOther),
+
+ ty::Closure(..)
+ | ty::Generator(..)
+ | ty::GeneratorWitness(..)
+ | ty::GeneratorWitnessMIR(..)
+ | ty::Bound(..)
+ | ty::Placeholder(..)
+ | ty::Infer(..) => span_bug!(sp, "weird self type for autotrait impl"),
+
+ ty::Error(..) => (LocalImpl::Allow, NonlocalImpl::Allow),
+ };
+
+ if trait_def_id.is_local() {
+ match local_impl {
+ LocalImpl::Allow => {}
+ LocalImpl::Disallow { problematic_kind } => {
+ let msg = format!(
+ "traits with a default impl, like `{trait}`, \
+ cannot be implemented for {problematic_kind} `{self_ty}`",
+ trait = tcx.def_path_str(trait_def_id),
+ );
+ let label = format!(
+ "a trait object implements `{trait}` if and only if `{trait}` \
+ is one of the trait object's trait bounds",
+ trait = tcx.def_path_str(trait_def_id),
+ );
+ let reported =
+ struct_span_err!(tcx.sess, sp, E0321, "{}", msg).note(label).emit();
+ return Err(reported);
}
}
- _ => Some((
- format!(
- "cross-crate traits with a default impl, like `{}`, can \
+ } else {
+ if let Some((msg, label)) = match nonlocal_impl {
+ NonlocalImpl::Allow => None,
+ NonlocalImpl::DisallowBecauseNonlocal => Some((
+ format!(
+ "cross-crate traits with a default impl, like `{}`, \
+ can only be implemented for a struct/enum type \
+ defined in the current crate",
+ tcx.def_path_str(trait_def_id)
+ ),
+ "can't implement cross-crate trait for type in another crate",
+ )),
+ NonlocalImpl::DisallowOther => Some((
+ format!(
+ "cross-crate traits with a default impl, like `{}`, can \
only be implemented for a struct/enum type, not `{}`",
- tcx.def_path_str(trait_def_id),
- self_ty
- ),
- "can't implement cross-crate trait with a default impl for \
- non-struct/enum type",
- )),
- };
-
- if let Some((msg, label)) = msg {
- let reported =
- struct_span_err!(tcx.sess, sp, E0321, "{}", msg).span_label(sp, label).emit();
- return Err(reported);
+ tcx.def_path_str(trait_def_id),
+ self_ty
+ ),
+ "can't implement cross-crate trait with a default impl for \
+ non-struct/enum type",
+ )),
+ } {
+ let reported =
+ struct_span_err!(tcx.sess, sp, E0321, "{}", msg).span_label(sp, label).emit();
+ return Err(reported);
+ }
}
}
visitor.visit_ty(ty);
let mut diag = bad_placeholder(tcx, visitor.0, "return type");
let ret_ty = fn_sig.output();
- if ret_ty.is_suggestable(tcx, false) {
+ if let Some(ret_ty) = ret_ty.make_suggestable(tcx, false) {
diag.span_suggestion(
ty.span,
"replace with the correct return type",
ret_ty,
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, false))
- {
- diag.span_suggestion(
- ty.span,
- "replace with the correct return type",
- fn_sig,
- Applicability::MachineApplicable,
- );
- }
+ } else if matches!(ret_ty.kind(), ty::FnDef(..))
+ && let Some(fn_sig) = ret_ty.fn_sig(tcx).make_suggestable(tcx, false)
+ {
+ diag.span_suggestion(
+ ty.span,
+ "replace with the correct return type",
+ fn_sig,
+ Applicability::MachineApplicable,
+ );
} else if let Some(sugg) = suggest_impl_trait(tcx, ret_ty, ty.span, hir_id, def_id) {
diag.span_suggestion(
ty.span,
let trait_name = tcx.item_name(trait_def_id);
let args_tuple = substs.type_at(1);
let ty::Tuple(types) = *args_tuple.kind() else { return None; };
- if !types.is_suggestable(tcx, false) {
- return None;
- }
+ let types = types.make_suggestable(tcx, false)?;
let maybe_ret =
if item_ty.is_unit() { String::new() } else { format!(" -> {item_ty}") };
Some(format!(
// FIXME(compiler-errors): We may benefit from resolving regions here.
if ocx.select_where_possible().is_empty()
&& let item_ty = infcx.resolve_vars_if_possible(item_ty)
- && item_ty.is_suggestable(tcx, false)
+ && let Some(item_ty) = item_ty.make_suggestable(tcx, false)
&& let Some(sugg) = formatter(tcx, infcx.resolve_vars_if_possible(substs), trait_def_id, assoc_item_def_id, item_ty)
{
return Some(sugg);
use rustc_middle::ty::print::with_forced_trimmed_paths;
use rustc_middle::ty::subst::InternalSubsts;
use rustc_middle::ty::util::IntTypeExt;
-use rustc_middle::ty::{self, DefIdTree, Ty, TyCtxt, TypeFolder, TypeSuperFoldable, TypeVisitable};
+use rustc_middle::ty::{
+ self, DefIdTree, IsSuggestable, Ty, TyCtxt, TypeFolder, TypeSuperFoldable, TypeVisitable,
+};
use rustc_span::symbol::Ident;
use rustc_span::{Span, DUMMY_SP};
) -> Ty<'a> {
// Attempts to make the type nameable by turning FnDefs into FnPtrs.
struct MakeNameable<'tcx> {
- success: bool,
tcx: TyCtxt<'tcx>,
}
- impl<'tcx> MakeNameable<'tcx> {
- fn new(tcx: TyCtxt<'tcx>) -> Self {
- MakeNameable { success: true, tcx }
- }
- }
-
impl<'tcx> TypeFolder<'tcx> for MakeNameable<'tcx> {
fn tcx(&self) -> TyCtxt<'tcx> {
self.tcx
}
fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
- if !self.success {
- return ty;
- }
-
- match ty.kind() {
+ let ty = match *ty.kind() {
ty::FnDef(def_id, substs) => {
- self.tcx.mk_fn_ptr(self.tcx.fn_sig(*def_id).subst(self.tcx, substs))
+ self.tcx.mk_fn_ptr(self.tcx.fn_sig(def_id).subst(self.tcx, substs))
}
- // FIXME: non-capturing closures should also suggest a function pointer
- ty::Closure(..) | ty::Generator(..) => {
- self.success = false;
- ty
- }
- _ => ty.super_fold_with(self),
- }
+ _ => ty,
+ };
+
+ ty.super_fold_with(self)
}
}
suggestions.clear();
}
- // Suggesting unnameable types won't help.
- let mut mk_nameable = MakeNameable::new(tcx);
- let ty = mk_nameable.fold_ty(ty);
- let sugg_ty = if mk_nameable.success { Some(ty) } else { None };
- if let Some(sugg_ty) = sugg_ty {
+ if let Some(ty) = ty.make_suggestable(tcx, false) {
err.span_suggestion(
span,
&format!("provide a type for the {item}", item = kind),
- format!("{colon} {sugg_ty}"),
+ format!("{colon} {ty}"),
Applicability::MachineApplicable,
);
} else {
let mut diag = bad_placeholder(tcx, vec![span], kind);
if !ty.references_error() {
- let mut mk_nameable = MakeNameable::new(tcx);
- let ty = mk_nameable.fold_ty(ty);
- let sugg_ty = if mk_nameable.success { Some(ty) } else { None };
- if let Some(sugg_ty) = sugg_ty {
+ if let Some(ty) = ty.make_suggestable(tcx, false) {
diag.span_suggestion(
span,
"replace with the correct type",
- sugg_ty,
- Applicability::MaybeIncorrect,
+ ty,
+ Applicability::MachineApplicable,
);
} else {
with_forced_trimmed_paths!(diag.span_note(
|| self.suggest_clone_for_ref(err, expr, expr_ty, expected)
|| self.suggest_into(err, expr, expr_ty, expected)
|| self.suggest_floating_point_literal(err, expr, expected)
+ || self.suggest_null_ptr_for_literal_zero_given_to_ptr_arg(err, expr, expected)
|| self.note_result_coercion(err, expr, expected, expr_ty);
if !suggested {
self.point_at_expr_source_of_inferred_type(err, expr, expr_ty, expected, expr.span);
None if let Some(e) = self.tainted_by_errors() => self.tcx.ty_error_with_guaranteed(e),
None => {
bug!(
- "no type for node {}: {} in fcx {}",
- id,
+ "no type for node {} in fcx {}",
self.tcx.hir().node_to_string(id),
self.tag()
);
kind: hir::ImplItemKind::Fn(ref sig, ..),
..
}) => Some((&sig.decl, ident, false)),
+ Node::Expr(&hir::Expr {
+ hir_id,
+ kind: hir::ExprKind::Closure(..),
+ ..
+ }) if let Some(Node::Expr(&hir::Expr {
+ hir_id,
+ kind: hir::ExprKind::Call(..),
+ ..
+ })) = self.tcx.hir().find_parent(hir_id) &&
+ let Some(Node::Item(&hir::Item {
+ ident,
+ kind: hir::ItemKind::Fn(ref sig, ..),
+ ..
+ })) = self.tcx.hir().find_parent(hir_id) => {
+ Some((&sig.decl, ident, ident.name != sym::main))
+ },
_ => None,
}
}
--- /dev/null
+use crate::FnCtxt;
+use rustc_hir as hir;
+use rustc_hir::def::Res;
+use rustc_middle::ty::{self, DefIdTree, Ty};
+use rustc_trait_selection::traits;
+
+impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
+ /**
+ * Recursively searches for the most-specific blamable expression.
+ * For example, if you have a chain of constraints like:
+ * - want `Vec<i32>: Copy`
+ * - because `Option<Vec<i32>>: Copy` needs `Vec<i32>: Copy` because `impl <T: Copy> Copy for Option<T>`
+ * - because `(Option<Vec<i32>, bool)` needs `Option<Vec<i32>>: Copy` because `impl <A: Copy, B: Copy> Copy for (A, B)`
+ * then if you pass in `(Some(vec![1, 2, 3]), false)`, this helper `point_at_specific_expr_if_possible`
+ * will find the expression `vec![1, 2, 3]` as the "most blameable" reason for this missing constraint.
+ *
+ * This function only updates the error span.
+ */
+ pub fn blame_specific_expr_if_possible(
+ &self,
+ error: &mut traits::FulfillmentError<'tcx>,
+ expr: &'tcx hir::Expr<'tcx>,
+ ) {
+ // Whether it succeeded or failed, it likely made some amount of progress.
+ // In the very worst case, it's just the same `expr` we originally passed in.
+ let expr = match self.blame_specific_expr_if_possible_for_obligation_cause_code(
+ &error.obligation.cause.code(),
+ expr,
+ ) {
+ Ok(expr) => expr,
+ Err(expr) => expr,
+ };
+
+ // Either way, use this expression to update the error span.
+ // If it doesn't overlap the existing span at all, use the original span.
+ // FIXME: It would possibly be better to do this more continuously, at each level...
+ error.obligation.cause.span = expr
+ .span
+ .find_ancestor_in_same_ctxt(error.obligation.cause.span)
+ .unwrap_or(error.obligation.cause.span);
+ }
+
+ fn blame_specific_expr_if_possible_for_obligation_cause_code(
+ &self,
+ obligation_cause_code: &traits::ObligationCauseCode<'tcx>,
+ expr: &'tcx hir::Expr<'tcx>,
+ ) -> Result<&'tcx hir::Expr<'tcx>, &'tcx hir::Expr<'tcx>> {
+ match obligation_cause_code {
+ traits::ObligationCauseCode::ExprBindingObligation(_, _, _, _) => {
+ // This is the "root"; we assume that the `expr` is already pointing here.
+ // Therefore, we return `Ok` so that this `expr` can be refined further.
+ Ok(expr)
+ }
+ traits::ObligationCauseCode::ImplDerivedObligation(impl_derived) => self
+ .blame_specific_expr_if_possible_for_derived_predicate_obligation(
+ impl_derived,
+ expr,
+ ),
+ _ => {
+ // We don't recognize this kind of constraint, so we cannot refine the expression
+ // any further.
+ Err(expr)
+ }
+ }
+ }
+
+ /// We want to achieve the error span in the following example:
+ ///
+ /// ```ignore (just for demonstration)
+ /// struct Burrito<Filling> {
+ /// filling: Filling,
+ /// }
+ /// impl <Filling: Delicious> Delicious for Burrito<Filling> {}
+ /// fn eat_delicious_food<Food: Delicious>(_food: Food) {}
+ ///
+ /// fn will_type_error() {
+ /// eat_delicious_food(Burrito { filling: Kale });
+ /// } // ^--- The trait bound `Kale: Delicious`
+ /// // is not satisfied
+ /// ```
+ ///
+ /// Without calling this function, the error span will cover the entire argument expression.
+ ///
+ /// Before we do any of this logic, we recursively call `point_at_specific_expr_if_possible` on the parent
+ /// obligation. Hence we refine the `expr` "outwards-in" and bail at the first kind of expression/impl we don't recognize.
+ ///
+ /// This function returns a `Result<&Expr, &Expr>` - either way, it returns the `Expr` whose span should be
+ /// reported as an error. If it is `Ok`, then it means it refined successfull. If it is `Err`, then it may be
+ /// only a partial success - but it cannot be refined even further.
+ fn blame_specific_expr_if_possible_for_derived_predicate_obligation(
+ &self,
+ obligation: &traits::ImplDerivedObligationCause<'tcx>,
+ expr: &'tcx hir::Expr<'tcx>,
+ ) -> Result<&'tcx hir::Expr<'tcx>, &'tcx hir::Expr<'tcx>> {
+ // First, we attempt to refine the `expr` for our span using the parent obligation.
+ // If this cannot be done, then we are already stuck, so we stop early (hence the use
+ // of the `?` try operator here).
+ let expr = self.blame_specific_expr_if_possible_for_obligation_cause_code(
+ &*obligation.derived.parent_code,
+ expr,
+ )?;
+
+ // This is the "trait" (meaning, the predicate "proved" by this `impl`) which provides the `Self` type we care about.
+ // For the purposes of this function, we hope that it is a `struct` type, and that our current `expr` is a literal of
+ // that struct type.
+ let impl_trait_self_ref: Option<ty::TraitRef<'tcx>> =
+ self.tcx.impl_trait_ref(obligation.impl_def_id).map(|impl_def| impl_def.skip_binder());
+
+ let Some(impl_trait_self_ref) = impl_trait_self_ref else {
+ // It is possible that this is absent. In this case, we make no progress.
+ return Err(expr);
+ };
+
+ // We only really care about the `Self` type itself, which we extract from the ref.
+ let impl_self_ty: Ty<'tcx> = impl_trait_self_ref.self_ty();
+
+ let impl_predicates: ty::GenericPredicates<'tcx> =
+ self.tcx.predicates_of(obligation.impl_def_id);
+ let Some(impl_predicate_index) = obligation.impl_def_predicate_index else {
+ // We don't have the index, so we can only guess.
+ return Err(expr);
+ };
+
+ if impl_predicate_index >= impl_predicates.predicates.len() {
+ // This shouldn't happen, but since this is only a diagnostic improvement, avoid breaking things.
+ return Err(expr);
+ }
+ let relevant_broken_predicate: ty::PredicateKind<'tcx> =
+ impl_predicates.predicates[impl_predicate_index].0.kind().skip_binder();
+
+ match relevant_broken_predicate {
+ ty::PredicateKind::Clause(ty::Clause::Trait(broken_trait)) => {
+ // ...
+ self.blame_specific_part_of_expr_corresponding_to_generic_param(
+ broken_trait.trait_ref.self_ty().into(),
+ expr,
+ impl_self_ty.into(),
+ )
+ }
+ _ => Err(expr),
+ }
+ }
+
+ /// Drills into `expr` to arrive at the equivalent location of `find_generic_param` in `in_ty`.
+ /// For example, given
+ /// - expr: `(Some(vec![1, 2, 3]), false)`
+ /// - param: `T`
+ /// - in_ty: `(Option<Vec<T>, bool)`
+ /// we would drill until we arrive at `vec![1, 2, 3]`.
+ ///
+ /// If successful, we return `Ok(refined_expr)`. If unsuccesful, we return `Err(partially_refined_expr`),
+ /// which will go as far as possible. For example, given `(foo(), false)` instead, we would drill to
+ /// `foo()` and then return `Err("foo()")`.
+ ///
+ /// This means that you can (and should) use the `?` try operator to chain multiple calls to this
+ /// function with different types, since you can only continue drilling the second time if you
+ /// succeeded the first time.
+ fn blame_specific_part_of_expr_corresponding_to_generic_param(
+ &self,
+ param: ty::GenericArg<'tcx>,
+ expr: &'tcx hir::Expr<'tcx>,
+ in_ty: ty::GenericArg<'tcx>,
+ ) -> Result<&'tcx hir::Expr<'tcx>, &'tcx hir::Expr<'tcx>> {
+ if param == in_ty {
+ // The types match exactly, so we have drilled as far as we can.
+ return Ok(expr);
+ }
+
+ let ty::GenericArgKind::Type(in_ty) = in_ty.unpack() else {
+ return Err(expr);
+ };
+
+ if let (hir::ExprKind::Tup(expr_elements), ty::Tuple(in_ty_elements)) =
+ (&expr.kind, in_ty.kind())
+ {
+ if in_ty_elements.len() != expr_elements.len() {
+ return Err(expr);
+ }
+ // Find out which of `in_ty_elements` refer to `param`.
+ // FIXME: It may be better to take the first if there are multiple,
+ // just so that the error points to a smaller expression.
+ let Some((drill_expr, drill_ty)) = Self::is_iterator_singleton(expr_elements.iter().zip( in_ty_elements.iter()).filter(|(_expr_elem, in_ty_elem)| {
+ Self::find_param_in_ty((*in_ty_elem).into(), param)
+ })) else {
+ // The param is not mentioned, or it is mentioned in multiple indexes.
+ return Err(expr);
+ };
+
+ return self.blame_specific_part_of_expr_corresponding_to_generic_param(
+ param,
+ drill_expr,
+ drill_ty.into(),
+ );
+ }
+
+ if let (
+ hir::ExprKind::Struct(expr_struct_path, expr_struct_fields, _expr_struct_rest),
+ ty::Adt(in_ty_adt, in_ty_adt_generic_args),
+ ) = (&expr.kind, in_ty.kind())
+ {
+ // First, confirm that this struct is the same one as in the types, and if so,
+ // find the right variant.
+ let Res::Def(expr_struct_def_kind, expr_struct_def_id) = self.typeck_results.borrow().qpath_res(expr_struct_path, expr.hir_id) else {
+ return Err(expr);
+ };
+
+ let variant_def_id = match expr_struct_def_kind {
+ hir::def::DefKind::Struct => {
+ if in_ty_adt.did() != expr_struct_def_id {
+ // FIXME: Deal with type aliases?
+ return Err(expr);
+ }
+ expr_struct_def_id
+ }
+ hir::def::DefKind::Variant => {
+ // If this is a variant, its parent is the type definition.
+ if in_ty_adt.did() != self.tcx.parent(expr_struct_def_id) {
+ // FIXME: Deal with type aliases?
+ return Err(expr);
+ }
+ expr_struct_def_id
+ }
+ _ => {
+ return Err(expr);
+ }
+ };
+
+ // We need to know which of the generic parameters mentions our target param.
+ // We expect that at least one of them does, since it is expected to be mentioned.
+ let Some((drill_generic_index, generic_argument_type)) =
+ Self::is_iterator_singleton(
+ in_ty_adt_generic_args.iter().enumerate().filter(
+ |(_index, in_ty_generic)| {
+ Self::find_param_in_ty(*in_ty_generic, param)
+ },
+ ),
+ ) else {
+ return Err(expr);
+ };
+
+ let struct_generic_parameters: &ty::Generics = self.tcx.generics_of(in_ty_adt.did());
+ if drill_generic_index >= struct_generic_parameters.params.len() {
+ return Err(expr);
+ }
+
+ let param_to_point_at_in_struct = self.tcx.mk_param_from_def(
+ struct_generic_parameters.param_at(drill_generic_index, self.tcx),
+ );
+
+ // We make 3 steps:
+ // Suppose we have a type like
+ // ```ignore (just for demonstration)
+ // struct ExampleStruct<T> {
+ // enabled: bool,
+ // item: Option<(usize, T, bool)>,
+ // }
+ //
+ // f(ExampleStruct {
+ // enabled: false,
+ // item: Some((0, Box::new(String::new()), 1) }, true)),
+ // });
+ // ```
+ // Here, `f` is passed a `ExampleStruct<Box<String>>`, but it wants
+ // for `String: Copy`, which isn't true here.
+ //
+ // (1) First, we drill into `.item` and highlight that expression
+ // (2) Then we use the template type `Option<(usize, T, bool)>` to
+ // drill into the `T`, arriving at a `Box<String>` expression.
+ // (3) Then we keep going, drilling into this expression using our
+ // outer contextual information.
+
+ // (1) Find the (unique) field which mentions the type in our constraint:
+ let (field_expr, field_type) = self
+ .point_at_field_if_possible(
+ in_ty_adt.did(),
+ param_to_point_at_in_struct,
+ variant_def_id,
+ expr_struct_fields,
+ )
+ .ok_or(expr)?;
+
+ // (2) Continue drilling into the struct, ignoring the struct's
+ // generic argument types.
+ let expr = self.blame_specific_part_of_expr_corresponding_to_generic_param(
+ param_to_point_at_in_struct,
+ field_expr,
+ field_type.into(),
+ )?;
+
+ // (3) Continue drilling into the expression, having "passed
+ // through" the struct entirely.
+ return self.blame_specific_part_of_expr_corresponding_to_generic_param(
+ param,
+ expr,
+ generic_argument_type,
+ );
+ }
+
+ if let (
+ hir::ExprKind::Call(expr_callee, expr_args),
+ ty::Adt(in_ty_adt, in_ty_adt_generic_args),
+ ) = (&expr.kind, in_ty.kind())
+ {
+ let hir::ExprKind::Path(expr_callee_path) = &expr_callee.kind else {
+ // FIXME: This case overlaps with another one worth handling,
+ // which should happen above since it applies to non-ADTs:
+ // we can drill down into regular generic functions.
+ return Err(expr);
+ };
+ // This is (possibly) a constructor call, like `Some(...)` or `MyStruct(a, b, c)`.
+
+ let Res::Def(expr_struct_def_kind, expr_ctor_def_id) = self.typeck_results.borrow().qpath_res(expr_callee_path, expr_callee.hir_id) else {
+ return Err(expr);
+ };
+
+ let variant_def_id = match expr_struct_def_kind {
+ hir::def::DefKind::Ctor(hir::def::CtorOf::Struct, hir::def::CtorKind::Fn) => {
+ if in_ty_adt.did() != self.tcx.parent(expr_ctor_def_id) {
+ // FIXME: Deal with type aliases?
+ return Err(expr);
+ }
+ self.tcx.parent(expr_ctor_def_id)
+ }
+ hir::def::DefKind::Ctor(hir::def::CtorOf::Variant, hir::def::CtorKind::Fn) => {
+ // If this is a variant, its parent is the type definition.
+ if in_ty_adt.did() != self.tcx.parent(expr_ctor_def_id) {
+ // FIXME: Deal with type aliases?
+ return Err(expr);
+ }
+ expr_ctor_def_id
+ }
+ _ => {
+ return Err(expr);
+ }
+ };
+
+ // We need to know which of the generic parameters mentions our target param.
+ // We expect that at least one of them does, since it is expected to be mentioned.
+ let Some((drill_generic_index, generic_argument_type)) =
+ Self::is_iterator_singleton(
+ in_ty_adt_generic_args.iter().enumerate().filter(
+ |(_index, in_ty_generic)| {
+ Self::find_param_in_ty(*in_ty_generic, param)
+ },
+ ),
+ ) else {
+ return Err(expr);
+ };
+
+ let struct_generic_parameters: &ty::Generics = self.tcx.generics_of(in_ty_adt.did());
+ if drill_generic_index >= struct_generic_parameters.params.len() {
+ return Err(expr);
+ }
+
+ let param_to_point_at_in_struct = self.tcx.mk_param_from_def(
+ struct_generic_parameters.param_at(drill_generic_index, self.tcx),
+ );
+
+ // We make 3 steps:
+ // Suppose we have a type like
+ // ```ignore (just for demonstration)
+ // struct ExampleStruct<T> {
+ // enabled: bool,
+ // item: Option<(usize, T, bool)>,
+ // }
+ //
+ // f(ExampleStruct {
+ // enabled: false,
+ // item: Some((0, Box::new(String::new()), 1) }, true)),
+ // });
+ // ```
+ // Here, `f` is passed a `ExampleStruct<Box<String>>`, but it wants
+ // for `String: Copy`, which isn't true here.
+ //
+ // (1) First, we drill into `.item` and highlight that expression
+ // (2) Then we use the template type `Option<(usize, T, bool)>` to
+ // drill into the `T`, arriving at a `Box<String>` expression.
+ // (3) Then we keep going, drilling into this expression using our
+ // outer contextual information.
+
+ // (1) Find the (unique) field index which mentions the type in our constraint:
+ let Some((field_index, field_type)) = Self::is_iterator_singleton(
+ in_ty_adt
+ .variant_with_id(variant_def_id)
+ .fields
+ .iter()
+ .map(|field| field.ty(self.tcx, *in_ty_adt_generic_args))
+ .enumerate()
+ .filter(|(_index, field_type)| Self::find_param_in_ty((*field_type).into(), param))
+ ) else {
+ return Err(expr);
+ };
+
+ if field_index >= expr_args.len() {
+ return Err(expr);
+ }
+
+ // (2) Continue drilling into the struct, ignoring the struct's
+ // generic argument types.
+ let expr = self.blame_specific_part_of_expr_corresponding_to_generic_param(
+ param_to_point_at_in_struct,
+ &expr_args[field_index],
+ field_type.into(),
+ )?;
+
+ // (3) Continue drilling into the expression, having "passed
+ // through" the struct entirely.
+ return self.blame_specific_part_of_expr_corresponding_to_generic_param(
+ param,
+ expr,
+ generic_argument_type,
+ );
+ }
+
+ // At this point, none of the basic patterns matched.
+ // One major possibility which remains is that we have a function call.
+ // In this case, it's often possible to dive deeper into the call to find something to blame,
+ // but this is not always possible.
+
+ Err(expr)
+ }
+
+ // FIXME: This can be made into a private, non-impl function later.
+ /// Traverses the given ty (either a `ty::Ty` or a `ty::GenericArg`) and searches for references
+ /// to the given `param_to_point_at`. Returns `true` if it finds any use of the param.
+ pub fn find_param_in_ty(
+ ty: ty::GenericArg<'tcx>,
+ param_to_point_at: ty::GenericArg<'tcx>,
+ ) -> bool {
+ let mut walk = ty.walk();
+ while let Some(arg) = walk.next() {
+ if arg == param_to_point_at {
+ return true;
+ } else if let ty::GenericArgKind::Type(ty) = arg.unpack()
+ && let ty::Alias(ty::Projection, ..) = ty.kind()
+ {
+ // This logic may seem a bit strange, but typically when
+ // we have a projection type in a function signature, the
+ // argument that's being passed into that signature is
+ // not actually constraining that projection's substs in
+ // a meaningful way. So we skip it, and see improvements
+ // in some UI tests.
+ walk.skip_current_subtree();
+ }
+ }
+ false
+ }
+
+ // FIXME: This can be made into a private, non-impl function later.
+ /// Returns `Some(iterator.next())` if it has exactly one item, and `None` otherwise.
+ pub fn is_iterator_singleton<T>(mut iterator: impl Iterator<Item = T>) -> Option<T> {
+ match (iterator.next(), iterator.next()) {
+ (_, Some(_)) => None,
+ (first, _) => first,
+ }
+ }
+}
use std::iter;
use std::mem;
-use std::ops::ControlFlow;
use std::slice;
+use std::ops::ControlFlow;
+
impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
pub(in super::super) fn check_casts(&mut self) {
// don't hold the borrow to deferred_cast_checks while checking to avoid borrow checker errors
.into_iter()
.flatten()
{
- if self.point_at_arg_if_possible(
+ if self.blame_specific_arg_if_possible(
error,
def_id,
param,
.into_iter()
.flatten()
{
- if self.point_at_arg_if_possible(
+ if self.blame_specific_arg_if_possible(
error,
def_id,
param,
for param in
[param_to_point_at, fallback_param_to_point_at, self_param_to_point_at]
{
- if let Some(param) = param
- && self.point_at_field_if_possible(
- error,
+ if let Some(param) = param {
+ let refined_expr = self.point_at_field_if_possible(
def_id,
param,
variant_def_id,
fields,
- )
- {
- return true;
+ );
+
+ match refined_expr {
+ None => {}
+ Some((refined_expr, _)) => {
+ error.obligation.cause.span = refined_expr
+ .span
+ .find_ancestor_in_same_ctxt(error.obligation.cause.span)
+ .unwrap_or(refined_expr.span);
+ return true;
+ }
+ }
}
}
}
}
}
- fn point_at_arg_if_possible(
+ /// - `blame_specific_*` means that the function will recursively traverse the expression,
+ /// looking for the most-specific-possible span to blame.
+ ///
+ /// - `point_at_*` means that the function will only go "one level", pointing at the specific
+ /// expression mentioned.
+ ///
+ /// `blame_specific_arg_if_possible` will find the most-specific expression anywhere inside
+ /// the provided function call expression, and mark it as responsible for the fullfillment
+ /// error.
+ fn blame_specific_arg_if_possible(
&self,
error: &mut traits::FulfillmentError<'tcx>,
def_id: DefId,
.inputs()
.iter()
.enumerate()
- .filter(|(_, ty)| find_param_in_ty(**ty, param_to_point_at))
+ .filter(|(_, ty)| Self::find_param_in_ty((**ty).into(), param_to_point_at))
.collect();
// If there's one field that references the given generic, great!
if let [(idx, _)] = args_referencing_param.as_slice()
&& let Some(arg) = receiver
.map_or(args.get(*idx), |rcvr| if *idx == 0 { Some(rcvr) } else { args.get(*idx - 1) }) {
+
error.obligation.cause.span = arg.span.find_ancestor_in_same_ctxt(error.obligation.cause.span).unwrap_or(arg.span);
+
+ if let hir::Node::Expr(arg_expr) = self.tcx.hir().get(arg.hir_id) {
+ // This is more specific than pointing at the entire argument.
+ self.blame_specific_expr_if_possible(error, arg_expr)
+ }
+
error.obligation.cause.map_code(|parent_code| {
ObligationCauseCode::FunctionArgumentObligation {
arg_hir_id: arg.hir_id,
false
}
- fn point_at_field_if_possible(
+ // FIXME: Make this private and move to mod adjust_fulfillment_errors
+ pub fn point_at_field_if_possible(
&self,
- error: &mut traits::FulfillmentError<'tcx>,
def_id: DefId,
param_to_point_at: ty::GenericArg<'tcx>,
variant_def_id: DefId,
expr_fields: &[hir::ExprField<'tcx>],
- ) -> bool {
+ ) -> Option<(&'tcx hir::Expr<'tcx>, Ty<'tcx>)> {
let def = self.tcx.adt_def(def_id);
let identity_substs = ty::InternalSubsts::identity_for_item(self.tcx, def_id);
.iter()
.filter(|field| {
let field_ty = field.ty(self.tcx, identity_substs);
- find_param_in_ty(field_ty, param_to_point_at)
+ Self::find_param_in_ty(field_ty.into(), param_to_point_at)
})
.collect();
// same rules that check_expr_struct uses for macro hygiene.
if self.tcx.adjust_ident(expr_field.ident, variant_def_id) == field.ident(self.tcx)
{
- error.obligation.cause.span = expr_field
- .expr
- .span
- .find_ancestor_in_same_ctxt(error.obligation.cause.span)
- .unwrap_or(expr_field.span);
- return true;
+ return Some((expr_field.expr, self.tcx.type_of(field.did)));
}
}
}
- false
+ None
}
fn point_at_path_if_possible(
}
}
}
-
-fn find_param_in_ty<'tcx>(ty: Ty<'tcx>, param_to_point_at: ty::GenericArg<'tcx>) -> bool {
- let mut walk = ty.walk();
- while let Some(arg) = walk.next() {
- if arg == param_to_point_at {
- return true;
- } else if let ty::GenericArgKind::Type(ty) = arg.unpack()
- && let ty::Alias(ty::Projection, ..) = ty.kind()
- {
- // This logic may seem a bit strange, but typically when
- // we have a projection type in a function signature, the
- // argument that's being passed into that signature is
- // not actually constraining that projection's substs in
- // a meaningful way. So we skip it, and see improvements
- // in some UI tests.
- walk.skip_current_subtree();
- }
- }
- false
-}
mod _impl;
+mod adjust_fulfillment_errors;
mod arg_matrix;
mod checks;
mod suggestions;
use crate::errors::{AddReturnTypeSuggestion, ExpectedReturnTypeLabel};
use crate::method::probe::{IsSuggestion, Mode, ProbeScope};
use rustc_ast::util::parser::{ExprPrecedence, PREC_POSTFIX};
-use rustc_errors::{Applicability, Diagnostic, MultiSpan};
+use rustc_errors::{fluent, Applicability, Diagnostic, MultiSpan};
use rustc_hir as hir;
use rustc_hir::def::{CtorKind, CtorOf, DefKind};
use rustc_hir::lang_items::LangItem;
use rustc_hir_analysis::astconv::AstConv;
use rustc_infer::traits::{self, StatementAsExpression};
use rustc_middle::lint::in_external_macro;
+use rustc_middle::ty::print::with_no_trimmed_paths;
use rustc_middle::ty::{
self, suggest_constraining_type_params, Binder, DefIdTree, IsSuggestable, ToPredicate, Ty,
TypeVisitable,
if let ty::Adt(adt, _) = peeled.kind()
&& Some(adt.did()) == self.tcx.lang_items().string()
{
+ let sugg = if ref_cnt == 0 {
+ ".as_deref()"
+ } else {
+ ".map(|x| x.as_str())"
+ };
err.span_suggestion_verbose(
expr.span.shrink_to_hi(),
- "try converting the passed type into a `&str`",
- format!(".map(|x| &*{}x)", "*".repeat(ref_cnt)),
- Applicability::MaybeIncorrect,
+ fluent::hir_typeck_convert_to_str,
+ sugg,
+ Applicability::MachineApplicable,
);
return true;
}
return true;
}
&hir::FnRetTy::DefaultReturn(span) if expected.is_unit() => {
- if found.is_suggestable(self.tcx, false) {
+ if let Some(found) = found.make_suggestable(self.tcx, false) {
err.subdiagnostic(AddReturnTypeSuggestion::Add { span, found: found.to_string() });
return true;
} else if let ty::Closure(_, substs) = found.kind()
}
}
hir::FnRetTy::Return(ty) => {
+ let span = ty.span;
+
+ if let hir::TyKind::OpaqueDef(item_id, ..) = ty.kind
+ && let hir::Node::Item(hir::Item {
+ kind: hir::ItemKind::OpaqueTy(op_ty),
+ ..
+ }) = self.tcx.hir().get(item_id.hir_id())
+ && let hir::OpaqueTy {
+ bounds: [bound], ..
+ } = op_ty
+ && let hir::GenericBound::LangItemTrait(
+ hir::LangItem::Future, _, _, generic_args) = bound
+ && let hir::GenericArgs { bindings: [ty_binding], .. } = generic_args
+ && let hir::TypeBinding { kind, .. } = ty_binding
+ && let hir::TypeBindingKind::Equality { term } = kind
+ && let hir::Term::Ty(term_ty) = term {
+ // Check if async function's return type was omitted.
+ // Don't emit suggestions if the found type is `impl Future<...>`.
+ debug!("suggest_missing_return_type: found = {:?}", found);
+ if found.is_suggestable(self.tcx, false) {
+ if term_ty.span.is_empty() {
+ err.subdiagnostic(AddReturnTypeSuggestion::Add { span, found: found.to_string() });
+ return true;
+ } else {
+ err.subdiagnostic(ExpectedReturnTypeLabel::Other { span, expected });
+ }
+ }
+ }
+
// 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 span = ty.span;
let ty = self.astconv().ast_ty_to_ty(ty);
debug!("suggest_missing_return_type: return type {:?}", ty);
debug!("suggest_missing_return_type: expected type {:?}", ty);
}
}
+ /// Suggest providing `std::ptr::null()` or `std::ptr::null_mut()` if they
+ /// pass in a literal 0 to an raw pointer.
+ #[instrument(skip(self, err))]
+ pub(crate) fn suggest_null_ptr_for_literal_zero_given_to_ptr_arg(
+ &self,
+ err: &mut Diagnostic,
+ expr: &hir::Expr<'_>,
+ expected_ty: Ty<'tcx>,
+ ) -> bool {
+ // Expected type needs to be a raw pointer.
+ let ty::RawPtr(ty::TypeAndMut { mutbl, .. }) = expected_ty.kind() else {
+ return false;
+ };
+
+ // Provided expression needs to be a literal `0`.
+ let ExprKind::Lit(Spanned {
+ node: rustc_ast::LitKind::Int(0, _),
+ span,
+ }) = expr.kind else {
+ return false;
+ };
+
+ // We need to find a null pointer symbol to suggest
+ let null_sym = match mutbl {
+ hir::Mutability::Not => sym::ptr_null,
+ hir::Mutability::Mut => sym::ptr_null_mut,
+ };
+ let Some(null_did) = self.tcx.get_diagnostic_item(null_sym) else {
+ return false;
+ };
+ let null_path_str = with_no_trimmed_paths!(self.tcx.def_path_str(null_did));
+
+ // We have satisfied all requirements to provide a suggestion. Emit it.
+ err.span_suggestion(
+ span,
+ format!("if you meant to create a null pointer, use `{null_path_str}()`"),
+ null_path_str + "()",
+ Applicability::MachineApplicable,
+ );
+
+ true
+ }
+
pub(crate) fn suggest_associated_const(
&self,
err: &mut Diagnostic,
None if self.is_tainted_by_errors() => Err(()),
None => {
bug!(
- "no type for node {}: {} in mem_categorization",
- id,
+ "no type for node {} in mem_categorization",
self.tcx().hir().node_to_string(id)
);
}
traits::ImplDerivedObligationCause {
derived,
impl_def_id,
+ impl_def_predicate_index: None,
span,
},
))
if let Some(output_def_id) = output_def_id
&& let Some(trait_def_id) = trait_def_id
&& self.tcx.parent(output_def_id) == trait_def_id
- && output_ty.is_suggestable(self.tcx, false)
+ && let Some(output_ty) = output_ty.make_suggestable(self.tcx, false)
{
- Some(("Output", *output_ty))
+ Some(("Output", output_ty))
} else {
None
}
rustc_index = { path = "../rustc_index" }
rustc_macros = { path = "../rustc_macros" }
rustc_serialize = { path = "../rustc_serialize" }
-rustc_session = { path = "../rustc_session" }
rustc_span = { path = "../rustc_span" }
rustc_target = { path = "../rustc_target" }
smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
use rustc_data_structures::fx::{FxIndexMap, FxIndexSet};
use rustc_errors::{pluralize, struct_span_err, Diagnostic, ErrorGuaranteed, IntoDiagnosticArg};
-use rustc_errors::{Applicability, DiagnosticBuilder, DiagnosticStyledString, MultiSpan};
+use rustc_errors::{Applicability, DiagnosticBuilder, DiagnosticStyledString};
use rustc_hir as hir;
use rustc_hir::def::DefKind;
use rustc_hir::def_id::{DefId, LocalDefId};
for (key, values) in types.iter() {
let count = values.len();
let kind = key.descr();
- let mut returned_async_output_error = false;
for &sp in values {
- if sp.is_desugaring(DesugaringKind::Async) && !returned_async_output_error {
- if [sp] != err.span.primary_spans() {
- let mut span: MultiSpan = sp.into();
- span.push_span_label(
- sp,
- format!(
- "checked the `Output` of this `async fn`, {}{} {}{}",
- if count > 1 { "one of the " } else { "" },
- target,
- kind,
- pluralize!(count),
- ),
- );
- err.span_note(
- span,
- "while checking the return type of the `async fn`",
- );
- } else {
- err.span_label(
- sp,
- format!(
- "checked the `Output` of this `async fn`, {}{} {}{}",
- if count > 1 { "one of the " } else { "" },
- target,
- kind,
- pluralize!(count),
- ),
- );
- err.note("while checking the return type of the `async fn`");
- }
- returned_async_output_error = true;
- } else {
- err.span_label(
- sp,
- format!(
- "{}{} {}{}",
- if count == 1 { "the " } else { "one of the " },
- target,
- kind,
- pluralize!(count),
- ),
- );
- }
+ err.span_label(
+ sp,
+ format!(
+ "{}{} {}{}",
+ if count == 1 { "the " } else { "one of the " },
+ target,
+ kind,
+ pluralize!(count),
+ ),
+ );
}
}
}
// |
// = note: expected unit type `()`
// found closure `[closure@$DIR/issue-20862.rs:2:5: 2:14 x:_]`
- if !self.ignore_span.overlaps(span) {
+ //
+ // Also ignore opaque `Future`s that come from async fns.
+ if !self.ignore_span.overlaps(span)
+ && !span.is_desugaring(DesugaringKind::Async)
+ {
self.types.entry(kind).or_default().insert(span);
}
}
}
}
+ #[inline]
fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
if !t.needs_infer() && !t.has_erasable_regions() {
- return t;
- }
-
- let tcx = self.infcx.tcx;
-
- match *t.kind() {
- ty::Infer(ty::TyVar(v)) => {
- let opt_ty = self.infcx.inner.borrow_mut().type_variables().probe(v).known();
- self.freshen_ty(opt_ty, ty::TyVar(v), ty::FreshTy)
- }
+ t
+ } else {
+ match *t.kind() {
+ ty::Infer(v) => self.fold_infer_ty(v).unwrap_or(t),
- ty::Infer(ty::IntVar(v)) => self.freshen_ty(
- self.infcx
- .inner
- .borrow_mut()
- .int_unification_table()
- .probe_value(v)
- .map(|v| v.to_type(tcx)),
- ty::IntVar(v),
- ty::FreshIntTy,
- ),
+ // This code is hot enough that a non-debug assertion here makes a noticeable
+ // difference on benchmarks like `wg-grammar`.
+ #[cfg(debug_assertions)]
+ ty::Placeholder(..) | ty::Bound(..) => bug!("unexpected type {:?}", t),
- ty::Infer(ty::FloatVar(v)) => self.freshen_ty(
- self.infcx
- .inner
- .borrow_mut()
- .float_unification_table()
- .probe_value(v)
- .map(|v| v.to_type(tcx)),
- ty::FloatVar(v),
- ty::FreshFloatTy,
- ),
-
- ty::Infer(ty::FreshTy(ct) | ty::FreshIntTy(ct) | ty::FreshFloatTy(ct)) => {
- if ct >= self.ty_freshen_count {
- bug!(
- "Encountered a freshend type with id {} \
- but our counter is only at {}",
- ct,
- self.ty_freshen_count
- );
- }
- t
+ _ => t.super_fold_with(self),
}
-
- ty::Generator(..)
- | ty::Bool
- | ty::Char
- | ty::Int(..)
- | ty::Uint(..)
- | ty::Float(..)
- | ty::Adt(..)
- | ty::Str
- | ty::Error(_)
- | ty::Array(..)
- | ty::Slice(..)
- | ty::RawPtr(..)
- | ty::Ref(..)
- | ty::FnDef(..)
- | ty::FnPtr(_)
- | ty::Dynamic(..)
- | ty::Never
- | ty::Tuple(..)
- | ty::Alias(..)
- | ty::Foreign(..)
- | ty::Param(..)
- | ty::Closure(..)
- | ty::GeneratorWitnessMIR(..)
- | ty::GeneratorWitness(..) => t.super_fold_with(self),
-
- ty::Placeholder(..) | ty::Bound(..) => bug!("unexpected type {:?}", t),
}
}
}
}
}
+
+impl<'a, 'tcx> TypeFreshener<'a, 'tcx> {
+ // This is separate from `fold_ty` to keep that method small and inlinable.
+ #[inline(never)]
+ fn fold_infer_ty(&mut self, v: ty::InferTy) -> Option<Ty<'tcx>> {
+ match v {
+ ty::TyVar(v) => {
+ let opt_ty = self.infcx.inner.borrow_mut().type_variables().probe(v).known();
+ Some(self.freshen_ty(opt_ty, ty::TyVar(v), ty::FreshTy))
+ }
+
+ ty::IntVar(v) => Some(
+ self.freshen_ty(
+ self.infcx
+ .inner
+ .borrow_mut()
+ .int_unification_table()
+ .probe_value(v)
+ .map(|v| v.to_type(self.infcx.tcx)),
+ ty::IntVar(v),
+ ty::FreshIntTy,
+ ),
+ ),
+
+ ty::FloatVar(v) => Some(
+ self.freshen_ty(
+ self.infcx
+ .inner
+ .borrow_mut()
+ .float_unification_table()
+ .probe_value(v)
+ .map(|v| v.to_type(self.infcx.tcx)),
+ ty::FloatVar(v),
+ ty::FreshFloatTy,
+ ),
+ ),
+
+ ty::FreshTy(ct) | ty::FreshIntTy(ct) | ty::FreshFloatTy(ct) => {
+ if ct >= self.ty_freshen_count {
+ bug!(
+ "Encountered a freshend type with id {} \
+ but our counter is only at {}",
+ ct,
+ self.ty_freshen_count
+ );
+ }
+ None
+ }
+ }
+ }
+}
use rustc_middle::ty::subst::{GenericArg, GenericArgKind, InternalSubsts, SubstsRef};
use rustc_middle::ty::visit::TypeVisitable;
pub use rustc_middle::ty::IntVarValue;
-use rustc_middle::ty::{self, GenericParamDefKind, InferConst, Ty, TyCtxt};
+use rustc_middle::ty::{self, GenericParamDefKind, InferConst, InferTy, Ty, TyCtxt};
use rustc_middle::ty::{ConstVid, FloatVid, IntVid, TyVid};
use rustc_span::symbol::Symbol;
use rustc_span::Span;
where
T: TypeFoldable<'tcx>,
{
- if !value.needs_infer() {
- return value; // Avoid duplicated subst-folding.
+ if !value.has_non_region_infer() {
+ return value;
}
let mut r = resolve::OpportunisticVarResolver::new(self);
value.fold_with(&mut r)
/// If `ty` is a type variable of some kind, resolve it one level
/// (but do not resolve types found in the result). If `typ` is
/// not a type variable, just return it unmodified.
+ #[inline]
fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
- match *ty.kind() {
- ty::Infer(ty::TyVar(v)) => {
+ if let ty::Infer(v) = ty.kind() { self.fold_infer_ty(*v).unwrap_or(ty) } else { ty }
+ }
+
+ fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
+ if let ty::ConstKind::Infer(InferConst::Var(vid)) = ct.kind() {
+ self.infcx
+ .inner
+ .borrow_mut()
+ .const_unification_table()
+ .probe_value(vid)
+ .val
+ .known()
+ .unwrap_or(ct)
+ } else {
+ ct
+ }
+ }
+}
+
+impl<'a, 'tcx> ShallowResolver<'a, 'tcx> {
+ // This is separate from `fold_ty` to keep that method small and inlinable.
+ #[inline(never)]
+ fn fold_infer_ty(&mut self, v: InferTy) -> Option<Ty<'tcx>> {
+ match v {
+ ty::TyVar(v) => {
// Not entirely obvious: if `typ` is a type variable,
// it can be resolved to an int/float variable, which
// can then be recursively resolved, hence the
// Note: if these two lines are combined into one we get
// dynamic borrow errors on `self.inner`.
let known = self.infcx.inner.borrow_mut().type_variables().probe(v).known();
- known.map_or(ty, |t| self.fold_ty(t))
+ known.map(|t| self.fold_ty(t))
}
- ty::Infer(ty::IntVar(v)) => self
+ ty::IntVar(v) => self
.infcx
.inner
.borrow_mut()
.int_unification_table()
.probe_value(v)
- .map_or(ty, |v| v.to_type(self.infcx.tcx)),
+ .map(|v| v.to_type(self.infcx.tcx)),
- ty::Infer(ty::FloatVar(v)) => self
+ ty::FloatVar(v) => self
.infcx
.inner
.borrow_mut()
.float_unification_table()
.probe_value(v)
- .map_or(ty, |v| v.to_type(self.infcx.tcx)),
+ .map(|v| v.to_type(self.infcx.tcx)),
- _ => ty,
- }
- }
-
- fn fold_const(&mut self, ct: ty::Const<'tcx>) -> ty::Const<'tcx> {
- if let ty::ConstKind::Infer(InferConst::Var(vid)) = ct.kind() {
- self.infcx
- .inner
- .borrow_mut()
- .const_unification_table()
- .probe_value(vid)
- .val
- .known()
- .unwrap_or(ct)
- } else {
- ct
+ ty::FreshTy(_) | ty::FreshIntTy(_) | ty::FreshFloatTy(_) => None,
}
}
}
where
D: TypeRelatingDelegate<'tcx>,
{
- fn const_equate_obligation(&mut self, _a: ty::Const<'tcx>, _b: ty::Const<'tcx>) {
- // We don't have to worry about the equality of consts during borrow checking
- // as consts always have a static lifetime.
- // FIXME(oli-obk): is this really true? We can at least have HKL and with
- // inline consts we may have further lifetimes that may be unsound to treat as
- // 'static.
+ fn const_equate_obligation(&mut self, a: ty::Const<'tcx>, b: ty::Const<'tcx>) {
+ self.delegate.register_obligations(vec![Obligation::new(
+ self.tcx(),
+ ObligationCause::dummy(),
+ self.param_env(),
+ ty::Binder::dummy(ty::PredicateKind::ConstEquate(a, b)),
+ )]);
}
}
/// useful for printing messages etc but also required at various
/// points for correctness.
pub struct OpportunisticVarResolver<'a, 'tcx> {
- infcx: &'a InferCtxt<'tcx>,
+ // The shallow resolver is used to resolve inference variables at every
+ // level of the type.
+ shallow_resolver: crate::infer::ShallowResolver<'a, 'tcx>,
}
impl<'a, 'tcx> OpportunisticVarResolver<'a, 'tcx> {
#[inline]
pub fn new(infcx: &'a InferCtxt<'tcx>) -> Self {
- OpportunisticVarResolver { infcx }
+ OpportunisticVarResolver { shallow_resolver: crate::infer::ShallowResolver { infcx } }
}
}
impl<'a, 'tcx> TypeFolder<'tcx> for OpportunisticVarResolver<'a, 'tcx> {
fn tcx<'b>(&'b self) -> TyCtxt<'tcx> {
- self.infcx.tcx
+ TypeFolder::tcx(&self.shallow_resolver)
}
+ #[inline]
fn fold_ty(&mut self, t: Ty<'tcx>) -> Ty<'tcx> {
if !t.has_non_region_infer() {
t // micro-optimize -- if there is nothing in this type that this fold affects...
} else {
- let t = self.infcx.shallow_resolve(t);
+ let t = self.shallow_resolver.fold_ty(t);
t.super_fold_with(self)
}
}
if !ct.has_non_region_infer() {
ct // micro-optimize -- if there is nothing in this const that this fold affects...
} else {
- let ct = self.infcx.shallow_resolve(ct);
+ let ct = self.shallow_resolver.fold_const(ct);
ct.super_fold_with(self)
}
}
// Get predicates declared on the trait.
let predicates = tcx.super_predicates_of(data.def_id());
- let obligations = predicates.predicates.iter().map(|&(mut pred, span)| {
- // when parent predicate is non-const, elaborate it to non-const predicates.
- if data.constness == ty::BoundConstness::NotConst {
- pred = pred.without_const(tcx);
- }
-
- let cause = obligation.cause.clone().derived_cause(
- bound_predicate.rebind(data),
- |derived| {
- traits::ImplDerivedObligation(Box::new(
- traits::ImplDerivedObligationCause {
- derived,
- impl_def_id: data.def_id(),
- span,
- },
- ))
- },
- );
- predicate_obligation(
- pred.subst_supertrait(tcx, &bound_predicate.rebind(data.trait_ref)),
- obligation.param_env,
- cause,
- )
- });
+ let obligations =
+ predicates.predicates.iter().enumerate().map(|(index, &(mut pred, span))| {
+ // when parent predicate is non-const, elaborate it to non-const predicates.
+ if data.constness == ty::BoundConstness::NotConst {
+ pred = pred.without_const(tcx);
+ }
+
+ let cause = obligation.cause.clone().derived_cause(
+ bound_predicate.rebind(data),
+ |derived| {
+ traits::ImplDerivedObligation(Box::new(
+ traits::ImplDerivedObligationCause {
+ derived,
+ impl_def_id: data.def_id(),
+ impl_def_predicate_index: Some(index),
+ span,
+ },
+ ))
+ },
+ );
+ predicate_obligation(
+ pred.subst_supertrait(tcx, &bound_predicate.rebind(data.trait_ref)),
+ obligation.param_env,
+ cause,
+ )
+ });
debug!(?data, ?obligations, "super_predicates");
// Only keep those bounds that we haven't already seen.
rustc_parse = { path = "../rustc_parse" }
rustc_session = { path = "../rustc_session" }
rustc_span = { path = "../rustc_span" }
-rustc_serialize = { path = "../rustc_serialize" }
rustc_middle = { path = "../rustc_middle" }
rustc_ast_lowering = { path = "../rustc_ast_lowering" }
rustc_ast_passes = { path = "../rustc_ast_passes" }
-use crate::errors::{
- CantEmitMIR, EmojiIdentifier, ErrorWritingDependencies, FerrisIdentifier,
- GeneratedFileConflictsWithDirectory, InputFileWouldBeOverWritten, MixedBinCrate,
- MixedProcMacroCrate, OutDirError, ProcMacroCratePanicAbort, ProcMacroDocWithoutArg,
- TempsDirError,
-};
+use crate::errors;
use crate::interface::{Compiler, Result};
use crate::proc_macro_decls;
use crate::util;
if crate_types.len() > 1 {
if is_executable_crate {
- sess.emit_err(MixedBinCrate);
+ sess.emit_err(errors::MixedBinCrate);
}
if is_proc_macro_crate {
- sess.emit_err(MixedProcMacroCrate);
+ sess.emit_err(errors::MixedProcMacroCrate);
}
}
if is_proc_macro_crate && sess.panic_strategy() == PanicStrategy::Abort {
- sess.emit_warning(ProcMacroCratePanicAbort);
+ sess.emit_warning(errors::ProcMacroCratePanicAbort);
}
// For backwards compatibility, we don't try to run proc macro injection
// However, we do emit a warning, to let such users know that they should
// start passing '--crate-type proc-macro'
if has_proc_macro_decls && sess.opts.actually_rustdoc && !is_proc_macro_crate {
- sess.emit_warning(ProcMacroDocWithoutArg);
+ sess.emit_warning(errors::ProcMacroDocWithoutArg);
} else {
krate = sess.time("maybe_create_a_macro_crate", || {
let is_test_crate = sess.opts.test;
spans.sort();
if ident == sym::ferris {
let first_span = spans[0];
- sess.emit_err(FerrisIdentifier { spans, first_span });
+ sess.emit_err(errors::FerrisIdentifier { spans, first_span });
} else {
- sess.emit_err(EmojiIdentifier { spans, ident });
+ sess.emit_err(errors::EmojiIdentifier { spans, ident });
}
}
});
}
}
Err(error) => {
- sess.emit_fatal(ErrorWritingDependencies { path: &deps_filename, error });
+ sess.emit_fatal(errors::ErrorWritingDependencies { path: &deps_filename, error });
}
}
}
if let Some(ref input_path) = sess.io.input.opt_path() {
if sess.opts.will_create_output_file() {
if output_contains_path(&output_paths, input_path) {
- sess.emit_fatal(InputFileWouldBeOverWritten { path: input_path });
+ sess.emit_fatal(errors::InputFileWouldBeOverWritten { path: input_path });
}
if let Some(ref dir_path) = output_conflicts_with_dir(&output_paths) {
- sess.emit_fatal(GeneratedFileConflictsWithDirectory { input_path, dir_path });
+ sess.emit_fatal(errors::GeneratedFileConflictsWithDirectory {
+ input_path,
+ dir_path,
+ });
}
}
}
if let Some(ref dir) = sess.io.temps_dir {
if fs::create_dir_all(dir).is_err() {
- sess.emit_fatal(TempsDirError);
+ sess.emit_fatal(errors::TempsDirError);
}
}
if !only_dep_info {
if let Some(ref dir) = sess.io.output_dir {
if fs::create_dir_all(dir).is_err() {
- sess.emit_fatal(OutDirError);
+ sess.emit_fatal(errors::OutDirError);
}
}
}
if tcx.sess.opts.output_types.contains_key(&OutputType::Mir) {
if let Err(error) = rustc_mir_transform::dump_mir::emit_mir(tcx) {
- tcx.sess.emit_err(CantEmitMIR { error });
+ tcx.sess.emit_err(errors::CantEmitMIR { error });
tcx.sess.abort_if_errors();
}
}
pub cx: &'a LateContext<'b>,
pub def_id: DefId,
pub note: Option<Symbol>,
+ pub suggestion: Option<UnusedDefSuggestion>,
+}
+
+#[derive(Subdiagnostic)]
+pub enum UnusedDefSuggestion {
+ #[suggestion(
+ suggestion,
+ style = "verbose",
+ code = "let _ = ",
+ applicability = "machine-applicable"
+ )]
+ Default {
+ #[primary_span]
+ span: Span,
+ },
}
// Needed because of def_path_str
if let Some(note) = self.note {
diag.note(note.as_str());
}
+ if let Some(sugg) = self.suggestion {
+ diag.subdiagnostic(sugg);
+ }
diag
}
use crate::lints::{
PathStatementDrop, PathStatementDropSub, PathStatementNoEffect, UnusedAllocationDiag,
- UnusedAllocationMutDiag, UnusedClosure, UnusedDef, UnusedDelim, UnusedDelimSuggestion,
- UnusedGenerator, UnusedImportBracesDiag, UnusedOp, UnusedResult,
+ UnusedAllocationMutDiag, UnusedClosure, UnusedDef, UnusedDefSuggestion, UnusedDelim,
+ UnusedDelimSuggestion, UnusedGenerator, UnusedImportBracesDiag, UnusedOp, UnusedResult,
};
use crate::Lint;
use crate::{EarlyContext, EarlyLintPass, LateContext, LateLintPass, LintContext};
);
}
MustUsePath::Def(span, def_id, reason) => {
+ let suggestion = if matches!(
+ cx.tcx.get_diagnostic_name(*def_id),
+ Some(sym::add)
+ | Some(sym::sub)
+ | Some(sym::mul)
+ | Some(sym::div)
+ | Some(sym::rem)
+ | Some(sym::neg),
+ ) {
+ Some(UnusedDefSuggestion::Default { span: span.shrink_to_lo() })
+ } else {
+ None
+ };
cx.emit_spanned_lint(
UNUSED_MUST_USE,
*span,
cx,
def_id: *def_id,
note: *reason,
+ suggestion,
},
);
}
ArrayLenExpr,
AnonConst,
MatchArmExpr,
+ IndexExpr,
}
impl From<UnusedDelimsCtx> for &'static str {
UnusedDelimsCtx::LetScrutineeExpr => "`let` scrutinee expression",
UnusedDelimsCtx::ArrayLenExpr | UnusedDelimsCtx::AnonConst => "const expression",
UnusedDelimsCtx::MatchArmExpr => "match arm expression",
+ UnusedDelimsCtx::IndexExpr => "index expression",
}
}
}
keep_space: (bool, bool),
) {
let primary_span = if let Some((lo, hi)) = spans {
+ if hi.is_empty() {
+ // do not point at delims that do not exist
+ return;
+ }
MultiSpan::from(vec![lo, hi])
} else {
MultiSpan::from(value_span)
(value, UnusedDelimsCtx::ReturnValue, false, Some(left), None)
}
+ Index(_, ref value) => (value, UnusedDelimsCtx::IndexExpr, false, None, None),
+
Assign(_, ref value, _) | AssignOp(.., ref value) => {
(value, UnusedDelimsCtx::AssignedValue, false, None, None)
}
#include "llvm/Passes/StandardInstrumentations.h"
#include "llvm/Support/CBindingWrapping.h"
#include "llvm/Support/FileSystem.h"
+#if LLVM_VERSION_GE(17, 0)
+#include "llvm/Support/VirtualFileSystem.h"
+#endif
#include "llvm/Support/Host.h"
#if LLVM_VERSION_LT(14, 0)
#include "llvm/Support/TargetRegistry.h"
Optional<PGOOptions> PGOOpt;
#else
std::optional<PGOOptions> PGOOpt;
+#endif
+#if LLVM_VERSION_GE(17, 0)
+ auto FS = vfs::getRealFileSystem();
#endif
if (PGOGenPath) {
assert(!PGOUsePath && !PGOSampleUsePath);
- PGOOpt = PGOOptions(PGOGenPath, "", "", PGOOptions::IRInstr,
- PGOOptions::NoCSAction, DebugInfoForProfiling);
+ PGOOpt = PGOOptions(PGOGenPath, "", "",
+#if LLVM_VERSION_GE(17, 0)
+ FS,
+#endif
+ PGOOptions::IRInstr, PGOOptions::NoCSAction,
+ DebugInfoForProfiling);
} else if (PGOUsePath) {
assert(!PGOSampleUsePath);
- PGOOpt = PGOOptions(PGOUsePath, "", "", PGOOptions::IRUse,
- PGOOptions::NoCSAction, DebugInfoForProfiling);
+ PGOOpt = PGOOptions(PGOUsePath, "", "",
+#if LLVM_VERSION_GE(17, 0)
+ FS,
+#endif
+ PGOOptions::IRUse, PGOOptions::NoCSAction,
+ DebugInfoForProfiling);
} else if (PGOSampleUsePath) {
- PGOOpt = PGOOptions(PGOSampleUsePath, "", "", PGOOptions::SampleUse,
- PGOOptions::NoCSAction, DebugInfoForProfiling);
+ PGOOpt = PGOOptions(PGOSampleUsePath, "", "",
+#if LLVM_VERSION_GE(17, 0)
+ FS,
+#endif
+ PGOOptions::SampleUse, PGOOptions::NoCSAction,
+ DebugInfoForProfiling);
} else if (DebugInfoForProfiling) {
- PGOOpt = PGOOptions("", "", "", PGOOptions::NoAction,
- PGOOptions::NoCSAction, DebugInfoForProfiling);
+ PGOOpt = PGOOptions("", "", "",
+#if LLVM_VERSION_GE(17, 0)
+ FS,
+#endif
+ PGOOptions::NoAction, PGOOptions::NoCSAction,
+ DebugInfoForProfiling);
}
PassBuilder PB(TM, PTO, PGOOpt, &PIC);
//! Validates all used crates and extern libraries and loads their metadata
-use crate::errors::{
- ConflictingAllocErrorHandler, ConflictingGlobalAlloc, CrateNotPanicRuntime,
- GlobalAllocRequired, NoMultipleAllocErrorHandler, NoMultipleGlobalAlloc, NoPanicStrategy,
- NoTransitiveNeedsDep, NotProfilerRuntime, ProfilerBuiltinsNeedsCore,
-};
+use crate::errors;
use crate::locator::{CrateError, CrateLocator, CratePaths};
use crate::rmeta::{CrateDep, CrateMetadata, CrateNumMap, CrateRoot, MetadataBlob};
use proc_macro::bridge::client::ProcMacro;
use std::ops::Fn;
use std::path::Path;
+use std::time::Duration;
use std::{cmp, env};
#[derive(Clone)]
) -> Result<&'static [ProcMacro], CrateError> {
// Make sure the path contains a / or the linker will search for it.
let path = env::current_dir().unwrap().join(path);
- let lib = unsafe { libloading::Library::new(path) }
- .map_err(|err| CrateError::DlOpen(err.to_string()))?;
+ let lib = load_dylib(&path, 5).map_err(|err| CrateError::DlOpen(err))?;
let sym_name = self.sess.generate_proc_macro_decls_symbol(stable_crate_id);
let sym = unsafe { lib.get::<*const &[ProcMacro]>(sym_name.as_bytes()) }
// Sanity check the loaded crate to ensure it is indeed a panic runtime
// and the panic strategy is indeed what we thought it was.
if !data.is_panic_runtime() {
- self.sess.emit_err(CrateNotPanicRuntime { crate_name: name });
+ self.sess.emit_err(errors::CrateNotPanicRuntime { crate_name: name });
}
if data.required_panic_strategy() != Some(desired_strategy) {
- self.sess.emit_err(NoPanicStrategy { crate_name: name, strategy: desired_strategy });
+ self.sess
+ .emit_err(errors::NoPanicStrategy { crate_name: name, strategy: desired_strategy });
}
self.cstore.injected_panic_runtime = Some(cnum);
let name = Symbol::intern(&self.sess.opts.unstable_opts.profiler_runtime);
if name == sym::profiler_builtins && self.sess.contains_name(&krate.attrs, sym::no_core) {
- self.sess.emit_err(ProfilerBuiltinsNeedsCore);
+ self.sess.emit_err(errors::ProfilerBuiltinsNeedsCore);
}
let Some(cnum) = self.resolve_crate(name, DUMMY_SP, CrateDepKind::Implicit) else { return; };
// Sanity check the loaded crate to ensure it is indeed a profiler runtime
if !data.is_profiler_runtime() {
- self.sess.emit_err(NotProfilerRuntime { crate_name: name });
+ self.sess.emit_err(errors::NotProfilerRuntime { crate_name: name });
}
}
fn inject_allocator_crate(&mut self, krate: &ast::Crate) {
self.cstore.has_global_allocator = match &*global_allocator_spans(&self.sess, krate) {
[span1, span2, ..] => {
- self.sess.emit_err(NoMultipleGlobalAlloc { span2: *span2, span1: *span1 });
+ self.sess.emit_err(errors::NoMultipleGlobalAlloc { span2: *span2, span1: *span1 });
true
}
spans => !spans.is_empty(),
};
self.cstore.has_alloc_error_handler = match &*alloc_error_handler_spans(&self.sess, krate) {
[span1, span2, ..] => {
- self.sess.emit_err(NoMultipleAllocErrorHandler { span2: *span2, span1: *span1 });
+ self.sess
+ .emit_err(errors::NoMultipleAllocErrorHandler { span2: *span2, span1: *span1 });
true
}
spans => !spans.is_empty(),
if data.has_global_allocator() {
match global_allocator {
Some(other_crate) => {
- self.sess.emit_err(ConflictingGlobalAlloc {
+ self.sess.emit_err(errors::ConflictingGlobalAlloc {
crate_name: data.name(),
other_crate_name: other_crate,
});
if data.has_alloc_error_handler() {
match alloc_error_handler {
Some(other_crate) => {
- self.sess.emit_err(ConflictingAllocErrorHandler {
+ self.sess.emit_err(errors::ConflictingAllocErrorHandler {
crate_name: data.name(),
other_crate_name: other_crate,
});
if !self.sess.contains_name(&krate.attrs, sym::default_lib_allocator)
&& !self.cstore.iter_crate_data().any(|(_, data)| data.has_default_lib_allocator())
{
- self.sess.emit_err(GlobalAllocRequired);
+ self.sess.emit_err(errors::GlobalAllocRequired);
}
self.cstore.allocator_kind = Some(AllocatorKind::Default);
}
for dep in self.cstore.crate_dependencies_in_reverse_postorder(krate) {
let data = self.cstore.get_crate_data(dep);
if needs_dep(&data) {
- self.sess.emit_err(NoTransitiveNeedsDep {
+ self.sess.emit_err(errors::NoTransitiveNeedsDep {
crate_name: self.cstore.get_crate_data(krate).name(),
needs_crate_name: what,
deps_crate_name: data.name(),
visit::walk_crate(&mut f, krate);
f.spans
}
+
+// On Windows the compiler would sometimes intermittently fail to open the
+// proc-macro DLL with `Error::LoadLibraryExW`. It is suspected that something in the
+// system still holds a lock on the file, so we retry a few times before calling it
+// an error.
+fn load_dylib(path: &Path, max_attempts: usize) -> Result<libloading::Library, String> {
+ assert!(max_attempts > 0);
+
+ let mut last_error = None;
+
+ for attempt in 0..max_attempts {
+ match unsafe { libloading::Library::new(&path) } {
+ Ok(lib) => {
+ if attempt > 0 {
+ debug!(
+ "Loaded proc-macro `{}` after {} attempts.",
+ path.display(),
+ attempt + 1
+ );
+ }
+ return Ok(lib);
+ }
+ Err(err) => {
+ // Only try to recover from this specific error.
+ if !matches!(err, libloading::Error::LoadLibraryExW { .. }) {
+ return Err(err.to_string());
+ }
+
+ last_error = Some(err);
+ std::thread::sleep(Duration::from_millis(100));
+ debug!("Failed to load proc-macro `{}`. Retrying.", path.display());
+ }
+ }
+ }
+
+ debug!("Failed to load proc-macro `{}` even after {} attempts.", path.display(), max_attempts);
+ Err(format!("{} (retried {} times)", last_error.unwrap(), max_attempts))
+}
//! metadata::locator or metadata::creader for all the juicy details!
use crate::creader::Library;
-use crate::errors::{
- CannotFindCrate, CrateLocationUnknownType, DlError, ExternLocationNotExist,
- ExternLocationNotFile, FoundStaticlib, IncompatibleRustc, InvalidMetadataFiles,
- LibFilenameForm, MultipleCandidates, NewerCrateVersion, NoCrateWithTriple, NoDylibPlugin,
- NonAsciiName, StableCrateIdCollision, SymbolConflictsCurrent, SymbolConflictsOthers,
-};
+use crate::errors;
use crate::rmeta::{rustc_version, MetadataBlob, METADATA_HEADER};
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
pub(crate) fn report(self, sess: &Session, span: Span, missing_core: bool) {
match self {
CrateError::NonAsciiName(crate_name) => {
- sess.emit_err(NonAsciiName { span, crate_name });
+ sess.emit_err(errors::NonAsciiName { span, crate_name });
}
CrateError::ExternLocationNotExist(crate_name, loc) => {
- sess.emit_err(ExternLocationNotExist { span, crate_name, location: &loc });
+ sess.emit_err(errors::ExternLocationNotExist { span, crate_name, location: &loc });
}
CrateError::ExternLocationNotFile(crate_name, loc) => {
- sess.emit_err(ExternLocationNotFile { span, crate_name, location: &loc });
+ sess.emit_err(errors::ExternLocationNotFile { span, crate_name, location: &loc });
}
CrateError::MultipleCandidates(crate_name, flavor, candidates) => {
- sess.emit_err(MultipleCandidates { span, crate_name, flavor, candidates });
+ sess.emit_err(errors::MultipleCandidates { span, crate_name, flavor, candidates });
}
CrateError::SymbolConflictsCurrent(root_name) => {
- sess.emit_err(SymbolConflictsCurrent { span, crate_name: root_name });
+ sess.emit_err(errors::SymbolConflictsCurrent { span, crate_name: root_name });
}
CrateError::SymbolConflictsOthers(root_name) => {
- sess.emit_err(SymbolConflictsOthers { span, crate_name: root_name });
+ sess.emit_err(errors::SymbolConflictsOthers { span, crate_name: root_name });
}
CrateError::StableCrateIdCollision(crate_name0, crate_name1) => {
- sess.emit_err(StableCrateIdCollision { span, crate_name0, crate_name1 });
+ sess.emit_err(errors::StableCrateIdCollision { span, crate_name0, crate_name1 });
}
CrateError::DlOpen(s) | CrateError::DlSym(s) => {
- sess.emit_err(DlError { span, err: s });
+ sess.emit_err(errors::DlError { span, err: s });
}
CrateError::LocatorCombined(locator) => {
let crate_name = locator.crate_name;
if !locator.crate_rejections.via_filename.is_empty() {
let mismatches = locator.crate_rejections.via_filename.iter();
for CrateMismatch { path, .. } in mismatches {
- sess.emit_err(CrateLocationUnknownType { span, path: &path, crate_name });
- sess.emit_err(LibFilenameForm {
+ sess.emit_err(errors::CrateLocationUnknownType {
+ span,
+ path: &path,
+ crate_name,
+ });
+ sess.emit_err(errors::LibFilenameForm {
span,
dll_prefix: &locator.dll_prefix,
dll_suffix: &locator.dll_suffix,
));
}
}
- sess.emit_err(NewerCrateVersion {
+ sess.emit_err(errors::NewerCrateVersion {
span,
crate_name: crate_name,
add_info,
path.display(),
));
}
- sess.emit_err(NoCrateWithTriple {
+ sess.emit_err(errors::NoCrateWithTriple {
span,
crate_name,
locator_triple: locator.triple.triple(),
path.display()
));
}
- sess.emit_err(FoundStaticlib { span, crate_name, add_info, found_crates });
+ sess.emit_err(errors::FoundStaticlib {
+ span,
+ crate_name,
+ add_info,
+ found_crates,
+ });
} else if !locator.crate_rejections.via_version.is_empty() {
let mismatches = locator.crate_rejections.via_version.iter();
for CrateMismatch { path, got } in mismatches {
path.display(),
));
}
- sess.emit_err(IncompatibleRustc {
+ sess.emit_err(errors::IncompatibleRustc {
span,
crate_name,
add_info,
for CrateMismatch { path: _, got } in locator.crate_rejections.via_invalid {
crate_rejections.push(got);
}
- sess.emit_err(InvalidMetadataFiles {
+ sess.emit_err(errors::InvalidMetadataFiles {
span,
crate_name,
add_info,
crate_rejections,
});
} else {
- sess.emit_err(CannotFindCrate {
+ sess.emit_err(errors::CannotFindCrate {
span,
crate_name,
add_info,
}
}
CrateError::NonDylibPlugin(crate_name) => {
- sess.emit_err(NoDylibPlugin { span, crate_name });
+ sess.emit_err(errors::NoDylibPlugin { span, crate_name });
}
}
}
use rustc_span::symbol::{sym, Symbol};
use rustc_target::spec::abi::Abi;
-use crate::errors::{
- AsNeededCompatibility, BundleNeedsStatic, EmptyLinkName, EmptyRenamingTarget,
- FrameworkOnlyWindows, ImportNameTypeForm, ImportNameTypeRaw, ImportNameTypeX86,
- IncompatibleWasmLink, InvalidLinkModifier, LibFrameworkApple, LinkCfgForm,
- LinkCfgSinglePredicate, LinkFrameworkApple, LinkKindForm, LinkModifiersForm, LinkNameForm,
- LinkOrdinalRawDylib, LinkRequiresName, MissingNativeLibrary, MultipleCfgs,
- MultipleImportNameType, MultipleKindsInLink, MultipleLinkModifiers, MultipleModifiers,
- MultipleNamesInLink, MultipleRenamings, MultipleWasmImport, NoLinkModOverride, RawDylibNoNul,
- RenamingNoLink, UnexpectedLinkArg, UnknownImportNameType, UnknownLinkKind, UnknownLinkModifier,
- UnsupportedAbi, UnsupportedAbiI686, WasmImportForm, WholeArchiveNeedsStatic,
-};
+use crate::errors;
use std::path::PathBuf;
}
}
- sess.emit_fatal(MissingNativeLibrary::new(name, verbatim));
+ sess.emit_fatal(errors::MissingNativeLibrary::new(name, verbatim));
}
fn find_bundled_library(
match item.name_or_empty() {
sym::name => {
if name.is_some() {
- sess.emit_err(MultipleNamesInLink { span: item.span() });
+ sess.emit_err(errors::MultipleNamesInLink { span: item.span() });
continue;
}
let Some(link_name) = item.value_str() else {
- sess.emit_err(LinkNameForm { span: item.span() });
+ sess.emit_err(errors::LinkNameForm { span: item.span() });
continue;
};
let span = item.name_value_literal_span().unwrap();
if link_name.is_empty() {
- sess.emit_err(EmptyLinkName { span });
+ sess.emit_err(errors::EmptyLinkName { span });
}
name = Some((link_name, span));
}
sym::kind => {
if kind.is_some() {
- sess.emit_err(MultipleKindsInLink { span: item.span() });
+ sess.emit_err(errors::MultipleKindsInLink { span: item.span() });
continue;
}
let Some(link_kind) = item.value_str() else {
- sess.emit_err(LinkKindForm { span: item.span() });
+ sess.emit_err(errors::LinkKindForm { span: item.span() });
continue;
};
"dylib" => NativeLibKind::Dylib { as_needed: None },
"framework" => {
if !sess.target.is_like_osx {
- sess.emit_err(LinkFrameworkApple { span });
+ sess.emit_err(errors::LinkFrameworkApple { span });
}
NativeLibKind::Framework { as_needed: None }
}
"raw-dylib" => {
if !sess.target.is_like_windows {
- sess.emit_err(FrameworkOnlyWindows { span });
+ sess.emit_err(errors::FrameworkOnlyWindows { span });
} else if !features.raw_dylib && sess.target.arch == "x86" {
feature_err(
&sess.parse_sess,
NativeLibKind::RawDylib
}
kind => {
- sess.emit_err(UnknownLinkKind { span, kind });
+ sess.emit_err(errors::UnknownLinkKind { span, kind });
continue;
}
};
}
sym::modifiers => {
if modifiers.is_some() {
- sess.emit_err(MultipleLinkModifiers { span: item.span() });
+ sess.emit_err(errors::MultipleLinkModifiers { span: item.span() });
continue;
}
let Some(link_modifiers) = item.value_str() else {
- sess.emit_err(LinkModifiersForm { span: item.span() });
+ sess.emit_err(errors::LinkModifiersForm { span: item.span() });
continue;
};
modifiers = Some((link_modifiers, item.name_value_literal_span().unwrap()));
}
sym::cfg => {
if cfg.is_some() {
- sess.emit_err(MultipleCfgs { span: item.span() });
+ sess.emit_err(errors::MultipleCfgs { span: item.span() });
continue;
}
let Some(link_cfg) = item.meta_item_list() else {
- sess.emit_err(LinkCfgForm { span: item.span() });
+ sess.emit_err(errors::LinkCfgForm { span: item.span() });
continue;
};
let [NestedMetaItem::MetaItem(link_cfg)] = link_cfg else {
- sess.emit_err(LinkCfgSinglePredicate { span: item.span() });
+ sess.emit_err(errors::LinkCfgSinglePredicate { span: item.span() });
continue;
};
if !features.link_cfg {
}
sym::wasm_import_module => {
if wasm_import_module.is_some() {
- sess.emit_err(MultipleWasmImport { span: item.span() });
+ sess.emit_err(errors::MultipleWasmImport { span: item.span() });
continue;
}
let Some(link_wasm_import_module) = item.value_str() else {
- sess.emit_err(WasmImportForm { span: item.span() });
+ sess.emit_err(errors::WasmImportForm { span: item.span() });
continue;
};
wasm_import_module = Some((link_wasm_import_module, item.span()));
}
sym::import_name_type => {
if import_name_type.is_some() {
- sess.emit_err(MultipleImportNameType { span: item.span() });
+ sess.emit_err(errors::MultipleImportNameType { span: item.span() });
continue;
}
let Some(link_import_name_type) = item.value_str() else {
- sess.emit_err(ImportNameTypeForm { span: item.span() });
+ sess.emit_err(errors::ImportNameTypeForm { span: item.span() });
continue;
};
if self.tcx.sess.target.arch != "x86" {
- sess.emit_err(ImportNameTypeX86 { span: item.span() });
+ sess.emit_err(errors::ImportNameTypeX86 { span: item.span() });
continue;
}
"noprefix" => PeImportNameType::NoPrefix,
"undecorated" => PeImportNameType::Undecorated,
import_name_type => {
- sess.emit_err(UnknownImportNameType {
+ sess.emit_err(errors::UnknownImportNameType {
span: item.span(),
import_name_type,
});
import_name_type = Some((link_import_name_type, item.span()));
}
_ => {
- sess.emit_err(UnexpectedLinkArg { span: item.span() });
+ sess.emit_err(errors::UnexpectedLinkArg { span: item.span() });
}
}
}
let (modifier, value) = match modifier.strip_prefix(&['+', '-']) {
Some(m) => (m, modifier.starts_with('+')),
None => {
- sess.emit_err(InvalidLinkModifier { span });
+ sess.emit_err(errors::InvalidLinkModifier { span });
continue;
}
};
}
let assign_modifier = |dst: &mut Option<bool>| {
if dst.is_some() {
- sess.emit_err(MultipleModifiers { span, modifier });
+ sess.emit_err(errors::MultipleModifiers { span, modifier });
} else {
*dst = Some(value);
}
assign_modifier(bundle)
}
("bundle", _) => {
- sess.emit_err(BundleNeedsStatic { span });
+ sess.emit_err(errors::BundleNeedsStatic { span });
}
("verbatim", _) => assign_modifier(&mut verbatim),
assign_modifier(whole_archive)
}
("whole-archive", _) => {
- sess.emit_err(WholeArchiveNeedsStatic { span });
+ sess.emit_err(errors::WholeArchiveNeedsStatic { span });
}
("as-needed", Some(NativeLibKind::Dylib { as_needed }))
assign_modifier(as_needed)
}
("as-needed", _) => {
- sess.emit_err(AsNeededCompatibility { span });
+ sess.emit_err(errors::AsNeededCompatibility { span });
}
_ => {
- sess.emit_err(UnknownLinkModifier { span, modifier });
+ sess.emit_err(errors::UnknownLinkModifier { span, modifier });
}
}
}
if let Some((_, span)) = wasm_import_module {
if name.is_some() || kind.is_some() || modifiers.is_some() || cfg.is_some() {
- sess.emit_err(IncompatibleWasmLink { span });
+ sess.emit_err(errors::IncompatibleWasmLink { span });
}
} else if name.is_none() {
- sess.emit_err(LinkRequiresName { span: m.span });
+ sess.emit_err(errors::LinkRequiresName { span: m.span });
}
// Do this outside of the loop so that `import_name_type` can be specified before `kind`.
if let Some((_, span)) = import_name_type {
if kind != Some(NativeLibKind::RawDylib) {
- sess.emit_err(ImportNameTypeRaw { span });
+ sess.emit_err(errors::ImportNameTypeRaw { span });
}
}
let dll_imports = match kind {
Some(NativeLibKind::RawDylib) => {
if let Some((name, span)) = name && name.as_str().contains('\0') {
- sess.emit_err(RawDylibNoNul { span });
+ sess.emit_err(errors::RawDylibNoNul { span });
}
foreign_mod_items
.iter()
.iter()
.find(|a| a.has_name(sym::link_ordinal))
.unwrap();
- sess.emit_err(LinkOrdinalRawDylib { span: link_ordinal_attr.span });
+ sess.emit_err(errors::LinkOrdinalRawDylib {
+ span: link_ordinal_attr.span,
+ });
}
}
for lib in &self.tcx.sess.opts.libs {
if let NativeLibKind::Framework { .. } = lib.kind && !self.tcx.sess.target.is_like_osx {
// Cannot check this when parsing options because the target is not yet available.
- self.tcx.sess.emit_err(LibFrameworkApple);
+ self.tcx.sess.emit_err(errors::LibFrameworkApple);
}
if let Some(ref new_name) = lib.new_name {
let any_duplicate = self
.filter_map(|lib| lib.name.as_ref())
.any(|n| n.as_str() == lib.name);
if new_name.is_empty() {
- self.tcx.sess.emit_err(EmptyRenamingTarget { lib_name: &lib.name });
+ self.tcx.sess.emit_err(errors::EmptyRenamingTarget { lib_name: &lib.name });
} else if !any_duplicate {
- self.tcx.sess.emit_err(RenamingNoLink { lib_name: &lib.name });
+ self.tcx.sess.emit_err(errors::RenamingNoLink { lib_name: &lib.name });
} else if !renames.insert(&lib.name) {
- self.tcx.sess.emit_err(MultipleRenamings { lib_name: &lib.name });
+ self.tcx.sess.emit_err(errors::MultipleRenamings { lib_name: &lib.name });
}
}
}
// explicit `:rename` in particular.
if lib.has_modifiers() || passed_lib.has_modifiers() {
match lib.foreign_module {
- Some(def_id) => self.tcx.sess.emit_err(NoLinkModOverride {
- span: Some(self.tcx.def_span(def_id)),
- }),
- None => {
- self.tcx.sess.emit_err(NoLinkModOverride { span: None })
+ Some(def_id) => {
+ self.tcx.sess.emit_err(errors::NoLinkModOverride {
+ span: Some(self.tcx.def_span(def_id)),
+ })
}
+ None => self
+ .tcx
+ .sess
+ .emit_err(errors::NoLinkModOverride { span: None }),
};
}
if passed_lib.kind != NativeLibKind::Unspecified {
DllCallingConvention::Vectorcall(self.i686_arg_list_size(item))
}
_ => {
- self.tcx.sess.emit_fatal(UnsupportedAbiI686 { span: item.span });
+ self.tcx.sess.emit_fatal(errors::UnsupportedAbiI686 { span: item.span });
}
}
} else {
match abi {
Abi::C { .. } | Abi::Win64 { .. } | Abi::System { .. } => DllCallingConvention::C,
_ => {
- self.tcx.sess.emit_fatal(UnsupportedAbi { span: item.span });
+ self.tcx.sess.emit_fatal(errors::UnsupportedAbi { span: item.span });
}
}
};
impl<'a, 'tcx, T> Decodable<DecodeContext<'a, 'tcx>> for LazyArray<T> {
fn decode(decoder: &mut DecodeContext<'a, 'tcx>) -> Self {
let len = decoder.read_usize();
- if len == 0 { LazyArray::empty() } else { decoder.read_lazy_array(len) }
+ if len == 0 { LazyArray::default() } else { decoder.read_lazy_array(len) }
}
}
.tables
.children
.get(self, index)
- .unwrap_or_else(LazyArray::empty)
+ .expect("fields are not encoded for a variant")
.decode(self)
.map(|index| ty::FieldDef {
did: self.local_def_id(index),
.tables
.children
.get(self, item_id)
- .unwrap_or_else(LazyArray::empty)
+ .expect("variants are not encoded for an enum")
.decode(self)
.filter_map(|index| {
let kind = self.def_kind(index);
.tables
.fn_arg_names
.get(self, id)
- .unwrap_or_else(LazyArray::empty)
+ .expect("argument names not encoded for a function")
.decode((self, sess))
.nth(0)
.map_or(false, |ident| ident.name == kw::SelfLower)
.tables
.children
.get(self, id)
- .unwrap_or_else(LazyArray::empty)
+ .expect("associated items not encoded for an item")
.decode((self, sess))
.map(move |child_index| self.local_def_id(child_index))
}
fn get_associated_item(self, id: DefIndex, sess: &'a Session) -> ty::AssocItem {
let name = self.item_name(id);
- let kind = match self.def_kind(id) {
- DefKind::AssocConst => ty::AssocKind::Const,
- DefKind::AssocFn => ty::AssocKind::Fn,
- DefKind::AssocTy => ty::AssocKind::Type,
+ let (kind, has_self) = match self.def_kind(id) {
+ DefKind::AssocConst => (ty::AssocKind::Const, false),
+ DefKind::AssocFn => (ty::AssocKind::Fn, self.get_fn_has_self_parameter(id, sess)),
+ DefKind::AssocTy => (ty::AssocKind::Type, false),
_ => bug!("cannot get associated-item of `{:?}`", self.def_key(id)),
};
- let has_self = self.get_fn_has_self_parameter(id, sess);
let container = self.root.tables.assoc_container.get(self, id).unwrap();
ty::AssocItem {
.tables
.children
.get(self, id)
- .unwrap_or_else(LazyArray::empty)
+ .expect("fields not encoded for a struct")
.decode(self)
.map(move |index| respan(self.get_span(index, sess), self.item_name(index)))
}
.tables
.children
.get(self, id)
- .unwrap_or_else(LazyArray::empty)
+ .expect("fields not encoded for a struct")
.decode(self)
.map(move |field_index| self.get_visibility(field_index))
}
.tables
.inherent_impls
.get(self, id)
- .unwrap_or_else(LazyArray::empty)
.decode(self)
.map(|index| self.local_def_id(index)),
)
.tables
.inherent_impls
.get(self, ty_index)
- .unwrap_or_else(LazyArray::empty)
.decode(self)
.map(move |impl_index| (ty_def_id, self.local_def_id(impl_index)))
})
) -> DefPathHash {
*def_path_hashes
.entry(index)
- .or_insert_with(|| self.root.tables.def_path_hashes.get(self, index).unwrap())
+ .or_insert_with(|| self.root.tables.def_path_hashes.get(self, index))
}
#[inline]
use crate::creader::{CStore, LoadedMacro};
use crate::foreign_modules;
use crate::native_libs;
+use crate::rmeta::table::IsDefault;
use crate::rmeta::AttrFlags;
use rustc_ast as ast;
}
}
};
+ ($tcx:ident, $def_id:ident, $other:ident, $cdata:ident, $name:ident => { table_defaulted_array }) => {
+ provide_one! {
+ $tcx, $def_id, $other, $cdata, $name => {
+ let lazy = $cdata.root.tables.$name.get($cdata, $def_id.index);
+ if lazy.is_default() { &[] } else { $tcx.arena.alloc_from_iter(lazy.decode(($cdata, $tcx))) }
+ }
+ }
+ };
($tcx:ident, $def_id:ident, $other:ident, $cdata:ident, $name:ident => { table_direct }) => {
provide_one! {
$tcx, $def_id, $other, $cdata, $name => {
}
provide! { tcx, def_id, other, cdata,
- explicit_item_bounds => { table }
+ explicit_item_bounds => { table_defaulted_array }
explicit_predicates_of => { table }
generics_of => { table }
- inferred_outlives_of => { table }
+ inferred_outlives_of => { table_defaulted_array }
super_predicates_of => { table }
type_of => { table }
variances_of => { table }
symbol_table: FxHashMap<Symbol, usize>,
}
-/// If the current crate is a proc-macro, returns early with `LazyArray::empty()`.
+/// If the current crate is a proc-macro, returns early with `LazyArray::default()`.
/// This is useful for skipping the encoding of things that aren't needed
/// for proc-macro crates.
macro_rules! empty_proc_macro {
($self:ident) => {
if $self.is_proc_macro {
- return LazyArray::empty();
+ return LazyArray::default();
}
};
}
}
}
-// Shorthand for `$self.$tables.$table.set($def_id.index, $self.lazy_value($value))`, which would
+// Shorthand for `$self.$tables.$table.set_some($def_id.index, $self.lazy_value($value))`, which would
// normally need extra variables to avoid errors about multiple mutable borrows.
macro_rules! record {
($self:ident.$tables:ident.$table:ident[$def_id:expr] <- $value:expr) => {{
{
let value = $value;
let lazy = $self.lazy(value);
- $self.$tables.$table.set($def_id.index, lazy);
+ $self.$tables.$table.set_some($def_id.index, lazy);
}
}};
}
-// Shorthand for `$self.$tables.$table.set($def_id.index, $self.lazy_value($value))`, which would
+// Shorthand for `$self.$tables.$table.set_some($def_id.index, $self.lazy_value($value))`, which would
// normally need extra variables to avoid errors about multiple mutable borrows.
macro_rules! record_array {
+ ($self:ident.$tables:ident.$table:ident[$def_id:expr] <- $value:expr) => {{
+ {
+ let value = $value;
+ let lazy = $self.lazy_array(value);
+ $self.$tables.$table.set_some($def_id.index, lazy);
+ }
+ }};
+}
+
+macro_rules! record_defaulted_array {
($self:ident.$tables:ident.$table:ident[$def_id:expr] <- $value:expr) => {{
{
let value = $value;
{
let def_key = self.lazy(table.def_key(def_index));
let def_path_hash = table.def_path_hash(def_index);
- self.tables.def_keys.set(def_index, def_key);
+ self.tables.def_keys.set_some(def_index, def_key);
self.tables.def_path_hashes.set(def_index, def_path_hash);
}
} else {
for (def_index, def_key, def_path_hash) in table.enumerated_keys_and_path_hashes() {
let def_key = self.lazy(def_key);
- self.tables.def_keys.set(def_index, def_key);
+ self.tables.def_keys.set_some(def_index, def_key);
self.tables.def_path_hashes.set(def_index, *def_path_hash);
}
}
let on_disk_index: u32 =
on_disk_index.try_into().expect("cannot export more than U32_MAX files");
- adapted.set(on_disk_index, self.lazy(source_file));
+ adapted.set_some(on_disk_index, self.lazy(source_file));
}
adapted.encode(&mut self.opaque)
if state.is_doc_hidden {
attr_flags |= AttrFlags::IS_DOC_HIDDEN;
}
- if !attr_flags.is_empty() {
- self.tables.attr_flags.set_nullable(def_id.local_def_index, attr_flags);
- }
+ self.tables.attr_flags.set(def_id.local_def_index, attr_flags);
}
fn encode_def_ids(&mut self) {
let def_id = local_id.to_def_id();
let def_kind = tcx.opt_def_kind(local_id);
let Some(def_kind) = def_kind else { continue };
- self.tables.opt_def_kind.set(def_id.index, def_kind);
+ self.tables.opt_def_kind.set_some(def_id.index, def_kind);
let def_span = tcx.def_span(local_id);
record!(self.tables.def_span[def_id] <- def_span);
self.encode_attrs(local_id);
record!(self.tables.generics_of[def_id] <- g);
record!(self.tables.explicit_predicates_of[def_id] <- self.tcx.explicit_predicates_of(def_id));
let inferred_outlives = self.tcx.inferred_outlives_of(def_id);
- if !inferred_outlives.is_empty() {
- record_array!(self.tables.inferred_outlives_of[def_id] <- inferred_outlives);
- }
+ record_defaulted_array!(self.tables.inferred_outlives_of[def_id] <- inferred_outlives);
}
if should_encode_type(tcx, local_id, def_kind) {
record!(self.tables.type_of[def_id] <- self.tcx.type_of(def_id));
record!(self.tables.trait_impl_trait_tys[def_id] <- table);
}
}
+
let inherent_impls = tcx.with_stable_hashing_context(|hcx| {
tcx.crate_inherent_impls(()).inherent_impls.to_sorted(&hcx, true)
});
-
- for (def_id, implementations) in inherent_impls {
- if implementations.is_empty() {
- continue;
- }
- record_array!(self.tables.inherent_impls[def_id.to_def_id()] <- implementations.iter().map(|&def_id| {
+ for (def_id, impls) in inherent_impls {
+ record_defaulted_array!(self.tables.inherent_impls[def_id.to_def_id()] <- impls.iter().map(|def_id| {
assert!(def_id.is_local());
def_id.index
}));
};
record!(self.tables.variant_data[variant.def_id] <- data);
- self.tables.constness.set(variant.def_id.index, hir::Constness::Const);
+ self.tables.constness.set_some(variant.def_id.index, hir::Constness::Const);
record_array!(self.tables.children[variant.def_id] <- variant.fields.iter().map(|f| {
assert!(f.did.is_local());
f.did.index
}));
if let Some((CtorKind::Fn, ctor_def_id)) = variant.ctor {
- self.tables.constness.set(ctor_def_id.index, hir::Constness::Const);
+ self.tables.constness.set_some(ctor_def_id.index, hir::Constness::Const);
let fn_sig = tcx.fn_sig(ctor_def_id);
record!(self.tables.fn_sig[ctor_def_id] <- fn_sig);
// FIXME only encode signature for ctor_def_id
fn encode_explicit_item_bounds(&mut self, def_id: DefId) {
debug!("EncodeContext::encode_explicit_item_bounds({:?})", def_id);
let bounds = self.tcx.explicit_item_bounds(def_id);
- if !bounds.is_empty() {
- record_array!(self.tables.explicit_item_bounds[def_id] <- bounds);
- }
+ record_defaulted_array!(self.tables.explicit_item_bounds[def_id] <- bounds);
}
fn encode_info_for_trait_item(&mut self, def_id: DefId) {
let tcx = self.tcx;
let impl_defaultness = tcx.impl_defaultness(def_id.expect_local());
- self.tables.impl_defaultness.set(def_id.index, impl_defaultness);
+ self.tables.impl_defaultness.set_some(def_id.index, impl_defaultness);
let trait_item = tcx.associated_item(def_id);
- self.tables.assoc_container.set(def_id.index, trait_item.container);
+ self.tables.assoc_container.set_some(def_id.index, trait_item.container);
match trait_item.kind {
ty::AssocKind::Const => {}
ty::AssocKind::Fn => {
record_array!(self.tables.fn_arg_names[def_id] <- tcx.fn_arg_names(def_id));
- self.tables.asyncness.set(def_id.index, tcx.asyncness(def_id));
- self.tables.constness.set(def_id.index, hir::Constness::NotConst);
+ self.tables.asyncness.set_some(def_id.index, tcx.asyncness(def_id));
+ self.tables.constness.set_some(def_id.index, hir::Constness::NotConst);
}
ty::AssocKind::Type => {
self.encode_explicit_item_bounds(def_id);
let tcx = self.tcx;
let ast_item = self.tcx.hir().expect_impl_item(def_id.expect_local());
- self.tables.impl_defaultness.set(def_id.index, ast_item.defaultness);
+ self.tables.impl_defaultness.set_some(def_id.index, ast_item.defaultness);
let impl_item = self.tcx.associated_item(def_id);
- self.tables.assoc_container.set(def_id.index, impl_item.container);
+ self.tables.assoc_container.set_some(def_id.index, impl_item.container);
match impl_item.kind {
ty::AssocKind::Fn => {
let hir::ImplItemKind::Fn(ref sig, body) = ast_item.kind else { bug!() };
- self.tables.asyncness.set(def_id.index, sig.header.asyncness);
+ self.tables.asyncness.set_some(def_id.index, sig.header.asyncness);
record_array!(self.tables.fn_arg_names[def_id] <- self.tcx.hir().body_param_names(body));
// Can be inside `impl const Trait`, so using sig.header.constness is not reliable
let constness = if self.tcx.is_const_fn_raw(def_id) {
} else {
hir::Constness::NotConst
};
- self.tables.constness.set(def_id.index, constness);
+ self.tables.constness.set_some(def_id.index, constness);
}
ty::AssocKind::Const | ty::AssocKind::Type => {}
}
if let Some(trait_item_def_id) = impl_item.trait_item_def_id {
- self.tables.trait_item_def_id.set(def_id.index, trait_item_def_id.into());
+ self.tables.trait_item_def_id.set_some(def_id.index, trait_item_def_id.into());
}
if impl_item.kind == ty::AssocKind::Fn {
record!(self.tables.fn_sig[def_id] <- tcx.fn_sig(def_id));
- if tcx.is_intrinsic(def_id) {
- self.tables.is_intrinsic.set_nullable(def_id.index, true);
- }
+ self.tables.is_intrinsic.set(def_id.index, tcx.is_intrinsic(def_id));
}
}
match item.kind {
hir::ItemKind::Fn(ref sig, .., body) => {
- self.tables.asyncness.set(def_id.index, sig.header.asyncness);
+ self.tables.asyncness.set_some(def_id.index, sig.header.asyncness);
record_array!(self.tables.fn_arg_names[def_id] <- self.tcx.hir().body_param_names(body));
- self.tables.constness.set(def_id.index, sig.header.constness);
+ self.tables.constness.set_some(def_id.index, sig.header.constness);
}
hir::ItemKind::Macro(ref macro_def, _) => {
- if macro_def.macro_rules {
- self.tables.is_macro_rules.set_nullable(def_id.index, true);
- }
+ self.tables.is_macro_rules.set(def_id.index, macro_def.macro_rules);
record!(self.tables.macro_definition[def_id] <- &*macro_def.body);
}
hir::ItemKind::Mod(ref m) => {
}
hir::ItemKind::OpaqueTy(ref opaque) => {
self.encode_explicit_item_bounds(def_id);
- if matches!(opaque.origin, hir::OpaqueTyOrigin::TyAlias) {
- self.tables.is_type_alias_impl_trait.set_nullable(def_id.index, true);
- }
+ self.tables
+ .is_type_alias_impl_trait
+ .set(def_id.index, matches!(opaque.origin, hir::OpaqueTyOrigin::TyAlias));
}
hir::ItemKind::Impl(hir::Impl { defaultness, constness, .. }) => {
- self.tables.impl_defaultness.set(def_id.index, *defaultness);
- self.tables.constness.set(def_id.index, *constness);
+ self.tables.impl_defaultness.set_some(def_id.index, *defaultness);
+ self.tables.constness.set_some(def_id.index, *constness);
let trait_ref = self.tcx.impl_trait_ref(def_id).map(ty::EarlyBinder::skip_binder);
if let Some(trait_ref) = trait_ref {
let trait_def = self.tcx.trait_def(trait_ref.def_id);
if let Ok(mut an) = trait_def.ancestors(self.tcx, def_id) {
if let Some(specialization_graph::Node::Impl(parent)) = an.nth(1) {
- self.tables.impl_parent.set(def_id.index, parent.into());
+ self.tables.impl_parent.set_some(def_id.index, parent.into());
}
}
}
let polarity = self.tcx.impl_polarity(def_id);
- self.tables.impl_polarity.set(def_id.index, polarity);
+ self.tables.impl_polarity.set_some(def_id.index, polarity);
}
hir::ItemKind::Trait(..) => {
let trait_def = self.tcx.trait_def(def_id);
}
if let hir::ItemKind::Fn(..) = item.kind {
record!(self.tables.fn_sig[def_id] <- tcx.fn_sig(def_id));
- if tcx.is_intrinsic(def_id) {
- self.tables.is_intrinsic.set_nullable(def_id.index, true);
- }
+ self.tables.is_intrinsic.set(def_id.index, tcx.is_intrinsic(def_id));
}
if let hir::ItemKind::Impl { .. } = item.kind {
if let Some(trait_ref) = self.tcx.impl_trait_ref(def_id) {
ty::Closure(_, substs) => {
let constness = self.tcx.constness(def_id.to_def_id());
- self.tables.constness.set(def_id.to_def_id().index, constness);
+ self.tables.constness.set_some(def_id.to_def_id().index, constness);
record!(self.tables.fn_sig[def_id.to_def_id()] <- ty::EarlyBinder(substs.as_closure().sig()));
}
self.hygiene_ctxt.encode(
&mut (&mut *self, &mut syntax_contexts, &mut expn_data_table, &mut expn_hash_table),
|(this, syntax_contexts, _, _), index, ctxt_data| {
- syntax_contexts.set(index, this.lazy(ctxt_data));
+ syntax_contexts.set_some(index, this.lazy(ctxt_data));
},
|(this, _, expn_data_table, expn_hash_table), index, expn_data, hash| {
if let Some(index) = index.as_local() {
- expn_data_table.set(index.as_raw(), this.lazy(expn_data));
- expn_hash_table.set(index.as_raw(), this.lazy(hash));
+ expn_data_table.set_some(index.as_raw(), this.lazy(expn_data));
+ expn_hash_table.set_some(index.as_raw(), this.lazy(hash));
}
},
);
let spans = self.tcx.sess.parse_sess.proc_macro_quoted_spans();
for (i, span) in spans.into_iter().enumerate() {
let span = self.lazy(span);
- self.tables.proc_macro_quoted_spans.set(i, span);
+ self.tables.proc_macro_quoted_spans.set_some(i, span);
}
- self.tables.opt_def_kind.set(LOCAL_CRATE.as_def_id().index, DefKind::Mod);
+ self.tables.opt_def_kind.set_some(LOCAL_CRATE.as_def_id().index, DefKind::Mod);
record!(self.tables.def_span[LOCAL_CRATE.as_def_id()] <- tcx.def_span(LOCAL_CRATE.as_def_id()));
self.encode_attrs(LOCAL_CRATE.as_def_id().expect_local());
let vis = tcx.local_visibility(CRATE_DEF_ID).map_id(|def_id| def_id.local_def_index);
def_key.disambiguated_data.data = DefPathData::MacroNs(name);
let def_id = id.to_def_id();
- self.tables.opt_def_kind.set(def_id.index, DefKind::Macro(macro_kind));
- self.tables.proc_macro.set(def_id.index, macro_kind);
+ self.tables.opt_def_kind.set_some(def_id.index, DefKind::Macro(macro_kind));
+ self.tables.proc_macro.set_some(def_id.index, macro_kind);
self.encode_attrs(id);
record!(self.tables.def_keys[def_id] <- def_key);
record!(self.tables.def_ident_span[def_id] <- span);
Linkage::Static => Some(LinkagePreference::RequireStatic),
}));
}
- LazyArray::empty()
+ LazyArray::default()
}
fn encode_info_for_foreign_item(&mut self, def_id: DefId, nitem: &hir::ForeignItem<'_>) {
match nitem.kind {
hir::ForeignItemKind::Fn(_, ref names, _) => {
- self.tables.asyncness.set(def_id.index, hir::IsAsync::NotAsync);
+ self.tables.asyncness.set_some(def_id.index, hir::IsAsync::NotAsync);
record_array!(self.tables.fn_arg_names[def_id] <- *names);
let constness = if self.tcx.is_const_fn_raw(def_id) {
hir::Constness::Const
} else {
hir::Constness::NotConst
};
- self.tables.constness.set(def_id.index, constness);
+ self.tables.constness.set_some(def_id.index, constness);
record!(self.tables.fn_sig[def_id] <- tcx.fn_sig(def_id));
}
hir::ForeignItemKind::Static(..) | hir::ForeignItemKind::Type => {}
}
if let hir::ForeignItemKind::Fn(..) = nitem.kind {
- if tcx.is_intrinsic(def_id) {
- self.tables.is_intrinsic.set_nullable(def_id.index, true);
- }
+ self.tables.is_intrinsic.set(def_id.index, tcx.is_intrinsic(def_id));
}
}
}
type Value<'tcx> = LazyArray<T::Value<'tcx>>;
}
+impl<T> Default for LazyArray<T> {
+ fn default() -> LazyArray<T> {
+ LazyArray::from_position_and_num_elems(NonZeroUsize::new(1).unwrap(), 0)
+ }
+}
+
impl<T> LazyArray<T> {
fn from_position_and_num_elems(position: NonZeroUsize, num_elems: usize) -> LazyArray<T> {
LazyArray { position, num_elems, _marker: PhantomData }
}
-
- fn empty() -> LazyArray<T> {
- LazyArray::from_position_and_num_elems(NonZeroUsize::new(1).unwrap(), 0)
- }
}
/// A list of lazily-decoded values, with the added capability of random access.
/// Define `LazyTables` and `TableBuilders` at the same time.
macro_rules! define_tables {
(
- - nullable: $($name1:ident: Table<$IDX1:ty, $T1:ty>,)+
+ - defaulted: $($name1:ident: Table<$IDX1:ty, $T1:ty>,)+
- optional: $($name2:ident: Table<$IDX2:ty, $T2:ty>,)+
) => {
#[derive(MetadataEncodable, MetadataDecodable)]
}
define_tables! {
-- nullable:
+- defaulted:
is_intrinsic: Table<DefIndex, bool>,
is_macro_rules: Table<DefIndex, bool>,
is_type_alias_impl_trait: Table<DefIndex, bool>,
attr_flags: Table<DefIndex, AttrFlags>,
+ def_path_hashes: Table<DefIndex, DefPathHash>,
+ explicit_item_bounds: Table<DefIndex, LazyArray<(ty::Predicate<'static>, Span)>>,
+ inferred_outlives_of: Table<DefIndex, LazyArray<(ty::Clause<'static>, Span)>>,
+ inherent_impls: Table<DefIndex, LazyArray<DefIndex>>,
- optional:
attributes: Table<DefIndex, LazyArray<ast::Attribute>>,
lookup_const_stability: Table<DefIndex, LazyValue<attr::ConstStability>>,
lookup_default_body_stability: Table<DefIndex, LazyValue<attr::DefaultBodyStability>>,
lookup_deprecation_entry: Table<DefIndex, LazyValue<attr::Deprecation>>,
- // As an optimization, a missing entry indicates an empty `&[]`.
- explicit_item_bounds: Table<DefIndex, LazyArray<(ty::Predicate<'static>, Span)>>,
explicit_predicates_of: Table<DefIndex, LazyValue<ty::GenericPredicates<'static>>>,
generics_of: Table<DefIndex, LazyValue<ty::Generics>>,
- // As an optimization, a missing entry indicates an empty `&[]`.
- inferred_outlives_of: Table<DefIndex, LazyArray<(ty::Clause<'static>, Span)>>,
super_predicates_of: Table<DefIndex, LazyValue<ty::GenericPredicates<'static>>>,
type_of: Table<DefIndex, LazyValue<Ty<'static>>>,
variances_of: Table<DefIndex, LazyArray<ty::Variance>>,
generator_kind: Table<DefIndex, LazyValue<hir::GeneratorKind>>,
trait_def: Table<DefIndex, LazyValue<ty::TraitDef>>,
trait_item_def_id: Table<DefIndex, RawDefId>,
- inherent_impls: Table<DefIndex, LazyArray<DefIndex>>,
expn_that_defined: Table<DefIndex, LazyValue<ExpnId>>,
unused_generic_params: Table<DefIndex, LazyValue<UnusedGenericParams>>,
params_in_repr: Table<DefIndex, LazyValue<BitSet<u32>>>,
// `DefPathTable` up front, since we may only ever use a few
// definitions from any given crate.
def_keys: Table<DefIndex, LazyValue<DefKey>>,
- def_path_hashes: Table<DefIndex, DefPathHash>,
proc_macro_quoted_spans: Table<usize, LazyValue<Span>>,
generator_diagnostic_data: Table<DefIndex, LazyValue<GeneratorDiagnosticData<'static>>>,
variant_data: Table<DefIndex, LazyValue<VariantData>>,
use std::marker::PhantomData;
use std::num::NonZeroUsize;
+pub(super) trait IsDefault: Default {
+ fn is_default(&self) -> bool;
+}
+
+impl<T> IsDefault for Option<T> {
+ fn is_default(&self) -> bool {
+ self.is_none()
+ }
+}
+
+impl IsDefault for AttrFlags {
+ fn is_default(&self) -> bool {
+ self.is_empty()
+ }
+}
+
+impl IsDefault for bool {
+ fn is_default(&self) -> bool {
+ !self
+ }
+}
+
+impl IsDefault for u32 {
+ fn is_default(&self) -> bool {
+ *self == 0
+ }
+}
+
+impl<T> IsDefault for LazyArray<T> {
+ fn is_default(&self) -> bool {
+ self.num_elems == 0
+ }
+}
+
+impl IsDefault for DefPathHash {
+ fn is_default(&self) -> bool {
+ self.0 == Fingerprint::ZERO
+ }
+}
+
/// Helper trait, for encoding to, and decoding from, a fixed number of bytes.
/// Used mainly for Lazy positions and lengths.
/// Unchecked invariant: `Self::default()` should encode as `[0; BYTE_LEN]`,
/// but this has no impact on safety.
-pub(super) trait FixedSizeEncoding: Default {
+pub(super) trait FixedSizeEncoding: IsDefault {
/// This should be `[u8; BYTE_LEN]`;
/// Cannot use an associated `const BYTE_LEN: usize` instead due to const eval limitations.
type ByteArray;
fn write_to_bytes(self, b: &mut Self::ByteArray);
}
+/// This implementation is not used generically, but for reading/writing
+/// concrete `u32` fields in `Lazy*` structures, which may be zero.
impl FixedSizeEncoding for u32 {
type ByteArray = [u8; 4];
fn write_to_bytes(self, b: &mut [u8;1]) {
use $ty::*;
b[0] = match self {
- None => 0,
+ None => unreachable!(),
$(Some($($pat)*) => 1 + ${index()},)*
}
}
}
// We directly encode `DefPathHash` because a `LazyValue` would incur a 25% cost.
-impl FixedSizeEncoding for Option<DefPathHash> {
+impl FixedSizeEncoding for DefPathHash {
type ByteArray = [u8; 16];
#[inline]
fn from_bytes(b: &[u8; 16]) -> Self {
- Some(DefPathHash(Fingerprint::from_le_bytes(*b)))
+ DefPathHash(Fingerprint::from_le_bytes(*b))
}
#[inline]
fn write_to_bytes(self, b: &mut [u8; 16]) {
- let Some(DefPathHash(fingerprint)) = self else {
- panic!("Trying to encode absent DefPathHash.")
- };
- *b = fingerprint.to_le_bytes();
+ debug_assert!(!self.is_default());
+ *b = self.0.to_le_bytes();
}
}
#[inline]
fn from_bytes(b: &[u8; 8]) -> Self {
let krate = u32::from_le_bytes(b[0..4].try_into().unwrap());
- let index = u32::from_le_bytes(b[4..8].try_into().unwrap());
if krate == 0 {
return None;
}
+ let index = u32::from_le_bytes(b[4..8].try_into().unwrap());
Some(RawDefId { krate: krate - 1, index })
}
#[inline]
fn write_to_bytes(self, b: &mut [u8; 8]) {
match self {
- None => *b = [0; 8],
+ None => unreachable!(),
Some(RawDefId { krate, index }) => {
// CrateNum is less than `CrateNum::MAX_AS_U32`.
debug_assert!(krate < u32::MAX);
#[inline]
fn write_to_bytes(self, b: &mut [u8; 1]) {
+ debug_assert!(!self.is_default());
b[0] = self.bits();
}
}
#[inline]
fn write_to_bytes(self, b: &mut [u8; 1]) {
+ debug_assert!(!self.is_default());
b[0] = self as u8
}
}
#[inline]
fn write_to_bytes(self, b: &mut [u8; 4]) {
- let position = self.map_or(0, |lazy| lazy.position.get());
+ match self {
+ None => unreachable!(),
+ Some(lazy) => {
+ let position = lazy.position.get();
+ let position: u32 = position.try_into().unwrap();
+ position.write_to_bytes(b)
+ }
+ }
+ }
+}
+
+impl<T> LazyArray<T> {
+ #[inline]
+ fn write_to_bytes_impl(self, b: &mut [u8; 8]) {
+ let ([position_bytes, meta_bytes],[])= b.as_chunks_mut::<4>() else { panic!() };
+
+ let position = self.position.get();
let position: u32 = position.try_into().unwrap();
- position.write_to_bytes(b)
+ position.write_to_bytes(position_bytes);
+
+ let len = self.num_elems;
+ let len: u32 = len.try_into().unwrap();
+ len.write_to_bytes(meta_bytes);
+ }
+
+ fn from_bytes_impl(position_bytes: &[u8; 4], meta_bytes: &[u8; 4]) -> Option<LazyArray<T>> {
+ let position = NonZeroUsize::new(u32::from_bytes(position_bytes) as usize)?;
+ let len = u32::from_bytes(meta_bytes) as usize;
+ Some(LazyArray::from_position_and_num_elems(position, len))
}
}
-impl<T> FixedSizeEncoding for Option<LazyArray<T>> {
+impl<T> FixedSizeEncoding for LazyArray<T> {
type ByteArray = [u8; 8];
#[inline]
fn from_bytes(b: &[u8; 8]) -> Self {
- let ([ref position_bytes, ref meta_bytes],[])= b.as_chunks::<4>() else { panic!() };
- let position = NonZeroUsize::new(u32::from_bytes(position_bytes) as usize)?;
- let len = u32::from_bytes(meta_bytes) as usize;
- Some(LazyArray::from_position_and_num_elems(position, len))
+ let ([position_bytes, meta_bytes],[])= b.as_chunks::<4>() else { panic!() };
+ if *meta_bytes == [0; 4] {
+ return Default::default();
+ }
+ LazyArray::from_bytes_impl(position_bytes, meta_bytes).unwrap()
}
#[inline]
fn write_to_bytes(self, b: &mut [u8; 8]) {
- let ([ref mut position_bytes, ref mut meta_bytes],[])= b.as_chunks_mut::<4>() else { panic!() };
+ assert!(!self.is_default());
+ self.write_to_bytes_impl(b)
+ }
+}
- let position = self.map_or(0, |lazy| lazy.position.get());
- let position: u32 = position.try_into().unwrap();
- position.write_to_bytes(position_bytes);
+impl<T> FixedSizeEncoding for Option<LazyArray<T>> {
+ type ByteArray = [u8; 8];
- let len = self.map_or(0, |lazy| lazy.num_elems);
- let len: u32 = len.try_into().unwrap();
- len.write_to_bytes(meta_bytes);
+ #[inline]
+ fn from_bytes(b: &[u8; 8]) -> Self {
+ let ([position_bytes, meta_bytes],[])= b.as_chunks::<4>() else { panic!() };
+ LazyArray::from_bytes_impl(position_bytes, meta_bytes)
+ }
+
+ #[inline]
+ fn write_to_bytes(self, b: &mut [u8; 8]) {
+ match self {
+ None => unreachable!(),
+ Some(lazy) => lazy.write_to_bytes_impl(b),
+ }
}
}
where
Option<T>: FixedSizeEncoding<ByteArray = [u8; N]>,
{
- pub(crate) fn set(&mut self, i: I, value: T) {
- self.set_nullable(i, Some(value))
+ pub(crate) fn set_some(&mut self, i: I, value: T) {
+ self.set(i, Some(value))
}
}
impl<I: Idx, const N: usize, T: FixedSizeEncoding<ByteArray = [u8; N]>> TableBuilder<I, T> {
- pub(crate) fn set_nullable(&mut self, i: I, value: T) {
- // FIXME(eddyb) investigate more compact encodings for sparse tables.
- // On the PR @michaelwoerister mentioned:
- // > Space requirements could perhaps be optimized by using the HAMT `popcnt`
- // > trick (i.e. divide things into buckets of 32 or 64 items and then
- // > store bit-masks of which item in each bucket is actually serialized).
- self.blocks.ensure_contains_elem(i, || [0; N]);
- value.write_to_bytes(&mut self.blocks[i]);
+ /// Sets the table value if it is not default.
+ /// ATTENTION: For optimization default values are simply ignored by this function, because
+ /// right now metadata tables never need to reset non-default values to default. If such need
+ /// arises in the future then a new method (e.g. `clear` or `reset`) will need to be introduced
+ /// for doing that explicitly.
+ pub(crate) fn set(&mut self, i: I, value: T) {
+ if !value.is_default() {
+ // FIXME(eddyb) investigate more compact encodings for sparse tables.
+ // On the PR @michaelwoerister mentioned:
+ // > Space requirements could perhaps be optimized by using the HAMT `popcnt`
+ // > trick (i.e. divide things into buckets of 32 or 64 items and then
+ // > store bit-masks of which item in each bucket is actually serialized).
+ self.blocks.ensure_contains_elem(i, || [0; N]);
+ value.write_to_bytes(&mut self.blocks[i]);
+ }
}
pub(crate) fn encode(&self, buf: &mut FileEncoder) -> LazyTable<I, T> {
let start = self.position.get();
let bytes = &metadata.blob()[start..start + self.encoded_size];
let (bytes, []) = bytes.as_chunks::<N>() else { panic!() };
- match bytes.get(i.index()) {
- Some(bytes) => FixedSizeEncoding::from_bytes(bytes),
- None => FixedSizeEncoding::from_bytes(&[0; N]),
- }
+ bytes.get(i.index()).map_or_else(Default::default, FixedSizeEncoding::from_bytes)
}
/// Size of the table in entries, including possible gaps.
[decode] trait_impl_trait_tys: rustc_data_structures::fx::FxHashMap<rustc_hir::def_id::DefId, rustc_middle::ty::Ty<'tcx>>,
[] bit_set_u32: rustc_index::bit_set::BitSet<u32>,
+ [] external_constraints: rustc_middle::traits::solve::ExternalConstraintsData<'tcx>,
]);
)
}
#[track_caller]
pub fn parent_id(self, hir_id: HirId) -> HirId {
self.opt_parent_id(hir_id)
- .unwrap_or_else(|| bug!("No parent for node {:?}", self.node_to_string(hir_id)))
+ .unwrap_or_else(|| bug!("No parent for node {}", self.node_to_string(hir_id)))
}
pub fn get_parent(self, hir_id: HirId) -> Node<'hir> {
}
fn hir_id_to_string(map: Map<'_>, id: HirId) -> String {
- let id_str = format!(" (hir_id={})", id);
-
let path_str = |def_id: LocalDefId| map.tcx.def_path_str(def_id.to_def_id());
let span_str = || map.tcx.sess.source_map().span_to_snippet(map.span(id)).unwrap_or_default();
- let node_str = |prefix| format!("{} {}{}", prefix, span_str(), id_str);
+ let node_str = |prefix| format!("{id} ({prefix} `{}`)", span_str());
match map.find(id) {
Some(Node::Item(item)) => {
ItemKind::TraitAlias(..) => "trait alias",
ItemKind::Impl { .. } => "impl",
};
- format!("{} {}{}", item_str, path_str(item.owner_id.def_id), id_str)
+ format!("{id} ({item_str} {})", path_str(item.owner_id.def_id))
}
Some(Node::ForeignItem(item)) => {
- format!("foreign item {}{}", path_str(item.owner_id.def_id), id_str)
+ format!("{id} (foreign item {})", path_str(item.owner_id.def_id))
}
Some(Node::ImplItem(ii)) => {
let kind = match ii.kind {
ImplItemKind::Fn(..) => "method",
ImplItemKind::Type(_) => "assoc type",
};
- format!("{} {} in {}{}", kind, ii.ident, path_str(ii.owner_id.def_id), id_str)
+ format!("{id} ({kind} `{}` in {})", ii.ident, path_str(ii.owner_id.def_id))
}
Some(Node::TraitItem(ti)) => {
let kind = match ti.kind {
TraitItemKind::Type(..) => "assoc type",
};
- format!("{} {} in {}{}", kind, ti.ident, path_str(ti.owner_id.def_id), id_str)
+ format!("{id} ({kind} `{}` in {})", ti.ident, path_str(ti.owner_id.def_id))
}
Some(Node::Variant(ref variant)) => {
- format!("variant {} in {}{}", variant.ident, path_str(variant.def_id), id_str)
+ format!("{id} (variant `{}` in {})", variant.ident, path_str(variant.def_id))
}
Some(Node::Field(ref field)) => {
- format!("field {} in {}{}", field.ident, path_str(field.def_id), id_str)
+ format!("{id} (field `{}` in {})", field.ident, path_str(field.def_id))
}
Some(Node::AnonConst(_)) => node_str("const"),
Some(Node::Expr(_)) => node_str("expr"),
Some(Node::Infer(_)) => node_str("infer"),
Some(Node::Local(_)) => node_str("local"),
Some(Node::Ctor(ctor)) => format!(
- "ctor {}{}",
+ "{id} (ctor {})",
ctor.ctor_def_id().map_or("<missing path>".into(), |def_id| path_str(def_id)),
- id_str
),
Some(Node::Lifetime(_)) => node_str("lifetime"),
Some(Node::GenericParam(ref param)) => {
- format!("generic_param {}{}", path_str(param.def_id), id_str)
+ format!("{id} (generic_param {})", path_str(param.def_id))
}
- Some(Node::Crate(..)) => String::from("root_crate"),
- None => format!("unknown node{}", id_str),
+ Some(Node::Crate(..)) => String::from("(root_crate)"),
+ None => format!("{id} (unknown node)"),
}
}
AggregateKind::Closure(def_id, substs) => ty::tls::with(|tcx| {
let name = if tcx.sess.opts.unstable_opts.span_free_formats {
let substs = tcx.lift(substs).unwrap();
- format!(
- "[closure@{}]",
- tcx.def_path_str_with_substs(def_id.to_def_id(), substs),
- )
+ format!("[closure@{}]", tcx.def_path_str_with_substs(def_id, substs),)
} else {
let span = tcx.def_span(def_id);
format!(
let mut struct_fmt = fmt.debug_struct(&name);
// FIXME(project-rfc-2229#48): This should be a list of capture names/places
- if let Some(upvars) = tcx.upvars_mentioned(def_id) {
+ if let Some(def_id) = def_id.as_local()
+ && let Some(upvars) = tcx.upvars_mentioned(def_id)
+ {
for (&var_id, place) in iter::zip(upvars.keys(), places) {
let var_name = tcx.hir().name(var_id);
struct_fmt.field(var_name.as_str(), place);
}
+ } else {
+ for (index, place) in places.iter().enumerate() {
+ struct_fmt.field(&format!("{index}"), place);
+ }
}
struct_fmt.finish()
let mut struct_fmt = fmt.debug_struct(&name);
// FIXME(project-rfc-2229#48): This should be a list of capture names/places
- if let Some(upvars) = tcx.upvars_mentioned(def_id) {
+ if let Some(def_id) = def_id.as_local()
+ && let Some(upvars) = tcx.upvars_mentioned(def_id)
+ {
for (&var_id, place) in iter::zip(upvars.keys(), places) {
let var_name = tcx.hir().name(var_id);
struct_fmt.field(var_name.as_str(), place);
}
+ } else {
+ for (index, place) in places.iter().enumerate() {
+ struct_fmt.field(&format!("{index}"), place);
+ }
}
struct_fmt.finish()
/// active field index would identity the field `c`
Adt(DefId, VariantIdx, SubstsRef<'tcx>, Option<UserTypeAnnotationIndex>, Option<usize>),
- // Note: We can use LocalDefId since closures and generators a deaggregated
- // before codegen.
- Closure(LocalDefId, SubstsRef<'tcx>),
- Generator(LocalDefId, SubstsRef<'tcx>, hir::Movability),
+ Closure(DefId, SubstsRef<'tcx>),
+ Generator(DefId, SubstsRef<'tcx>, hir::Movability),
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, TyEncodable, TyDecodable, Hash, HashStable)]
AggregateKind::Adt(did, _, substs, _, _) => {
tcx.bound_type_of(did).subst(tcx, substs)
}
- AggregateKind::Closure(did, substs) => tcx.mk_closure(did.to_def_id(), substs),
+ AggregateKind::Closure(did, substs) => tcx.mk_closure(did, substs),
AggregateKind::Generator(did, substs, movability) => {
- tcx.mk_generator(did.to_def_id(), substs, movability)
+ tcx.mk_generator(did, substs, movability)
}
},
Rvalue::ShallowInitBox(_, ty) => tcx.mk_box(ty),
mod chalk;
pub mod query;
pub mod select;
+pub mod solve;
pub mod specialization_graph;
mod structural_impls;
pub mod util;
pub struct ImplDerivedObligationCause<'tcx> {
pub derived: DerivedObligationCause<'tcx>,
pub impl_def_id: DefId,
+ /// The index of the derived predicate in the parent impl's predicates.
+ pub impl_def_predicate_index: Option<usize>,
pub span: Span,
}
--- /dev/null
+use std::ops::ControlFlow;
+
+use rustc_data_structures::intern::Interned;
+
+use crate::ty::{FallibleTypeFolder, Ty, TypeFoldable, TypeFolder, TypeVisitable, TypeVisitor};
+
+#[derive(Debug, PartialEq, Eq, Copy, Clone, Hash)]
+pub struct ExternalConstraints<'tcx>(pub(crate) Interned<'tcx, ExternalConstraintsData<'tcx>>);
+
+impl<'tcx> std::ops::Deref for ExternalConstraints<'tcx> {
+ type Target = ExternalConstraintsData<'tcx>;
+
+ fn deref(&self) -> &Self::Target {
+ &*self.0
+ }
+}
+
+/// Additional constraints returned on success.
+#[derive(Debug, PartialEq, Eq, Clone, Hash, Default)]
+pub struct ExternalConstraintsData<'tcx> {
+ // FIXME: implement this.
+ pub regions: (),
+ pub opaque_types: Vec<(Ty<'tcx>, Ty<'tcx>)>,
+}
+
+impl<'tcx> TypeFoldable<'tcx> for ExternalConstraints<'tcx> {
+ fn try_fold_with<F: FallibleTypeFolder<'tcx>>(self, folder: &mut F) -> Result<Self, F::Error> {
+ Ok(FallibleTypeFolder::tcx(folder).intern_external_constraints(ExternalConstraintsData {
+ regions: (),
+ opaque_types: self
+ .opaque_types
+ .iter()
+ .map(|opaque| opaque.try_fold_with(folder))
+ .collect::<Result<_, F::Error>>()?,
+ }))
+ }
+
+ fn fold_with<F: TypeFolder<'tcx>>(self, folder: &mut F) -> Self {
+ TypeFolder::tcx(folder).intern_external_constraints(ExternalConstraintsData {
+ regions: (),
+ opaque_types: self.opaque_types.iter().map(|opaque| opaque.fold_with(folder)).collect(),
+ })
+ }
+}
+
+impl<'tcx> TypeVisitable<'tcx> for ExternalConstraints<'tcx> {
+ fn visit_with<V: TypeVisitor<'tcx>>(
+ &self,
+ visitor: &mut V,
+ ) -> std::ops::ControlFlow<V::BreakTy> {
+ self.regions.visit_with(visitor)?;
+ self.opaque_types.visit_with(visitor)?;
+ ControlFlow::Continue(())
+ }
+}
};
use crate::thir::Thir;
use crate::traits;
+use crate::traits::solve::{ExternalConstraints, ExternalConstraintsData};
use crate::ty::query::{self, TyCtxtAt};
use crate::ty::{
self, AdtDef, AdtDefData, AdtKind, Binder, Const, ConstData, DefIdTree, FloatTy, FloatVar,
bound_variable_kinds: InternedSet<'tcx, List<ty::BoundVariableKind>>,
layout: InternedSet<'tcx, LayoutS<VariantIdx>>,
adt_def: InternedSet<'tcx, AdtDefData>,
+ external_constraints: InternedSet<'tcx, ExternalConstraintsData<'tcx>>,
}
impl<'tcx> CtxtInterners<'tcx> {
bound_variable_kinds: Default::default(),
layout: Default::default(),
adt_def: Default::default(),
+ external_constraints: Default::default(),
}
}
const_allocation: intern_const_alloc(Allocation): ConstAllocation -> ConstAllocation<'tcx>,
layout: intern_layout(LayoutS<VariantIdx>): Layout -> Layout<'tcx>,
adt_def: intern_adt_def(AdtDefData): AdtDef -> AdtDef<'tcx>,
+ external_constraints: intern_external_constraints(ExternalConstraintsData<'tcx>): ExternalConstraints -> ExternalConstraints<'tcx>,
}
macro_rules! slice_interners {
self.late_bound_vars_map(id.owner)
.and_then(|map| map.get(&id.local_id).cloned())
.unwrap_or_else(|| {
- bug!("No bound vars found for {:?} ({:?})", self.hir().node_to_string(id), id)
+ bug!("No bound vars found for {}", self.hir().node_to_string(id))
})
.iter(),
)
use std::ops::ControlFlow;
use crate::ty::{
- visit::TypeVisitable, AliasTy, Const, ConstKind, DefIdTree, InferConst, InferTy, Opaque,
- PolyTraitPredicate, Projection, Ty, TyCtxt, TypeSuperVisitable, TypeVisitor,
+ visit::TypeVisitable, AliasTy, Const, ConstKind, DefIdTree, FallibleTypeFolder, InferConst,
+ InferTy, Opaque, PolyTraitPredicate, Projection, Ty, TyCtxt, TypeFoldable, TypeSuperFoldable,
+ TypeSuperVisitable, TypeVisitor,
};
use rustc_data_structures::fx::FxHashMap;
}
}
-pub trait IsSuggestable<'tcx> {
+pub trait IsSuggestable<'tcx>: Sized {
/// Whether this makes sense to suggest in a diagnostic.
///
/// We filter out certain types and constants since they don't provide
/// Only if `infer_suggestable` is true, we consider type and const
/// inference variables to be suggestable.
fn is_suggestable(self, tcx: TyCtxt<'tcx>, infer_suggestable: bool) -> bool;
+
+ fn make_suggestable(self, tcx: TyCtxt<'tcx>, infer_suggestable: bool) -> Option<Self>;
}
impl<'tcx, T> IsSuggestable<'tcx> for T
where
- T: TypeVisitable<'tcx>,
+ T: TypeVisitable<'tcx> + TypeFoldable<'tcx>,
{
fn is_suggestable(self, tcx: TyCtxt<'tcx>, infer_suggestable: bool) -> bool {
self.visit_with(&mut IsSuggestableVisitor { tcx, infer_suggestable }).is_continue()
}
+
+ fn make_suggestable(self, tcx: TyCtxt<'tcx>, infer_suggestable: bool) -> Option<T> {
+ self.try_fold_with(&mut MakeSuggestableFolder { tcx, infer_suggestable }).ok()
+ }
}
pub fn suggest_arbitrary_trait_bound<'tcx>(
c.super_visit_with(self)
}
}
+
+pub struct MakeSuggestableFolder<'tcx> {
+ tcx: TyCtxt<'tcx>,
+ infer_suggestable: bool,
+}
+
+impl<'tcx> FallibleTypeFolder<'tcx> for MakeSuggestableFolder<'tcx> {
+ type Error = ();
+
+ fn tcx(&self) -> TyCtxt<'tcx> {
+ self.tcx
+ }
+
+ fn try_fold_ty(&mut self, t: Ty<'tcx>) -> Result<Ty<'tcx>, Self::Error> {
+ let t = match *t.kind() {
+ Infer(InferTy::TyVar(_)) if self.infer_suggestable => t,
+
+ FnDef(def_id, substs) => {
+ self.tcx.mk_fn_ptr(self.tcx.fn_sig(def_id).subst(self.tcx, substs))
+ }
+
+ // FIXME(compiler-errors): We could replace these with infer, I guess.
+ Closure(..)
+ | Infer(..)
+ | Generator(..)
+ | GeneratorWitness(..)
+ | Bound(_, _)
+ | Placeholder(_)
+ | Error(_) => {
+ return Err(());
+ }
+
+ Alias(Opaque, AliasTy { def_id, .. }) => {
+ let parent = self.tcx.parent(def_id);
+ if let hir::def::DefKind::TyAlias | hir::def::DefKind::AssocTy = self.tcx.def_kind(parent)
+ && let Alias(Opaque, AliasTy { def_id: parent_opaque_def_id, .. }) = *self.tcx.type_of(parent).kind()
+ && parent_opaque_def_id == def_id
+ {
+ t
+ } else {
+ return Err(());
+ }
+ }
+
+ Param(param) => {
+ // FIXME: It would be nice to make this not use string manipulation,
+ // but it's pretty hard to do this, since `ty::ParamTy` is missing
+ // sufficient info to determine if it is synthetic, and we don't
+ // always have a convenient way of getting `ty::Generics` at the call
+ // sites we invoke `IsSuggestable::is_suggestable`.
+ if param.name.as_str().starts_with("impl ") {
+ return Err(());
+ }
+
+ t
+ }
+
+ _ => t,
+ };
+
+ t.try_super_fold_with(self)
+ }
+
+ fn try_fold_const(&mut self, c: Const<'tcx>) -> Result<Const<'tcx>, ()> {
+ let c = match c.kind() {
+ ConstKind::Infer(InferConst::Var(_)) if self.infer_suggestable => c,
+
+ ConstKind::Infer(..)
+ | ConstKind::Bound(..)
+ | ConstKind::Placeholder(..)
+ | ConstKind::Error(..) => {
+ return Err(());
+ }
+
+ _ => c,
+ };
+
+ c.try_super_fold_with(self)
+ }
+}
/// the infallible methods of this trait to ensure that the two APIs
/// are coherent.
pub trait TypeFolder<'tcx>: FallibleTypeFolder<'tcx, Error = !> {
- fn tcx<'a>(&'a self) -> TyCtxt<'tcx>;
+ fn tcx(&self) -> TyCtxt<'tcx>;
fn fold_binder<T>(&mut self, t: Binder<'tcx, T>) -> Binder<'tcx, T>
where
let tcx = cx.tcx();
let param_env = cx.param_env();
- let pointee_info =
- match *this.ty.kind() {
- ty::RawPtr(mt) if offset.bytes() == 0 => {
- tcx.layout_of(param_env.and(mt.ty)).ok().map(|layout| PointeeInfo {
- size: layout.size,
- align: layout.align.abi,
- safe: None,
- })
- }
- ty::FnPtr(fn_sig) if offset.bytes() == 0 => {
- tcx.layout_of(param_env.and(tcx.mk_fn_ptr(fn_sig))).ok().map(|layout| {
- PointeeInfo { size: layout.size, align: layout.align.abi, safe: None }
- })
- }
- ty::Ref(_, ty, mt) if offset.bytes() == 0 => {
- let kind = if tcx.sess.opts.optimize == OptLevel::No {
- // Use conservative pointer kind if not optimizing. This saves us the
- // Freeze/Unpin queries, and can save time in the codegen backend (noalias
- // attributes in LLVM have compile-time cost even in unoptimized builds).
- PointerKind::SharedMutable
- } else {
- match mt {
- hir::Mutability::Not => {
- if ty.is_freeze(tcx, cx.param_env()) {
- PointerKind::Frozen
- } else {
- PointerKind::SharedMutable
- }
- }
- hir::Mutability::Mut => {
- // References to self-referential structures should not be considered
- // noalias, as another pointer to the structure can be obtained, that
- // is not based-on the original reference. We consider all !Unpin
- // types to be potentially self-referential here.
- if ty.is_unpin(tcx, cx.param_env()) {
- PointerKind::UniqueBorrowed
- } else {
- PointerKind::UniqueBorrowedPinned
- }
- }
- }
- };
+ let pointee_info = match *this.ty.kind() {
+ ty::RawPtr(mt) if offset.bytes() == 0 => {
+ tcx.layout_of(param_env.and(mt.ty)).ok().map(|layout| PointeeInfo {
+ size: layout.size,
+ align: layout.align.abi,
+ safe: None,
+ })
+ }
+ ty::FnPtr(fn_sig) if offset.bytes() == 0 => {
+ tcx.layout_of(param_env.and(tcx.mk_fn_ptr(fn_sig))).ok().map(|layout| PointeeInfo {
+ size: layout.size,
+ align: layout.align.abi,
+ safe: None,
+ })
+ }
+ ty::Ref(_, ty, mt) if offset.bytes() == 0 => {
+ // Use conservative pointer kind if not optimizing. This saves us the
+ // Freeze/Unpin queries, and can save time in the codegen backend (noalias
+ // attributes in LLVM have compile-time cost even in unoptimized builds).
+ let optimize = tcx.sess.opts.optimize != OptLevel::No;
+ let kind = match mt {
+ hir::Mutability::Not => PointerKind::SharedRef {
+ frozen: optimize && ty.is_freeze(tcx, cx.param_env()),
+ },
+ hir::Mutability::Mut => PointerKind::MutableRef {
+ unpin: optimize && ty.is_unpin(tcx, cx.param_env()),
+ },
+ };
- tcx.layout_of(param_env.and(ty)).ok().map(|layout| PointeeInfo {
- size: layout.size,
- align: layout.align.abi,
- safe: Some(kind),
- })
- }
+ tcx.layout_of(param_env.and(ty)).ok().map(|layout| PointeeInfo {
+ size: layout.size,
+ align: layout.align.abi,
+ safe: Some(kind),
+ })
+ }
- _ => {
- let mut data_variant = match this.variants {
- // Within the discriminant field, only the niche itself is
- // always initialized, so we only check for a pointer at its
- // offset.
- //
- // If the niche is a pointer, it's either valid (according
- // to its type), or null (which the niche field's scalar
- // validity range encodes). This allows using
- // `dereferenceable_or_null` for e.g., `Option<&T>`, and
- // this will continue to work as long as we don't start
- // using more niches than just null (e.g., the first page of
- // the address space, or unaligned pointers).
- Variants::Multiple {
- tag_encoding: TagEncoding::Niche { untagged_variant, .. },
- tag_field,
- ..
- } if this.fields.offset(tag_field) == offset => {
- Some(this.for_variant(cx, untagged_variant))
- }
- _ => Some(this),
- };
+ _ => {
+ let mut data_variant = match this.variants {
+ // Within the discriminant field, only the niche itself is
+ // always initialized, so we only check for a pointer at its
+ // offset.
+ //
+ // If the niche is a pointer, it's either valid (according
+ // to its type), or null (which the niche field's scalar
+ // validity range encodes). This allows using
+ // `dereferenceable_or_null` for e.g., `Option<&T>`, and
+ // this will continue to work as long as we don't start
+ // using more niches than just null (e.g., the first page of
+ // the address space, or unaligned pointers).
+ Variants::Multiple {
+ tag_encoding: TagEncoding::Niche { untagged_variant, .. },
+ tag_field,
+ ..
+ } if this.fields.offset(tag_field) == offset => {
+ Some(this.for_variant(cx, untagged_variant))
+ }
+ _ => Some(this),
+ };
- if let Some(variant) = data_variant {
- // We're not interested in any unions.
- if let FieldsShape::Union(_) = variant.fields {
- data_variant = None;
- }
+ if let Some(variant) = data_variant {
+ // We're not interested in any unions.
+ if let FieldsShape::Union(_) = variant.fields {
+ data_variant = None;
}
+ }
- let mut result = None;
-
- if let Some(variant) = data_variant {
- // FIXME(erikdesjardins): handle non-default addrspace ptr sizes
- // (requires passing in the expected address space from the caller)
- let ptr_end = offset + Pointer(AddressSpace::DATA).size(cx);
- for i in 0..variant.fields.count() {
- let field_start = variant.fields.offset(i);
- if field_start <= offset {
- let field = variant.field(cx, i);
- result = field.to_result().ok().and_then(|field| {
- if ptr_end <= field_start + field.size {
- // We found the right field, look inside it.
- let field_info =
- field.pointee_info_at(cx, offset - field_start);
- field_info
- } else {
- None
- }
- });
- if result.is_some() {
- break;
+ let mut result = None;
+
+ if let Some(variant) = data_variant {
+ // FIXME(erikdesjardins): handle non-default addrspace ptr sizes
+ // (requires passing in the expected address space from the caller)
+ let ptr_end = offset + Pointer(AddressSpace::DATA).size(cx);
+ for i in 0..variant.fields.count() {
+ let field_start = variant.fields.offset(i);
+ if field_start <= offset {
+ let field = variant.field(cx, i);
+ result = field.to_result().ok().and_then(|field| {
+ if ptr_end <= field_start + field.size {
+ // We found the right field, look inside it.
+ let field_info =
+ field.pointee_info_at(cx, offset - field_start);
+ field_info
+ } else {
+ None
}
+ });
+ if result.is_some() {
+ break;
}
}
}
+ }
- // FIXME(eddyb) This should be for `ptr::Unique<T>`, not `Box<T>`.
- if let Some(ref mut pointee) = result {
- if let ty::Adt(def, _) = this.ty.kind() {
- if def.is_box() && offset.bytes() == 0 {
- pointee.safe = Some(PointerKind::UniqueOwned);
- }
+ // FIXME(eddyb) This should be for `ptr::Unique<T>`, not `Box<T>`.
+ if let Some(ref mut pointee) = result {
+ if let ty::Adt(def, _) = this.ty.kind() {
+ if def.is_box() && offset.bytes() == 0 {
+ let optimize = tcx.sess.opts.optimize != OptLevel::No;
+ pointee.safe = Some(PointerKind::Box {
+ unpin: optimize && this.ty.boxed_ty().is_unpin(tcx, cx.param_env()),
+ });
}
}
-
- result
}
- };
+
+ result
+ }
+ };
debug!(
"pointee_info_at (offset={:?}, type kind: {:?}) => {:?}",
p!(")")
}
ty::FnDef(def_id, substs) => {
- let sig = self.tcx().fn_sig(def_id).subst(self.tcx(), substs);
- p!(print(sig), " {{", print_value_path(def_id, substs), "}}");
+ if NO_QUERIES.with(|q| q.get()) {
+ p!(print_def_path(def_id, substs));
+ } else {
+ let sig = self.tcx().fn_sig(def_id).subst(self.tcx(), substs);
+ p!(print(sig), " {{", print_value_path(def_id, substs), "}}");
+ }
}
ty::FnPtr(ref bare_fn) => p!(print(bare_fn)),
ty::Infer(infer_ty) => {
}
ty::Placeholder(placeholder) => p!(write("Placeholder({:?})", placeholder)),
ty::Alias(ty::Opaque, ty::AliasTy { def_id, substs, .. }) => {
- // FIXME(eddyb) print this with `print_def_path`.
// We use verbose printing in 'NO_QUERIES' mode, to
// avoid needing to call `predicates_of`. This should
// only affect certain debug messages (e.g. messages printed
// from `rustc_middle::ty` during the computation of `tcx.predicates_of`),
// and should have no effect on any compiler output.
- if self.should_print_verbose() || NO_QUERIES.with(|q| q.get()) {
+ if self.should_print_verbose() {
+ // FIXME(eddyb) print this with `print_def_path`.
p!(write("Opaque({:?}, {:?})", def_id, substs));
return Ok(self);
}
let parent = self.tcx().parent(def_id);
match self.tcx().def_kind(parent) {
DefKind::TyAlias | DefKind::AssocTy => {
+ // NOTE: I know we should check for NO_QUERIES here, but it's alright.
+ // `type_of` on a type alias or assoc type should never cause a cycle.
if let ty::Alias(ty::Opaque, ty::AliasTy { def_id: d, .. }) =
*self.tcx().type_of(parent).kind()
{
p!(print_def_path(def_id, substs));
return Ok(self);
}
- _ => return self.pretty_print_opaque_impl_type(def_id, substs),
+ _ => {
+ if NO_QUERIES.with(|q| q.get()) {
+ p!(print_def_path(def_id, &[]));
+ return Ok(self);
+ } else {
+ return self.pretty_print_opaque_impl_type(def_id, substs);
+ }
+ }
}
}
ty::Str => p!("str"),
}
}
-/// Helper for `TyCtxtEnsure` to avoid a closure.
-#[inline(always)]
-fn noop<T>(_: &T) {}
-
-/// Helper to ensure that queries only return `Copy` types.
-#[inline(always)]
-fn copy<T: Copy>(x: &T) -> T {
- *x
-}
-
macro_rules! query_helper_param_ty {
(DefId) => { impl IntoQueryParam<DefId> };
(LocalDefId) => { impl IntoQueryParam<LocalDefId> };
let key = key.into_query_param();
opt_remap_env_constness!([$($modifiers)*][key]);
- let cached = try_get_cached(self.tcx, &self.tcx.query_caches.$name, &key, noop);
-
- match cached {
- Ok(()) => return,
- Err(()) => (),
- }
-
- self.tcx.queries.$name(self.tcx, DUMMY_SP, key, QueryMode::Ensure);
+ match try_get_cached(self.tcx, &self.tcx.query_caches.$name, &key) {
+ Some(_) => return,
+ None => self.tcx.queries.$name(self.tcx, DUMMY_SP, key, QueryMode::Ensure),
+ };
})*
}
let key = key.into_query_param();
opt_remap_env_constness!([$($modifiers)*][key]);
- let cached = try_get_cached(self.tcx, &self.tcx.query_caches.$name, &key, copy);
-
- match cached {
- Ok(value) => return value,
- Err(()) => (),
+ match try_get_cached(self.tcx, &self.tcx.query_caches.$name, &key) {
+ Some(value) => value,
+ None => self.tcx.queries.$name(self.tcx, self.span, key, QueryMode::Get).unwrap(),
}
-
- self.tcx.queries.$name(self.tcx, self.span, key, QueryMode::Get).unwrap()
})*
}
let tcx = self.tcx;
let cache = &tcx.query_caches.$name;
- let cached = try_get_cached(tcx, cache, &key, copy);
-
- match cached {
- Ok(old) => {
+ match try_get_cached(tcx, cache, &key) {
+ Some(old) => {
bug!(
"Trying to feed an already recorded value for query {} key={key:?}:\nold value: {old:?}\nnew value: {value:?}",
stringify!($name),
+ )
+ }
+ None => {
+ let dep_node = dep_graph::DepNode::construct(tcx, dep_graph::DepKind::$name, &key);
+ let dep_node_index = tcx.dep_graph.with_feed_task(
+ dep_node,
+ tcx,
+ key,
+ &value,
+ hash_result!([$($modifiers)*]),
);
+ cache.complete(key, value, dep_node_index)
}
- Err(()) => (),
}
-
- let dep_node = dep_graph::DepNode::construct(tcx, dep_graph::DepKind::$name, &key);
- let dep_node_index = tcx.dep_graph.with_feed_task(
- dep_node,
- tcx,
- key,
- &value,
- hash_result!([$($modifiers)*]),
- );
- cache.complete(key, value, dep_node_index)
}
})*
}
b = tcx.expand_abstract_consts(b);
}
+ debug!("{}.super_relate_consts(normed_a = {:?}, normed_b = {:?})", relation.tag(), a, b);
+
// Currently, the values that can be unified are primitive types,
// and those that derive both `PartialEq` and `Eq`, corresponding
// to structural-match types.
// FIXME(generic_const_exprs): is it possible to relate two consts which are not identical
// exprs? Should we care about that?
+ // FIXME(generic_const_exprs): relating the `ty()`s is a little weird since it is supposed to
+ // ICE If they mismatch. Unfortunately `ConstKind::Expr` is a little special and can be thought
+ // of as being generic over the argument types, however this is implicit so these types don't get
+ // related when we relate the substs of the item this const arg is for.
let expr = match (ae, be) {
- (Expr::Binop(a_op, al, ar), Expr::Binop(b_op, bl, br))
- if a_op == b_op && al.ty() == bl.ty() && ar.ty() == br.ty() =>
- {
+ (Expr::Binop(a_op, al, ar), Expr::Binop(b_op, bl, br)) if a_op == b_op => {
+ r.relate(al.ty(), bl.ty())?;
+ r.relate(ar.ty(), br.ty())?;
Expr::Binop(a_op, r.consts(al, bl)?, r.consts(ar, br)?)
}
- (Expr::UnOp(a_op, av), Expr::UnOp(b_op, bv))
- if a_op == b_op && av.ty() == bv.ty() =>
- {
+ (Expr::UnOp(a_op, av), Expr::UnOp(b_op, bv)) if a_op == b_op => {
+ r.relate(av.ty(), bv.ty())?;
Expr::UnOp(a_op, r.consts(av, bv)?)
}
- (Expr::Cast(ak, av, at), Expr::Cast(bk, bv, bt))
- if ak == bk && av.ty() == bv.ty() =>
- {
+ (Expr::Cast(ak, av, at), Expr::Cast(bk, bv, bt)) if ak == bk => {
+ r.relate(av.ty(), bv.ty())?;
Expr::Cast(ak, r.consts(av, bv)?, r.tys(at, bt)?)
}
(Expr::FunctionCall(af, aa), Expr::FunctionCall(bf, ba))
- if aa.len() == ba.len()
- && af.ty() == bf.ty()
- && aa
- .iter()
- .zip(ba.iter())
- .all(|(a_arg, b_arg)| a_arg.ty() == b_arg.ty()) =>
+ if aa.len() == ba.len() =>
{
+ r.relate(af.ty(), bf.ty())?;
let func = r.consts(af, bf)?;
let mut related_args = Vec::with_capacity(aa.len());
for (a_arg, b_arg) in aa.iter().zip(ba.iter()) {
cf.is_break()
}
+ /// Checks whether a type recursively contains any closure
+ ///
+ /// Example: `Option<[closure@file.rs:4:20]>` returns true
+ pub fn contains_closure(self) -> bool {
+ struct ContainsClosureVisitor;
+
+ impl<'tcx> TypeVisitor<'tcx> for ContainsClosureVisitor {
+ type BreakTy = ();
+
+ fn visit_ty(&mut self, t: Ty<'tcx>) -> ControlFlow<Self::BreakTy> {
+ if let ty::Closure(_, _) = t.kind() {
+ ControlFlow::Break(())
+ } else {
+ t.super_visit_with(self)
+ }
+ }
+ }
+
+ let cf = self.visit_with(&mut ContainsClosureVisitor);
+ cf.is_break()
+ }
+
/// Returns the type and mutability of `*ty`.
///
/// The parameter `explicit` indicates if this is an *explicit* dereference.
pub fn node_type(&self, id: hir::HirId) -> Ty<'tcx> {
self.node_type_opt(id).unwrap_or_else(|| {
- bug!("node_type: no type for node `{}`", tls::with(|tcx| tcx.hir().node_to_string(id)))
+ bug!("node_type: no type for node {}", tls::with(|tcx| tcx.hir().node_to_string(id)))
})
}
fn invalid_hir_id_for_typeck_results(hir_owner: OwnerId, hir_id: hir::HirId) {
ty::tls::with(|tcx| {
bug!(
- "node {} with HirId::owner {:?} cannot be placed in TypeckResults with hir_owner {:?}",
+ "node {} cannot be placed in TypeckResults with hir_owner {:?}",
tcx.hir().node_to_string(hir_id),
- hir_id.owner,
hir_owner
)
});
either = "1"
rustc_middle = { path = "../rustc_middle" }
rustc_apfloat = { path = "../rustc_apfloat" }
-rustc_attr = { path = "../rustc_attr" }
rustc_data_structures = { path = "../rustc_data_structures" }
rustc_index = { path = "../rustc_index" }
rustc_errors = { path = "../rustc_errors" }
// We implicitly set the discriminant to 0. See
// librustc_mir/transform/deaggregator.rs for details.
let movability = movability.unwrap();
- Box::new(AggregateKind::Generator(closure_id, substs, movability))
+ Box::new(AggregateKind::Generator(
+ closure_id.to_def_id(),
+ substs,
+ movability,
+ ))
}
UpvarSubsts::Closure(substs) => {
- Box::new(AggregateKind::Closure(closure_id, substs))
+ Box::new(AggregateKind::Closure(closure_id.to_def_id(), substs))
}
};
block.and(Rvalue::Aggregate(result, operands))
rustc_macros = { path = "../rustc_macros" }
rustc_middle = { path = "../rustc_middle" }
rustc_serialize = { path = "../rustc_serialize" }
-rustc_session = { path = "../rustc_session" }
rustc_target = { path = "../rustc_target" }
rustc_span = { path = "../rustc_span" }
}
/// Invokes `f` on all direct fields of `ty`.
-fn iter_fields<'tcx>(
+pub fn iter_fields<'tcx>(
ty: Ty<'tcx>,
tcx: TyCtxt<'tcx>,
mut f: impl FnMut(Option<VariantIdx>, Field, Ty<'tcx>),
}
/// Returns all locals with projections that have their reference or address taken.
-fn excluded_locals(body: &Body<'_>) -> IndexVec<Local, bool> {
+pub fn excluded_locals(body: &Body<'_>) -> IndexVec<Local, bool> {
struct Collector {
result: IndexVec<Local, bool>,
}
}
}
&AggregateKind::Closure(def_id, _) | &AggregateKind::Generator(def_id, _, _) => {
+ let def_id = def_id.expect_local();
let UnsafetyCheckResult { violations, used_unsafe_blocks, .. } =
self.tcx.unsafety_check_result(def_id);
self.register_violations(violations, used_unsafe_blocks.iter().copied());
}
fn visit_statement(&mut self, stmt: &mut Statement<'tcx>, loc: Location) {
- if let StatementKind::StorageDead(l) = stmt.kind
- && self.storage_to_remove.contains(l)
- {
- stmt.make_nop();
- } else if let StatementKind::Assign(box (ref place, ref mut rvalue)) = stmt.kind
- && place.as_local().is_some()
- {
- // Do not replace assignments.
- self.visit_rvalue(rvalue, loc)
- } else {
- self.super_statement(stmt, loc);
+ match stmt.kind {
+ // When removing storage statements, we need to remove both (#107511).
+ StatementKind::StorageLive(l) | StatementKind::StorageDead(l)
+ if self.storage_to_remove.contains(l) =>
+ {
+ stmt.make_nop()
+ }
+ StatementKind::Assign(box (ref place, ref mut rvalue))
+ if place.as_local().is_some() =>
+ {
+ // Do not replace assignments.
+ self.visit_rvalue(rvalue, loc)
+ }
+ _ => self.super_statement(stmt, loc),
}
}
}
use rustc_const_eval::const_eval::CheckAlignment;
use rustc_const_eval::interpret::{ConstValue, ImmTy, Immediate, InterpCx, Scalar};
use rustc_data_structures::fx::FxHashMap;
+use rustc_hir::def::DefKind;
use rustc_middle::mir::visit::{MutVisitor, Visitor};
use rustc_middle::mir::*;
use rustc_middle::ty::{self, Ty, TyCtxt};
state: &mut State<Self::Value>,
) {
match rvalue {
+ Rvalue::Aggregate(kind, operands) => {
+ let target = self.map().find(target.as_ref());
+ if let Some(target) = target {
+ state.flood_idx_with(target, self.map(), FlatSet::Bottom);
+ let field_based = match **kind {
+ AggregateKind::Tuple | AggregateKind::Closure(..) => true,
+ AggregateKind::Adt(def_id, ..) => {
+ matches!(self.tcx.def_kind(def_id), DefKind::Struct)
+ }
+ _ => false,
+ };
+ if field_based {
+ for (field_index, operand) in operands.iter().enumerate() {
+ if let Some(field) = self
+ .map()
+ .apply(target, TrackElem::Field(Field::from_usize(field_index)))
+ {
+ let result = self.handle_operand(operand, state);
+ state.assign_idx(field, result, self.map());
+ }
+ }
+ }
+ }
+ }
Rvalue::CheckedBinaryOp(op, box (left, right)) => {
let target = self.map().find(target.as_ref());
if let Some(target) = target {
+++ /dev/null
-use crate::util::expand_aggregate;
-use crate::MirPass;
-use rustc_middle::mir::*;
-use rustc_middle::ty::TyCtxt;
-
-pub struct Deaggregator;
-
-impl<'tcx> MirPass<'tcx> for Deaggregator {
- fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
- let basic_blocks = body.basic_blocks.as_mut_preserves_cfg();
- for bb in basic_blocks {
- bb.expand_statements(|stmt| {
- // FIXME(eddyb) don't match twice on `stmt.kind` (post-NLL).
- match stmt.kind {
- // FIXME(#48193) Deaggregate arrays when it's cheaper to do so.
- StatementKind::Assign(box (
- _,
- Rvalue::Aggregate(box AggregateKind::Array(_), _),
- )) => {
- return None;
- }
- StatementKind::Assign(box (_, Rvalue::Aggregate(_, _))) => {}
- _ => return None,
- }
-
- let stmt = stmt.replace_nop();
- let source_info = stmt.source_info;
- let StatementKind::Assign(box (lhs, Rvalue::Aggregate(kind, operands))) = stmt.kind else {
- bug!();
- };
-
- Some(expand_aggregate(
- lhs,
- operands.into_iter().map(|op| {
- let ty = op.ty(&body.local_decls, tcx);
- (op, ty)
- }),
- *kind,
- source_info,
- tcx,
- ))
- });
- }
- }
-}
use crate::deref_separator::deref_finder;
use crate::simplify;
-use crate::util::expand_aggregate;
use crate::MirPass;
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use rustc_errors::pluralize;
assert_eq!(self.state_adt_ref.variant(idx).fields.len(), 0);
// FIXME(swatinem): assert that `val` is indeed unit?
- statements.extend(expand_aggregate(
- Place::return_place(),
- std::iter::empty(),
- kind,
+ statements.push(Statement {
+ kind: StatementKind::Assign(Box::new((
+ Place::return_place(),
+ Rvalue::Aggregate(Box::new(kind), vec![]),
+ ))),
source_info,
- self.tcx,
- ));
+ });
return;
}
// else: `Poll::Ready(x)`, `GeneratorState::Yielded(x)` or `GeneratorState::Complete(x)`
assert_eq!(self.state_adt_ref.variant(idx).fields.len(), 1);
- let ty = self
- .tcx
- .bound_type_of(self.state_adt_ref.variant(idx).fields[0].did)
- .subst(self.tcx, self.state_substs);
-
- statements.extend(expand_aggregate(
- Place::return_place(),
- std::iter::once((val, ty)),
- kind,
+ statements.push(Statement {
+ kind: StatementKind::Assign(Box::new((
+ Place::return_place(),
+ Rvalue::Aggregate(Box::new(kind), vec![val]),
+ ))),
source_info,
- self.tcx,
- ));
+ });
}
// Create a Place referencing a generator struct field
mod ctfe_limit;
mod dataflow_const_prop;
mod dead_store_elimination;
-mod deaggregator;
mod deduce_param_attrs;
mod deduplicate_blocks;
mod deref_separator;
&elaborate_box_derefs::ElaborateBoxDerefs,
&generator::StateTransform,
&add_retag::AddRetag,
- // Deaggregator is necessary for const prop. We may want to consider implementing
- // CTFE support for aggregates.
- &deaggregator::Deaggregator,
&Lint(const_prop_lint::ConstProp),
];
pm::run_passes_no_validate(tcx, body, passes, Some(MirPhase::Runtime(RuntimePhase::Initial)));
use std::fmt;
use std::iter;
-use crate::util::expand_aggregate;
use crate::{
abort_unwinding_calls, add_call_guards, add_moves_for_packed_drops, deref_separator,
pass_manager as pm, remove_noop_landing_pads, simplify,
// return;
debug!("build_ctor: variant_index={:?}", variant_index);
- let statements = expand_aggregate(
- Place::return_place(),
- adt_def.variant(variant_index).fields.iter().enumerate().map(|(idx, field_def)| {
- (Operand::Move(Place::from(Local::new(idx + 1))), field_def.ty(tcx, substs))
- }),
- AggregateKind::Adt(adt_def.did(), variant_index, substs, None, None),
+ let kind = AggregateKind::Adt(adt_def.did(), variant_index, substs, None, None);
+ let variant = adt_def.variant(variant_index);
+ let statement = Statement {
+ kind: StatementKind::Assign(Box::new((
+ Place::return_place(),
+ Rvalue::Aggregate(
+ Box::new(kind),
+ (0..variant.fields.len())
+ .map(|idx| Operand::Move(Place::from(Local::new(idx + 1))))
+ .collect(),
+ ),
+ ))),
source_info,
- tcx,
- )
- .collect();
+ };
let start_block = BasicBlockData {
- statements,
+ statements: vec![statement],
terminator: Some(Terminator { source_info, kind: TerminatorKind::Return }),
is_cleanup: false,
};
use crate::MirPass;
-use rustc_data_structures::fx::{FxIndexMap, IndexEntry};
use rustc_index::bit_set::BitSet;
use rustc_index::vec::IndexVec;
+use rustc_middle::mir::patch::MirPatch;
use rustc_middle::mir::visit::*;
use rustc_middle::mir::*;
-use rustc_middle::ty::TyCtxt;
+use rustc_middle::ty::{Ty, TyCtxt};
+use rustc_mir_dataflow::value_analysis::{excluded_locals, iter_fields};
pub struct ScalarReplacementOfAggregates;
sess.mir_opt_level() >= 3
}
+ #[instrument(level = "debug", skip(self, tcx, body))]
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
- let escaping = escaping_locals(&*body);
- debug!(?escaping);
- let replacements = compute_flattening(tcx, body, escaping);
- debug!(?replacements);
- replace_flattened_locals(tcx, body, replacements);
+ debug!(def_id = ?body.source.def_id());
+ let mut excluded = excluded_locals(body);
+ loop {
+ debug!(?excluded);
+ let escaping = escaping_locals(&excluded, body);
+ debug!(?escaping);
+ let replacements = compute_flattening(tcx, body, escaping);
+ debug!(?replacements);
+ let all_dead_locals = replace_flattened_locals(tcx, body, replacements);
+ if !all_dead_locals.is_empty() {
+ for local in excluded.indices() {
+ excluded[local] |= all_dead_locals.contains(local);
+ }
+ excluded.raw.resize(body.local_decls.len(), false);
+ } else {
+ break;
+ }
+ }
}
}
/// Identify all locals that are not eligible for SROA.
///
/// There are 3 cases:
-/// - the aggegated local is used or passed to other code (function parameters and arguments);
+/// - the aggregated local is used or passed to other code (function parameters and arguments);
/// - the locals is a union or an enum;
/// - the local's address is taken, and thus the relative addresses of the fields are observable to
/// client code.
-fn escaping_locals(body: &Body<'_>) -> BitSet<Local> {
+fn escaping_locals(excluded: &IndexVec<Local, bool>, body: &Body<'_>) -> BitSet<Local> {
let mut set = BitSet::new_empty(body.local_decls.len());
set.insert_range(RETURN_PLACE..=Local::from_usize(body.arg_count));
for (local, decl) in body.local_decls().iter_enumerated() {
- if decl.ty.is_union() || decl.ty.is_enum() {
+ if decl.ty.is_union() || decl.ty.is_enum() || excluded[local] {
set.insert(local);
}
}
self.super_place(place, context, location);
}
- fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
- if let Rvalue::AddressOf(.., place) | Rvalue::Ref(.., place) = rvalue {
- if !place.is_indirect() {
- // Raw pointers may be used to access anything inside the enclosing place.
- self.set.insert(place.local);
- return;
+ fn visit_assign(
+ &mut self,
+ lvalue: &Place<'tcx>,
+ rvalue: &Rvalue<'tcx>,
+ location: Location,
+ ) {
+ if lvalue.as_local().is_some() {
+ match rvalue {
+ // Aggregate assignments are expanded in run_pass.
+ Rvalue::Aggregate(..) | Rvalue::Use(..) => {
+ self.visit_rvalue(rvalue, location);
+ return;
+ }
+ _ => {}
}
}
- self.super_rvalue(rvalue, location)
+ self.super_assign(lvalue, rvalue, location)
}
fn visit_statement(&mut self, statement: &Statement<'tcx>, location: Location) {
- if let StatementKind::StorageLive(..)
- | StatementKind::StorageDead(..)
- | StatementKind::Deinit(..) = statement.kind
- {
+ match statement.kind {
// Storage statements are expanded in run_pass.
- return;
+ StatementKind::StorageLive(..)
+ | StatementKind::StorageDead(..)
+ | StatementKind::Deinit(..) => return,
+ _ => self.super_statement(statement, location),
}
- self.super_statement(statement, location)
- }
-
- fn visit_terminator(&mut self, terminator: &Terminator<'tcx>, location: Location) {
- // Drop implicitly calls `drop_in_place`, which takes a `&mut`.
- // This implies that `Drop` implicitly takes the address of the place.
- if let TerminatorKind::Drop { place, .. }
- | TerminatorKind::DropAndReplace { place, .. } = terminator.kind
- {
- if !place.is_indirect() {
- // Raw pointers may be used to access anything inside the enclosing place.
- self.set.insert(place.local);
- return;
- }
- }
- self.super_terminator(terminator, location);
}
// We ignore anything that happens in debuginfo, since we expand it using
#[derive(Default, Debug)]
struct ReplacementMap<'tcx> {
- fields: FxIndexMap<PlaceRef<'tcx>, Local>,
+ /// Pre-computed list of all "new" locals for each "old" local. This is used to expand storage
+ /// and deinit statement and debuginfo.
+ fragments: IndexVec<Local, Option<IndexVec<Field, Option<(Ty<'tcx>, Local)>>>>,
+}
+
+impl<'tcx> ReplacementMap<'tcx> {
+ fn replace_place(&self, tcx: TyCtxt<'tcx>, place: PlaceRef<'tcx>) -> Option<Place<'tcx>> {
+ let &[PlaceElem::Field(f, _), ref rest @ ..] = place.projection else { return None; };
+ let fields = self.fragments[place.local].as_ref()?;
+ let (_, new_local) = fields[f]?;
+ Some(Place { local: new_local, projection: tcx.intern_place_elems(&rest) })
+ }
+
+ fn place_fragments(
+ &self,
+ place: Place<'tcx>,
+ ) -> Option<impl Iterator<Item = (Field, Ty<'tcx>, Local)> + '_> {
+ let local = place.as_local()?;
+ let fields = self.fragments[local].as_ref()?;
+ Some(fields.iter_enumerated().filter_map(|(field, &opt_ty_local)| {
+ let (ty, local) = opt_ty_local?;
+ Some((field, ty, local))
+ }))
+ }
}
/// Compute the replacement of flattened places into locals.
body: &mut Body<'tcx>,
escaping: BitSet<Local>,
) -> ReplacementMap<'tcx> {
- let mut visitor = PreFlattenVisitor {
- tcx,
- escaping,
- local_decls: &mut body.local_decls,
- map: Default::default(),
- };
- for (block, bbdata) in body.basic_blocks.iter_enumerated() {
- visitor.visit_basic_block_data(block, bbdata);
- }
- return visitor.map;
-
- struct PreFlattenVisitor<'tcx, 'll> {
- tcx: TyCtxt<'tcx>,
- local_decls: &'ll mut LocalDecls<'tcx>,
- escaping: BitSet<Local>,
- map: ReplacementMap<'tcx>,
- }
-
- impl<'tcx, 'll> PreFlattenVisitor<'tcx, 'll> {
- fn create_place(&mut self, place: PlaceRef<'tcx>) {
- if self.escaping.contains(place.local) {
- return;
- }
+ let mut fragments = IndexVec::from_elem(None, &body.local_decls);
- match self.map.fields.entry(place) {
- IndexEntry::Occupied(_) => {}
- IndexEntry::Vacant(v) => {
- let ty = place.ty(&*self.local_decls, self.tcx).ty;
- let local = self.local_decls.push(LocalDecl {
- ty,
- user_ty: None,
- ..self.local_decls[place.local].clone()
- });
- v.insert(local);
- }
- }
- }
- }
-
- impl<'tcx, 'll> Visitor<'tcx> for PreFlattenVisitor<'tcx, 'll> {
- fn visit_place(&mut self, place: &Place<'tcx>, _: PlaceContext, _: Location) {
- if let &[PlaceElem::Field(..), ..] = &place.projection[..] {
- let pr = PlaceRef { local: place.local, projection: &place.projection[..1] };
- self.create_place(pr)
- }
+ for local in body.local_decls.indices() {
+ if escaping.contains(local) {
+ continue;
}
+ let decl = body.local_decls[local].clone();
+ let ty = decl.ty;
+ iter_fields(ty, tcx, |variant, field, field_ty| {
+ if variant.is_some() {
+ // Downcasts are currently not supported.
+ return;
+ };
+ let new_local =
+ body.local_decls.push(LocalDecl { ty: field_ty, user_ty: None, ..decl.clone() });
+ fragments.get_or_insert_with(local, IndexVec::new).insert(field, (field_ty, new_local));
+ });
}
+ ReplacementMap { fragments }
}
/// Perform the replacement computed by `compute_flattening`.
tcx: TyCtxt<'tcx>,
body: &mut Body<'tcx>,
replacements: ReplacementMap<'tcx>,
-) {
+) -> BitSet<Local> {
let mut all_dead_locals = BitSet::new_empty(body.local_decls.len());
- for p in replacements.fields.keys() {
- all_dead_locals.insert(p.local);
+ for (local, replacements) in replacements.fragments.iter_enumerated() {
+ if replacements.is_some() {
+ all_dead_locals.insert(local);
+ }
}
debug!(?all_dead_locals);
if all_dead_locals.is_empty() {
- return;
+ return all_dead_locals;
}
- let mut fragments = IndexVec::new();
- for (k, v) in &replacements.fields {
- fragments.ensure_contains_elem(k.local, || Vec::new());
- fragments[k.local].push((k.projection, *v));
- }
- debug!(?fragments);
-
let mut visitor = ReplacementVisitor {
tcx,
local_decls: &body.local_decls,
- replacements,
+ replacements: &replacements,
all_dead_locals,
- fragments,
+ patch: MirPatch::new(body),
};
for (bb, data) in body.basic_blocks.as_mut_preserves_cfg().iter_enumerated_mut() {
visitor.visit_basic_block_data(bb, data);
for var_debug_info in &mut body.var_debug_info {
visitor.visit_var_debug_info(var_debug_info);
}
+ let ReplacementVisitor { patch, all_dead_locals, .. } = visitor;
+ patch.apply(body);
+ all_dead_locals
}
struct ReplacementVisitor<'tcx, 'll> {
/// This is only used to compute the type for `VarDebugInfoContents::Composite`.
local_decls: &'ll LocalDecls<'tcx>,
/// Work to do.
- replacements: ReplacementMap<'tcx>,
+ replacements: &'ll ReplacementMap<'tcx>,
/// This is used to check that we are not leaving references to replaced locals behind.
all_dead_locals: BitSet<Local>,
- /// Pre-computed list of all "new" locals for each "old" local. This is used to expand storage
- /// and deinit statement and debuginfo.
- fragments: IndexVec<Local, Vec<(&'tcx [PlaceElem<'tcx>], Local)>>,
+ patch: MirPatch<'tcx>,
}
-impl<'tcx, 'll> ReplacementVisitor<'tcx, 'll> {
- fn gather_debug_info_fragments(
- &self,
- place: PlaceRef<'tcx>,
- ) -> Vec<VarDebugInfoFragment<'tcx>> {
+impl<'tcx> ReplacementVisitor<'tcx, '_> {
+ fn gather_debug_info_fragments(&self, local: Local) -> Option<Vec<VarDebugInfoFragment<'tcx>>> {
let mut fragments = Vec::new();
- let parts = &self.fragments[place.local];
- for (proj, replacement_local) in parts {
- if proj.starts_with(place.projection) {
- fragments.push(VarDebugInfoFragment {
- projection: proj[place.projection.len()..].to_vec(),
- contents: Place::from(*replacement_local),
- });
- }
- }
- fragments
- }
-
- fn replace_place(&self, place: PlaceRef<'tcx>) -> Option<Place<'tcx>> {
- if let &[PlaceElem::Field(..), ref rest @ ..] = place.projection {
- let pr = PlaceRef { local: place.local, projection: &place.projection[..1] };
- let local = self.replacements.fields.get(&pr)?;
- Some(Place { local: *local, projection: self.tcx.intern_place_elems(&rest) })
- } else {
- None
+ let parts = self.replacements.place_fragments(local.into())?;
+ for (field, ty, replacement_local) in parts {
+ fragments.push(VarDebugInfoFragment {
+ projection: vec![PlaceElem::Field(field, ty)],
+ contents: Place::from(replacement_local),
+ });
}
+ Some(fragments)
}
}
self.tcx
}
- fn visit_statement(&mut self, statement: &mut Statement<'tcx>, location: Location) {
- if let StatementKind::StorageLive(..)
- | StatementKind::StorageDead(..)
- | StatementKind::Deinit(..) = statement.kind
- {
- // Storage statements are expanded in run_pass.
- return;
- }
- self.super_statement(statement, location)
- }
-
fn visit_place(&mut self, place: &mut Place<'tcx>, context: PlaceContext, location: Location) {
- if let Some(repl) = self.replace_place(place.as_ref()) {
+ if let Some(repl) = self.replacements.replace_place(self.tcx, place.as_ref()) {
*place = repl
} else {
self.super_place(place, context, location)
}
}
+ #[instrument(level = "trace", skip(self))]
+ fn visit_statement(&mut self, statement: &mut Statement<'tcx>, location: Location) {
+ match statement.kind {
+ // Duplicate storage and deinit statements, as they pretty much apply to all fields.
+ StatementKind::StorageLive(l) => {
+ if let Some(final_locals) = self.replacements.place_fragments(l.into()) {
+ for (_, _, fl) in final_locals {
+ self.patch.add_statement(location, StatementKind::StorageLive(fl));
+ }
+ statement.make_nop();
+ }
+ return;
+ }
+ StatementKind::StorageDead(l) => {
+ if let Some(final_locals) = self.replacements.place_fragments(l.into()) {
+ for (_, _, fl) in final_locals {
+ self.patch.add_statement(location, StatementKind::StorageDead(fl));
+ }
+ statement.make_nop();
+ }
+ return;
+ }
+ StatementKind::Deinit(box place) => {
+ if let Some(final_locals) = self.replacements.place_fragments(place) {
+ for (_, _, fl) in final_locals {
+ self.patch
+ .add_statement(location, StatementKind::Deinit(Box::new(fl.into())));
+ }
+ statement.make_nop();
+ return;
+ }
+ }
+
+ // We have `a = Struct { 0: x, 1: y, .. }`.
+ // We replace it by
+ // ```
+ // a_0 = x
+ // a_1 = y
+ // ...
+ // ```
+ StatementKind::Assign(box (place, Rvalue::Aggregate(_, ref mut operands))) => {
+ if let Some(local) = place.as_local()
+ && let Some(final_locals) = &self.replacements.fragments[local]
+ {
+ // This is ok as we delete the statement later.
+ let operands = std::mem::take(operands);
+ for (&opt_ty_local, mut operand) in final_locals.iter().zip(operands) {
+ if let Some((_, new_local)) = opt_ty_local {
+ // Replace mentions of SROA'd locals that appear in the operand.
+ self.visit_operand(&mut operand, location);
+
+ let rvalue = Rvalue::Use(operand);
+ self.patch.add_statement(
+ location,
+ StatementKind::Assign(Box::new((new_local.into(), rvalue))),
+ );
+ }
+ }
+ statement.make_nop();
+ return;
+ }
+ }
+
+ // We have `a = some constant`
+ // We add the projections.
+ // ```
+ // a_0 = a.0
+ // a_1 = a.1
+ // ...
+ // ```
+ // ConstProp will pick up the pieces and replace them by actual constants.
+ StatementKind::Assign(box (place, Rvalue::Use(Operand::Constant(_)))) => {
+ if let Some(final_locals) = self.replacements.place_fragments(place) {
+ for (field, ty, new_local) in final_locals {
+ let rplace = self.tcx.mk_place_field(place, field, ty);
+ let rvalue = Rvalue::Use(Operand::Move(rplace));
+ self.patch.add_statement(
+ location,
+ StatementKind::Assign(Box::new((new_local.into(), rvalue))),
+ );
+ }
+ // We still need `place.local` to exist, so don't make it nop.
+ return;
+ }
+ }
+
+ // We have `a = move? place`
+ // We replace it by
+ // ```
+ // a_0 = move? place.0
+ // a_1 = move? place.1
+ // ...
+ // ```
+ StatementKind::Assign(box (lhs, Rvalue::Use(ref op))) => {
+ let (rplace, copy) = match *op {
+ Operand::Copy(rplace) => (rplace, true),
+ Operand::Move(rplace) => (rplace, false),
+ Operand::Constant(_) => bug!(),
+ };
+ if let Some(final_locals) = self.replacements.place_fragments(lhs) {
+ for (field, ty, new_local) in final_locals {
+ let rplace = self.tcx.mk_place_field(rplace, field, ty);
+ debug!(?rplace);
+ let rplace = self
+ .replacements
+ .replace_place(self.tcx, rplace.as_ref())
+ .unwrap_or(rplace);
+ debug!(?rplace);
+ let rvalue = if copy {
+ Rvalue::Use(Operand::Copy(rplace))
+ } else {
+ Rvalue::Use(Operand::Move(rplace))
+ };
+ self.patch.add_statement(
+ location,
+ StatementKind::Assign(Box::new((new_local.into(), rvalue))),
+ );
+ }
+ statement.make_nop();
+ return;
+ }
+ }
+
+ _ => {}
+ }
+ self.super_statement(statement, location)
+ }
+
+ #[instrument(level = "trace", skip(self))]
fn visit_var_debug_info(&mut self, var_debug_info: &mut VarDebugInfo<'tcx>) {
match &mut var_debug_info.value {
VarDebugInfoContents::Place(ref mut place) => {
- if let Some(repl) = self.replace_place(place.as_ref()) {
+ if let Some(repl) = self.replacements.replace_place(self.tcx, place.as_ref()) {
*place = repl;
- } else if self.all_dead_locals.contains(place.local) {
+ } else if let Some(local) = place.as_local()
+ && let Some(fragments) = self.gather_debug_info_fragments(local)
+ {
let ty = place.ty(self.local_decls, self.tcx).ty;
- let fragments = self.gather_debug_info_fragments(place.as_ref());
var_debug_info.value = VarDebugInfoContents::Composite { ty, fragments };
}
}
VarDebugInfoContents::Composite { ty: _, ref mut fragments } => {
let mut new_fragments = Vec::new();
+ debug!(?fragments);
fragments
.drain_filter(|fragment| {
- if let Some(repl) = self.replace_place(fragment.contents.as_ref()) {
+ if let Some(repl) =
+ self.replacements.replace_place(self.tcx, fragment.contents.as_ref())
+ {
fragment.contents = repl;
- true
- } else if self.all_dead_locals.contains(fragment.contents.local) {
- let frg = self.gather_debug_info_fragments(fragment.contents.as_ref());
+ false
+ } else if let Some(local) = fragment.contents.as_local()
+ && let Some(frg) = self.gather_debug_info_fragments(local)
+ {
new_fragments.extend(frg.into_iter().map(|mut f| {
f.projection.splice(0..0, fragment.projection.iter().copied());
f
}));
- false
- } else {
true
+ } else {
+ false
}
})
.for_each(drop);
+ debug!(?fragments);
+ debug!(?new_fragments);
fragments.extend(new_fragments);
}
VarDebugInfoContents::Const(_) => {}
}
}
- fn visit_basic_block_data(&mut self, bb: BasicBlock, bbdata: &mut BasicBlockData<'tcx>) {
- self.super_basic_block_data(bb, bbdata);
-
- #[derive(Debug)]
- enum Stmt {
- StorageLive,
- StorageDead,
- Deinit,
- }
-
- bbdata.expand_statements(|stmt| {
- let source_info = stmt.source_info;
- let (stmt, origin_local) = match &stmt.kind {
- StatementKind::StorageLive(l) => (Stmt::StorageLive, *l),
- StatementKind::StorageDead(l) => (Stmt::StorageDead, *l),
- StatementKind::Deinit(p) if let Some(l) = p.as_local() => (Stmt::Deinit, l),
- _ => return None,
- };
- if !self.all_dead_locals.contains(origin_local) {
- return None;
- }
- let final_locals = self.fragments.get(origin_local)?;
- Some(final_locals.iter().map(move |&(_, l)| {
- let kind = match stmt {
- Stmt::StorageLive => StatementKind::StorageLive(l),
- Stmt::StorageDead => StatementKind::StorageDead(l),
- Stmt::Deinit => StatementKind::Deinit(Box::new(l.into())),
- };
- Statement { source_info, kind }
- }))
- });
- }
-
fn visit_local(&mut self, local: &mut Local, _: PlaceContext, _: Location) {
assert!(!self.all_dead_locals.contains(*local));
}
+use std::borrow::Cow;
+
use rustc_ast::token::Token;
use rustc_ast::{Path, Visibility};
use rustc_errors::{fluent, AddToDiagnostic, Applicability, EmissionGuarantee, IntoDiagnostic};
AddIn(#[primary_span] Span),
}
+#[derive(Diagnostic)]
+#[diag(parse_missing_expression_in_for_loop)]
+pub(crate) struct MissingExpressionInForLoop {
+ #[primary_span]
+ #[suggestion(
+ code = "/* expression */ ",
+ applicability = "has-placeholders",
+ style = "verbose"
+ )]
+ pub span: Span,
+}
+
#[derive(Diagnostic)]
#[diag(parse_missing_comma_after_match_arm)]
pub(crate) struct MissingCommaAfterMatchArm {
#[diag(parse_inclusive_range_match_arrow)]
pub(crate) struct InclusiveRangeMatchArrow {
#[primary_span]
+ pub arrow: Span,
+ #[label]
pub span: Span,
- #[suggestion(
- suggestion_add_space,
- style = "verbose",
- code = " ",
- applicability = "machine-applicable"
- )]
+ #[suggestion(style = "verbose", code = " ", applicability = "machine-applicable")]
pub after_pat: Span,
}
pub span: Span,
}
+#[derive(Diagnostic)]
+#[diag(parse_unexpected_default_value_for_lifetime_in_generic_parameters)]
+pub(crate) struct UnexpectedDefaultValueForLifetimeInGenericParameters {
+ #[primary_span]
+ #[label]
+ pub span: Span,
+}
+
#[derive(Diagnostic)]
#[diag(parse_multiple_where_clauses)]
pub(crate) struct MultipleWhereClauses {
#[diag(parse_dot_dot_dot_for_remaining_fields)]
pub(crate) struct DotDotDotForRemainingFields {
#[primary_span]
- #[suggestion(code = "..", applicability = "machine-applicable")]
+ #[suggestion(code = "..", style = "verbose", applicability = "machine-applicable")]
pub span: Span,
+ pub token_str: Cow<'static, str>,
}
#[derive(Diagnostic)]
use rustc_data_structures::static_assert_size;
// tidy-alphabetical-start
static_assert_size!(AttrWrapper, 16);
- static_assert_size!(LazyAttrTokenStreamImpl, 144);
+ static_assert_size!(LazyAttrTokenStreamImpl, 120);
// tidy-alphabetical-end
}
/// Some special error handling for the "top-level" patterns in a match arm,
/// `for` loop, `let`, &c. (in contrast to subpatterns within such).
- pub(crate) fn maybe_recover_colon_colon_in_pat_typo_or_anon_enum(
+ pub(crate) fn maybe_recover_colon_colon_in_pat_typo(
&mut self,
mut first_pat: P<Pat>,
expected: Option<Expected>,
// Create error for "unexpected `:`".
match self.expected_one_of_not_found(&[], &[]) {
Err(mut err) => {
- snapshot_pat.bump(); // Skip the `:`.
- snapshot_type.bump(); // Skip the `:`.
+ // Skip the `:`.
+ snapshot_pat.bump();
+ snapshot_type.bump();
match snapshot_pat.parse_pat_no_top_alt(expected) {
Err(inner_err) => {
inner_err.cancel();
AttrWrapper, BlockMode, ClosureSpans, ForceCollect, Parser, PathStyle, Restrictions,
SemiColonMode, SeqSep, TokenExpectType, TokenType, TrailingToken,
};
-use crate::errors::{
- ArrayBracketsInsteadOfSpaces, ArrayBracketsInsteadOfSpacesSugg, AsyncBlockIn2015,
- AsyncMoveOrderIncorrect, BracesForStructLiteral, CatchAfterTry, CommaAfterBaseStruct,
- ComparisonInterpretedAsGeneric, ComparisonOrShiftInterpretedAsGenericSugg,
- DoCatchSyntaxRemoved, DotDotDot, EqFieldInit, ExpectedElseBlock, ExpectedEqForLetExpr,
- ExpectedExpressionFoundLet, FieldExpressionWithGeneric, FloatLiteralRequiresIntegerPart,
- FoundExprWouldBeStmt, HelpUseLatestEdition, IfExpressionLetSomeSub,
- IfExpressionMissingCondition, IfExpressionMissingThenBlock, IfExpressionMissingThenBlockSub,
- InvalidBlockMacroSegment, InvalidComparisonOperator, InvalidComparisonOperatorSub,
- InvalidInterpolatedExpression, InvalidLiteralSuffixOnTupleIndex, InvalidLogicalOperator,
- InvalidLogicalOperatorSub, LabeledLoopInBreak, LeadingPlusNotSupported, LeftArrowOperator,
- LifetimeInBorrowExpression, MacroInvocationWithQualifiedPath, MalformedLoopLabel,
- MatchArmBodyWithoutBraces, MatchArmBodyWithoutBracesSugg, MissingCommaAfterMatchArm,
- MissingDotDot, MissingInInForLoop, MissingInInForLoopSub, MissingSemicolonBeforeArray,
- NoFieldsForFnCall, NotAsNegationOperator, NotAsNegationOperatorSub,
- OuterAttributeNotAllowedOnIfElse, ParenthesesWithStructFields,
- RequireColonAfterLabeledExpression, ShiftInterpretedAsGeneric, StructLiteralNotAllowedHere,
- StructLiteralNotAllowedHereSugg, TildeAsUnaryOperator, UnexpectedIfWithIf,
- UnexpectedTokenAfterLabel, UnexpectedTokenAfterLabelSugg, WrapExpressionInParentheses,
-};
+use crate::errors;
use crate::maybe_recover_from_interpolated_ty_qpath;
use core::mem;
use rustc_ast::ptr::P;
}
.into();
let invalid = format!("{}=", &sugg);
- self.sess.emit_err(InvalidComparisonOperator {
+ self.sess.emit_err(errors::InvalidComparisonOperator {
span: sp,
invalid: invalid.clone(),
- sub: InvalidComparisonOperatorSub::Correctable {
+ sub: errors::InvalidComparisonOperatorSub::Correctable {
span: sp,
invalid,
correct: sugg,
&& self.prev_token.span.hi() == self.token.span.lo()
{
let sp = op.span.to(self.token.span);
- self.sess.emit_err(InvalidComparisonOperator {
+ self.sess.emit_err(errors::InvalidComparisonOperator {
span: sp,
invalid: "<>".into(),
- sub: InvalidComparisonOperatorSub::Correctable {
+ sub: errors::InvalidComparisonOperatorSub::Correctable {
span: sp,
invalid: "<>".into(),
correct: "!=".into(),
&& self.prev_token.span.hi() == self.token.span.lo()
{
let sp = op.span.to(self.token.span);
- self.sess.emit_err(InvalidComparisonOperator {
+ self.sess.emit_err(errors::InvalidComparisonOperator {
span: sp,
invalid: "<=>".into(),
- sub: InvalidComparisonOperatorSub::Spaceship(sp),
+ sub: errors::InvalidComparisonOperatorSub::Spaceship(sp),
});
self.bump();
}
/// but the next token implies this should be parsed as an expression.
/// For example: `if let Some(x) = x { x } else { 0 } / 2`.
fn error_found_expr_would_be_stmt(&self, lhs: &Expr) {
- self.sess.emit_err(FoundExprWouldBeStmt {
+ self.sess.emit_err(errors::FoundExprWouldBeStmt {
span: self.token.span,
token: self.token.clone(),
suggestion: ExprParenthesesNeeded::surrounding(lhs.span),
}
(Some(op), _) => (op, self.token.span),
(None, Some((Ident { name: sym::and, span }, false))) if self.may_recover() => {
- self.sess.emit_err(InvalidLogicalOperator {
+ self.sess.emit_err(errors::InvalidLogicalOperator {
span: self.token.span,
incorrect: "and".into(),
- sub: InvalidLogicalOperatorSub::Conjunction(self.token.span),
+ sub: errors::InvalidLogicalOperatorSub::Conjunction(self.token.span),
});
(AssocOp::LAnd, span)
}
(None, Some((Ident { name: sym::or, span }, false))) if self.may_recover() => {
- self.sess.emit_err(InvalidLogicalOperator {
+ self.sess.emit_err(errors::InvalidLogicalOperator {
span: self.token.span,
incorrect: "or".into(),
- sub: InvalidLogicalOperatorSub::Disjunction(self.token.span),
+ sub: errors::InvalidLogicalOperatorSub::Disjunction(self.token.span),
});
(AssocOp::LOr, span)
}
}
// `+lit`
token::BinOp(token::Plus) if this.look_ahead(1, |tok| tok.is_numeric_lit()) => {
- let mut err =
- LeadingPlusNotSupported { span: lo, remove_plus: None, add_parentheses: None };
+ let mut err = errors::LeadingPlusNotSupported {
+ span: lo,
+ remove_plus: None,
+ add_parentheses: None,
+ };
// a block on the LHS might have been intended to be an expression instead
if let Some(sp) = this.sess.ambiguous_block_expr_parse.borrow().get(&lo) {
/// Recover on `~expr` in favor of `!expr`.
fn recover_tilde_expr(&mut self, lo: Span) -> PResult<'a, (Span, ExprKind)> {
- self.sess.emit_err(TildeAsUnaryOperator(lo));
+ self.sess.emit_err(errors::TildeAsUnaryOperator(lo));
self.parse_unary_expr(lo, UnOp::Not)
}
let negated_token = self.look_ahead(1, |t| t.clone());
let sub_diag = if negated_token.is_numeric_lit() {
- NotAsNegationOperatorSub::SuggestNotBitwise
+ errors::NotAsNegationOperatorSub::SuggestNotBitwise
} else if negated_token.is_bool_lit() {
- NotAsNegationOperatorSub::SuggestNotLogical
+ errors::NotAsNegationOperatorSub::SuggestNotLogical
} else {
- NotAsNegationOperatorSub::SuggestNotDefault
+ errors::NotAsNegationOperatorSub::SuggestNotDefault
};
- self.sess.emit_err(NotAsNegationOperator {
+ self.sess.emit_err(errors::NotAsNegationOperator {
negated: negated_token.span,
negated_desc: super::token_descr(&negated_token),
// Span the `not` plus trailing whitespace to avoid
match self.parse_labeled_expr(label, false) {
Ok(expr) => {
type_err.cancel();
- self.sess.emit_err(MalformedLoopLabel {
+ self.sess.emit_err(errors::MalformedLoopLabel {
span: label.ident.span,
correct_label: label.ident,
});
);
let args_span = self.look_ahead(1, |t| t.span).to(span_after_type);
- let suggestion = ComparisonOrShiftInterpretedAsGenericSugg {
+ let suggestion = errors::ComparisonOrShiftInterpretedAsGenericSugg {
left: expr.span.shrink_to_lo(),
right: expr.span.shrink_to_hi(),
};
match self.token.kind {
- token::Lt => self.sess.emit_err(ComparisonInterpretedAsGeneric {
- comparison: self.token.span,
- r#type: path,
- args: args_span,
- suggestion,
- }),
+ token::Lt => {
+ self.sess.emit_err(errors::ComparisonInterpretedAsGeneric {
+ comparison: self.token.span,
+ r#type: path,
+ args: args_span,
+ suggestion,
+ })
+ }
token::BinOp(token::Shl) => {
- self.sess.emit_err(ShiftInterpretedAsGeneric {
+ self.sess.emit_err(errors::ShiftInterpretedAsGeneric {
shift: self.token.span,
r#type: path,
args: args_span,
}
fn error_remove_borrow_lifetime(&self, span: Span, lt_span: Span) {
- self.sess.emit_err(LifetimeInBorrowExpression { span, lifetime_span: lt_span });
+ self.sess.emit_err(errors::LifetimeInBorrowExpression { span, lifetime_span: lt_span });
}
/// Parse `mut?` or `raw [ const | mut ]`.
let close_paren = self.prev_token.span;
let span = lo.to(self.prev_token.span);
if !fields.is_empty() {
- let mut replacement_err = ParenthesesWithStructFields {
+ let mut replacement_err = errors::ParenthesesWithStructFields {
span,
r#type: path,
- braces_for_struct: BracesForStructLiteral {
+ braces_for_struct: errors::BracesForStructLiteral {
first: open_paren,
second: close_paren,
},
- no_fields_for_fn: NoFieldsForFnCall {
+ no_fields_for_fn: errors::NoFieldsForFnCall {
fields: fields
.into_iter()
.map(|field| field.span.until(field.expr.span))
} else {
// Field access `expr.f`
if let Some(args) = seg.args {
- self.sess.emit_err(FieldExpressionWithGeneric(args.span()));
+ self.sess.emit_err(errors::FieldExpressionWithGeneric(args.span()));
}
let span = lo.to(self.prev_token.span);
let (span, kind) = if self.eat(&token::Not) {
// MACRO INVOCATION expression
if qself.is_some() {
- self.sess.emit_err(MacroInvocationWithQualifiedPath(path.span));
+ self.sess.emit_err(errors::MacroInvocationWithQualifiedPath(path.span));
}
let lo = path.span;
let mac = P(MacCall {
{
let (lit, _) =
self.recover_unclosed_char(label_.ident, Parser::mk_token_lit_char, |self_| {
- self_.sess.create_err(UnexpectedTokenAfterLabel {
+ self_.sess.create_err(errors::UnexpectedTokenAfterLabel {
span: self_.token.span,
remove_label: None,
enclose_in_block: None,
&& (self.check_noexpect(&TokenKind::Comma) || self.check_noexpect(&TokenKind::Gt))
{
// We're probably inside of a `Path<'a>` that needs a turbofish
- self.sess.emit_err(UnexpectedTokenAfterLabel {
+ self.sess.emit_err(errors::UnexpectedTokenAfterLabel {
span: self.token.span,
remove_label: None,
enclose_in_block: None,
consume_colon = false;
Ok(self.mk_expr_err(lo))
} else {
- let mut err = UnexpectedTokenAfterLabel {
+ let mut err = errors::UnexpectedTokenAfterLabel {
span: self.token.span,
remove_label: None,
enclose_in_block: None,
return expr;
}
- err.enclose_in_block = Some(UnexpectedTokenAfterLabelSugg {
+ err.enclose_in_block = Some(errors::UnexpectedTokenAfterLabelSugg {
left: span.shrink_to_lo(),
right: span.shrink_to_hi(),
});
}?;
if !ate_colon && consume_colon {
- self.sess.emit_err(RequireColonAfterLabeledExpression {
+ self.sess.emit_err(errors::RequireColonAfterLabeledExpression {
span: expr.span,
label: lo,
label_end: lo.shrink_to_hi(),
self.bump(); // `catch`
let span = lo.to(self.prev_token.span);
- self.sess.emit_err(DoCatchSyntaxRemoved { span });
+ self.sess.emit_err(errors::DoCatchSyntaxRemoved { span });
self.parse_try_block(lo)
}
// The value expression can be a labeled loop, see issue #86948, e.g.:
// `loop { break 'label: loop { break 'label 42; }; }`
let lexpr = self.parse_labeled_expr(label, true)?;
- self.sess.emit_err(LabeledLoopInBreak {
+ self.sess.emit_err(errors::LabeledLoopInBreak {
span: lexpr.span,
- sub: WrapExpressionInParentheses {
+ sub: errors::WrapExpressionInParentheses {
left: lexpr.span.shrink_to_lo(),
right: lexpr.span.shrink_to_hi(),
},
};
if let Some(expr) = expr {
if matches!(expr.kind, ExprKind::Err) {
- let mut err = InvalidInterpolatedExpression { span: self.token.span }
+ let mut err = errors::InvalidInterpolatedExpression { span: self.token.span }
.into_diagnostic(&self.sess.span_diagnostic);
err.downgrade_to_delayed_bug();
return Err(err);
});
if let Some(token) = &recovered {
self.bump();
- self.sess.emit_err(FloatLiteralRequiresIntegerPart {
+ self.sess.emit_err(errors::FloatLiteralRequiresIntegerPart {
span: token.span,
correct: pprust::token_to_string(token).into_owned(),
});
if [sym::i32, sym::u32, sym::isize, sym::usize].contains(&suffix) {
// #59553: warn instead of reject out of hand to allow the fix to percolate
// through the ecosystem when people fix their macros
- self.sess.emit_warning(InvalidLiteralSuffixOnTupleIndex {
+ self.sess.emit_warning(errors::InvalidLiteralSuffixOnTupleIndex {
span,
suffix,
exception: Some(()),
});
} else {
- self.sess.emit_err(InvalidLiteralSuffixOnTupleIndex { span, suffix, exception: None });
+ self.sess.emit_err(errors::InvalidLiteralSuffixOnTupleIndex {
+ span,
+ suffix,
+ exception: None,
+ });
}
}
let mut snapshot = self.create_snapshot_for_diagnostic();
match snapshot.parse_array_or_repeat_expr(Delimiter::Brace) {
Ok(arr) => {
- self.sess.emit_err(ArrayBracketsInsteadOfSpaces {
+ self.sess.emit_err(errors::ArrayBracketsInsteadOfSpaces {
span: arr.span,
- sub: ArrayBracketsInsteadOfSpacesSugg {
+ sub: errors::ArrayBracketsInsteadOfSpacesSugg {
left: lo,
right: snapshot.prev_token.span,
},
.span_to_snippet(snapshot.token.span)
.map_or(false, |snippet| snippet == "]") =>
{
- return Err(MissingSemicolonBeforeArray {
+ return Err(errors::MissingSemicolonBeforeArray {
open_delim: open_delim_span,
semicolon: prev_span.shrink_to_hi(),
}.into_diagnostic(&self.sess.span_diagnostic));
}
if self.token.is_whole_block() {
- self.sess.emit_err(InvalidBlockMacroSegment {
+ self.sess.emit_err(errors::InvalidBlockMacroSegment {
span: self.token.span,
context: lo.to(self.token.span),
});
ClosureBinder::NotPresent
};
- let constness = self.parse_constness(Case::Sensitive);
+ let constness = self.parse_closure_constness(Case::Sensitive);
let movability =
if self.eat_keyword(kw::Static) { Movability::Static } else { Movability::Movable };
}
if self.token.kind == TokenKind::Semi
- && matches!(self.token_cursor.frame.delim_sp, Some((Delimiter::Parenthesis, _)))
+ && matches!(self.token_cursor.stack.last(), Some((_, Delimiter::Parenthesis, _)))
&& self.may_recover()
{
// It is likely that the closure body is a block but where the
// Check for `move async` and recover
if self.check_keyword(kw::Async) {
let move_async_span = self.token.span.with_lo(self.prev_token.span.data().lo);
- Err(AsyncMoveOrderIncorrect { span: move_async_span }
+ Err(errors::AsyncMoveOrderIncorrect { span: move_async_span }
.into_diagnostic(&self.sess.span_diagnostic))
} else {
Ok(CaptureBy::Value)
let block = match &mut cond.kind {
ExprKind::Binary(Spanned { span: binop_span, .. }, _, right)
if let ExprKind::Block(_, None) = right.kind => {
- self.sess.emit_err(IfExpressionMissingThenBlock {
+ self.sess.emit_err(errors::IfExpressionMissingThenBlock {
if_span: lo,
missing_then_block_sub:
- IfExpressionMissingThenBlockSub::UnfinishedCondition(cond_span.shrink_to_lo().to(*binop_span)),
+ errors::IfExpressionMissingThenBlockSub::UnfinishedCondition(cond_span.shrink_to_lo().to(*binop_span)),
let_else_sub: None,
});
std::mem::replace(right, this.mk_expr_err(binop_span.shrink_to_hi()))
},
ExprKind::Block(_, None) => {
- self.sess.emit_err(IfExpressionMissingCondition {
+ self.sess.emit_err(errors::IfExpressionMissingCondition {
if_span: lo.shrink_to_hi(),
block_span: self.sess.source_map().start_point(cond_span),
});
block
} else {
let let_else_sub = matches!(cond.kind, ExprKind::Let(..))
- .then(|| IfExpressionLetSomeSub { if_span: lo.until(cond_span) });
+ .then(|| errors::IfExpressionLetSomeSub { if_span: lo.until(cond_span) });
- self.sess.emit_err(IfExpressionMissingThenBlock {
+ self.sess.emit_err(errors::IfExpressionMissingThenBlock {
if_span: lo,
- missing_then_block_sub: IfExpressionMissingThenBlockSub::AddThenBlock(
+ missing_then_block_sub: errors::IfExpressionMissingThenBlockSub::AddThenBlock(
cond_span.shrink_to_hi(),
),
let_else_sub,
TokenKind::AndAnd | TokenKind::Ident(kw::If, _) | TokenKind::Ident(kw::While, _)
);
if !self.restrictions.contains(Restrictions::ALLOW_LET) || not_in_chain {
- self.sess.emit_err(ExpectedExpressionFoundLet { span: self.token.span });
+ self.sess.emit_err(errors::ExpectedExpressionFoundLet { span: self.token.span });
}
self.bump(); // Eat `let` token
CommaRecoveryMode::LikelyTuple,
)?;
if self.token == token::EqEq {
- self.sess.emit_err(ExpectedEqForLetExpr {
+ self.sess.emit_err(errors::ExpectedEqForLetExpr {
span: self.token.span,
sugg_span: self.token.span,
});
if self.check(&TokenKind::OpenDelim(Delimiter::Brace))
&& classify::expr_requires_semi_to_be_stmt(&cond) =>
{
- self.sess.emit_err(ExpectedElseBlock {
+ self.sess.emit_err(errors::ExpectedElseBlock {
first_tok_span,
first_tok,
else_span,
[x0 @ xn] | [x0, .., xn] => (x0.span.to(xn.span), xn.span),
};
let ctx = if is_ctx_else { "else" } else { "if" };
- self.sess.emit_err(OuterAttributeNotAllowedOnIfElse {
+ self.sess.emit_err(errors::OuterAttributeNotAllowedOnIfElse {
last,
branch_span,
ctx_span,
if let ExprKind::Binary(Spanned { span: binop_span, node: binop}, _, right) = &cond.kind &&
let BinOpKind::And = binop &&
let ExprKind::If(cond, ..) = &right.kind {
- Err(self.sess.create_err(UnexpectedIfWithIf(binop_span.shrink_to_hi().to(cond.span.shrink_to_lo()))))
+ Err(self.sess.create_err(errors::UnexpectedIfWithIf(binop_span.shrink_to_hi().to(cond.span.shrink_to_lo()))))
} else {
Ok(())
}
let pat = self.recover_parens_around_for_head(pat, begin_paren);
+ // Recover from missing expression in `for` loop
+ if matches!(expr.kind, ExprKind::Block(..))
+ && !matches!(self.token.kind, token::OpenDelim(token::Delimiter::Brace))
+ && self.may_recover()
+ {
+ self.sess
+ .emit_err(errors::MissingExpressionInForLoop { span: expr.span.shrink_to_lo() });
+ let err_expr = self.mk_expr(expr.span, ExprKind::Err);
+ let block = self.mk_block(vec![], BlockCheckMode::Default, self.prev_token.span);
+ return Ok(self.mk_expr(
+ lo.to(self.prev_token.span),
+ ExprKind::ForLoop(pat, err_expr, block, opt_label),
+ ));
+ }
+
let (attrs, loop_block) = self.parse_inner_attrs_and_block()?;
let kind = ExprKind::ForLoop(pat, expr, loop_block, opt_label);
// Possibly using JS syntax (#75311).
let span = self.token.span;
self.bump();
- (span, MissingInInForLoopSub::InNotOf)
+ (span, errors::MissingInInForLoopSub::InNotOf)
} else {
- (self.prev_token.span.between(self.token.span), MissingInInForLoopSub::AddIn)
+ (self.prev_token.span.between(self.token.span), errors::MissingInInForLoopSub::AddIn)
};
- self.sess.emit_err(MissingInInForLoop { span, sub: sub(span) });
+ self.sess.emit_err(errors::MissingInInForLoop { span, sub: sub(span) });
}
/// Parses a `while` or `while let` expression (`while` token already eaten).
let err = |this: &Parser<'_>, stmts: Vec<ast::Stmt>| {
let span = stmts[0].span.to(stmts[stmts.len() - 1].span);
- this.sess.emit_err(MatchArmBodyWithoutBraces {
+ this.sess.emit_err(errors::MatchArmBodyWithoutBraces {
statements: span,
arrow: arrow_span,
num_statements: stmts.len(),
sub: if stmts.len() > 1 {
- MatchArmBodyWithoutBracesSugg::AddBraces {
+ errors::MatchArmBodyWithoutBracesSugg::AddBraces {
left: span.shrink_to_lo(),
right: span.shrink_to_hi(),
}
} else {
- MatchArmBodyWithoutBracesSugg::UseComma { semicolon: semi_sp }
+ errors::MatchArmBodyWithoutBracesSugg::UseComma { semicolon: semi_sp }
},
});
this.mk_expr_err(span)
);
err.emit();
this.bump();
+ } else if matches!(
+ (&this.prev_token.kind, &this.token.kind),
+ (token::DotDotEq, token::Gt)
+ ) {
+ // `error_inclusive_range_match_arrow` handles cases like `0..=> {}`,
+ // so we supress the error here
+ err.delay_as_bug();
+ this.bump();
} else {
return Err(err);
}
.is_ok();
if pattern_follows && snapshot.check(&TokenKind::FatArrow) {
err.cancel();
- this.sess.emit_err(MissingCommaAfterMatchArm {
+ this.sess.emit_err(errors::MissingCommaAfterMatchArm {
span: hi.shrink_to_hi(),
});
return Ok(true);
fn parse_try_block(&mut self, span_lo: Span) -> PResult<'a, P<Expr>> {
let (attrs, body) = self.parse_inner_attrs_and_block()?;
if self.eat_keyword(kw::Catch) {
- Err(CatchAfterTry { span: self.prev_token.span }
+ Err(errors::CatchAfterTry { span: self.prev_token.span }
.into_diagnostic(&self.sess.span_diagnostic))
} else {
let span = span_lo.to(body.span);
let expr = self.parse_struct_expr(qself.clone(), path.clone(), true);
if let (Ok(expr), false) = (&expr, struct_allowed) {
// This is a struct literal, but we don't can't accept them here.
- self.sess.emit_err(StructLiteralNotAllowedHere {
+ self.sess.emit_err(errors::StructLiteralNotAllowedHere {
span: expr.span,
- sub: StructLiteralNotAllowedHereSugg {
+ sub: errors::StructLiteralNotAllowedHereSugg {
left: path.span.shrink_to_lo(),
right: expr.span.shrink_to_hi(),
},
let mut async_block_err = |e: &mut Diagnostic, span: Span| {
recover_async = true;
- AsyncBlockIn2015 { span }.add_to_diagnostic(e);
- HelpUseLatestEdition::new().add_to_diagnostic(e);
+ errors::AsyncBlockIn2015 { span }.add_to_diagnostic(e);
+ errors::HelpUseLatestEdition::new().add_to_diagnostic(e);
};
while self.token != token::CloseDelim(close_delim) {
if self.token != token::Comma {
return;
}
- self.sess.emit_err(CommaAfterBaseStruct {
+ self.sess.emit_err(errors::CommaAfterBaseStruct {
span: span.to(self.prev_token.span),
comma: self.token.span,
});
{
// recover from typo of `...`, suggest `..`
let span = self.prev_token.span;
- self.sess.emit_err(MissingDotDot { token_span: span, sugg_span: span });
+ self.sess.emit_err(errors::MissingDotDot { token_span: span, sugg_span: span });
return true;
}
false
return;
}
- self.sess.emit_err(EqFieldInit {
+ self.sess.emit_err(errors::EqFieldInit {
span: self.token.span,
eq: field_name.span.shrink_to_hi().to(self.token.span),
});
}
fn err_dotdotdot_syntax(&self, span: Span) {
- self.sess.emit_err(DotDotDot { span });
+ self.sess.emit_err(errors::DotDotDot { span });
}
fn err_larrow_operator(&self, span: Span) {
- self.sess.emit_err(LeftArrowOperator { span });
+ self.sess.emit_err(errors::LeftArrowOperator { span });
}
fn mk_assign_op(&self, binop: BinOp, lhs: P<Expr>, rhs: P<Expr>) -> ExprKind {
use crate::errors::{
- MultipleWhereClauses, UnexpectedSelfInGenericParameters, WhereClauseBeforeTupleStructBody,
+ MultipleWhereClauses, UnexpectedDefaultValueForLifetimeInGenericParameters,
+ UnexpectedSelfInGenericParameters, WhereClauseBeforeTupleStructBody,
WhereClauseBeforeTupleStructBodySugg,
};
} else {
(None, Vec::new())
};
+
+ if this.check_noexpect(&token::Eq)
+ && this.look_ahead(1, |t| t.is_lifetime())
+ {
+ let lo = this.token.span;
+ // Parse `= 'lifetime`.
+ this.bump(); // `=`
+ this.bump(); // `'lifetime`
+ let span = lo.to(this.prev_token.span);
+ this.sess.emit_err(
+ UnexpectedDefaultValueForLifetimeInGenericParameters { span },
+ );
+ }
+
Some(ast::GenericParam {
ident: lifetime.ident,
id: lifetime.id,
let ext = self.parse_extern(case);
if let Async::Yes { span, .. } = asyncness {
- if span.rust_2015() {
+ if span.is_rust_2015() {
self.sess.emit_err(AsyncFnIn2015 { span, help: HelpUseLatestEdition::new() });
}
}
use rustc_ast::ptr::P;
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};
+use rustc_ast::tokenstream::{AttributesData, DelimSpan, Spacing};
+use rustc_ast::tokenstream::{TokenStream, TokenTree, TokenTreeCursor};
use rustc_ast::util::case::Case;
use rustc_ast::AttrId;
use rustc_ast::DUMMY_NODE_ID;
// This type is used a lot, e.g. it's cloned when matching many declarative macro rules with nonterminals. 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<'_>, 336);
+rustc_data_structures::static_assert_size!(Parser<'_>, 312);
/// Stores span information about a closure.
#[derive(Clone)]
}
}
+/// Iterator over a `TokenStream` that produces `Token`s. It's a bit odd that
+/// we (a) lex tokens into a nice tree structure (`TokenStream`), and then (b)
+/// use this type to emit them as a linear sequence. But a linear sequence is
+/// what the parser expects, for the most part.
#[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>,
+ // Cursor for the current (innermost) token stream. The delimiters for this
+ // token stream are found in `self.stack.last()`; when that is `None` then
+ // we are in the outermost token stream which never has delimiters.
+ tree_cursor: TokenTreeCursor,
+
+ // Token streams surrounding the current one. The delimiters for stack[n]'s
+ // tokens are in `stack[n-1]`. `stack[0]` (when present) has no delimiters
+ // because it's the outermost token stream which never has delimiters.
+ stack: Vec<(TokenTreeCursor, Delimiter, DelimSpan)>,
+
desugar_doc_comments: bool,
+
// 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
// (e.g. '>>' into '>' and '>), so that the parser
break_last_token: bool,
}
-#[derive(Clone)]
-struct TokenCursorFrame {
- delim_sp: Option<(Delimiter, DelimSpan)>,
- tree_cursor: tokenstream::Cursor,
-}
-
-impl TokenCursorFrame {
- fn new(delim_sp: Option<(Delimiter, DelimSpan)>, tts: TokenStream) -> Self {
- TokenCursorFrame { delim_sp, tree_cursor: tts.into_trees() }
- }
-}
-
impl TokenCursor {
fn next(&mut self, desugar_doc_comments: bool) -> (Token, Spacing) {
self.inlined_next(desugar_doc_comments)
// 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) = self.frame.tree_cursor.next_ref() {
+ if let Some(tree) = self.tree_cursor.next_ref() {
match tree {
&TokenTree::Token(ref token, spacing) => match (desugar_doc_comments, token) {
(true, &Token { kind: token::DocComment(_, attr_style, data), span }) => {
- return self.desugar(attr_style, data, span);
+ let desugared = self.desugar(attr_style, data, span);
+ self.tree_cursor.replace_prev_and_rewind(desugared);
+ // Continue to get the first token of the desugared doc comment.
+ }
+ _ => {
+ debug_assert!(!matches!(
+ token.kind,
+ token::OpenDelim(_) | token::CloseDelim(_)
+ ));
+ return (token.clone(), spacing);
}
- _ => 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));
+ let trees = tts.clone().into_trees();
+ self.stack.push((mem::replace(&mut self.tree_cursor, trees), delim, sp));
if delim != Delimiter::Invisible {
return (Token::new(token::OpenDelim(delim), sp.open), Spacing::Alone);
}
// No open delimiter 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;
+ } else if let Some((tree_cursor, delim, span)) = self.stack.pop() {
+ // We have exhausted this token stream. Move back to its parent token stream.
+ self.tree_cursor = tree_cursor;
+ if delim != Delimiter::Invisible {
return (Token::new(token::CloseDelim(delim), span.close), Spacing::Alone);
}
- self.frame = frame;
// No close delimiter to return; continue on to the next iteration.
} else {
+ // We have exhausted the outermost token stream.
return (Token::new(token::Eof, DUMMY_SP), Spacing::Alone);
}
}
}
- fn desugar(&mut self, attr_style: AttrStyle, data: Symbol, span: Span) -> (Token, Spacing) {
+ // Desugar a doc comment into something like `#[doc = r"foo"]`.
+ fn desugar(&mut self, attr_style: AttrStyle, data: Symbol, span: Span) -> Vec<TokenTree> {
// Searches for the occurrences of `"#*` and returns the minimum number of `#`s
// required to wrap the text. E.g.
// - `abc d` is wrapped as `r"abc d"` (num_of_hashes = 0)
.collect::<TokenStream>(),
);
- self.stack.push(mem::replace(
- &mut self.frame,
- TokenCursorFrame::new(
- None,
- if attr_style == AttrStyle::Inner {
- [
- TokenTree::token_alone(token::Pound, span),
- TokenTree::token_alone(token::Not, span),
- body,
- ]
- .into_iter()
- .collect::<TokenStream>()
- } else {
- [TokenTree::token_alone(token::Pound, span), body]
- .into_iter()
- .collect::<TokenStream>()
- },
- ),
- ));
-
- self.next(/* desugar_doc_comments */ false)
+ if attr_style == AttrStyle::Inner {
+ vec![
+ TokenTree::token_alone(token::Pound, span),
+ TokenTree::token_alone(token::Not, span),
+ body,
+ ]
+ } else {
+ vec![TokenTree::token_alone(token::Pound, span), body]
+ }
}
}
restrictions: Restrictions::empty(),
expected_tokens: Vec::new(),
token_cursor: TokenCursor {
- frame: TokenCursorFrame::new(None, tokens),
+ tree_cursor: tokens.into_trees(),
stack: Vec::new(),
num_next_calls: 0,
desugar_doc_comments,
fn check_const_closure(&self) -> bool {
self.is_keyword_ahead(0, &[kw::Const])
&& self.look_ahead(1, |t| match &t.kind {
- token::Ident(kw::Move | kw::Static | kw::Async, _)
- | token::OrOr
- | token::BinOp(token::Or) => true,
+ // async closures do not work with const closures, so we do not parse that here.
+ token::Ident(kw::Move | kw::Static, _) | token::OrOr | token::BinOp(token::Or) => {
+ true
+ }
_ => false,
})
}
return looker(&self.token);
}
- let frame = &self.token_cursor.frame;
- if let Some((delim, span)) = frame.delim_sp && delim != Delimiter::Invisible {
+ let tree_cursor = &self.token_cursor.tree_cursor;
+ if let Some(&(_, delim, span)) = self.token_cursor.stack.last()
+ && delim != Delimiter::Invisible
+ {
let all_normal = (0..dist).all(|i| {
- let token = frame.tree_cursor.look_ahead(i);
+ let token = tree_cursor.look_ahead(i);
!matches!(token, Some(TokenTree::Delimited(_, Delimiter::Invisible, _)))
});
if all_normal {
- return match frame.tree_cursor.look_ahead(dist - 1) {
+ return match tree_cursor.look_ahead(dist - 1) {
Some(tree) => match tree {
TokenTree::Token(token, _) => looker(token),
TokenTree::Delimited(dspan, delim, _) => {
/// Parses constness: `const` or nothing.
fn parse_constness(&mut self, case: Case) -> Const {
- // Avoid const blocks to be parsed as const items
- if self.look_ahead(1, |t| t != &token::OpenDelim(Delimiter::Brace))
+ self.parse_constness_(case, false)
+ }
+
+ /// Parses constness for closures
+ fn parse_closure_constness(&mut self, case: Case) -> Const {
+ self.parse_constness_(case, true)
+ }
+
+ fn parse_constness_(&mut self, case: Case, is_closure: bool) -> Const {
+ // Avoid const blocks and const closures to be parsed as const items
+ if (self.check_const_closure() == is_closure)
+ && self.look_ahead(1, |t| t != &token::OpenDelim(Delimiter::Brace))
&& self.eat_keyword_case(kw::Const, case)
{
Const::Yes(self.prev_token.uninterpolated_span())
pub(crate) fn parse_token_tree(&mut self) -> TokenTree {
match self.token.kind {
token::OpenDelim(..) => {
- // 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();
+ // Grab the tokens within the delimiters.
+ let tree_cursor = &self.token_cursor.tree_cursor;
+ let stream = tree_cursor.stream.clone();
+ let (_, delim, span) = *self.token_cursor.stack.last().unwrap();
// Advance the token cursor through the entire delimited
// sequence. After getting the `OpenDelim` we are *within* the
// Check if the user wrote `foo:bar` instead of `foo::bar`.
if ra == RecoverColon::Yes {
- first_pat =
- self.maybe_recover_colon_colon_in_pat_typo_or_anon_enum(first_pat, expected);
+ first_pat = self.maybe_recover_colon_colon_in_pat_typo(first_pat, expected);
}
if let Some(leading_vert_span) = leading_vert_span {
}
token::Gt if no_space => {
let after_pat = span.with_hi(span.hi() - rustc_span::BytePos(1)).shrink_to_hi();
- self.sess.emit_err(InclusiveRangeMatchArrow { span, after_pat });
+ self.sess.emit_err(InclusiveRangeMatchArrow { span, arrow: tok.span, after_pat });
}
_ => {
self.sess.emit_err(InclusiveRangeNoEnd { span });
}
ate_comma = false;
- if self.check(&token::DotDot) || self.token == token::DotDotDot {
+ if self.check(&token::DotDot)
+ || self.check_noexpect(&token::DotDotDot)
+ || self.check_keyword(kw::Underscore)
+ {
etc = true;
let mut etc_sp = self.token.span;
- self.recover_one_fewer_dotdot();
- self.bump(); // `..` || `...`
+ self.recover_bad_dot_dot();
+ self.bump(); // `..` || `...` || `_`
if self.token == token::CloseDelim(Delimiter::Brace) {
etc_span = Some(etc_sp);
Ok((fields, etc))
}
- /// Recover on `...` as if it were `..` to avoid further errors.
+ /// Recover on `...` or `_` as if it were `..` to avoid further errors.
/// See issue #46718.
- fn recover_one_fewer_dotdot(&self) {
- if self.token != token::DotDotDot {
+ fn recover_bad_dot_dot(&self) {
+ if self.token == token::DotDot {
return;
}
- self.sess.emit_err(DotDotDotForRemainingFields { span: self.token.span });
+ let token_str = pprust::token_to_string(&self.token);
+ self.sess.emit_err(DotDotDotForRemainingFields { span: self.token.span, token_str });
}
fn parse_pat_field(&mut self, lo: Span, attrs: AttrVec) -> PResult<'a, PatField> {
use super::{
AttrWrapper, BlockMode, FnParseMode, ForceCollect, Parser, Restrictions, SemiColonMode,
};
-use crate::errors::{
- AssignmentElseNotAllowed, CompoundAssignmentExpressionInLet, ConstLetMutuallyExclusive,
- DocCommentDoesNotDocumentAnything, ExpectedStatementAfterOuterAttr, InvalidCurlyInLetElse,
- InvalidExpressionInLetElse, InvalidIdentiferStartsWithNumber, InvalidVariableDeclaration,
- InvalidVariableDeclarationSub, WrapExpressionInParentheses,
-};
+use crate::errors;
use crate::maybe_whole;
use rustc_ast as ast;
if self.token.is_keyword(kw::Mut) && self.is_keyword_ahead(1, &[kw::Let]) {
self.bump();
let mut_let_span = lo.to(self.token.span);
- self.sess.emit_err(InvalidVariableDeclaration {
+ self.sess.emit_err(errors::InvalidVariableDeclaration {
span: mut_let_span,
- sub: InvalidVariableDeclarationSub::SwitchMutLetOrder(mut_let_span),
+ sub: errors::InvalidVariableDeclarationSub::SwitchMutLetOrder(mut_let_span),
});
}
Ok(Some(if self.token.is_keyword(kw::Let) {
self.parse_local_mk(lo, attrs, capture_semi, force_collect)?
} else if self.is_kw_followed_by_ident(kw::Mut) && self.may_recover() {
- self.recover_stmt_local_after_let(lo, attrs, InvalidVariableDeclarationSub::MissingLet)?
+ self.recover_stmt_local_after_let(
+ lo,
+ attrs,
+ errors::InvalidVariableDeclarationSub::MissingLet,
+ )?
} else if self.is_kw_followed_by_ident(kw::Auto) && self.may_recover() {
self.bump(); // `auto`
self.recover_stmt_local_after_let(
lo,
attrs,
- InvalidVariableDeclarationSub::UseLetNotAuto,
+ errors::InvalidVariableDeclarationSub::UseLetNotAuto,
)?
} else if self.is_kw_followed_by_ident(sym::var) && self.may_recover() {
self.bump(); // `var`
self.recover_stmt_local_after_let(
lo,
attrs,
- InvalidVariableDeclarationSub::UseLetNotVar,
+ errors::InvalidVariableDeclarationSub::UseLetNotVar,
)?
} else if self.check_path() && !self.token.is_qpath_start() && !self.is_path_start_item() {
// We have avoided contextual keywords like `union`, items with `crate` visibility,
let bl = self.parse_block()?;
// Destructuring assignment ... else.
// This is not allowed, but point it out in a nice way.
- self.sess.emit_err(AssignmentElseNotAllowed { span: e.span.to(bl.span) });
+ self.sess.emit_err(errors::AssignmentElseNotAllowed { span: e.span.to(bl.span) });
}
self.mk_stmt(lo.to(e.span), StmtKind::Expr(e))
} else {
&& let attrs = attrs.take_for_recovery(self.sess)
&& let attrs @ [.., last] = &*attrs {
if last.is_doc_comment() {
- self.sess.emit_err(DocCommentDoesNotDocumentAnything {
+ self.sess.emit_err(errors::DocCommentDoesNotDocumentAnything {
span: last.span,
missing_comma: None,
});
} else if attrs.iter().any(|a| a.style == AttrStyle::Outer) {
- self.sess.emit_err(ExpectedStatementAfterOuterAttr { span: last.span });
+ self.sess.emit_err(errors::ExpectedStatementAfterOuterAttr { span: last.span });
}
}
}
&mut self,
lo: Span,
attrs: AttrWrapper,
- subdiagnostic: fn(Span) -> InvalidVariableDeclarationSub,
+ subdiagnostic: fn(Span) -> errors::InvalidVariableDeclarationSub,
) -> PResult<'a, Stmt> {
let stmt =
self.collect_tokens_trailing_token(attrs, ForceCollect::Yes, |this, attrs| {
TrailingToken::None,
))
})?;
- self.sess.emit_err(InvalidVariableDeclaration { span: lo, sub: subdiagnostic(lo) });
+ self.sess.emit_err(errors::InvalidVariableDeclaration { span: lo, sub: subdiagnostic(lo) });
Ok(stmt)
}
let lo = self.prev_token.span;
if self.token.is_keyword(kw::Const) && self.look_ahead(1, |t| t.is_ident()) {
- self.sess.emit_err(ConstLetMutuallyExclusive { span: lo.to(self.token.span) });
+ self.sess.emit_err(errors::ConstLetMutuallyExclusive { span: lo.to(self.token.span) });
self.bump();
}
rustc_ast::MetaItemLit::from_token(&self.token).is_none() &&
(lit.kind == token::LitKind::Integer || lit.kind == token::LitKind::Float) &&
self.look_ahead(1, |t| matches!(t.kind, token::Eq) || matches!(t.kind, token::Colon ) ) {
- return Err(self.sess.create_err(InvalidIdentiferStartsWithNumber { span: self.token.span }));
+ return Err(self.sess.create_err(errors::InvalidIdentiferStartsWithNumber { span: self.token.span }));
}
Ok(())
}
fn check_let_else_init_bool_expr(&self, init: &ast::Expr) {
if let ast::ExprKind::Binary(op, ..) = init.kind {
if op.node.lazy() {
- self.sess.emit_err(InvalidExpressionInLetElse {
+ self.sess.emit_err(errors::InvalidExpressionInLetElse {
span: init.span,
operator: op.node.to_string(),
- sugg: WrapExpressionInParentheses {
+ sugg: errors::WrapExpressionInParentheses {
left: init.span.shrink_to_lo(),
right: init.span.shrink_to_hi(),
},
fn check_let_else_init_trailing_brace(&self, init: &ast::Expr) {
if let Some(trailing) = classify::expr_trailing_brace(init) {
- self.sess.emit_err(InvalidCurlyInLetElse {
+ self.sess.emit_err(errors::InvalidCurlyInLetElse {
span: trailing.span.with_lo(trailing.span.hi() - BytePos(1)),
- sugg: WrapExpressionInParentheses {
+ sugg: errors::WrapExpressionInParentheses {
left: trailing.span.shrink_to_lo(),
right: trailing.span.shrink_to_hi(),
},
let eq_consumed = match self.token.kind {
token::BinOpEq(..) => {
// Recover `let x <op>= 1` as `let x = 1`
- self.sess.emit_err(CompoundAssignmentExpressionInLet { span: self.token.span });
+ self.sess
+ .emit_err(errors::CompoundAssignmentExpressionInLet { span: self.token.span });
self.bump();
true
}
self as ast, BareFnTy, FnRetTy, GenericBound, GenericBounds, GenericParam, Generics, Lifetime,
MacCall, MutTy, Mutability, PolyTraitRef, TraitBoundModifier, TraitObjectSyntax, Ty, TyKind,
};
-use rustc_ast_pretty::pprust;
use rustc_errors::{Applicability, PResult};
use rustc_span::source_map::Span;
use rustc_span::symbol::{kw, sym, Ident};
No,
}
-#[derive(PartialEq, Clone, Copy)]
+#[derive(PartialEq)]
pub(super) enum RecoverQPath {
Yes,
No,
}
-#[derive(PartialEq, Clone, Copy)]
pub(super) enum RecoverQuestionMark {
Yes,
No,
}
-#[derive(PartialEq, Clone, Copy)]
-pub(super) enum RecoverAnonEnum {
- Yes,
- No,
-}
-
/// Signals whether parsing a type should recover `->`.
///
/// More specifically, when parsing a function like:
}
// Is `...` (`CVarArgs`) legal at this level of type parsing?
-#[derive(PartialEq, Clone, Copy)]
+#[derive(PartialEq)]
enum AllowCVariadic {
Yes,
No,
RecoverReturnSign::Yes,
None,
RecoverQuestionMark::Yes,
- RecoverAnonEnum::No,
)
}
RecoverReturnSign::Yes,
Some(ty_params),
RecoverQuestionMark::Yes,
- RecoverAnonEnum::No,
)
}
RecoverReturnSign::Yes,
None,
RecoverQuestionMark::Yes,
- RecoverAnonEnum::Yes,
)
}
RecoverReturnSign::Yes,
None,
RecoverQuestionMark::Yes,
- RecoverAnonEnum::No,
)
}
RecoverReturnSign::Yes,
None,
RecoverQuestionMark::No,
- RecoverAnonEnum::No,
)
}
RecoverReturnSign::Yes,
None,
RecoverQuestionMark::No,
- RecoverAnonEnum::No,
)
}
RecoverReturnSign::OnlyFatArrow,
None,
RecoverQuestionMark::Yes,
- RecoverAnonEnum::No,
)
}
recover_return_sign,
None,
RecoverQuestionMark::Yes,
- RecoverAnonEnum::Yes,
)?;
FnRetTy::Ty(ty)
} else if recover_return_sign.can_recover(&self.token.kind) {
recover_return_sign,
None,
RecoverQuestionMark::Yes,
- RecoverAnonEnum::Yes,
)?;
FnRetTy::Ty(ty)
} else {
recover_return_sign: RecoverReturnSign,
ty_generics: Option<&Generics>,
recover_question_mark: RecoverQuestionMark,
- recover_anon_enum: RecoverAnonEnum,
) -> PResult<'a, P<Ty>> {
let allow_qpath_recovery = recover_qpath == RecoverQPath::Yes;
maybe_recover_from_interpolated_ty_qpath!(self, allow_qpath_recovery);
AllowPlus::Yes => self.maybe_recover_from_bad_type_plus(&ty)?,
AllowPlus::No => self.maybe_report_ambiguous_plus(impl_dyn_multi, &ty),
}
- if RecoverQuestionMark::Yes == recover_question_mark {
+ if let RecoverQuestionMark::Yes = recover_question_mark {
ty = self.maybe_recover_from_question_mark(ty);
}
- if recover_anon_enum == RecoverAnonEnum::Yes
- && self.check_noexpect(&token::BinOp(token::Or))
- && self.look_ahead(1, |t| t.can_begin_type())
- {
- let mut pipes = vec![self.token.span];
- let mut types = vec![ty];
- loop {
- if !self.eat(&token::BinOp(token::Or)) {
- break;
- }
- pipes.push(self.prev_token.span);
- types.push(self.parse_ty_common(
- allow_plus,
- allow_c_variadic,
- recover_qpath,
- recover_return_sign,
- ty_generics,
- recover_question_mark,
- RecoverAnonEnum::No,
- )?);
- }
- let mut err = self.struct_span_err(pipes, "anonymous enums are not supported");
- for ty in &types {
- err.span_label(ty.span, "");
- }
- err.help(&format!(
- "create a named `enum` and use it here instead:\nenum Name {{\n{}\n}}",
- types
- .iter()
- .enumerate()
- .map(|(i, t)| format!(
- " Variant{}({}),",
- i + 1, // Lets not confuse people with zero-indexing :)
- pprust::to_string(|s| s.print_type(&t)),
- ))
- .collect::<Vec<_>>()
- .join("\n"),
- ));
- err.emit();
- return Ok(self.mk_ty(lo.to(self.prev_token.span), TyKind::Err));
- }
if allow_qpath_recovery { self.maybe_recover_from_bad_qpath(ty) } else { Ok(ty) }
}
/// Is a `dyn B0 + ... + Bn` type allowed here?
fn is_explicit_dyn_type(&mut self) -> bool {
self.check_keyword(kw::Dyn)
- && (!self.token.uninterpolated_span().rust_2015()
+ && (self.token.uninterpolated_span().rust_2018()
|| self.look_ahead(1, |t| {
(t.can_begin_bound() || t.kind == TokenKind::BinOp(token::Star))
&& !can_continue_type_after_non_fn_ident(t)
);
}
} else {
- self.suggest_positional_arg_instead_of_captured_arg(arg);
+ if let Some(&(_, maybe)) = self.cur.peek() {
+ if maybe == '?' {
+ self.suggest_format();
+ } else {
+ self.suggest_positional_arg_instead_of_captured_arg(arg);
+ }
+ }
}
Some(NextArgument(Box::new(arg)))
}
if found { Some(cur) } else { None }
}
+ fn suggest_format(&mut self) {
+ if let (Some(pos), Some(_)) = (self.consume_pos('?'), self.consume_pos(':')) {
+ let word = self.word();
+ let _end = self.current_pos();
+ let pos = self.to_span_index(pos);
+ self.errors.insert(
+ 0,
+ ParseError {
+ description: "expected format parameter to occur after `:`".to_owned(),
+ note: Some(
+ format!("`?` comes after `:`, try `{}:{}` instead", word, "?").to_owned(),
+ ),
+ label: "expected `?` to occur after `:`".to_owned(),
+ span: pos.to(pos),
+ secondary_label: None,
+ should_be_replaced_with_positional_argument: false,
+ },
+ );
+ }
+ }
+
fn suggest_positional_arg_instead_of_captured_arg(&mut self, arg: Argument<'a>) {
if let Some(end) = self.consume_pos('.') {
let byte_pos = self.to_span_index(end);
//! conflicts between multiple such attributes attached to the same
//! item.
-use crate::errors::{
- self, AttrApplication, DebugVisualizerUnreadable, InvalidAttrAtCrateLevel, ObjectLifetimeErr,
- OnlyHasEffectOn, ProcMacroDiffArguments, ProcMacroInvalidAbi, ProcMacroMissingArguments,
- ProcMacroTypeError, ProcMacroUnsafe, TransparentIncompatible, UnrecognizedReprHint,
-};
+use crate::errors;
use rustc_ast::{ast, AttrStyle, Attribute, LitKind, MetaItemKind, MetaItemLit, NestedMetaItem};
use rustc_data_structures::fx::FxHashMap;
use rustc_errors::{fluent, Applicability, IntoDiagnosticArg, MultiSpan};
UNUSED_ATTRIBUTES,
hir_id,
attr.span,
- OnlyHasEffectOn {
+ errors::OnlyHasEffectOn {
attr_name: attr.name_or_empty(),
target_name: allowed_target.name().replace(' ', "_"),
},
ObjectLifetimeDefault::Param(def_id) => tcx.item_name(def_id).to_string(),
ObjectLifetimeDefault::Ambiguous => "Ambiguous".to_owned(),
};
- tcx.sess.emit_err(ObjectLifetimeErr { span: p.span, repr });
+ tcx.sess.emit_err(errors::ObjectLifetimeErr { span: p.span, repr });
}
}
}
match target {
Target::Struct | Target::Union | Target::Enum => continue,
_ => {
- self.tcx.sess.emit_err(AttrApplication::StructEnumUnion {
+ self.tcx.sess.emit_err(errors::AttrApplication::StructEnumUnion {
hint_span: hint.span(),
span,
});
match target {
Target::Struct | Target::Union | Target::Enum | Target::Fn => continue,
_ => {
- self.tcx.sess.emit_err(AttrApplication::StructEnumFunctionUnion {
- hint_span: hint.span(),
- span,
- });
+ self.tcx.sess.emit_err(
+ errors::AttrApplication::StructEnumFunctionUnion {
+ hint_span: hint.span(),
+ span,
+ },
+ );
}
}
}
sym::packed => {
if target != Target::Struct && target != Target::Union {
- self.tcx.sess.emit_err(AttrApplication::StructUnion {
+ self.tcx.sess.emit_err(errors::AttrApplication::StructUnion {
hint_span: hint.span(),
span,
});
sym::simd => {
is_simd = true;
if target != Target::Struct {
- self.tcx
- .sess
- .emit_err(AttrApplication::Struct { hint_span: hint.span(), span });
+ self.tcx.sess.emit_err(errors::AttrApplication::Struct {
+ hint_span: hint.span(),
+ span,
+ });
} else {
continue;
}
match target {
Target::Struct | Target::Union | Target::Enum => continue,
_ => {
- self.tcx.sess.emit_err(AttrApplication::StructEnumUnion {
+ self.tcx.sess.emit_err(errors::AttrApplication::StructEnumUnion {
hint_span: hint.span(),
span,
});
| sym::usize => {
int_reprs += 1;
if target != Target::Enum {
- self.tcx
- .sess
- .emit_err(AttrApplication::Enum { hint_span: hint.span(), span });
+ self.tcx.sess.emit_err(errors::AttrApplication::Enum {
+ hint_span: hint.span(),
+ span,
+ });
} else {
continue;
}
}
_ => {
- self.tcx.sess.emit_err(UnrecognizedReprHint { span: hint.span() });
+ self.tcx.sess.emit_err(errors::UnrecognizedReprHint { span: hint.span() });
continue;
}
};
// Error on repr(transparent, <anything else>).
if is_transparent && hints.len() > 1 {
let hint_spans: Vec<_> = hint_spans.clone().collect();
- self.tcx
- .sess
- .emit_err(TransparentIncompatible { hint_spans, target: target.to_string() });
+ self.tcx.sess.emit_err(errors::TransparentIncompatible {
+ hint_spans,
+ target: target.to_string(),
+ });
}
// Warn on repr(u8, u16), repr(C, simd), and c-like-enum-repr(C, u8)
if (int_reprs > 1)
match std::fs::File::open(&file) {
Ok(_) => true,
Err(error) => {
- self.tcx.sess.emit_err(DebugVisualizerUnreadable {
+ self.tcx.sess.emit_err(errors::DebugVisualizerUnreadable {
span: meta_item.span,
file: &file,
error,
let drcx = DeepRejectCtxt { treat_obligation_params: TreatParams::AsInfer };
if sig.abi != Abi::Rust {
- tcx.sess.emit_err(ProcMacroInvalidAbi { span: hir_sig.span, abi: sig.abi.name() });
+ tcx.sess.emit_err(errors::ProcMacroInvalidAbi {
+ span: hir_sig.span,
+ abi: sig.abi.name(),
+ });
self.abort.set(true);
}
if sig.unsafety == Unsafety::Unsafe {
- tcx.sess.emit_err(ProcMacroUnsafe { span: hir_sig.span });
+ tcx.sess.emit_err(errors::ProcMacroUnsafe { span: hir_sig.span });
self.abort.set(true);
}
// Typecheck the output
if !drcx.types_may_unify(output, tokenstream) {
- tcx.sess.emit_err(ProcMacroTypeError {
+ tcx.sess.emit_err(errors::ProcMacroTypeError {
span: hir_sig.decl.output.span(),
found: output,
kind,
}
if sig.inputs().len() < expected_input_count {
- tcx.sess.emit_err(ProcMacroMissingArguments {
+ tcx.sess.emit_err(errors::ProcMacroMissingArguments {
expected_input_count,
span: hir_sig.span,
kind,
sig.inputs().iter().zip(hir_sig.decl.inputs).take(expected_input_count)
{
if !drcx.types_may_unify(*arg, tokenstream) {
- tcx.sess.emit_err(ProcMacroTypeError {
+ tcx.sess.emit_err(errors::ProcMacroTypeError {
span: input.span,
found: *arg,
kind,
let body_id = tcx.hir().body_owned_by(id.def_id);
let excess = tcx.hir().body(body_id).params.get(expected_input_count..);
if let Some(excess @ [begin @ end] | excess @ [begin, .., end]) = excess {
- tcx.sess.emit_err(ProcMacroDiffArguments {
+ tcx.sess.emit_err(errors::ProcMacroDiffArguments {
span: begin.span.to(end.span),
count: excess.len(),
kind,
if attr.style == AttrStyle::Inner {
for attr_to_check in ATTRS_TO_CHECK {
if attr.has_name(*attr_to_check) {
- tcx.sess.emit_err(InvalidAttrAtCrateLevel {
+ tcx.sess.emit_err(errors::InvalidAttrAtCrateLevel {
span: attr.span,
snippet: tcx.sess.source_map().span_to_snippet(attr.span).ok(),
name: *attr_to_check,
.expect("owning item has no entry");
if max != self.hir_ids_seen.len() - 1 {
- // Collect the missing ItemLocalIds
- let missing: Vec<_> = (0..=max as u32)
- .filter(|&i| !self.hir_ids_seen.contains(ItemLocalId::from_u32(i)))
- .collect();
-
- // Try to map those to something more useful
- let mut missing_items = Vec::with_capacity(missing.len());
+ let hir = self.tcx.hir();
+ let pretty_owner = hir.def_path(owner.def_id).to_string_no_crate_verbose();
- for local_id in missing {
- let hir_id = HirId { owner, local_id: ItemLocalId::from_u32(local_id) };
+ let missing_items: Vec<_> = (0..=max as u32)
+ .map(|i| ItemLocalId::from_u32(i))
+ .filter(|&local_id| !self.hir_ids_seen.contains(local_id))
+ .map(|local_id| hir.node_to_string(HirId { owner, local_id }))
+ .collect();
- trace!("missing hir id {:#?}", hir_id);
+ let seen_items: Vec<_> = self
+ .hir_ids_seen
+ .iter()
+ .map(|local_id| hir.node_to_string(HirId { owner, local_id }))
+ .collect();
- missing_items.push(format!(
- "[local_id: {}, owner: {}]",
- local_id,
- self.tcx.hir().def_path(owner.def_id).to_string_no_crate_verbose()
- ));
- }
self.error(|| {
format!(
"ItemLocalIds not assigned densely in {}. \
- Max ItemLocalId = {}, missing IDs = {:#?}; seens IDs = {:#?}",
- self.tcx.hir().def_path(owner.def_id).to_string_no_crate_verbose(),
- max,
- missing_items,
- self.hir_ids_seen
- .iter()
- .map(|local_id| HirId { owner, local_id })
- .map(|h| format!("({:?} {})", h, self.tcx.hir().node_to_string(h)))
- .collect::<Vec<_>>()
+ Max ItemLocalId = {}, missing IDs = {:#?}; seen IDs = {:#?}",
+ pretty_owner, max, missing_items, seen_items
)
});
}
//! but are not declared in one single location (unlike lang features), which means we need to
//! collect them instead.
-use rustc_ast::{Attribute, MetaItemKind};
+use rustc_ast::Attribute;
use rustc_attr::{rust_version_symbol, VERSION_PLACEHOLDER};
use rustc_hir::intravisit::Visitor;
use rustc_middle::hir::nested_filter;
// Find a stability attribute: one of #[stable(…)], #[unstable(…)],
// #[rustc_const_stable(…)], #[rustc_const_unstable(…)] or #[rustc_default_body_unstable].
if let Some(stab_attr) = stab_attrs.iter().find(|stab_attr| attr.has_name(**stab_attr)) {
- let meta_kind = attr.meta_kind();
- if let Some(MetaItemKind::List(ref metas)) = meta_kind {
+ if let Some(metas) = attr.meta_item_list() {
let mut feature = None;
let mut since = None;
for meta in metas {
//! A pass that annotates every item and method with its stability level,
//! propagating default levels lexically from parent to children ast nodes.
-use crate::errors::{
- self, CannotStabilizeDeprecated, DeprecatedAttribute, DuplicateFeatureErr,
- FeatureOnlyOnNightly, ImpliedFeatureNotExist, InvalidDeprecationVersion, InvalidStability,
- MissingConstErr, MissingConstStabAttr, MissingStabilityAttr, TraitImplConstStable,
- UnknownFeature, UselessStability,
-};
+use crate::errors;
use rustc_attr::{
self as attr, rust_version_symbol, ConstStability, Stability, StabilityLevel, Unstable,
UnstableReason, VERSION_PLACEHOLDER,
{
self.tcx
.sess
- .emit_err(MissingConstErr { fn_sig_span: fn_sig.span, const_span });
+ .emit_err(errors::MissingConstErr { fn_sig_span: fn_sig.span, const_span });
}
}
}
if let Some((rustc_attr::Deprecation { is_since_rustc_version: true, .. }, span)) = &depr {
if stab.is_none() {
- self.tcx.sess.emit_err(DeprecatedAttribute { span: *span });
+ self.tcx.sess.emit_err(errors::DeprecatedAttribute { span: *span });
}
}
if kind == AnnotationKind::Prohibited
|| (kind == AnnotationKind::Container && stab.level.is_stable() && is_deprecated)
{
- self.tcx.sess.emit_err(UselessStability { span, item_sp });
+ self.tcx.sess.emit_err(errors::UselessStability { span, item_sp });
}
debug!("annotate: found {:?}", stab);
{
match stab_v.parse::<u64>() {
Err(_) => {
- self.tcx.sess.emit_err(InvalidStability { span, item_sp });
+ self.tcx.sess.emit_err(errors::InvalidStability { span, item_sp });
break;
}
Ok(stab_vp) => match dep_v.parse::<u64>() {
Ok(dep_vp) => match dep_vp.cmp(&stab_vp) {
Ordering::Less => {
- self.tcx
- .sess
- .emit_err(CannotStabilizeDeprecated { span, item_sp });
+ self.tcx.sess.emit_err(errors::CannotStabilizeDeprecated {
+ span,
+ item_sp,
+ });
break;
}
Ordering::Equal => continue,
},
Err(_) => {
if dep_v != "TBD" {
- self.tcx
- .sess
- .emit_err(InvalidDeprecationVersion { span, item_sp });
+ self.tcx.sess.emit_err(errors::InvalidDeprecationVersion {
+ span,
+ item_sp,
+ });
}
break;
}
&& self.effective_visibilities.is_reachable(def_id)
{
let descr = self.tcx.def_kind(def_id).descr(def_id.to_def_id());
- self.tcx.sess.emit_err(MissingStabilityAttr { span, descr });
+ self.tcx.sess.emit_err(errors::MissingStabilityAttr { span, descr });
}
}
if is_const && is_stable && missing_const_stability_attribute && is_reachable {
let descr = self.tcx.def_kind(def_id).descr(def_id.to_def_id());
- self.tcx.sess.emit_err(MissingConstStabAttr { span, descr });
+ self.tcx.sess.emit_err(errors::MissingConstStabAttr { span, descr });
}
}
}
&& *constness == hir::Constness::Const
&& const_stab.map_or(false, |(stab, _)| stab.is_const_stable())
{
- self.tcx.sess.emit_err(TraitImplConstStable { span: item.span });
+ self.tcx.sess.emit_err(errors::TraitImplConstStable { span: item.span });
}
}
}
if !lang_features.insert(feature) {
// Warn if the user enables a lang feature multiple times.
- tcx.sess.emit_err(DuplicateFeatureErr { span, feature });
+ tcx.sess.emit_err(errors::DuplicateFeatureErr { span, feature });
}
}
let mut remaining_lib_features = FxIndexMap::default();
for (feature, span) in declared_lib_features {
if !tcx.sess.opts.unstable_features.is_nightly_build() {
- tcx.sess.emit_err(FeatureOnlyOnNightly {
+ tcx.sess.emit_err(errors::FeatureOnlyOnNightly {
span: *span,
release_channel: env!("CFG_RELEASE_CHANNEL"),
});
}
if remaining_lib_features.contains_key(&feature) {
// Warn if the user enables a lib feature multiple times.
- tcx.sess.emit_err(DuplicateFeatureErr { span: *span, feature: *feature });
+ tcx.sess.emit_err(errors::DuplicateFeatureErr { span: *span, feature: *feature });
}
remaining_lib_features.insert(feature, *span);
}
}
for (feature, span) in remaining_lib_features {
- tcx.sess.emit_err(UnknownFeature { span, feature: *feature });
+ tcx.sess.emit_err(errors::UnknownFeature { span, feature: *feature });
}
for (implied_by, feature) in remaining_implications {
.map(|(_, span)| span)
.or_else(|| local_defined_features.unstable.get(&feature))
.expect("feature that implied another does not exist");
- tcx.sess.emit_err(ImpliedFeatureNotExist { span, feature, implied_by });
+ tcx.sess.emit_err(errors::ImpliedFeatureNotExist { span, feature, implied_by });
}
// FIXME(#44232): the `used_features` table no longer exists, so we
rustc_middle = { path = "../rustc_middle" }
rustc_session = { path = "../rustc_session" }
rustc_span = { path = "../rustc_span" }
-rustc_trait_selection = { path = "../rustc_trait_selection" }
rustc_hir_analysis = { path = "../rustc_hir_analysis" }
tracing = "0.1"
rustc_serialize = { path = "../rustc_serialize" }
rustc_session = { path = "../rustc_session" }
rustc_span = { path = "../rustc_span" }
-rustc_target = { path = "../rustc_target" }
thin-vec = "0.2.9"
tracing = "0.1"
kind: DepKind,
name: &'static str,
) -> QueryStackFrame<DepKind> {
- // Disable visible paths printing for performance reasons.
- // Showing visible path instead of any path is not that important in production.
- let description = ty::print::with_no_visible_paths!(
- // Force filename-line mode to avoid invoking `type_of` query.
- ty::print::with_forced_impl_filename_line!(do_describe(tcx.tcx, key))
+ // Avoid calling queries while formatting the description
+ let description = ty::print::with_no_queries!(
+ // Disable visible paths printing for performance reasons.
+ // Showing visible path instead of any path is not that important in production.
+ ty::print::with_no_visible_paths!(
+ // Force filename-line mode to avoid invoking `type_of` query.
+ ty::print::with_forced_impl_filename_line!(do_describe(tcx.tcx, key))
+ )
);
let description =
if tcx.sess.verbose() { format!("{description} [{name:?}]") } else { description };
pub trait CacheSelector<'tcx, V> {
type Cache
where
- V: Clone;
+ V: Copy;
type ArenaCache;
}
pub trait QueryStorage {
type Value: Debug;
- type Stored: Clone;
+ type Stored: Copy;
/// Store a value without putting it in the cache.
/// This is meant to be used with cycle errors.
/// It returns the shard index and a lock guard to the shard,
/// which will be used if the query is not in the cache and we need
/// to compute it.
- fn lookup<R, OnHit>(
- &self,
- key: &Self::Key,
- // `on_hit` can be called while holding a lock to the query state shard.
- on_hit: OnHit,
- ) -> Result<R, ()>
- where
- OnHit: FnOnce(&Self::Stored, DepNodeIndex) -> R;
+ fn lookup(&self, key: &Self::Key) -> Option<(Self::Stored, DepNodeIndex)>;
fn complete(&self, key: Self::Key, value: Self::Value, index: DepNodeIndex) -> Self::Stored;
impl<'tcx, K: Eq + Hash, V: 'tcx> CacheSelector<'tcx, V> for DefaultCacheSelector<K> {
type Cache = DefaultCache<K, V>
where
- V: Clone;
+ V: Copy;
type ArenaCache = ArenaCache<'tcx, K, V>;
}
}
}
-impl<K: Eq + Hash, V: Clone + Debug> QueryStorage for DefaultCache<K, V> {
+impl<K: Eq + Hash, V: Copy + Debug> QueryStorage for DefaultCache<K, V> {
type Value = V;
type Stored = V;
impl<K, V> QueryCache for DefaultCache<K, V>
where
K: Eq + Hash + Clone + Debug,
- V: Clone + Debug,
+ V: Copy + Debug,
{
type Key = K;
#[inline(always)]
- fn lookup<R, OnHit>(&self, key: &K, on_hit: OnHit) -> Result<R, ()>
- where
- OnHit: FnOnce(&V, DepNodeIndex) -> R,
- {
+ fn lookup(&self, key: &K) -> Option<(V, DepNodeIndex)> {
let key_hash = sharded::make_hash(key);
#[cfg(parallel_compiler)]
let lock = self.cache.get_shard_by_hash(key_hash).lock();
let lock = self.cache.lock();
let result = lock.raw_entry().from_key_hashed_nocheck(key_hash, key);
- if let Some((_, value)) = result {
- let hit_result = on_hit(&value.0, value.1);
- Ok(hit_result)
- } else {
- Err(())
- }
+ if let Some((_, value)) = result { Some(*value) } else { None }
}
#[inline]
type Key = K;
#[inline(always)]
- fn lookup<R, OnHit>(&self, key: &K, on_hit: OnHit) -> Result<R, ()>
- where
- OnHit: FnOnce(&&'tcx V, DepNodeIndex) -> R,
- {
+ fn lookup(&self, key: &K) -> Option<(&'tcx V, DepNodeIndex)> {
let key_hash = sharded::make_hash(key);
#[cfg(parallel_compiler)]
let lock = self.cache.get_shard_by_hash(key_hash).lock();
let lock = self.cache.lock();
let result = lock.raw_entry().from_key_hashed_nocheck(key_hash, key);
- if let Some((_, value)) = result {
- let hit_result = on_hit(&&value.0, value.1);
- Ok(hit_result)
- } else {
- Err(())
- }
+ if let Some((_, value)) = result { Some((&value.0, value.1)) } else { None }
}
#[inline]
impl<'tcx, K: Idx, V: 'tcx> CacheSelector<'tcx, V> for VecCacheSelector<K> {
type Cache = VecCache<K, V>
where
- V: Clone;
+ V: Copy;
type ArenaCache = VecArenaCache<'tcx, K, V>;
}
}
}
-impl<K: Eq + Idx, V: Clone + Debug> QueryStorage for VecCache<K, V> {
+impl<K: Eq + Idx, V: Copy + Debug> QueryStorage for VecCache<K, V> {
type Value = V;
type Stored = V;
impl<K, V> QueryCache for VecCache<K, V>
where
K: Eq + Idx + Clone + Debug,
- V: Clone + Debug,
+ V: Copy + Debug,
{
type Key = K;
#[inline(always)]
- fn lookup<R, OnHit>(&self, key: &K, on_hit: OnHit) -> Result<R, ()>
- where
- OnHit: FnOnce(&V, DepNodeIndex) -> R,
- {
+ fn lookup(&self, key: &K) -> Option<(V, DepNodeIndex)> {
#[cfg(parallel_compiler)]
let lock = self.cache.get_shard_by_hash(key.index() as u64).lock();
#[cfg(not(parallel_compiler))]
let lock = self.cache.lock();
- if let Some(Some(value)) = lock.get(*key) {
- let hit_result = on_hit(&value.0, value.1);
- Ok(hit_result)
- } else {
- Err(())
- }
+ if let Some(Some(value)) = lock.get(*key) { Some(*value) } else { None }
}
#[inline]
type Key = K;
#[inline(always)]
- fn lookup<R, OnHit>(&self, key: &K, on_hit: OnHit) -> Result<R, ()>
- where
- OnHit: FnOnce(&&'tcx V, DepNodeIndex) -> R,
- {
+ fn lookup(&self, key: &K) -> Option<(&'tcx V, DepNodeIndex)> {
#[cfg(parallel_compiler)]
let lock = self.cache.get_shard_by_hash(key.index() as u64).lock();
#[cfg(not(parallel_compiler))]
let lock = self.cache.lock();
- if let Some(Some(value)) = lock.get(*key) {
- let hit_result = on_hit(&&value.0, value.1);
- Ok(hit_result)
- } else {
- Err(())
- }
+ if let Some(Some(value)) = lock.get(*key) { Some((&value.0, value.1)) } else { None }
}
#[inline]
type Key: DepNodeParams<Qcx::DepContext> + Eq + Hash + Clone + Debug;
type Value: Debug;
- type Stored: Debug + Clone + std::borrow::Borrow<Self::Value>;
+ type Stored: Debug + Copy + std::borrow::Borrow<Self::Value>;
type Cache: QueryCache<Key = Self::Key, Stored = Self::Stored, Value = Self::Value>;
where
Qcx: QueryContext + crate::query::HasDepContext<DepKind = D>,
V: std::fmt::Debug + Value<Qcx::DepContext, Qcx::DepKind>,
- R: Clone,
+ R: Copy,
{
let error = report_cycle(qcx.dep_context().sess(), &cycle_error);
let value = handle_cycle_error(*qcx.dep_context(), &cycle_error, error, handler);
/// which will be used if the query is not in the cache and we need
/// to compute it.
#[inline]
-pub fn try_get_cached<Tcx, C, R, OnHit>(
- tcx: Tcx,
- cache: &C,
- key: &C::Key,
- // `on_hit` can be called while holding a lock to the query cache
- on_hit: OnHit,
-) -> Result<R, ()>
+pub fn try_get_cached<Tcx, C>(tcx: Tcx, cache: &C, key: &C::Key) -> Option<C::Stored>
where
C: QueryCache,
Tcx: DepContext,
- OnHit: FnOnce(&C::Stored) -> R,
{
- cache.lookup(&key, |value, index| {
- if std::intrinsics::unlikely(tcx.profiler().enabled()) {
- tcx.profiler().query_cache_hit(index.into());
+ match cache.lookup(&key) {
+ Some((value, index)) => {
+ if std::intrinsics::unlikely(tcx.profiler().enabled()) {
+ tcx.profiler().query_cache_hit(index.into());
+ }
+ tcx.dep_graph().read_index(index);
+ Some(value)
}
- tcx.dep_graph().read_index(index);
- on_hit(value)
- })
+ None => None,
+ }
}
fn try_execute_query<Q, Qcx>(
if Q::FEEDABLE {
// We may have put a value inside the cache from inside the execution.
// Verify that it has the same hash as what we have now, to ensure consistency.
- let _ = cache.lookup(&key, |cached_result, _| {
+ if let Some((cached_result, _)) = cache.lookup(&key) {
let hasher = Q::HASH_RESULT.expect("feedable forbids no_hash");
- let old_hash = qcx.dep_context().with_stable_hashing_context(|mut hcx| hasher(&mut hcx, cached_result.borrow()));
- let new_hash = qcx.dep_context().with_stable_hashing_context(|mut hcx| hasher(&mut hcx, &result));
+ let old_hash = qcx.dep_context().with_stable_hashing_context(|mut hcx| {
+ hasher(&mut hcx, cached_result.borrow())
+ });
+ let new_hash = qcx
+ .dep_context()
+ .with_stable_hashing_context(|mut hcx| hasher(&mut hcx, &result));
debug_assert_eq!(
- old_hash, new_hash,
+ old_hash,
+ new_hash,
"Computed query value for {:?}({:?}) is inconsistent with fed value,\ncomputed={:#?}\nfed={:#?}",
- Q::DEP_KIND, key, result, cached_result,
+ Q::DEP_KIND,
+ key,
+ result,
+ cached_result,
);
- });
+ }
}
let result = job.complete(cache, result, dep_node_index);
(result, Some(dep_node_index))
}
#[cfg(parallel_compiler)]
TryGetJob::JobCompleted(query_blocked_prof_timer) => {
- let (v, index) = cache
- .lookup(&key, |value, index| (value.clone(), index))
- .unwrap_or_else(|_| panic!("value must be in cache after waiting"));
+ let Some((v, index)) = cache.lookup(&key) else {
+ panic!("value must be in cache after waiting")
+ };
if std::intrinsics::unlikely(qcx.dep_context().profiler().enabled()) {
qcx.dep_context().profiler().query_cache_hit(index.into());
// We may be concurrently trying both execute and force a query.
// Ensure that only one of them runs the query.
let cache = Q::query_cache(qcx);
- let cached = cache.lookup(&key, |_, index| {
+ if let Some((_, index)) = cache.lookup(&key) {
if std::intrinsics::unlikely(qcx.dep_context().profiler().enabled()) {
qcx.dep_context().profiler().query_cache_hit(index.into());
}
- });
-
- match cached {
- Ok(()) => return,
- Err(()) => {}
+ return;
}
let state = Q::query_state(qcx);
let ident = path.segments.get(0).expect("empty path in visibility").ident;
let crate_root = if ident.is_path_segment_keyword() {
None
- } else if ident.span.rust_2015() {
+ } else if ident.span.is_rust_2015() {
Some(Segment::from_ident(Ident::new(
kw::PathRoot,
path.span.shrink_to_lo().with_ctxt(ident.span.ctxt()),
// appears, so imports in braced groups can have roots prepended independently.
let is_glob = matches!(use_tree.kind, ast::UseTreeKind::Glob);
let crate_root = match prefix_iter.peek() {
- Some(seg) if !seg.ident.is_path_segment_keyword() && seg.ident.span.rust_2015() => {
+ Some(seg) if !seg.ident.is_path_segment_keyword() && seg.ident.span.is_rust_2015() => {
Some(seg.ident.span.ctxt())
}
- None if is_glob && use_tree.span.rust_2015() => Some(use_tree.span.ctxt()),
+ None if is_glob && use_tree.span.is_rust_2015() => Some(use_tree.span.ctxt()),
_ => None,
}
.map(|ctxt| {
let first_name = match path.get(0) {
// In the 2018 edition this lint is a hard error, so nothing to do
- Some(seg) if seg.ident.span.rust_2015() && self.session.rust_2015() => seg.ident.name,
+ Some(seg) if seg.ident.span.is_rust_2015() && self.session.is_rust_2015() => {
+ seg.ident.name
+ }
_ => return,
};
Applicability::MaybeIncorrect,
)),
)
- } else if self.session.rust_2015() {
+ } else if self.session.is_rust_2015() {
(
format!("maybe a missing crate `{ident}`?"),
Some((
mut path: Vec<Segment>,
parent_scope: &ParentScope<'b>,
) -> Option<(Vec<Segment>, Option<String>)> {
- if path[1].ident.span.rust_2015() {
+ if path[1].ident.span.is_rust_2015() {
return None;
}
// 4c. Standard library prelude (de-facto closed, controlled).
// 6. Language prelude: builtin attributes (closed, controlled).
- let rust_2015 = ctxt.edition().rust_2015();
+ let rust_2015 = ctxt.edition().is_rust_2015();
let (ns, macro_kind, is_absolute_path) = match scope_set {
ScopeSet::All(ns, _) => (ns, None, false),
ScopeSet::AbsolutePath(ns) => (ns, None, true),
module = Some(ModuleOrUniformRoot::ExternPrelude);
continue;
}
- if name == kw::PathRoot && ident.span.rust_2015() && self.session.rust_2018() {
+ if name == kw::PathRoot && ident.span.is_rust_2015() && self.session.rust_2018()
+ {
// `::a::b` from 2015 macro on 2018 global edition
module = Some(ModuleOrUniformRoot::CrateRootAndExternPrelude);
continue;
let segments = &use_tree.prefix.segments;
if !segments.is_empty() {
let ident = segments[0].ident;
- if ident.is_path_segment_keyword() || ident.span.rust_2015() {
+ if ident.is_path_segment_keyword() || ident.span.is_rust_2015() {
return;
}
"!",
Applicability::MaybeIncorrect,
);
- if path_str == "try" && span.rust_2015() {
+ if path_str == "try" && span.is_rust_2015() {
err.note("if you want the `try` keyword, you need Rust 2018 or later");
}
}
// Sort variants so the largest ones are shown first. A stable sort is
// used here so that source code order is preserved for all variants
// that have the same size.
- variants.sort_by(|info1, info2| info2.size.cmp(&info1.size));
+ // Except for Generators, whose variants are already sorted according to
+ // their yield points in `variant_info_for_generator`.
+ if kind != DataTypeKind::Generator {
+ variants.sort_by(|info1, info2| info2.size.cmp(&info1.size));
+ }
let info = TypeSizeInfo {
kind,
type_description: type_desc.to_string(),
pub use crate::code_stats::{DataTypeKind, FieldInfo, FieldKind, SizeKind, VariantInfo};
use crate::config::Input;
use crate::config::{self, CrateType, InstrumentCoverage, OptLevel, OutputType, SwitchWithOptPath};
-use crate::errors::{
- BranchProtectionRequiresAArch64, CannotEnableCrtStaticLinux, CannotMixAndMatchSanitizers,
- LinkerPluginToWindowsNotSupported, NotCircumventFeature, OptimisationFuelExhausted,
- ProfileSampleUseFileDoesNotExist, ProfileUseFileDoesNotExist, SanitizerCfiEnabled,
- SanitizerNotSupported, SanitizersNotSupported, SkippingConstChecks,
- SplitDebugInfoUnstablePlatform, StackProtectorNotSupportedForTarget,
- TargetRequiresUnwindTables, UnleashedFeatureHelp, UnstableVirtualFunctionElimination,
- UnsupportedDwarfVersion,
-};
+use crate::errors;
use crate::parse::{add_feature_diagnostics, ParseSess};
use crate::search_paths::{PathKind, SearchPath};
use crate::{filesearch, lint};
if !unleashed_features.is_empty() {
let mut must_err = false;
// Create a diagnostic pointing at where things got unleashed.
- self.emit_warning(SkippingConstChecks {
+ self.emit_warning(errors::SkippingConstChecks {
unleashed_features: unleashed_features
.iter()
.map(|(span, gate)| {
gate.map(|gate| {
must_err = true;
- UnleashedFeatureHelp::Named { span: *span, gate }
+ errors::UnleashedFeatureHelp::Named { span: *span, gate }
})
- .unwrap_or(UnleashedFeatureHelp::Unnamed { span: *span })
+ .unwrap_or(errors::UnleashedFeatureHelp::Unnamed { span: *span })
})
.collect(),
});
// If we should err, make sure we did.
if must_err && self.has_errors().is_none() {
// We have skipped a feature gate, and not run into other errors... reject.
- self.emit_err(NotCircumventFeature);
+ self.emit_err(errors::NotCircumventFeature);
}
}
}
// We only call `msg` in case we can actually emit warnings.
// Otherwise, this could cause a `delay_good_path_bug` to
// trigger (issue #79546).
- self.emit_warning(OptimisationFuelExhausted { msg: msg() });
+ self.emit_warning(errors::OptimisationFuelExhausted { msg: msg() });
}
fuel.out_of_fuel = true;
} else if fuel.remaining > 0 {
}
/// Is this edition 2015?
- pub fn rust_2015(&self) -> bool {
- self.edition().rust_2015()
+ pub fn is_rust_2015(&self) -> bool {
+ self.edition().is_rust_2015()
}
/// Are we allowed to use features from the Rust 2018 edition?
&& sess.opts.cg.prefer_dynamic
&& sess.target.is_like_windows
{
- sess.emit_err(LinkerPluginToWindowsNotSupported);
+ sess.emit_err(errors::LinkerPluginToWindowsNotSupported);
}
// Make sure that any given profiling data actually exists so LLVM can't
// decide to silently skip PGO.
if let Some(ref path) = sess.opts.cg.profile_use {
if !path.exists() {
- sess.emit_err(ProfileUseFileDoesNotExist { path });
+ sess.emit_err(errors::ProfileUseFileDoesNotExist { path });
}
}
// Do the same for sample profile data.
if let Some(ref path) = sess.opts.unstable_opts.profile_sample_use {
if !path.exists() {
- sess.emit_err(ProfileSampleUseFileDoesNotExist { path });
+ sess.emit_err(errors::ProfileSampleUseFileDoesNotExist { path });
}
}
// Unwind tables cannot be disabled if the target requires them.
if let Some(include_uwtables) = sess.opts.cg.force_unwind_tables {
if sess.target.requires_uwtable && !include_uwtables {
- sess.emit_err(TargetRequiresUnwindTables);
+ sess.emit_err(errors::TargetRequiresUnwindTables);
}
}
match unsupported_sanitizers.into_iter().count() {
0 => {}
1 => {
- sess.emit_err(SanitizerNotSupported { us: unsupported_sanitizers.to_string() });
+ sess.emit_err(errors::SanitizerNotSupported { us: unsupported_sanitizers.to_string() });
}
_ => {
- sess.emit_err(SanitizersNotSupported { us: unsupported_sanitizers.to_string() });
+ sess.emit_err(errors::SanitizersNotSupported {
+ us: unsupported_sanitizers.to_string(),
+ });
}
}
// Cannot mix and match sanitizers.
let mut sanitizer_iter = sess.opts.unstable_opts.sanitizer.into_iter();
if let (Some(first), Some(second)) = (sanitizer_iter.next(), sanitizer_iter.next()) {
- sess.emit_err(CannotMixAndMatchSanitizers {
+ sess.emit_err(errors::CannotMixAndMatchSanitizers {
first: first.to_string(),
second: second.to_string(),
});
// Cannot enable crt-static with sanitizers on Linux
if sess.crt_static(None) && !sess.opts.unstable_opts.sanitizer.is_empty() {
- sess.emit_err(CannotEnableCrtStaticLinux);
+ sess.emit_err(errors::CannotEnableCrtStaticLinux);
}
// LLVM CFI and VFE both require LTO.
if sess.lto() != config::Lto::Fat {
if sess.is_sanitizer_cfi_enabled() {
- sess.emit_err(SanitizerCfiEnabled);
+ sess.emit_err(errors::SanitizerCfiEnabled);
}
if sess.opts.unstable_opts.virtual_function_elimination {
- sess.emit_err(UnstableVirtualFunctionElimination);
+ sess.emit_err(errors::UnstableVirtualFunctionElimination);
}
}
// LLVM CFI and KCFI are mutually exclusive
if sess.is_sanitizer_cfi_enabled() && sess.is_sanitizer_kcfi_enabled() {
- sess.emit_err(CannotMixAndMatchSanitizers {
+ sess.emit_err(errors::CannotMixAndMatchSanitizers {
first: "cfi".to_string(),
second: "kcfi".to_string(),
});
if sess.opts.unstable_opts.stack_protector != StackProtector::None {
if !sess.target.options.supports_stack_protector {
- sess.emit_warning(StackProtectorNotSupportedForTarget {
+ sess.emit_warning(errors::StackProtectorNotSupportedForTarget {
stack_protector: sess.opts.unstable_opts.stack_protector,
target_triple: &sess.opts.target_triple,
});
}
if sess.opts.unstable_opts.branch_protection.is_some() && sess.target.arch != "aarch64" {
- sess.emit_err(BranchProtectionRequiresAArch64);
+ sess.emit_err(errors::BranchProtectionRequiresAArch64);
}
if let Some(dwarf_version) = sess.opts.unstable_opts.dwarf_version {
if dwarf_version > 5 {
- sess.emit_err(UnsupportedDwarfVersion { dwarf_version });
+ sess.emit_err(errors::UnsupportedDwarfVersion { dwarf_version });
}
}
if !sess.target.options.supported_split_debuginfo.contains(&sess.split_debuginfo())
&& !sess.opts.unstable_opts.unstable_options
{
- sess.emit_err(SplitDebugInfoUnstablePlatform { debuginfo: sess.split_debuginfo() });
+ sess.emit_err(errors::SplitDebugInfoUnstablePlatform { debuginfo: sess.split_debuginfo() });
}
}
}
}
+impl Default for DefPathHash {
+ fn default() -> Self {
+ DefPathHash(Fingerprint::ZERO)
+ }
+}
+
impl Borrow<Fingerprint> for DefPathHash {
#[inline]
fn borrow(&self) -> &Fingerprint {
}
/// Is this edition 2015?
- pub fn rust_2015(self) -> bool {
+ pub fn is_rust_2015(self) -> bool {
self == Edition::Edition2015
}
}
#[inline]
- pub fn rust_2015(self) -> bool {
- self.edition().rust_2015()
+ pub fn is_rust_2015(self) -> bool {
+ self.edition().is_rust_2015()
}
#[inline]
use crate::spec::{Cc, FramePointer, LinkerFlavor, Lld, Target, TargetOptions};
pub fn target() -> Target {
- let llvm_target = "arm64-apple-ios14.0-macabi";
+ let llvm_target = "arm64-apple-ios-macabi";
let arch = Arch::Arm64_macabi;
let mut base = opts("ios", arch);
// These arguments are not actually invoked - they just have
// to look right to pass App Store validation.
bitcode_llvm_cmdline: "-triple\0\
- arm64-apple-ios14.0-macabi\0\
+ arm64-apple-ios-macabi\0\
-emit-obj\0\
-disable-llvm-passes\0\
-Os\0"
use crate::spec::{Cc, LinkerFlavor, Lld, StackProbeType, Target, TargetOptions};
pub fn target() -> Target {
- let llvm_target = "x86_64-apple-ios13.0-macabi";
+ let llvm_target = "x86_64-apple-ios-macabi";
let arch = Arch::X86_64_macabi;
let mut base = opts("ios", arch);
rustc_hir = { path = "../rustc_hir" }
rustc_index = { path = "../rustc_index" }
rustc_infer = { path = "../rustc_infer" }
-rustc_lint_defs = { path = "../rustc_lint_defs" }
rustc_macros = { path = "../rustc_macros" }
rustc_query_system = { path = "../rustc_query_system" }
rustc_serialize = { path = "../rustc_serialize" }
AliasBound,
}
+/// Methods used to assemble candidates for either trait or projection goals.
pub(super) trait GoalKind<'tcx>: TypeFoldable<'tcx> + Copy + Eq {
fn self_ty(self) -> Ty<'tcx>;
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
) -> Vec<CanonicalResponse<'tcx>>;
+
+ fn consider_builtin_discriminant_kind_candidate(
+ ecx: &mut EvalCtxt<'_, 'tcx>,
+ goal: Goal<'tcx, Self>,
+ ) -> QueryResult<'tcx>;
}
impl<'tcx> EvalCtxt<'_, 'tcx> {
G::consider_builtin_generator_candidate(self, goal)
} else if lang_items.unsize_trait() == Some(trait_def_id) {
G::consider_builtin_unsize_candidate(self, goal)
+ } else if lang_items.discriminant_kind_trait() == Some(trait_def_id) {
+ G::consider_builtin_discriminant_kind_candidate(self, goal)
} else {
Err(NoSolution)
};
use rustc_infer::traits::query::NoSolution;
use rustc_infer::traits::Obligation;
use rustc_middle::infer::canonical::Certainty as OldCertainty;
-use rustc_middle::ty::{self, Ty, TyCtxt};
+use rustc_middle::traits::solve::{ExternalConstraints, ExternalConstraintsData};
+use rustc_middle::ty::{self, TyCtxt};
use rustc_middle::ty::{
CoercePredicate, RegionOutlivesPredicate, SubtypePredicate, ToPredicate, TypeOutlivesPredicate,
};
Goal { param_env: obligation.param_env, predicate: obligation.predicate }
}
}
-
-#[derive(Debug, PartialEq, Eq, Clone, Hash, TypeFoldable, TypeVisitable)]
+#[derive(Debug, PartialEq, Eq, Clone, Copy, Hash, TypeFoldable, TypeVisitable)]
pub struct Response<'tcx> {
pub var_values: CanonicalVarValues<'tcx>,
/// Additional constraints returned by this query.
Overflow,
}
-/// Additional constraints returned on success.
-#[derive(Debug, PartialEq, Eq, Clone, Hash, TypeFoldable, TypeVisitable, Default)]
-pub struct ExternalConstraints<'tcx> {
- // FIXME: implement this.
- regions: (),
- opaque_types: Vec<(Ty<'tcx>, Ty<'tcx>)>,
-}
-
type CanonicalGoal<'tcx, T = ty::Predicate<'tcx>> = Canonical<'tcx, Goal<'tcx, T>>;
type CanonicalResponse<'tcx> = Canonical<'tcx, Response<'tcx>>;
/// The result of evaluating a canonical query.
EvalCtxt { infcx, var_values, search_graph, in_projection_eq_hack: false };
let result = ecx.compute_goal(goal);
- // FIXME: `Response` should be `Copy`
- if search_graph.try_finalize_goal(tcx, canonical_goal, result.clone()) {
+ if search_graph.try_finalize_goal(tcx, canonical_goal, result) {
return result;
}
}
}
fn make_canonical_response(&self, certainty: Certainty) -> QueryResult<'tcx> {
- let external_constraints = take_external_constraints(self.infcx)?;
+ let external_constraints = compute_external_query_constraints(self.infcx)?;
Ok(self.infcx.canonicalize_response(Response {
var_values: self.var_values,
}
#[instrument(level = "debug", skip(infcx), ret)]
-fn take_external_constraints<'tcx>(
+fn compute_external_query_constraints<'tcx>(
infcx: &InferCtxt<'tcx>,
) -> Result<ExternalConstraints<'tcx>, NoSolution> {
let region_obligations = infcx.take_registered_region_obligations();
let opaque_types = infcx.take_opaque_types_for_query_response();
- Ok(ExternalConstraints {
+ Ok(infcx.tcx.intern_external_constraints(ExternalConstraintsData {
// FIXME: Now that's definitely wrong :)
//
// Should also do the leak check here I think
regions: drop(region_obligations),
opaque_types,
- })
+ }))
}
fn instantiate_canonical_query_response<'tcx>(
Certainty::Yes => OldCertainty::Proven,
Certainty::Maybe(_) => OldCertainty::Ambiguous,
},
- opaque_types: resp.external_constraints.opaque_types,
+ // FIXME: This to_owned makes me sad, but we should eventually impl
+ // `instantiate_query_response_and_region_obligations` separately
+ // instead of piggybacking off of the old implementation.
+ opaque_types: resp.external_constraints.opaque_types.to_owned(),
value: resp.certainty,
}),
) else { bug!(); };
variables: goal.variables,
value: Response {
var_values: CanonicalVarValues::make_identity(tcx, goal.variables),
- external_constraints: Default::default(),
+ // FIXME: maybe we should store the "no response" version in tcx, like
+ // we do for tcx.types and stuff.
+ external_constraints: tcx
+ .intern_external_constraints(ExternalConstraintsData::default()),
certainty,
},
})
) -> Vec<super::CanonicalResponse<'tcx>> {
bug!("`Unsize` does not have an associated type: {:?}", goal);
}
+
+ fn consider_builtin_discriminant_kind_candidate(
+ ecx: &mut EvalCtxt<'_, 'tcx>,
+ goal: Goal<'tcx, Self>,
+ ) -> QueryResult<'tcx> {
+ let discriminant = goal.predicate.self_ty().discriminant_ty(ecx.tcx());
+ ecx.infcx
+ .probe(|_| ecx.eq_term_and_make_canonical_response(goal, Certainty::Yes, discriminant))
+ }
}
/// This behavior is also implemented in `rustc_ty_utils` and in the old `project` code.
}
pub(super) fn provisional_result(&self, entry_index: EntryIndex) -> QueryResult<'tcx> {
- // FIXME: Responses should probably be `Copy` as well
- self.entries[entry_index].response.clone()
+ self.entries[entry_index].response
}
}
responses
}
+
+ fn consider_builtin_discriminant_kind_candidate(
+ ecx: &mut EvalCtxt<'_, 'tcx>,
+ _goal: Goal<'tcx, Self>,
+ ) -> QueryResult<'tcx> {
+ // `DiscriminantKind` is automatically implemented for every type.
+ ecx.make_canonical_response(Certainty::Yes)
+ }
}
impl<'tcx> EvalCtxt<'_, 'tcx> {
.cloned()
.unwrap_or_else(|| {
bug!(
- "node_type: no type for node `{}`",
+ "node_type: no type for node {}",
ty::tls::with(|tcx| tcx
.hir()
.node_to_string(await_expr.hir_id))
// NOTE: This check happens last, because it results in a lint, and not a
// hard error.
- if tcx
- .predicates_of(method.def_id)
- .predicates
- .iter()
- // A trait object can't claim to live more than the concrete type,
- // so outlives predicates will always hold.
- .cloned()
- .filter(|(p, _)| p.to_opt_type_outlives().is_none())
- .any(|pred| contains_illegal_self_type_reference(tcx, trait_def_id, pred))
- {
+ if tcx.predicates_of(method.def_id).predicates.iter().any(|&(pred, span)| {
+ // dyn Trait is okay:
+ //
+ // trait Trait {
+ // fn f(&self) where Self: 'static;
+ // }
+ //
+ // because a trait object can't claim to live longer than the concrete
+ // type. If the lifetime bound holds on dyn Trait then it's guaranteed
+ // to hold as well on the concrete type.
+ if pred.to_opt_type_outlives().is_some() {
+ return false;
+ }
+
+ // dyn Trait is okay:
+ //
+ // auto trait AutoTrait {}
+ //
+ // trait Trait {
+ // fn f(&self) where Self: AutoTrait;
+ // }
+ //
+ // because `impl AutoTrait for dyn Trait` is disallowed by coherence.
+ // Traits with a default impl are implemented for a trait object if and
+ // only if the autotrait is one of the trait object's trait bounds, like
+ // in `dyn Trait + AutoTrait`. This guarantees that trait objects only
+ // implement auto traits if the underlying type does as well.
+ if let ty::PredicateKind::Clause(ty::Clause::Trait(ty::TraitPredicate {
+ trait_ref: pred_trait_ref,
+ constness: ty::BoundConstness::NotConst,
+ polarity: ty::ImplPolarity::Positive,
+ })) = pred.kind().skip_binder()
+ && pred_trait_ref.self_ty() == tcx.types.self_param
+ && tcx.trait_is_auto(pred_trait_ref.def_id)
+ {
+ // Consider bounds like `Self: Bound<Self>`. Auto traits are not
+ // allowed to have generic parameters so `auto trait Bound<T> {}`
+ // would already have reported an error at the definition of the
+ // auto trait.
+ if pred_trait_ref.substs.len() != 1 {
+ tcx.sess.diagnostic().delay_span_bug(
+ span,
+ "auto traits cannot have generic parameters",
+ );
+ }
+ return false;
+ }
+
+ contains_illegal_self_type_reference(tcx, trait_def_id, pred.clone())
+ }) {
return Some(MethodViolationCode::WhereClauseReferencesSelf);
}
ImplDerivedObligation(Box::new(ImplDerivedObligationCause {
derived,
impl_def_id,
+ impl_def_predicate_index: None,
span: obligation.cause.span,
}))
});
assert_eq!(predicates.parent, None);
let predicates = predicates.instantiate_own(tcx, substs);
let mut obligations = Vec::with_capacity(predicates.len());
- for (predicate, span) in predicates {
+ for (index, (predicate, span)) in predicates.into_iter().enumerate() {
let cause = cause.clone().derived_cause(parent_trait_pred, |derived| {
ImplDerivedObligation(Box::new(ImplDerivedObligationCause {
derived,
impl_def_id: def_id,
+ impl_def_predicate_index: Some(index),
span,
}))
});
[dependencies]
tracing = "0.1"
-rustc_attr = { path = "../rustc_attr" }
rustc_middle = { path = "../rustc_middle" }
rustc_data_structures = { path = "../rustc_data_structures" }
rustc_hir = { path = "../rustc_hir" }
if let Some(kind) = pointee.safe {
attrs.pointee_align = Some(pointee.align);
- // `Box` (`UniqueBorrowed`) are not necessarily dereferenceable
- // for the entire duration of the function as they can be deallocated
- // at any time. Same for shared mutable references. If LLVM had a
- // way to say "dereferenceable on entry" we could use it here.
+ // `Box` are not necessarily dereferenceable for the entire duration of the function as
+ // they can be deallocated at any time. Same for non-frozen shared references (see
+ // <https://github.com/rust-lang/rust/pull/98017>), and for mutable references to
+ // potentially self-referential types (see
+ // <https://github.com/rust-lang/unsafe-code-guidelines/issues/381>). If LLVM had a way
+ // to say "dereferenceable on entry" we could use it here.
attrs.pointee_size = match kind {
- PointerKind::UniqueBorrowed
- | PointerKind::UniqueBorrowedPinned
- | PointerKind::Frozen => pointee.size,
- PointerKind::SharedMutable | PointerKind::UniqueOwned => Size::ZERO,
+ PointerKind::Box { .. }
+ | PointerKind::SharedRef { frozen: false }
+ | PointerKind::MutableRef { unpin: false } => Size::ZERO,
+ PointerKind::SharedRef { frozen: true }
+ | PointerKind::MutableRef { unpin: true } => pointee.size,
};
// The aliasing rules for `Box<T>` are still not decided, but currently we emit
// versions at all anymore. We still support turning it off using -Zmutable-noalias.
let noalias_mut_ref = cx.tcx.sess.opts.unstable_opts.mutable_noalias;
- // `&mut` pointer parameters never alias other parameters,
- // or mutable global data
+ // `&T` where `T` contains no `UnsafeCell<U>` is immutable, and can be marked as both
+ // `readonly` and `noalias`, as LLVM's definition of `noalias` is based solely on memory
+ // dependencies rather than pointer equality. However this only applies to arguments,
+ // not return values.
//
- // `&T` where `T` contains no `UnsafeCell<U>` is immutable,
- // and can be marked as both `readonly` and `noalias`, as
- // LLVM's definition of `noalias` is based solely on memory
- // dependencies rather than pointer equality
+ // `&mut T` and `Box<T>` where `T: Unpin` are unique and hence `noalias`.
let no_alias = match kind {
- PointerKind::SharedMutable | PointerKind::UniqueBorrowedPinned => false,
- PointerKind::UniqueBorrowed => noalias_mut_ref,
- PointerKind::UniqueOwned => noalias_for_box,
- PointerKind::Frozen => true,
+ PointerKind::SharedRef { frozen } => frozen,
+ PointerKind::MutableRef { unpin } => unpin && noalias_mut_ref,
+ PointerKind::Box { unpin } => unpin && noalias_for_box,
};
// We can never add `noalias` in return position; that LLVM attribute has some very surprising semantics
// (see <https://github.com/rust-lang/unsafe-code-guidelines/issues/385#issuecomment-1368055745>).
attrs.set(ArgAttribute::NoAlias);
}
- if kind == PointerKind::Frozen && !is_return {
+ if matches!(kind, PointerKind::SharedRef { frozen: true }) && !is_return {
attrs.set(ArgAttribute::ReadOnly);
}
}
hir::ItemKind::Impl(ref impl_) => tcx.arena.alloc_from_iter(
impl_.items.iter().map(|impl_item_ref| impl_item_ref.id.owner_id.to_def_id()),
),
- hir::ItemKind::TraitAlias(..) => &[],
_ => span_bug!(item.span, "associated_item_def_ids: not impl or trait"),
}
}
fn associated_items(tcx: TyCtxt<'_>, def_id: DefId) -> ty::AssocItems<'_> {
- let items = tcx.associated_item_def_ids(def_id).iter().map(|did| tcx.associated_item(*did));
- ty::AssocItems::new(items)
+ if tcx.is_trait_alias(def_id) {
+ ty::AssocItems::new(Vec::new())
+ } else {
+ let items = tcx.associated_item_def_ids(def_id).iter().map(|did| tcx.associated_item(*did));
+ ty::AssocItems::new(items)
+ }
}
fn impl_item_implementor_ids(tcx: TyCtxt<'_>, impl_id: DefId) -> FxHashMap<DefId, DefId> {
})
.collect();
- let variant_infos: Vec<_> = generator
+ let mut variant_infos: Vec<_> = generator
.variant_fields
.iter_enumerated()
.map(|(variant_idx, variant_def)| {
}
})
.collect();
+
+ // The first three variants are hardcoded to be `UNRESUMED`, `RETURNED` and `POISONED`.
+ // We will move the `RETURNED` and `POISONED` elements to the end so we
+ // are left with a sorting order according to the generators yield points:
+ // First `Unresumed`, then the `SuspendN` followed by `Returned` and `Panicked` (POISONED).
+ let end_states = variant_infos.drain(1..=2);
+ let end_states: Vec<_> = end_states.collect();
+ variant_infos.extend(end_states);
+
(
variant_infos,
match tag_encoding {
# Select LTO mode that will be used for compiling rustc. By default, thin local LTO
# (LTO within a single crate) is used (like for any Rust crate). You can also select
-# "thin" or "fat" to apply Thin/Fat LTO to the `rustc_driver` dylib.
+# "thin" or "fat" to apply Thin/Fat LTO to the `rustc_driver` dylib, or "off" to disable
+# LTO entirely.
#lto = "thin-local"
# =============================================================================
-use test::Bencher;
+use test::{black_box, Bencher};
const CHARS: [char; 9] = ['0', 'x', '2', '5', 'A', 'f', '7', '8', '9'];
const RADIX: [u32; 5] = [2, 8, 10, 16, 32];
#[bench]
fn bench_to_digit_radix_2(b: &mut Bencher) {
- b.iter(|| CHARS.iter().cycle().take(10_000).map(|c| c.to_digit(2)).min())
+ b.iter(|| CHARS.iter().cycle().take(10_000).map(|c| black_box(c).to_digit(2)).min())
}
#[bench]
fn bench_to_digit_radix_10(b: &mut Bencher) {
- b.iter(|| CHARS.iter().cycle().take(10_000).map(|c| c.to_digit(10)).min())
+ b.iter(|| CHARS.iter().cycle().take(10_000).map(|c| black_box(c).to_digit(10)).min())
}
#[bench]
fn bench_to_digit_radix_16(b: &mut Bencher) {
- b.iter(|| CHARS.iter().cycle().take(10_000).map(|c| c.to_digit(16)).min())
+ b.iter(|| CHARS.iter().cycle().take(10_000).map(|c| black_box(c).to_digit(16)).min())
}
#[bench]
fn bench_to_digit_radix_36(b: &mut Bencher) {
- b.iter(|| CHARS.iter().cycle().take(10_000).map(|c| c.to_digit(36)).min())
+ b.iter(|| CHARS.iter().cycle().take(10_000).map(|c| black_box(c).to_digit(36)).min())
}
#[bench]
.cycle()
.zip(RADIX.iter().cycle())
.take(10_000)
- .map(|(c, radix)| c.to_digit(*radix))
+ .map(|(c, radix)| black_box(c).to_digit(*radix))
.min()
})
}
#[bench]
fn bench_to_ascii_uppercase(b: &mut Bencher) {
- b.iter(|| CHARS.iter().cycle().take(10_000).map(|c| c.to_ascii_uppercase()).min())
+ b.iter(|| CHARS.iter().cycle().take(10_000).map(|c| black_box(c).to_ascii_uppercase()).min())
}
#[bench]
fn bench_to_ascii_lowercase(b: &mut Bencher) {
- b.iter(|| CHARS.iter().cycle().take(10_000).map(|c| c.to_ascii_lowercase()).min())
+ b.iter(|| CHARS.iter().cycle().take(10_000).map(|c| black_box(c).to_ascii_lowercase()).min())
}
#[bench]
fn bench_ascii_mix_to_uppercase(b: &mut Bencher) {
- b.iter(|| (0..=255).cycle().take(10_000).map(|b| char::from(b).to_uppercase()).count())
+ b.iter(|| {
+ (0..=255).cycle().take(10_000).map(|b| black_box(char::from(b)).to_uppercase()).count()
+ })
}
#[bench]
fn bench_ascii_mix_to_lowercase(b: &mut Bencher) {
- b.iter(|| (0..=255).cycle().take(10_000).map(|b| char::from(b).to_lowercase()).count())
+ b.iter(|| {
+ (0..=255).cycle().take(10_000).map(|b| black_box(char::from(b)).to_lowercase()).count()
+ })
}
#[bench]
fn bench_ascii_char_to_uppercase(b: &mut Bencher) {
- b.iter(|| (0..=127).cycle().take(10_000).map(|b| char::from(b).to_uppercase()).count())
+ b.iter(|| {
+ (0..=127).cycle().take(10_000).map(|b| black_box(char::from(b)).to_uppercase()).count()
+ })
}
#[bench]
fn bench_ascii_char_to_lowercase(b: &mut Bencher) {
- b.iter(|| (0..=127).cycle().take(10_000).map(|b| char::from(b).to_lowercase()).count())
+ b.iter(|| {
+ (0..=127).cycle().take(10_000).map(|b| black_box(char::from(b)).to_lowercase()).count()
+ })
}
#[bench]
fn bench_non_ascii_char_to_uppercase(b: &mut Bencher) {
- b.iter(|| (128..=255).cycle().take(10_000).map(|b| char::from(b).to_uppercase()).count())
+ b.iter(|| {
+ (128..=255).cycle().take(10_000).map(|b| black_box(char::from(b)).to_uppercase()).count()
+ })
}
#[bench]
fn bench_non_ascii_char_to_lowercase(b: &mut Bencher) {
- b.iter(|| (128..=255).cycle().take(10_000).map(|b| char::from(b).to_lowercase()).count())
+ b.iter(|| {
+ (128..=255).cycle().take(10_000).map(|b| black_box(char::from(b)).to_lowercase()).count()
+ })
}
use super::super::*;
use core::num::flt2dec::strategy::dragon::*;
use std::mem::MaybeUninit;
-use test::Bencher;
+use test::{black_box, Bencher};
#[bench]
fn bench_small_shortest(b: &mut Bencher) {
let decoded = decode_finite(3.141592f64);
let mut buf = [MaybeUninit::new(0); MAX_SIG_DIGITS];
b.iter(|| {
- format_shortest(&decoded, &mut buf);
+ format_shortest(black_box(&decoded), &mut buf);
});
}
let decoded = decode_finite(f64::MAX);
let mut buf = [MaybeUninit::new(0); MAX_SIG_DIGITS];
b.iter(|| {
- format_shortest(&decoded, &mut buf);
+ format_shortest(black_box(&decoded), &mut buf);
});
}
let decoded = decode_finite(3.141592f64);
let mut buf = [MaybeUninit::new(0); 3];
b.iter(|| {
- format_exact(&decoded, &mut buf, i16::MIN);
+ format_exact(black_box(&decoded), &mut buf, i16::MIN);
});
}
let decoded = decode_finite(f64::MAX);
let mut buf = [MaybeUninit::new(0); 3];
b.iter(|| {
- format_exact(&decoded, &mut buf, i16::MIN);
+ format_exact(black_box(&decoded), &mut buf, i16::MIN);
});
}
let decoded = decode_finite(3.141592f64);
let mut buf = [MaybeUninit::new(0); 12];
b.iter(|| {
- format_exact(&decoded, &mut buf, i16::MIN);
+ format_exact(black_box(&decoded), &mut buf, i16::MIN);
});
}
let decoded = decode_finite(f64::MAX);
let mut buf = [MaybeUninit::new(0); 12];
b.iter(|| {
- format_exact(&decoded, &mut buf, i16::MIN);
+ format_exact(black_box(&decoded), &mut buf, i16::MIN);
});
}
let decoded = decode_finite(3.141592f64);
let mut buf = [MaybeUninit::new(0); 1024];
b.iter(|| {
- format_exact(&decoded, &mut buf, i16::MIN);
+ format_exact(black_box(&decoded), &mut buf, i16::MIN);
});
}
let decoded = decode_finite(f64::MAX);
let mut buf = [MaybeUninit::new(0); 1024];
b.iter(|| {
- format_exact(&decoded, &mut buf, i16::MIN);
+ format_exact(black_box(&decoded), &mut buf, i16::MIN);
});
}
use super::super::*;
use core::num::flt2dec::strategy::grisu::*;
use std::mem::MaybeUninit;
-use test::Bencher;
+use test::{black_box, Bencher};
pub fn decode_finite<T: DecodableFloat>(v: T) -> Decoded {
match decode(v).1 {
let decoded = decode_finite(3.141592f64);
let mut buf = [MaybeUninit::new(0); MAX_SIG_DIGITS];
b.iter(|| {
- format_shortest(&decoded, &mut buf);
+ format_shortest(black_box(&decoded), &mut buf);
});
}
let decoded = decode_finite(f64::MAX);
let mut buf = [MaybeUninit::new(0); MAX_SIG_DIGITS];
b.iter(|| {
- format_shortest(&decoded, &mut buf);
+ format_shortest(black_box(&decoded), &mut buf);
});
}
let decoded = decode_finite(3.141592f64);
let mut buf = [MaybeUninit::new(0); 3];
b.iter(|| {
- format_exact(&decoded, &mut buf, i16::MIN);
+ format_exact(black_box(&decoded), &mut buf, i16::MIN);
});
}
let decoded = decode_finite(f64::MAX);
let mut buf = [MaybeUninit::new(0); 3];
b.iter(|| {
- format_exact(&decoded, &mut buf, i16::MIN);
+ format_exact(black_box(&decoded), &mut buf, i16::MIN);
});
}
let decoded = decode_finite(3.141592f64);
let mut buf = [MaybeUninit::new(0); 12];
b.iter(|| {
- format_exact(&decoded, &mut buf, i16::MIN);
+ format_exact(black_box(&decoded), &mut buf, i16::MIN);
});
}
let decoded = decode_finite(f64::MAX);
let mut buf = [MaybeUninit::new(0); 12];
b.iter(|| {
- format_exact(&decoded, &mut buf, i16::MIN);
+ format_exact(black_box(&decoded), &mut buf, i16::MIN);
});
}
let decoded = decode_finite(3.141592f64);
let mut buf = [MaybeUninit::new(0); 1024];
b.iter(|| {
- format_exact(&decoded, &mut buf, i16::MIN);
+ format_exact(black_box(&decoded), &mut buf, i16::MIN);
});
}
let decoded = decode_finite(f64::MAX);
let mut buf = [MaybeUninit::new(0); 1024];
b.iter(|| {
- format_exact(&decoded, &mut buf, i16::MIN);
+ format_exact(black_box(&decoded), &mut buf, i16::MIN);
});
}
#![stable(feature = "rust1", since = "1.0.0")]
-use crate::const_closure::ConstFnMutClosure;
use crate::marker::Destruct;
use self::Ordering::*;
F: ~const Destruct,
K: ~const Destruct,
{
- const fn imp<T, F: ~const FnMut(&T) -> K, K: ~const Ord>(
- f: &mut F,
- (v1, v2): (&T, &T),
- ) -> Ordering
- where
- T: ~const Destruct,
- K: ~const Destruct,
- {
- f(v1).cmp(&f(v2))
- }
- max_by(v1, v2, ConstFnMutClosure::new(&mut f, imp))
+ max_by(v1, v2, const |v1, v2| f(v1).cmp(&f(v2)))
}
// Implementation of PartialEq, Eq, PartialOrd and Ord for primitive types
+++ /dev/null
-use crate::marker::Destruct;
-use crate::marker::Tuple;
-
-/// Struct representing a closure with mutably borrowed data.
-///
-/// Example:
-/// ```no_build
-/// #![feature(const_mut_refs)]
-/// use crate::const_closure::ConstFnMutClosure;
-/// const fn imp(state: &mut i32, (arg,): (i32,)) -> i32 {
-/// *state += arg;
-/// *state
-/// }
-/// let mut i = 5;
-/// let mut cl = ConstFnMutClosure::new(&mut i, imp);
-///
-/// assert!(7 == cl(2));
-/// assert!(8 == cl(1));
-/// ```
-pub(crate) struct ConstFnMutClosure<CapturedData, Function> {
- /// The Data captured by the Closure.
- /// Must be either a (mutable) reference or a tuple of (mutable) references.
- pub data: CapturedData,
- /// The Function of the Closure, must be: Fn(CapturedData, ClosureArgs) -> ClosureReturn
- pub func: Function,
-}
-impl<'a, CapturedData: ?Sized, Function> ConstFnMutClosure<&'a mut CapturedData, Function> {
- /// Function for creating a new closure.
- ///
- /// `data` is the a mutable borrow of data that is captured from the environment.
- /// If you want Data to be a tuple of mutable Borrows, the struct must be constructed manually.
- ///
- /// `func` is the function of the closure, it gets the data and a tuple of the arguments closure
- /// and return the return value of the closure.
- pub(crate) const fn new<ClosureArguments, ClosureReturnValue>(
- data: &'a mut CapturedData,
- func: Function,
- ) -> Self
- where
- Function: ~const Fn(&mut CapturedData, ClosureArguments) -> ClosureReturnValue,
- {
- Self { data, func }
- }
-}
-
-macro_rules! impl_fn_mut_tuple {
- ($($var:ident)*) => {
- #[allow(unused_parens)]
- impl<'a, $($var,)* ClosureArguments: Tuple, Function, ClosureReturnValue> const
- FnOnce<ClosureArguments> for ConstFnMutClosure<($(&'a mut $var),*), Function>
- where
- Function: ~const Fn(($(&mut $var),*), ClosureArguments) -> ClosureReturnValue+ ~const Destruct,
- {
- type Output = ClosureReturnValue;
-
- extern "rust-call" fn call_once(mut self, args: ClosureArguments) -> Self::Output {
- self.call_mut(args)
- }
- }
- #[allow(unused_parens)]
- impl<'a, $($var,)* ClosureArguments: Tuple, Function, ClosureReturnValue> const
- FnMut<ClosureArguments> for ConstFnMutClosure<($(&'a mut $var),*), Function>
- where
- Function: ~const Fn(($(&mut $var),*), ClosureArguments)-> ClosureReturnValue,
- {
- extern "rust-call" fn call_mut(&mut self, args: ClosureArguments) -> Self::Output {
- #[allow(non_snake_case)]
- let ($($var),*) = &mut self.data;
- (self.func)(($($var),*), args)
- }
- }
- };
-}
-impl_fn_mut_tuple!(A);
-impl_fn_mut_tuple!(A B);
-impl_fn_mut_tuple!(A B C);
-impl_fn_mut_tuple!(A B C D);
-impl_fn_mut_tuple!(A B C D E);
use crate::array;
-use crate::const_closure::ConstFnMutClosure;
use crate::iter::{ByRefSized, FusedIterator, Iterator, TrustedRandomAccessNoCoerce};
use crate::mem::{self, MaybeUninit};
use crate::ops::{ControlFlow, NeverShortCircuit, Try};
I: Iterator,
{
#[inline]
- default fn fold<B, F>(mut self, init: B, mut f: F) -> B
+ default fn fold<B, F>(mut self, init: B, f: F) -> B
where
Self: Sized,
F: FnMut(B, Self::Item) -> B,
{
- let fold = ConstFnMutClosure::new(&mut f, NeverShortCircuit::wrap_mut_2_imp);
- self.try_fold(init, fold).0
+ self.try_fold(init, NeverShortCircuit::wrap_mut_2(f)).0
}
}
-use crate::{
- const_closure::ConstFnMutClosure,
- ops::{NeverShortCircuit, Try},
-};
+use crate::ops::{NeverShortCircuit, Try};
/// Like `Iterator::by_ref`, but requiring `Sized` so it can forward generics.
///
}
#[inline]
- fn fold<B, F>(self, init: B, mut f: F) -> B
+ fn fold<B, F>(self, init: B, f: F) -> B
where
F: FnMut(B, Self::Item) -> B,
{
// `fold` needs ownership, so this can't forward directly.
- I::try_fold(self.0, init, ConstFnMutClosure::new(&mut f, NeverShortCircuit::wrap_mut_2_imp))
- .0
+ I::try_fold(self.0, init, NeverShortCircuit::wrap_mut_2(f)).0
}
#[inline]
}
#[inline]
- fn rfold<B, F>(self, init: B, mut f: F) -> B
+ fn rfold<B, F>(self, init: B, f: F) -> B
where
F: FnMut(B, Self::Item) -> B,
{
// `rfold` needs ownership, so this can't forward directly.
- I::try_rfold(
- self.0,
- init,
- ConstFnMutClosure::new(&mut f, NeverShortCircuit::wrap_mut_2_imp),
- )
- .0
+ I::try_rfold(self.0, init, NeverShortCircuit::wrap_mut_2(f)).0
}
#[inline]
};
(@internal $fold:ident -> $try_fold:ident) => {
#[inline]
- fn $fold<AAA, FFF>(mut self, init: AAA, mut fold: FFF) -> AAA
+ fn $fold<AAA, FFF>(mut self, init: AAA, fold: FFF) -> AAA
where
FFF: FnMut(AAA, Self::Item) -> AAA,
{
- use crate::const_closure::ConstFnMutClosure;
use crate::ops::NeverShortCircuit;
- let fold = ConstFnMutClosure::new(&mut fold, NeverShortCircuit::wrap_mut_2_imp);
- self.$try_fold(init, fold).0
+ self.$try_fold(init, NeverShortCircuit::wrap_mut_2(fold)).0
}
};
}
mod tuple;
mod unit;
-mod const_closure;
-
#[stable(feature = "core_primitive", since = "1.43.0")]
pub mod primitive;
pub const MIN: Self = 0;
/// The largest value that can be represented by this integer type
- #[doc = concat!("(2<sup>", $BITS, "</sup> − 1", $bound_condition, ")")]
+ #[doc = concat!("(2<sup>", $BITS, "</sup> − 1", $bound_condition, ").")]
///
/// # Examples
///
/// ```
/// assert_eq!(12 + 1, 13);
/// ```
- #[must_use]
+ #[must_use = "this returns the result of the operation, without modifying the original"]
+ #[rustc_diagnostic_item = "add"]
#[stable(feature = "rust1", since = "1.0.0")]
fn add(self, rhs: Rhs) -> Self::Output;
}
/// ```
/// assert_eq!(12 - 1, 11);
/// ```
- #[must_use]
+ #[must_use = "this returns the result of the operation, without modifying the original"]
+ #[rustc_diagnostic_item = "sub"]
#[stable(feature = "rust1", since = "1.0.0")]
fn sub(self, rhs: Rhs) -> Self::Output;
}
/// ```
/// assert_eq!(12 * 2, 24);
/// ```
- #[must_use]
+ #[must_use = "this returns the result of the operation, without modifying the original"]
+ #[rustc_diagnostic_item = "mul"]
#[stable(feature = "rust1", since = "1.0.0")]
fn mul(self, rhs: Rhs) -> Self::Output;
}
/// ```
/// assert_eq!(12 / 2, 6);
/// ```
- #[must_use]
+ #[must_use = "this returns the result of the operation, without modifying the original"]
+ #[rustc_diagnostic_item = "div"]
#[stable(feature = "rust1", since = "1.0.0")]
fn div(self, rhs: Rhs) -> Self::Output;
}
/// ```
/// assert_eq!(12 % 10, 2);
/// ```
- #[must_use]
+ #[must_use = "this returns the result of the operation, without modifying the original"]
+ #[rustc_diagnostic_item = "rem"]
#[stable(feature = "rust1", since = "1.0.0")]
fn rem(self, rhs: Rhs) -> Self::Output;
}
/// let x: i32 = 12;
/// assert_eq!(-x, -12);
/// ```
- #[must_use]
+ #[must_use = "this returns the result of the operation, without modifying the original"]
+ #[rustc_diagnostic_item = "neg"]
#[stable(feature = "rust1", since = "1.0.0")]
fn neg(self) -> Self::Output;
}
pub(crate) struct NeverShortCircuit<T>(pub T);
impl<T> NeverShortCircuit<T> {
- /// Implementation for building `ConstFnMutClosure` for wrapping the output of a ~const FnMut in a `NeverShortCircuit`.
#[inline]
- pub const fn wrap_mut_2_imp<A, B, F: ~const FnMut(A, B) -> T>(
- f: &mut F,
- (a, b): (A, B),
- ) -> NeverShortCircuit<T> {
- NeverShortCircuit(f(a, b))
+ pub fn wrap_mut_2<A, B>(
+ mut f: impl ~const FnMut(A, B) -> T,
+ ) -> impl ~const FnMut(A, B) -> Self {
+ cfg_if! {
+ if #[cfg(bootstrap)] {
+ #[allow(unused_parens)]
+ (const move |a, b| NeverShortCircuit(f(a, b)))
+ } else {
+ const move |a, b| NeverShortCircuit(f(a, b))
+ }
+ }
}
}
// Transforming contained values
/////////////////////////////////////////////////////////////////////////
- /// Maps an `Option<T>` to `Option<U>` by applying a function to a contained value.
+ /// Maps an `Option<T>` to `Option<U>` by applying a function to a contained value (if `Some`) or returns `None` (if `None`).
///
/// # Examples
///
/// let maybe_some_string = Some(String::from("Hello, World!"));
/// // `Option::map` takes self *by value*, consuming `maybe_some_string`
/// let maybe_some_len = maybe_some_string.map(|s| s.len());
- ///
/// assert_eq!(maybe_some_len, Some(13));
+ ///
+ /// let x: Option<&str> = None;
+ /// assert_eq!(x.map(|s| s.len()), None);
/// ```
#[inline]
#[stable(feature = "rust1", since = "1.0.0")]
///
/// The hash map will be able to hold at least `capacity` elements without
/// reallocating. This method is allowed to allocate for more elements than
- /// `capacity`. If `capacity` is 0, the hash set will not allocate.
+ /// `capacity`. If `capacity` is 0, the hash map will not allocate.
///
/// # Examples
///
unsafe { intrinsics::ceilf32(self) }
}
- /// Returns the nearest integer to `self`. Round half-way cases away from
- /// `0.0`.
+ /// Returns the nearest integer to `self`. If a value is half-way between two
+ /// integers, round away from `0.0`.
///
/// # Examples
///
unsafe { intrinsics::ceilf64(self) }
}
- /// Returns the nearest integer to `self`. Round half-way cases away from
- /// `0.0`.
+ /// Returns the nearest integer to `self`. If a value is half-way between two
+ /// integers, round away from `0.0`.
///
/// # Examples
///
// doesn't accidentally get printed.
#[cfg_attr(test, derive(Debug))]
enum ErrorData<C> {
- Os(i32),
+ Os(RawOsError),
Simple(ErrorKind),
SimpleMessage(&'static SimpleMessage),
Custom(C),
}
+/// The type of raw OS error codes returned by [`Error::raw_os_error`].
+///
+/// This is an [`i32`] on all currently supported platforms, but platforms
+/// added in the future (such as UEFI) may use a different primitive type like
+/// [`usize`]. Use `as`or [`into`] conversions where applicable to ensure maximum
+/// portability.
+///
+/// [`into`]: Into::into
+#[unstable(feature = "raw_os_error_ty", issue = "none")]
+pub type RawOsError = i32;
+
// `#[repr(align(4))]` is probably redundant, it should have that value or
// higher already. We include it just because repr_bitpacked.rs's encoding
// requires an alignment >= 4 (note that `#[repr(align)]` will not reduce the
#[must_use]
#[inline]
pub fn last_os_error() -> Error {
- Error::from_raw_os_error(sys::os::errno() as i32)
+ Error::from_raw_os_error(sys::os::errno())
}
/// Creates a new instance of an [`Error`] from a particular OS error code.
#[stable(feature = "rust1", since = "1.0.0")]
#[must_use]
#[inline]
- pub fn from_raw_os_error(code: i32) -> Error {
+ pub fn from_raw_os_error(code: RawOsError) -> Error {
Error { repr: Repr::new_os(code) }
}
#[stable(feature = "rust1", since = "1.0.0")]
#[must_use]
#[inline]
- pub fn raw_os_error(&self) -> Option<i32> {
+ pub fn raw_os_error(&self) -> Option<RawOsError> {
match self.repr.data() {
ErrorData::Os(i) => Some(i),
ErrorData::Custom(..) => None,
//! to use a pointer type to store something that may hold an integer, some of
//! the time.
-use super::{Custom, ErrorData, ErrorKind, SimpleMessage};
+use super::{Custom, ErrorData, ErrorKind, RawOsError, SimpleMessage};
use alloc::boxed::Box;
use core::marker::PhantomData;
use core::mem::{align_of, size_of};
}
#[inline]
- pub(super) fn new_os(code: i32) -> Self {
+ pub(super) fn new_os(code: RawOsError) -> Self {
let utagged = ((code as usize) << 32) | TAG_OS;
// Safety: `TAG_OS` is not zero, so the result of the `|` is not 0.
let res = Self(unsafe { NonNull::new_unchecked(ptr::invalid_mut(utagged)) }, PhantomData);
let bits = ptr.as_ptr().addr();
match bits & TAG_MASK {
TAG_OS => {
- let code = ((bits as i64) >> 32) as i32;
+ let code = ((bits as i64) >> 32) as RawOsError;
ErrorData::Os(code)
}
TAG_SIMPLE => {
static_assert!(align_of::<SimpleMessage>() >= TAG_MASK + 1);
static_assert!(align_of::<Custom>() >= TAG_MASK + 1);
+// `RawOsError` must be an alias for `i32`.
+const _: fn(RawOsError) -> i32 = |os| os;
+
static_assert!(@usize_eq: TAG_MASK & TAG_SIMPLE_MESSAGE, TAG_SIMPLE_MESSAGE);
static_assert!(@usize_eq: TAG_MASK & TAG_CUSTOM, TAG_CUSTOM);
static_assert!(@usize_eq: TAG_MASK & TAG_OS, TAG_OS);
//! non-64bit targets, where the packed 64 bit representation wouldn't work, and
//! would have no benefit.
-use super::{Custom, ErrorData, ErrorKind, SimpleMessage};
+use super::{Custom, ErrorData, ErrorKind, RawOsError, SimpleMessage};
use alloc::boxed::Box;
type Inner = ErrorData<Box<Custom>>;
Self(Inner::Custom(b))
}
#[inline]
- pub(super) fn new_os(code: i32) -> Self {
+ pub(super) fn new_os(code: RawOsError) -> Self {
Self(Inner::Os(code))
}
#[inline]
#[test]
fn test_os_packing() {
- for code in -20i32..20i32 {
+ for code in -20..20 {
let e = Error::from_raw_os_error(code);
assert_eq!(e.raw_os_error(), Some(code));
assert_matches!(
#[stable(feature = "bufwriter_into_parts", since = "1.56.0")]
pub use self::buffered::WriterPanicked;
+#[unstable(feature = "raw_os_error_ty", issue = "none")]
+pub use self::error::RawOsError;
pub(crate) use self::stdio::attempt_print_to_stderr;
#[unstable(feature = "internal_output_capture", issue = "none")]
#[doc(no_inline, hidden)]
is bootstrapped and in general, some of the technical details of the build
system.
-## Using rustbuild
+Note that this README only covers internal information, not how to use the tool.
+Please check [bootstrapping dev guide][bootstrapping-dev-guide] for further information.
-The rustbuild build system has a primary entry point, a top level `x.py` script:
+[bootstrapping-dev-guide]: https://rustc-dev-guide.rust-lang.org/building/bootstrapping.html
-```sh
-$ python ./x.py build
-```
-
-Note that if you're on Unix, you should be able to execute the script directly:
-
-```sh
-$ ./x.py build
-```
-
-The script accepts commands, flags, and arguments to determine what to do:
-
-* `build` - a general purpose command for compiling code. Alone, `build` will
- bootstrap the entire compiler, and otherwise, arguments passed indicate what to
- build. For example:
-
- ```
- # build the whole compiler
- ./x.py build --stage 2
-
- # build the stage1 compiler
- ./x.py build
-
- # build stage0 libstd
- ./x.py build --stage 0 library/std
-
- # build a particular crate in stage0
- ./x.py build --stage 0 library/test
- ```
-
- If files that would normally be rebuilt from stage 0 are dirty, the rebuild can be
- overridden using `--keep-stage 0`. Using `--keep-stage n` will skip all steps
- that belong to stage n or earlier:
-
- ```
- # build stage 1, keeping old build products for stage 0
- ./x.py build --keep-stage 0
- ```
-
-* `test` - a command for executing unit tests. Like the `build` command, this
- will execute the entire test suite by default, and otherwise, it can be used to
- select which test suite is run:
-
- ```
- # run all unit tests
- ./x.py test
-
- # execute tool tests
- ./x.py test tidy
-
- # execute the UI test suite
- ./x.py test tests/ui
-
- # execute only some tests in the UI test suite
- ./x.py test tests/ui --test-args substring-of-test-name
-
- # execute tests in the standard library in stage0
- ./x.py test --stage 0 library/std
-
- # execute tests in the core and standard library in stage0,
- # without running doc tests (thus avoid depending on building the compiler)
- ./x.py test --stage 0 --no-doc library/core library/std
+## Introduction
- # execute all doc tests
- ./x.py test src/doc
- ```
+The build system defers most of the complicated logic managing invocations
+of rustc and rustdoc to Cargo itself. However, moving through various stages
+and copying artifacts is still necessary for it to do. Each time rustbuild
+is invoked, it will iterate through the list of predefined steps and execute
+each serially in turn if it matches the paths passed or is a default rule.
+For each step rustbuild relies on the step internally being incremental and
+parallel. Note, though, that the `-j` parameter to rustbuild gets forwarded
+to appropriate test harnesses and such.
-* `doc` - a command for building documentation. Like above, can take arguments
- for what to document.
-
-## Configuring rustbuild
-
-rustbuild offers a TOML-based configuration system with a `config.toml`
-file. An example of this configuration can be found at `config.toml.example`,
-and the configuration file can also be passed as `--config path/to/config.toml`
-if the build system is being invoked manually (via the python script).
-
-You can generate a config.toml using `./configure` options if you want to automate creating the file without having to edit it.
-
-Finally, rustbuild makes use of the [cc-rs crate] which has [its own
-method][env-vars] of configuring C compilers and C flags via environment
-variables.
-
-[cc-rs crate]: https://github.com/alexcrichton/cc-rs
-[env-vars]: https://github.com/alexcrichton/cc-rs#external-configuration-via-environment-variables
-
-## Build stages
+## Build phases
The rustbuild build system goes through a few phases to actually build the
compiler. What actually happens when you invoke rustbuild is:
-1. The entry point script, `x.py` is run. This script is
- responsible for downloading the stage0 compiler/Cargo binaries, and it then
- compiles the build system itself (this folder). Finally, it then invokes the
- actual `bootstrap` binary build system.
+1. The entry point script(`x` for unix like systems, `x.ps1` for windows systems,
+ `x.py` cross-platform) is run. This script is responsible for downloading the stage0
+ compiler/Cargo binaries, and it then compiles the build system itself (this folder).
+ Finally, it then invokes the actual `bootstrap` binary build system.
2. In Rust, `bootstrap` will slurp up all configuration, perform a number of
sanity checks (whether compilers exist, for example), and then start building the
stage0 artifacts.
The goal of each stage is to (a) leverage Cargo as much as possible and failing
that (b) leverage Rust as much as possible!
-## Incremental builds
-
-You can configure rustbuild to use incremental compilation with the
-`--incremental` flag:
-
-```sh
-$ ./x.py build --incremental
-```
-
-The `--incremental` flag will store incremental compilation artifacts
-in `build/<host>/stage0-incremental`. Note that we only use incremental
-compilation for the stage0 -> stage1 compilation -- this is because
-the stage1 compiler is changing, and we don't try to cache and reuse
-incremental artifacts across different versions of the compiler.
-
-You can always drop the `--incremental` to build as normal (but you
-will still be using the local nightly as your bootstrap).
-
## Directory Layout
This build system houses all output under the `build` directory, which looks
# system will link (using hard links) output from stageN-{std,rustc} into
# each of these directories.
#
- # In theory, there is no extra build output in these directories.
+ # In theory these are working rustc sysroot directories, meaning there is
+ # no extra build output in these directories.
stage1/
stage2/
stage3/
```
-## Cargo projects
-
-The current build is unfortunately not quite as simple as `cargo build` in a
-directory, but rather the compiler is split into three different Cargo projects:
-
-* `library/std` - the standard library
-* `library/test` - testing support, depends on libstd
-* `compiler/rustc` - the actual compiler itself
-
-Each "project" has a corresponding Cargo.lock file with all dependencies, and
-this means that building the compiler involves running Cargo three times. The
-structure here serves two goals:
-
-1. Facilitating dependencies coming from crates.io. These dependencies don't
- depend on `std`, so libstd is a separate project compiled ahead of time
- before the actual compiler builds.
-2. Splitting "host artifacts" from "target artifacts". That is, when building
- code for an arbitrary target, you don't need the entire compiler, but you'll
- end up needing libraries like libtest that depend on std but also want to use
- crates.io dependencies. Hence, libtest is split out as its own project that
- is sequenced after `std` but before `rustc`. This project is built for all
- targets.
-
-There is some loss in build parallelism here because libtest can be compiled in
-parallel with a number of rustc artifacts, but in theory, the loss isn't too bad!
-
-## Build tools
-
-We've actually got quite a few tools that we use in the compiler's build system
-and for testing. To organize these, each tool is a project in `src/tools` with a
-corresponding `Cargo.toml`. All tools are compiled with Cargo (currently having
-independent `Cargo.lock` files) and do not currently explicitly depend on the
-compiler or standard library. Compiling each tool is sequenced after the
-appropriate libstd/libtest/librustc compile above.
-
## Extending rustbuild
-So, you'd like to add a feature to the rustbuild build system or just fix a bug.
-Great! One of the major motivational factors for moving away from `make` is that
-Rust is in theory much easier to read, modify, and write. If you find anything
-excessively confusing, please open an issue on this, and we'll try to get it
-documented or simplified, pronto.
+When you use the bootstrap system, you'll call it through the entry point script
+(`x`, `x.ps1`, or `x.py`). However, most of the code lives in `src/bootstrap`.
+`bootstrap` has a difficult problem: it is written in Rust, but yet it is run
+before the Rust compiler is built! To work around this, there are two components
+of bootstrap: the main one written in rust, and `bootstrap.py`. `bootstrap.py`
+is what gets run by entry point script. It takes care of downloading the `stage0`
+compiler, which will then build the bootstrap binary written in Rust.
-First up, you'll probably want to read over the documentation above, as that'll
-give you a high level overview of what rustbuild is doing. You also probably
-want to play around a bit yourself by just getting it up and running before you
-dive too much into the actual build system itself.
+Because there are two separate codebases behind `x.py`, they need to
+be kept in sync. In particular, both `bootstrap.py` and the bootstrap binary
+parse `config.toml` and read the same command line arguments. `bootstrap.py`
+keeps these in sync by setting various environment variables, and the
+programs sometimes have to add arguments that are explicitly ignored, to be
+read by the other.
-After that, each module in rustbuild should have enough documentation to keep
-you up and running. Some general areas that you may be interested in modifying
-are:
+Some general areas that you may be interested in modifying are:
* Adding a new build tool? Take a look at `bootstrap/tool.rs` for examples of
other tools.
Changes that do not affect contributors to the compiler or users
building rustc from source don't need an update to `VERSION`.
-If you have any questions, feel free to reach out on the `#t-infra` channel in
-the [Rust Zulip server][rust-zulip] or ask on internals.rust-lang.org. When
-you encounter bugs, please file issues on the rust-lang/rust issue tracker.
+If you have any questions, feel free to reach out on the `#t-infra/bootstrap` channel
+at [Rust Bootstrap Zulip server][rust-bootstrap-zulip]. When you encounter bugs,
+please file issues on the [Rust issue tracker][rust-issue-tracker].
-[rust-zulip]: https://rust-lang.zulipchat.com/#narrow/stream/242791-t-infra
+[rust-bootstrap-zulip]: https://rust-lang.zulipchat.com/#narrow/stream/t-infra.2Fbootstrap
+[rust-issue-tracker]: https://github.com/rust-lang/rust/issues
if stage >= 1 {
cargo.rustflag("-Cembed-bitcode=yes");
}
+ if builder.config.rust_lto == RustcLto::Off {
+ cargo.rustflag("-Clto=off");
+ }
// By default, rustc does not include unwind tables unless they are required
// for a particular target. They are not required by RISC-V targets, but
cargo.rustflag("-Cembed-bitcode=yes");
}
RustcLto::ThinLocal => { /* Do nothing, this is the default */ }
+ RustcLto::Off => {
+ cargo.rustflag("-Clto=off");
+ }
+ }
+ } else {
+ if builder.config.rust_lto == RustcLto::Off {
+ cargo.rustflag("-Clto=off");
}
}
}
/// LTO mode used for compiling rustc itself.
-#[derive(Default, Clone)]
+#[derive(Default, Clone, PartialEq)]
pub enum RustcLto {
+ Off,
#[default]
ThinLocal,
Thin,
"thin-local" => Ok(RustcLto::ThinLocal),
"thin" => Ok(RustcLto::Thin),
"fat" => Ok(RustcLto::Fat),
+ "off" => Ok(RustcLto::Off),
_ => Err(format!("Invalid value for rustc LTO: {}", s)),
}
}
incremental = true
# Print backtrace on internal compiler errors during bootstrap
backtrace-on-ice = true
+# Make the compiler and standard library faster to build, at the expense of a ~20% runtime slowdown.
+lto = "off"
[llvm]
# Will download LLVM from CI if available on your platform.
[rust]
# This greatly increases the speed of rebuilds, especially when there are only minor changes. However, it makes the initial build slightly slower.
incremental = true
+# Make the compiler and standard library faster to build, at the expense of a ~20% runtime slowdown.
+lto = "off"
[llvm]
# Will download LLVM from CI if available on your platform.
//! crates.io and Cargo.
//! * A standard interface to build across all platforms, including MSVC
//!
-//! ## Architecture
-//!
-//! The build system defers most of the complicated logic managing invocations
-//! of rustc and rustdoc to Cargo itself. However, moving through various stages
-//! and copying artifacts is still necessary for it to do. Each time rustbuild
-//! is invoked, it will iterate through the list of predefined steps and execute
-//! each serially in turn if it matches the paths passed or is a default rule.
-//! For each step rustbuild relies on the step internally being incremental and
-//! parallel. Note, though, that the `-j` parameter to rustbuild gets forwarded
-//! to appropriate test harnesses and such.
-//!
-//! Most of the "meaty" steps that matter are backed by Cargo, which does indeed
-//! have its own parallelism and incremental management. Later steps, like
-//! tests, aren't incremental and simply run the entire suite currently.
-//! However, compiletest itself tries to avoid running tests when the artifacts
-//! that are involved (mainly the compiler) haven't changed.
-//!
-//! When you execute `x.py build`, the steps executed are:
-//!
-//! * First, the python script is run. This will automatically download the
-//! stage0 rustc and cargo according to `src/stage0.json`, or use the cached
-//! versions if they're available. These are then used to compile rustbuild
-//! itself (using Cargo). Finally, control is then transferred to rustbuild.
-//!
-//! * Rustbuild takes over, performs sanity checks, probes the environment,
-//! reads configuration, and starts executing steps as it reads the command
-//! line arguments (paths) or going through the default rules.
-//!
-//! The build output will be something like the following:
-//!
-//! Building stage0 std artifacts
-//! Copying stage0 std
-//! Building stage0 test artifacts
-//! Copying stage0 test
-//! Building stage0 compiler artifacts
-//! Copying stage0 rustc
-//! Assembling stage1 compiler
-//! Building stage1 std artifacts
-//! Copying stage1 std
-//! Building stage1 test artifacts
-//! Copying stage1 test
-//! Building stage1 compiler artifacts
-//! Copying stage1 rustc
-//! Assembling stage2 compiler
-//! Uplifting stage1 std
-//! Uplifting stage1 test
-//! Uplifting stage1 rustc
-//!
-//! Let's disect that a little:
-//!
-//! ## Building stage0 {std,test,compiler} artifacts
-//!
-//! These steps use the provided (downloaded, usually) compiler to compile the
-//! local Rust source into libraries we can use.
-//!
-//! ## Copying stage0 {std,test,rustc}
-//!
-//! This copies the build output from Cargo into
-//! `build/$HOST/stage0-sysroot/lib/rustlib/$ARCH/lib`. FIXME: this step's
-//! documentation should be expanded -- the information already here may be
-//! incorrect.
-//!
-//! ## Assembling stage1 compiler
-//!
-//! This copies the libraries we built in "building stage0 ... artifacts" into
-//! the stage1 compiler's lib directory. These are the host libraries that the
-//! compiler itself uses to run. These aren't actually used by artifacts the new
-//! compiler generates. This step also copies the rustc and rustdoc binaries we
-//! generated into build/$HOST/stage/bin.
-//!
-//! The stage1/bin/rustc is a fully functional compiler, but it doesn't yet have
-//! any libraries to link built binaries or libraries to. The next 3 steps will
-//! provide those libraries for it; they are mostly equivalent to constructing
-//! the stage1/bin compiler so we don't go through them individually.
-//!
-//! ## Uplifting stage1 {std,test,rustc}
-//!
-//! This step copies the libraries from the stage1 compiler sysroot into the
-//! stage2 compiler. This is done to avoid rebuilding the compiler; libraries
-//! we'd build in this step should be identical (in function, if not necessarily
-//! identical on disk) so there's no need to recompile the compiler again. Note
-//! that if you want to, you can enable the full-bootstrap option to change this
-//! behavior.
-//!
-//! Each step is driven by a separate Cargo project and rustbuild orchestrates
-//! copying files between steps and otherwise preparing for Cargo to run.
-//!
//! ## Further information
//!
//! More documentation can be found in each respective module below, and you can
cmd.arg("--bless");
}
- builder.info("tidy check");
- try_run(builder, &mut cmd);
-
if builder.config.channel == "dev" || builder.config.channel == "nightly" {
builder.info("fmt check");
if builder.initial_rustfmt().is_none() {
}
crate::format::format(&builder, !builder.config.cmd.bless(), &[]);
}
+
+ builder.info("tidy check");
+ try_run(builder, &mut cmd);
+
+ builder.ensure(ExpandYamlAnchors {});
}
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
import signal
import subprocess
import sys
-from typing import ClassVar, List
+from typing import ClassVar, List, Optional
@dataclass
class TestEnvironment:
rust_dir: str
sdk_dir: str
- target_arch: str
- package_server_pid: int = None
- emu_addr: str = None
- libstd_name: str = None
- libtest_name: str = None
+ target: str
+ package_server_pid: Optional[int] = None
+ emu_addr: Optional[str] = None
+ libstd_name: Optional[str] = None
+ libtest_name: Optional[str] = None
verbose: bool = False
@staticmethod
return os.path.abspath(tmp_dir)
return os.path.join(os.path.dirname(__file__), "tmp~")
+ @staticmethod
+ def triple_to_arch(triple):
+ if "x86_64" in triple:
+ return "x64"
+ elif "aarch64" in triple:
+ return "arm64"
+ else:
+ raise Exception(f"Unrecognized target triple {triple}")
+
@classmethod
def env_file_path(cls):
return os.path.join(cls.tmp_dir(), "test_env.json")
return cls(
os.path.abspath(args.rust),
os.path.abspath(args.sdk),
- args.target_arch,
+ args.target,
verbose=args.verbose,
)
return cls(
test_env["rust_dir"],
test_env["sdk_dir"],
- test_env["target_arch"],
+ test_env["target"],
libstd_name=test_env["libstd_name"],
libtest_name=test_env["libtest_name"],
emu_addr=test_env["emu_addr"],
verbose=test_env["verbose"],
)
- def image_name(self):
- if self.target_arch == "x64":
- return "qemu-x64"
- if self.target_arch == "arm64":
- return "qemu-arm64"
- raise Exception(f"Unrecognized target architecture {self.target_arch}")
-
def write_to_file(self):
with open(self.env_file_path(), "w", encoding="utf-8") as f:
f.write(json.dumps(self.__dict__))
def repo_dir(self):
return os.path.join(self.tmp_dir(), self.TEST_REPO_NAME)
- def rustlib_dir(self):
- if self.target_arch == "x64":
- return "x86_64-unknown-fuchsia"
- if self.target_arch == "arm64":
- return "aarch64-unknown-fuchsia"
- raise Exception(f"Unrecognized target architecture {self.target_arch}")
-
def libs_dir(self):
return os.path.join(
self.rust_dir,
return os.path.join(
self.libs_dir(),
"rustlib",
- self.rustlib_dir(),
+ self.target,
"lib",
)
"--emulator-log",
self.emulator_log_path(),
"--image-name",
- self.image_name(),
+ "qemu-" + self.triple_to_arch(self.target),
],
stdout=self.subprocess_output(),
stderr=self.subprocess_output(),
package_dir=package_dir,
package_name=package_name,
rust_dir=self.rust_dir,
- rustlib_dir=self.rustlib_dir(),
+ rustlib_dir=self.target,
sdk_dir=self.sdk_dir,
libstd_name=self.libstd_name,
libtest_name=self.libtest_name,
- target_arch=self.target_arch,
+ target_arch=self.triple_to_arch(self.target),
)
)
for shared_lib in shared_libs:
action="store_true",
)
start_parser.add_argument(
- "--target-arch",
- help="the architecture of the image to test",
+ "--target",
+ help="the target platform to test",
required=True,
)
start_parser.set_defaults(func=start)
- &shared-ci-variables
CI_JOB_NAME: ${{ matrix.name }}
+ CARGO_REGISTRIES_CRATES_IO_PROTOCOL: sparse
- &public-variables
SCCACHE_BUCKET: rust-lang-ci-sccache2
ci_dir=`cd $(dirname $0) && pwd`
source "$ci_dir/shared.sh"
+export CARGO_REGISTRIES_CRATES_IO_PROTOCOL=sparse
+
if ! isCI || isCiBranch auto || isCiBranch beta || isCiBranch try || isCiBranch try-perf; then
RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --set build.print-step-timings --enable-verbose-tests"
RUST_CONFIGURE_ARGS="$RUST_CONFIGURE_ARGS --set build.metrics"
import time
import traceback
import urllib.request
-from collections import OrderedDict
from io import StringIO
from pathlib import Path
-from typing import Callable, Dict, Iterable, List, Optional, Union
+from typing import Callable, Dict, Iterable, Iterator, List, Optional, Tuple, Union
PGO_HOST = os.environ["PGO_HOST"]
return False
+def get_timestamp() -> float:
+ return time.time()
+
+
+Duration = float
+TimerSection = Union[Duration, "Timer"]
+
+
+def iterate_sections(section: TimerSection, name: str, level: int = 0) -> Iterator[Tuple[int, str, Duration]]:
+ """
+ Hierarchically iterate the sections of a timer, in a depth-first order.
+ """
+ if isinstance(section, Duration):
+ yield (level, name, section)
+ elif isinstance(section, Timer):
+ yield (level, name, section.total_duration())
+ for (child_name, child_section) in section.sections:
+ yield from iterate_sections(child_section, child_name, level=level + 1)
+ else:
+ assert False
+
+
class Timer:
- def __init__(self):
- # We want this dictionary to be ordered by insertion.
- # We use `OrderedDict` for compatibility with older Python versions.
- self.stages = OrderedDict()
+ def __init__(self, parent_names: Tuple[str, ...] = ()):
+ self.sections: List[Tuple[str, TimerSection]] = []
+ self.section_active = False
+ self.parent_names = parent_names
@contextlib.contextmanager
- def stage(self, name: str):
- assert name not in self.stages
+ def section(self, name: str) -> "Timer":
+ assert not self.section_active
+ self.section_active = True
- start = time.time()
+ start = get_timestamp()
exc = None
+
+ child_timer = Timer(parent_names=self.parent_names + (name, ))
+ full_name = " > ".join(child_timer.parent_names)
try:
- LOGGER.info(f"Stage `{name}` starts")
- yield
+ LOGGER.info(f"Section `{full_name}` starts")
+ yield child_timer
except BaseException as exception:
exc = exception
raise
finally:
- end = time.time()
+ end = get_timestamp()
duration = end - start
- self.stages[name] = duration
+
+ if child_timer.has_children():
+ self.sections.append((name, child_timer))
+ else:
+ self.sections.append((name, duration))
if exc is None:
- LOGGER.info(f"Stage `{name}` ended: OK ({duration:.2f}s)")
+ LOGGER.info(f"Section `{full_name}` ended: OK ({duration:.2f}s)")
+ else:
+ LOGGER.info(f"Section `{full_name}` ended: FAIL ({duration:.2f}s)")
+ self.section_active = False
+
+ def total_duration(self) -> Duration:
+ duration = 0
+ for (_, section) in self.sections:
+ if isinstance(section, Duration):
+ duration += section
else:
- LOGGER.info(f"Stage `{name}` ended: FAIL ({duration:.2f}s)")
+ duration += section.total_duration()
+ return duration
+
+ def has_children(self) -> bool:
+ return len(self.sections) > 0
def print_stats(self):
- total_duration = sum(self.stages.values())
+ rows = []
+ for (child_name, child_section) in self.sections:
+ for (level, name, duration) in iterate_sections(child_section, child_name, level=0):
+ label = f"{' ' * level}{name}:"
+ rows.append((label, duration))
- # 57 is the width of the whole table
- divider = "-" * 57
+ # Empty row
+ rows.append(("", ""))
+
+ total_duration_label = "Total duration:"
+ total_duration = self.total_duration()
+ rows.append((total_duration_label, humantime(total_duration)))
+
+ space_after_label = 2
+ max_label_length = max(16, max(len(label) for (label, _) in rows)) + space_after_label
+
+ table_width = max_label_length + 23
+ divider = "-" * table_width
with StringIO() as output:
print(divider, file=output)
- for (name, duration) in self.stages.items():
- pct = (duration / total_duration) * 100
- name_str = f"{name}:"
- print(f"{name_str:<34} {duration:>12.2f}s ({pct:>5.2f}%)", file=output)
-
- total_duration_label = "Total duration:"
- print(f"{total_duration_label:<34} {total_duration:>12.2f}s", file=output)
+ for (label, duration) in rows:
+ if isinstance(duration, Duration):
+ pct = (duration / total_duration) * 100
+ value = f"{duration:>12.2f}s ({pct:>5.2f}%)"
+ else:
+ value = f"{duration:>{len(total_duration_label) + 7}}"
+ print(f"{label:<{max_label_length}} {value}", file=output)
print(divider, file=output, end="")
LOGGER.info(f"Timer results\n{output.getvalue()}")
os.chdir(cwd)
+def humantime(time_s: float) -> str:
+ hours = time_s // 3600
+ time_s = time_s % 3600
+ minutes = time_s // 60
+ seconds = time_s % 60
+
+ result = ""
+ if hours > 0:
+ result += f"{int(hours)}h "
+ if minutes > 0:
+ result += f"{int(minutes)}m "
+ result += f"{round(seconds)}s"
+ return result
+
+
def move_path(src: Path, dst: Path):
LOGGER.info(f"Moving `{src}` to `{dst}`")
shutil.move(src, dst)
pipeline.build_rustc_perf()
# Stage 1: Build rustc + PGO instrumented LLVM
- with timer.stage("Build rustc (LLVM PGO)"):
- build_rustc(pipeline, args=[
- "--llvm-profile-generate"
- ], env=dict(
- LLVM_PROFILE_DIR=str(pipeline.llvm_profile_dir_root() / "prof-%p")
- ))
+ with timer.section("Stage 1 (LLVM PGO)") as stage1:
+ with stage1.section("Build rustc and LLVM"):
+ build_rustc(pipeline, args=[
+ "--llvm-profile-generate"
+ ], env=dict(
+ LLVM_PROFILE_DIR=str(pipeline.llvm_profile_dir_root() / "prof-%p")
+ ))
- with timer.stage("Gather profiles (LLVM PGO)"):
- gather_llvm_profiles(pipeline)
+ with stage1.section("Gather profiles"):
+ gather_llvm_profiles(pipeline)
clear_llvm_files(pipeline)
final_build_args += [
]
# Stage 2: Build PGO instrumented rustc + LLVM
- with timer.stage("Build rustc (rustc PGO)"):
- build_rustc(pipeline, args=[
- "--rust-profile-generate",
- pipeline.rustc_profile_dir_root()
- ])
+ with timer.section("Stage 2 (rustc PGO)") as stage2:
+ with stage2.section("Build rustc and LLVM"):
+ build_rustc(pipeline, args=[
+ "--rust-profile-generate",
+ pipeline.rustc_profile_dir_root()
+ ])
- with timer.stage("Gather profiles (rustc PGO)"):
- gather_rustc_profiles(pipeline)
+ with stage2.section("Gather profiles"):
+ gather_rustc_profiles(pipeline)
clear_llvm_files(pipeline)
final_build_args += [
# Stage 3: Build rustc + BOLT instrumented LLVM
if pipeline.supports_bolt():
- with timer.stage("Build rustc (LLVM BOLT)"):
- build_rustc(pipeline, args=[
- "--llvm-profile-use",
- pipeline.llvm_profile_merged_file(),
- "--llvm-bolt-profile-generate",
- ])
- with timer.stage("Gather profiles (LLVM BOLT)"):
- gather_llvm_bolt_profiles(pipeline)
+ with timer.section("Stage 3 (LLVM BOLT)") as stage3:
+ with stage3.section("Build rustc and LLVM"):
+ build_rustc(pipeline, args=[
+ "--llvm-profile-use",
+ pipeline.llvm_profile_merged_file(),
+ "--llvm-bolt-profile-generate",
+ ])
+ with stage3.section("Gather profiles"):
+ gather_llvm_bolt_profiles(pipeline)
clear_llvm_files(pipeline)
final_build_args += [
]
# Stage 4: Build PGO optimized rustc + PGO/BOLT optimized LLVM
- with timer.stage("Final build"):
+ with timer.section("Stage 4 (final build)"):
cmd(final_build_args)
src/ci/docker/scripts/fuchsia-test-runner.py start
--rust ${RUST_SRC_PATH}/install
--sdk ${SDK_PATH}
- --target-arch {x64,arm64}
+ --target-triple {x86_64-unknown-fuchsia|aarch64-unknown-fuchsia}
```
Where `${RUST_SRC_PATH}/install` is the `prefix` set in `config.toml` and
);
```
+#### else blocks (let-else statements)
+
+If a let statement contains an `else` component, also known as a let-else statement,
+then the `else` component should be formatted according to the same rules as the `else` block
+in [control flow expressions (i.e. if-else, and if-let-else expressions)](./expressions.md#control-flow-expressions).
+Apply the same formatting rules to the components preceding
+the `else` block (i.e. the `let pattern: Type = initializer_expr ...` portion)
+as described [above](#let-statements)
+
+Similarly to if-else expressions, if the initializer
+expression is multi-lined, then the `else` keyword and opening brace of the block (i.e. `else {`)
+should be put on the same line as the end of the initializer
+expression with a preceding space if all the following are true:
+
+* The initializer expression ends with one or more closing
+ parentheses, square brackets, and/or braces
+* There is nothing else on that line
+* That line is not indented beyond the indent of the first line containing the `let` keyword
+
+For example:
+
+```rust
+let Some(x) = y.foo(
+ "abc",
+ fairly_long_identifier,
+ "def",
+ "123456",
+ "string",
+ "cheese",
+) else {
+ bar()
+}
+```
+
+Otherwise, the `else` keyword and opening brace should be placed on the next line after the end of the initializer expression, and should not be indented (the `else` keyword should be aligned with the `let` keyword).
+
+For example:
+
+```rust
+let Some(x) = abcdef()
+ .foo(
+ "abc",
+ some_really_really_really_long_ident,
+ "ident",
+ "123456",
+ )
+ .bar()
+ .baz()
+ .qux("fffffffffffffffff")
+else {
+ foo_bar()
+}
+```
+
+##### Single line let-else statements
+
+The entire let-else statement may be formatted on a single line if all the following are true:
+
+* the entire statement is *short*
+* the `else` block contains a single-line expression and no statements
+* the `else` block contains no comments
+* the let statement components preceding the `else` block can be formatted on a single line
+
+```rust
+let Some(1) = opt else { return };
+
+let Some(1) = opt else {
+ return;
+};
+
+let Some(1) = opt else {
+ // nope
+ return
+};
+```
+
+Formatters may allow users to configure the value of the threshold
+used to determine whether a let-else statement is *short*.
### Macros in statement position
get_all_import_attributes(use_node, cx.tcx, item.owner_id.def_id, &mut extra_attrs);
}
- if !extra_attrs.is_empty() {
+ let mut item = if !extra_attrs.is_empty() {
extra_attrs.extend_from_slice(inline::load_attrs(cx, def_id));
let attrs = Attributes::from_ast(&extra_attrs);
let cfg = extra_attrs.cfg(cx.tcx, &cx.cache.hidden_cfg);
- vec![Item::from_def_id_and_attrs_and_parts(
- def_id,
- Some(name),
- kind,
- Box::new(attrs),
- cfg,
- )]
+ Item::from_def_id_and_attrs_and_parts(def_id, Some(name), kind, Box::new(attrs), cfg)
} else {
- vec![Item::from_def_id_and_parts(def_id, Some(name), kind, cx)]
- }
+ Item::from_def_id_and_parts(def_id, Some(name), kind, cx)
+ };
+ item.inline_stmt_id = import_id.map(|def_id| def_id.to_def_id());
+ vec![item]
})
}
if f.alternate() {
write!(f, ": {:#}", print_generic_bounds(bounds, cx))?;
} else {
- write!(f, ": {}", print_generic_bounds(bounds, cx))?;
+ write!(f, ": {}", print_generic_bounds(bounds, cx))?;
}
}
if f.alternate() {
write!(f, " = {:#}", ty.print(cx))?;
} else {
- write!(f, " = {}", ty.print(cx))?;
+ write!(f, " = {}", ty.print(cx))?;
}
}
if f.alternate() {
write!(f, "const {}: {:#}", self.name, ty.print(cx))?;
} else {
- write!(f, "const {}: {}", self.name, ty.print(cx))?;
+ write!(f, "const {}: {}", self.name, ty.print(cx))?;
}
if let Some(default) = default {
if f.alternate() {
write!(f, " = {:#}", default)?;
} else {
- write!(f, " = {}", default)?;
+ write!(f, " = {}", default)?;
}
}
} 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 padding_amout =
+ if ending == Ending::Newline { indent + 4 } else { indent + "fn where ".len() };
+
+ for _ in 0..padding_amout {
+ br_with_padding.push_str(" ");
}
let where_preds = where_preds.to_string().replace("<br>", &br_with_padding);
if ending == Ending::Newline {
- let mut clause = " ".repeat(indent.saturating_sub(1));
+ let mut clause = " ".repeat(indent.saturating_sub(1));
write!(clause, "<span class=\"where fmt-newline\">where{where_preds},</span>")?;
clause
} else {
if indent == 0 {
format!("<br><span class=\"where\">where{where_preds}</span>")
} else {
+ // put the first one on the same line as the 'where' keyword
+ let where_preds = where_preds.replacen(&br_with_padding, " ", 1);
+
let mut clause = br_with_padding;
- clause.truncate(clause.len() - 4 * " ".len());
+ clause.truncate(clause.len() - "where ".len());
+
write!(clause, "<span class=\"where\">where{where_preds}</span>")?;
clause
}
let declaration_len = header_len + args_plain.len() + arrow_plain.len();
let output = if declaration_len > 80 {
- let full_pad = format!("<br>{}", " ".repeat(indent + 4));
- let close_pad = format!("<br>{}", " ".repeat(indent));
+ let full_pad = format!("<br>{}", " ".repeat(indent + 4));
+ let close_pad = format!("<br>{}", " ".repeat(indent));
format!(
"({pad}{args}{close}){arrow}",
pad = if self.inputs.values.is_empty() { "" } else { &full_pad },
if f.alternate() {
write!(f, ": {:#}", print_generic_bounds(bounds, cx))?;
} else {
- write!(f, ": {}", print_generic_bounds(bounds, cx))?;
+ write!(f, ": {}", print_generic_bounds(bounds, cx))?;
}
}
}
/// E.g. if `heading_offset: HeadingOffset::H2`, then `# something` renders an `<h2>`.
pub heading_offset: HeadingOffset,
}
-/// A tuple struct like `Markdown` that renders the markdown with a table of contents.
-pub(crate) struct MarkdownWithToc<'a>(
- pub(crate) &'a str,
- pub(crate) &'a mut IdMap,
- pub(crate) ErrorCodes,
- pub(crate) Edition,
- pub(crate) &'a Option<Playground>,
-);
+/// A struct like `Markdown` that renders the markdown with a table of contents.
+pub(crate) struct MarkdownWithToc<'a> {
+ pub(crate) content: &'a str,
+ pub(crate) ids: &'a mut IdMap,
+ pub(crate) error_codes: ErrorCodes,
+ pub(crate) edition: Edition,
+ pub(crate) playground: &'a Option<Playground>,
+}
/// A tuple struct like `Markdown` that renders the markdown escaping HTML tags
/// and includes no paragraph tags.
pub(crate) struct MarkdownItemInfo<'a>(pub(crate) &'a str, pub(crate) &'a mut IdMap);
impl MarkdownWithToc<'_> {
pub(crate) fn into_string(self) -> String {
- let MarkdownWithToc(md, ids, codes, edition, playground) = self;
+ let MarkdownWithToc { content: md, ids, error_codes: codes, edition, playground } = self;
let p = Parser::new_ext(md, main_body_opts()).into_offset_iter();
if count_consts != 0 && count_methods != 0 {
w.write_str("\n");
}
+
+ if !required_methods.is_empty() {
+ write!(w, " // Required method{}\n", pluralize(required_methods.len()));
+ }
for (pos, m) in required_methods.iter().enumerate() {
render_assoc_item(
w,
if !required_methods.is_empty() && !provided_methods.is_empty() {
w.write_str("\n");
}
+
+ if !provided_methods.is_empty() {
+ write!(w, " // Provided method{}\n", pluralize(provided_methods.len()));
+ }
for (pos, m) in provided_methods.iter().enumerate() {
render_assoc_item(
w,
cx,
RenderMode::Normal,
);
- match *m.kind {
- clean::MethodItem(ref inner, _)
- if !inner.generics.where_predicates.is_empty() =>
- {
- w.write_str(",\n { ... }\n");
- }
- _ => {
- w.write_str(" { ... }\n");
- }
- }
+
+ w.write_str(" { ... }\n");
if pos < provided_methods.len() - 1 {
w.write_str("<span class=\"item-spacer\"></span>");
fn print_tuple_struct_fields(w: &mut Buffer, cx: &Context<'_>, s: &[clean::Item]) {
for (i, ty) in s.iter().enumerate() {
if i > 0 {
- w.write_str(", ");
+ w.write_str(", ");
}
match *ty.kind {
clean::StrippedItem(box clean::StructFieldItem(_)) => w.write_str("_"),
"<div class=\"sub-variant-field\">\
<span id=\"{id}\" class=\"small-section-header\">\
<a href=\"#{id}\" class=\"anchor field\">§</a>\
- <code>{f}: {t}</code>\
+ <code>{f}: {t}</code>\
</span>",
id = id,
f = field.name.unwrap(),
Ok((ret, krates))
}
- /// Read a file and return all lines that match the <code>"{crate}":{data},\</code> format,
+ /// Read a file and return all lines that match the <code>"{crate}":{data},\ </code> format,
/// and return a tuple `(Vec<DataString>, Vec<CrateNameString>)`.
///
/// This forms the payload of files that look like this:
font-weight: 600;
margin: 0;
padding: 0;
+ white-space: pre-wrap;
}
#crate-search,
.fn .where,
.where.fmt-newline {
display: block;
+ white-space: pre-wrap;
font-size: 0.875rem;
}
background-repeat: no-repeat;
background-size: 20px;
background-position: calc(100% - 2px) 56%;
- /* image is black color */
- background-image: url("down-arrow-927217e04c7463ac.svg");
+ /* down arrow (image is black color) */
+ background-image: url('data:image/svg+xml,<svg xmlns="http://www.w3.org/2000/svg" \
+ width="128" height="128" viewBox="-30 -20 176 176"><path d="M111,40.5L64,87.499L17,40.5" \
+ fill="none" stroke="black" strike-linecap="square" stroke-miterlimit="10" stroke-width="12"/> \
+ </svg>');
/* changes the arrow image color */
filter: var(--crate-search-div-filter);
}
.item-spacer {
width: 100%;
height: 12px;
+ display: block;
}
.out-of-band > span.since {
}
details.toggle > summary::before {
- background: url("toggle-plus-1092eb4930d581b0.svg") no-repeat top left;
+ /* toggle plus */
+ background: url('data:image/svg+xml,<svg width="17" height="17" \
+shape-rendering="crispEdges" stroke="black" fill="none" xmlns="http://www.w3.org/2000/svg"><path \
+d="M5 2.5H2.5v12H5m7-12h2.5v12H12M5 8.5h7M8.5 12V8.625v0V5"/></svg>') no-repeat top left;
content: "";
cursor: pointer;
width: 16px;
}
details.toggle[open] > summary::before {
- background: url("toggle-minus-31bbd6e4c77f5c96.svg") no-repeat top left;
+ /* toggle minus */
+ background: url('data:image/svg+xml,<svg width="17" height="17" \
+shape-rendering="crispEdges" stroke="black" fill="none" xmlns="http://www.w3.org/2000/svg"><path \
+d="M5 2.5H2.5v12H5m7-12h2.5v12H12M5 8.5h7"/></svg>') no-repeat top left;
}
details.toggle[open] > summary::after {
--scrape-example-code-wrapper-background-end: rgba(15, 20, 25, 0);
}
-h1, h2, h3, h4 {
- color: white;
-}
-h1 a {
+h1, h2, h3, h4,
+h1 a, .sidebar h2 a, .sidebar h3 a,
+#source-sidebar > .title {
color: #fff;
}
h4 {
.docblock code {
color: #ffb454;
}
-.code-header {
- color: #e6e1cf;
-}
-.docblock pre > code, pre > code {
- color: #e6e1cf;
-}
-.item-info code {
- color: #e6e1cf;
-}
.docblock a > code {
color: #39AFD7 !important;
}
-pre, .rustdoc.source .example-wrap {
+.code-header,
+.docblock pre > code,
+pre, pre > code,
+.item-info code,
+.rustdoc.source .example-wrap {
color: #e6e1cf;
}
.sidebar .current,
-.sidebar a:hover {
+.sidebar a:hover,
+#source-sidebar div.files > a:hover, details.dir-entry summary:hover,
+#source-sidebar div.files > a:focus, details.dir-entry summary:focus,
+#source-sidebar div.files > a.selected {
color: #ffb44c;
}
border-right: 1px solid #ffb44c;
}
-.search-results a:hover {
- color: #fff !important;
- background-color: #3c3c3c;
-}
-
+.search-results a:hover,
.search-results a:focus {
color: #fff !important;
background-color: #3c3c3c;
}
+
.search-results a {
color: #0096cf;
}
color: #c5c5c5;
}
-.sidebar h2 a,
-.sidebar h3 a {
- color: white;
-}
-
.result-name .primitive > i, .result-name .keyword > i {
color: #788797;
}
#settings-menu > a img {
filter: invert(100);
}
-
-#source-sidebar > .title {
- color: #fff;
-}
-#source-sidebar div.files > a:hover, details.dir-entry summary:hover,
-#source-sidebar div.files > a:focus, details.dir-entry summary:focus,
-#source-sidebar div.files > a.selected {
- color: #ffb44c;
-}
+++ /dev/null
-<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" id="Layer_1" width="128" height="128" enable-background="new 0 0 128 128" version="1.1" viewBox="-30 -20 176 176" xml:space="preserve"><g><line x1="111" x2="64" y1="40.5" y2="87.499" fill="none" stroke="#000000" stroke-linecap="square" stroke-miterlimit="10" stroke-width="12"/><line x1="64" x2="17" y1="87.499" y2="40.5" fill="none" stroke="#000000" stroke-linecap="square" stroke-miterlimit="10" stroke-width="12"/></g></svg>
\ No newline at end of file
+++ /dev/null
-<svg width="17" height="17" shape-rendering="crispEdges" stroke="#000" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M5 2.5H2.5v12H5m7-12h2.5v12H12M5 8.5h7"/></svg>
\ No newline at end of file
+++ /dev/null
-<svg width="17" height="17" shape-rendering="crispEdges" stroke="#000" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M5 2.5H2.5v12H5m7-12h2.5v12H12M5 8.5h7M8.5 12V8.625v0V5"/></svg>
\ No newline at end of file
}
function initSearch(rawSearchIndex) {
- const MAX_LEV_DISTANCE = 3;
const MAX_RESULTS = 200;
const NO_TYPE_FILTER = -1;
/**
* @param {QueryElement} elem - The element from the parsed query.
* @param {integer} defaultLev - This is the value to return in case there are no generics.
*
- * @return {integer} - Returns the best match (if any) or `MAX_LEV_DISTANCE + 1`.
+ * @return {integer} - Returns the best match (if any) or `maxLevDistance + 1`.
*/
- function checkGenerics(row, elem, defaultLev) {
+ function checkGenerics(row, elem, defaultLev, maxLevDistance) {
if (row.generics.length === 0) {
- return elem.generics.length === 0 ? defaultLev : MAX_LEV_DISTANCE + 1;
+ return elem.generics.length === 0 ? defaultLev : maxLevDistance + 1;
} else if (row.generics.length > 0 && row.generics[0].name === null) {
- return checkGenerics(row.generics[0], elem, defaultLev);
+ return checkGenerics(row.generics[0], elem, defaultLev, maxLevDistance);
}
// The names match, but we need to be sure that all generics kinda
// match as well.
elem_name = entry.name;
if (elem_name === "") {
// Pure generic, needs to check into it.
- if (checkGenerics(entry, elem, MAX_LEV_DISTANCE + 1) !== 0) {
- return MAX_LEV_DISTANCE + 1;
+ if (checkGenerics(entry, elem, maxLevDistance + 1, maxLevDistance) !== 0) {
+ return maxLevDistance + 1;
}
continue;
}
}
}
if (match === null) {
- return MAX_LEV_DISTANCE + 1;
+ return maxLevDistance + 1;
}
elems[match] -= 1;
if (elems[match] === 0) {
}
return 0;
}
- return MAX_LEV_DISTANCE + 1;
+ return maxLevDistance + 1;
}
/**
*
* @return {integer} - Returns a Levenshtein distance to the best match.
*/
- function checkIfInGenerics(row, elem) {
- let lev = MAX_LEV_DISTANCE + 1;
+ function checkIfInGenerics(row, elem, maxLevDistance) {
+ let lev = maxLevDistance + 1;
for (const entry of row.generics) {
- lev = Math.min(checkType(entry, elem, true), lev);
+ lev = Math.min(checkType(entry, elem, true, maxLevDistance), lev);
if (lev === 0) {
break;
}
* @param {boolean} literalSearch
*
* @return {integer} - Returns a Levenshtein distance to the best match. If there is
- * no match, returns `MAX_LEV_DISTANCE + 1`.
+ * no match, returns `maxLevDistance + 1`.
*/
- function checkType(row, elem, literalSearch) {
+ function checkType(row, elem, literalSearch, maxLevDistance) {
if (row.name === null) {
// This is a pure "generic" search, no need to run other checks.
if (row.generics.length > 0) {
- return checkIfInGenerics(row, elem);
+ return checkIfInGenerics(row, elem, maxLevDistance);
}
- return MAX_LEV_DISTANCE + 1;
+ return maxLevDistance + 1;
}
let lev = levenshtein(row.name, elem.name);
return 0;
}
}
- return MAX_LEV_DISTANCE + 1;
+ return maxLevDistance + 1;
} else if (elem.generics.length > 0) {
- return checkGenerics(row, elem, MAX_LEV_DISTANCE + 1);
+ return checkGenerics(row, elem, maxLevDistance + 1, maxLevDistance);
}
return 0;
} else if (row.generics.length > 0) {
}
// The name didn't match so we now check if the type we're looking for is inside
// the generics!
- lev = checkIfInGenerics(row, elem);
- // Now whatever happens, the returned distance is "less good" so we should mark
- // it as such, and so we add 0.5 to the distance to make it "less good".
- return lev + 0.5;
- } else if (lev > MAX_LEV_DISTANCE) {
+ lev = Math.min(lev, checkIfInGenerics(row, elem, maxLevDistance));
+ return lev;
+ } else if (lev > maxLevDistance) {
// So our item's name doesn't match at all and has generics.
//
// Maybe it's present in a sub generic? For example "f<A<B<C>>>()", if we're
// looking for "B<C>", we'll need to go down.
- return checkIfInGenerics(row, elem);
+ return checkIfInGenerics(row, elem, maxLevDistance);
} else {
// At this point, the name kinda match and we have generics to check, so
// let's go!
- const tmp_lev = checkGenerics(row, elem, lev);
- if (tmp_lev > MAX_LEV_DISTANCE) {
- return MAX_LEV_DISTANCE + 1;
+ const tmp_lev = checkGenerics(row, elem, lev, maxLevDistance);
+ if (tmp_lev > maxLevDistance) {
+ return maxLevDistance + 1;
}
// We compute the median value of both checks and return it.
return (tmp_lev + lev) / 2;
} else if (elem.generics.length > 0) {
// In this case, we were expecting generics but there isn't so we simply reject this
// one.
- return MAX_LEV_DISTANCE + 1;
+ return maxLevDistance + 1;
}
// No generics on our query or on the target type so we can return without doing
// anything else.
* @param {integer} typeFilter
*
* @return {integer} - Returns a Levenshtein distance to the best match. If there is no
- * match, returns `MAX_LEV_DISTANCE + 1`.
+ * match, returns `maxLevDistance + 1`.
*/
- function findArg(row, elem, typeFilter) {
- let lev = MAX_LEV_DISTANCE + 1;
+ function findArg(row, elem, typeFilter, maxLevDistance) {
+ let lev = maxLevDistance + 1;
if (row && row.type && row.type.inputs && row.type.inputs.length > 0) {
for (const input of row.type.inputs) {
if (!typePassesFilter(typeFilter, input.ty)) {
continue;
}
- lev = Math.min(lev, checkType(input, elem, parsedQuery.literalSearch));
+ lev = Math.min(
+ lev,
+ checkType(input, elem, parsedQuery.literalSearch, maxLevDistance)
+ );
if (lev === 0) {
return 0;
}
}
}
- return parsedQuery.literalSearch ? MAX_LEV_DISTANCE + 1 : lev;
+ return parsedQuery.literalSearch ? maxLevDistance + 1 : lev;
}
/**
* @param {integer} typeFilter
*
* @return {integer} - Returns a Levenshtein distance to the best match. If there is no
- * match, returns `MAX_LEV_DISTANCE + 1`.
+ * match, returns `maxLevDistance + 1`.
*/
- function checkReturned(row, elem, typeFilter) {
- let lev = MAX_LEV_DISTANCE + 1;
+ function checkReturned(row, elem, typeFilter, maxLevDistance) {
+ let lev = maxLevDistance + 1;
if (row && row.type && row.type.output.length > 0) {
const ret = row.type.output;
if (!typePassesFilter(typeFilter, ret_ty.ty)) {
continue;
}
- lev = Math.min(lev, checkType(ret_ty, elem, parsedQuery.literalSearch));
+ lev = Math.min(
+ lev,
+ checkType(ret_ty, elem, parsedQuery.literalSearch, maxLevDistance)
+ );
if (lev === 0) {
return 0;
}
}
}
- return parsedQuery.literalSearch ? MAX_LEV_DISTANCE + 1 : lev;
+ return parsedQuery.literalSearch ? maxLevDistance + 1 : lev;
}
- function checkPath(contains, ty) {
+ function checkPath(contains, ty, maxLevDistance) {
if (contains.length === 0) {
return 0;
}
- let ret_lev = MAX_LEV_DISTANCE + 1;
+ let ret_lev = maxLevDistance + 1;
const path = ty.path.split("::");
if (ty.parent && ty.parent.name) {
const length = path.length;
const clength = contains.length;
if (clength > length) {
- return MAX_LEV_DISTANCE + 1;
+ return maxLevDistance + 1;
}
for (let i = 0; i < length; ++i) {
if (i + clength > length) {
let aborted = false;
for (let x = 0; x < clength; ++x) {
const lev = levenshtein(path[i + x], contains[x]);
- if (lev > MAX_LEV_DISTANCE) {
+ if (lev > maxLevDistance) {
aborted = true;
break;
}
* following condition:
*
* * If it is a "literal search" (`parsedQuery.literalSearch`), then `lev` must be 0.
- * * If it is not a "literal search", `lev` must be <= `MAX_LEV_DISTANCE`.
+ * * If it is not a "literal search", `lev` must be <= `maxLevDistance`.
*
* The `results` map contains information which will be used to sort the search results:
*
* @param {integer} lev
* @param {integer} path_lev
*/
- function addIntoResults(results, fullId, id, index, lev, path_lev) {
- const inBounds = lev <= MAX_LEV_DISTANCE || index !== -1;
+ function addIntoResults(results, fullId, id, index, lev, path_lev, maxLevDistance) {
+ const inBounds = lev <= maxLevDistance || index !== -1;
if (lev === 0 || (!parsedQuery.literalSearch && inBounds)) {
if (results[fullId] !== undefined) {
const result = results[fullId];
elem,
results_others,
results_in_args,
- results_returned
+ results_returned,
+ maxLevDistance
) {
if (!row || (filterCrates !== null && row.crate !== filterCrates)) {
return;
const fullId = row.id;
const searchWord = searchWords[pos];
- const in_args = findArg(row, elem, parsedQuery.typeFilter);
- const returned = checkReturned(row, elem, parsedQuery.typeFilter);
+ const in_args = findArg(row, elem, parsedQuery.typeFilter, maxLevDistance);
+ const returned = checkReturned(row, elem, parsedQuery.typeFilter, maxLevDistance);
// path_lev is 0 because no parent path information is currently stored
// in the search index
- addIntoResults(results_in_args, fullId, pos, -1, in_args, 0);
- addIntoResults(results_returned, fullId, pos, -1, returned, 0);
+ addIntoResults(results_in_args, fullId, pos, -1, in_args, 0, maxLevDistance);
+ addIntoResults(results_returned, fullId, pos, -1, returned, 0, maxLevDistance);
if (!typePassesFilter(parsedQuery.typeFilter, row.ty)) {
return;
// No need to check anything else if it's a "pure" generics search.
if (elem.name.length === 0) {
if (row.type !== null) {
- lev = checkGenerics(row.type, elem, MAX_LEV_DISTANCE + 1);
+ lev = checkGenerics(row.type, elem, maxLevDistance + 1, maxLevDistance);
// path_lev is 0 because we know it's empty
- addIntoResults(results_others, fullId, pos, index, lev, 0);
+ addIntoResults(results_others, fullId, pos, index, lev, 0, maxLevDistance);
}
return;
}
if (elem.fullPath.length > 1) {
- path_lev = checkPath(elem.pathWithoutLast, row);
- if (path_lev > MAX_LEV_DISTANCE) {
+ path_lev = checkPath(elem.pathWithoutLast, row, maxLevDistance);
+ if (path_lev > maxLevDistance) {
return;
}
}
lev = levenshtein(searchWord, elem.pathLast);
- if (index === -1 && lev + path_lev > MAX_LEV_DISTANCE) {
+ if (index === -1 && lev + path_lev > maxLevDistance) {
return;
}
- addIntoResults(results_others, fullId, pos, index, lev, path_lev);
+ addIntoResults(results_others, fullId, pos, index, lev, path_lev, maxLevDistance);
}
/**
* @param {integer} pos - Position in the `searchIndex`.
* @param {Object} results
*/
- function handleArgs(row, pos, results) {
+ function handleArgs(row, pos, results, maxLevDistance) {
if (!row || (filterCrates !== null && row.crate !== filterCrates)) {
return;
}
function checkArgs(elems, callback) {
for (const elem of elems) {
// There is more than one parameter to the query so all checks should be "exact"
- const lev = callback(row, elem, NO_TYPE_FILTER);
+ const lev = callback(row, elem, NO_TYPE_FILTER, maxLevDistance);
if (lev <= 1) {
nbLev += 1;
totalLev += lev;
return;
}
const lev = Math.round(totalLev / nbLev);
- addIntoResults(results, row.id, pos, 0, lev, 0);
+ addIntoResults(results, row.id, pos, 0, lev, 0, maxLevDistance);
}
function innerRunQuery() {
let elem, i, nSearchWords, in_returned, row;
+ let queryLen = 0;
+ for (const elem of parsedQuery.elems) {
+ queryLen += elem.name.length;
+ }
+ for (const elem of parsedQuery.returned) {
+ queryLen += elem.name.length;
+ }
+ const maxLevDistance = Math.floor(queryLen / 3);
+
if (parsedQuery.foundElems === 1) {
if (parsedQuery.elems.length === 1) {
elem = parsedQuery.elems[0];
elem,
results_others,
results_in_args,
- results_returned
+ results_returned,
+ maxLevDistance
);
}
} else if (parsedQuery.returned.length === 1) {
elem = parsedQuery.returned[0];
for (i = 0, nSearchWords = searchWords.length; i < nSearchWords; ++i) {
row = searchIndex[i];
- in_returned = checkReturned(row, elem, parsedQuery.typeFilter);
- addIntoResults(results_others, row.id, i, -1, in_returned);
+ in_returned = checkReturned(
+ row,
+ elem,
+ parsedQuery.typeFilter,
+ maxLevDistance
+ );
+ addIntoResults(results_others, row.id, i, -1, in_returned, maxLevDistance);
}
}
} else if (parsedQuery.foundElems > 0) {
for (i = 0, nSearchWords = searchWords.length; i < nSearchWords; ++i) {
- handleArgs(searchIndex[i], i, results_others);
+ handleArgs(searchIndex[i], i, results_others, maxLevDistance);
}
}
}
*
* @return {boolean} - Whether the result is valid or not
*/
- function validateResult(name, path, keys, parent) {
+ function validateResult(name, path, keys, parent, maxLevDistance) {
if (!keys || !keys.length) {
return true;
}
(parent !== undefined && parent.name !== undefined &&
parent.name.toLowerCase().indexOf(key) > -1) ||
// lastly check to see if the name was a levenshtein match
- levenshtein(name, key) <= MAX_LEV_DISTANCE)) {
+ levenshtein(name, key) <= maxLevDistance)) {
return false;
}
}
scrape_examples_js => "static/js/scrape-examples.js",
wheel_svg => "static/images/wheel.svg",
clipboard_svg => "static/images/clipboard.svg",
- down_arrow_svg => "static/images/down-arrow.svg",
- toggle_minus_png => "static/images/toggle-minus.svg",
- toggle_plus_png => "static/images/toggle-plus.svg",
copyright => "static/COPYRIGHT.txt",
license_apache => "static/LICENSE-APACHE.txt",
license_mit => "static/LICENSE-MIT.txt",
let mut ids = IdMap::new();
let error_codes = ErrorCodes::from(options.unstable_features.is_nightly_build());
let text = if !options.markdown_no_toc {
- MarkdownWithToc(text, &mut ids, error_codes, edition, &playground).into_string()
+ MarkdownWithToc {
+ content: text,
+ ids: &mut ids,
+ error_codes,
+ edition,
+ playground: &playground,
+ }
+ .into_string()
} else {
Markdown {
content: text,
//! Strip all doc(hidden) items from the output.
+
+use rustc_middle::ty::TyCtxt;
use rustc_span::symbol::sym;
use std::mem;
use crate::core::DocContext;
use crate::fold::{strip_item, DocFolder};
use crate::passes::{ImplStripper, Pass};
+use crate::visit_ast::inherits_doc_hidden;
pub(crate) const STRIP_HIDDEN: Pass = Pass {
name: "strip-hidden",
// strip all #[doc(hidden)] items
let krate = {
- let mut stripper = Stripper { retained: &mut retained, update_retained: true };
+ let mut stripper = Stripper {
+ retained: &mut retained,
+ update_retained: true,
+ tcx: cx.tcx,
+ is_in_hidden_item: false,
+ };
stripper.fold_crate(krate)
};
stripper.fold_crate(krate)
}
-struct Stripper<'a> {
+struct Stripper<'a, 'tcx> {
retained: &'a mut ItemIdSet,
update_retained: bool,
+ tcx: TyCtxt<'tcx>,
+ is_in_hidden_item: bool,
+}
+
+impl<'a, 'tcx> Stripper<'a, 'tcx> {
+ fn set_is_in_hidden_item_and_fold(&mut self, is_in_hidden_item: bool, i: Item) -> Item {
+ let prev = self.is_in_hidden_item;
+ self.is_in_hidden_item |= is_in_hidden_item;
+ let ret = self.fold_item_recur(i);
+ self.is_in_hidden_item = prev;
+ ret
+ }
+
+ /// In case `i` is a non-hidden impl block, then we special-case it by changing the value
+ /// of `is_in_hidden_item` to `true` because the impl children inherit its visibility.
+ fn recurse_in_impl(&mut self, i: Item) -> Item {
+ let prev = mem::replace(&mut self.is_in_hidden_item, false);
+ let ret = self.fold_item_recur(i);
+ self.is_in_hidden_item = prev;
+ ret
+ }
}
-impl<'a> DocFolder for Stripper<'a> {
+impl<'a, 'tcx> DocFolder for Stripper<'a, 'tcx> {
fn fold_item(&mut self, i: Item) -> Option<Item> {
- if i.attrs.lists(sym::doc).has_word(sym::hidden) {
- debug!("strip_hidden: stripping {:?} {:?}", i.type_(), i.name);
- // Use a dedicated hidden item for fields, variants, and modules.
- // We need to keep private fields and variants, so that the docs
- // can show a placeholder "// some variants omitted". We need to keep
- // private modules, because they can contain impl blocks, and impl
- // block privacy is inherited from the type and trait, not from the
- // module it's defined in. Both of these are marked "stripped," and
- // not included in the final docs, but since they still have an effect
- // on the final doc, cannot be completely removed from the Clean IR.
- match *i.kind {
- clean::StructFieldItem(..) | clean::ModuleItem(..) | clean::VariantItem(..) => {
- // We need to recurse into stripped modules to
- // strip things like impl methods but when doing so
- // we must not add any items to the `retained` set.
- let old = mem::replace(&mut self.update_retained, false);
- let ret = strip_item(self.fold_item_recur(i));
- self.update_retained = old;
- return Some(ret);
- }
- _ => return None,
+ let has_doc_hidden = i.attrs.lists(sym::doc).has_word(sym::hidden);
+ let is_impl = matches!(*i.kind, clean::ImplItem(..));
+ let mut is_hidden = has_doc_hidden;
+ if !is_impl {
+ is_hidden = self.is_in_hidden_item || has_doc_hidden;
+ if !is_hidden && i.inline_stmt_id.is_none() {
+ // We don't need to check if it's coming from a reexport since the reexport itself was
+ // already checked.
+ is_hidden = i
+ .item_id
+ .as_def_id()
+ .and_then(|def_id| def_id.as_local())
+ .map(|def_id| inherits_doc_hidden(self.tcx, def_id))
+ .unwrap_or(false);
}
- } else {
+ }
+ if !is_hidden {
if self.update_retained {
self.retained.insert(i.item_id);
}
+ return Some(if is_impl {
+ self.recurse_in_impl(i)
+ } else {
+ self.set_is_in_hidden_item_and_fold(false, i)
+ });
+ }
+ debug!("strip_hidden: stripping {:?} {:?}", i.type_(), i.name);
+ // Use a dedicated hidden item for fields, variants, and modules.
+ // We need to keep private fields and variants, so that the docs
+ // can show a placeholder "// some variants omitted". We need to keep
+ // private modules, because they can contain impl blocks, and impl
+ // block privacy is inherited from the type and trait, not from the
+ // module it's defined in. Both of these are marked "stripped," and
+ // not included in the final docs, but since they still have an effect
+ // on the final doc, cannot be completely removed from the Clean IR.
+ match *i.kind {
+ clean::StructFieldItem(..) | clean::ModuleItem(..) | clean::VariantItem(..) => {
+ // We need to recurse into stripped modules to
+ // strip things like impl methods but when doing so
+ // we must not add any items to the `retained` set.
+ let old = mem::replace(&mut self.update_retained, false);
+ let ret = strip_item(self.set_is_in_hidden_item_and_fold(true, i));
+ self.update_retained = old;
+ Some(ret)
+ }
+ _ => {
+ let ret = self.set_is_in_hidden_item_and_fold(true, i);
+ if has_doc_hidden {
+ // If the item itself has `#[doc(hidden)]`, then we simply remove it.
+ None
+ } else {
+ // However if it's a "descendant" of a `#[doc(hidden)]` item, then we strip it.
+ Some(strip_item(ret))
+ }
+ }
}
- Some(self.fold_item_recur(i))
}
}
use rustc_hir as hir;
use rustc_hir::def::{DefKind, Res};
use rustc_hir::def_id::{DefId, DefIdMap, LocalDefId, LocalDefIdSet};
+use rustc_hir::intravisit::{walk_item, Visitor};
use rustc_hir::{Node, CRATE_HIR_ID};
+use rustc_middle::hir::nested_filter;
use rustc_middle::ty::{DefIdTree, TyCtxt};
use rustc_span::def_id::{CRATE_DEF_ID, LOCAL_CRATE};
use rustc_span::symbol::{kw, sym, Symbol};
std::iter::once(crate_name).chain(relative).collect()
}
-pub(crate) fn inherits_doc_hidden(tcx: TyCtxt<'_>, mut node: LocalDefId) -> bool {
- while let Some(id) = tcx.opt_local_parent(node) {
- node = id;
- if tcx.is_doc_hidden(node.to_def_id()) {
+pub(crate) fn inherits_doc_hidden(tcx: TyCtxt<'_>, mut def_id: LocalDefId) -> bool {
+ let hir = tcx.hir();
+ while let Some(id) = tcx.opt_local_parent(def_id) {
+ def_id = id;
+ if tcx.is_doc_hidden(def_id.to_def_id()) {
return true;
+ } else if let Some(node) = hir.find_by_def_id(def_id) &&
+ matches!(
+ node,
+ hir::Node::Item(hir::Item { kind: hir::ItemKind::Impl(_), .. }),
+ )
+ {
+ // `impl` blocks stand a bit on their own: unless they have `#[doc(hidden)]` directly
+ // on them, they don't inherit it from the parent context.
+ return false;
}
}
false
}
-// Also, is there some reason that this doesn't use the 'visit'
-// framework from syntax?.
-
pub(crate) struct RustdocVisitor<'a, 'tcx> {
cx: &'a mut core::DocContext<'tcx>,
view_item_stack: LocalDefIdSet,
/// Are the current module and all of its parents public?
inside_public_path: bool,
exact_paths: DefIdMap<Vec<Symbol>>,
+ modules: Vec<Module<'tcx>>,
}
impl<'a, 'tcx> RustdocVisitor<'a, 'tcx> {
// If the root is re-exported, terminate all recursion.
let mut stack = LocalDefIdSet::default();
stack.insert(CRATE_DEF_ID);
+ let om = Module::new(
+ cx.tcx.crate_name(LOCAL_CRATE),
+ CRATE_DEF_ID,
+ cx.tcx.hir().root_module().spans.inner_span,
+ );
+
RustdocVisitor {
cx,
view_item_stack: stack,
inlining: false,
inside_public_path: true,
exact_paths: Default::default(),
+ modules: vec![om],
}
}
}
pub(crate) fn visit(mut self) -> Module<'tcx> {
- let mut top_level_module = self.visit_mod_contents(
- CRATE_DEF_ID,
- self.cx.tcx.hir().root_module(),
- self.cx.tcx.crate_name(LOCAL_CRATE),
- None,
- );
+ let root_module = self.cx.tcx.hir().root_module();
+ self.visit_mod_contents(CRATE_DEF_ID, root_module);
+
+ let mut top_level_module = self.modules.pop().unwrap();
// `#[macro_export] macro_rules!` items are reexported at the top level of the
// crate, regardless of where they're defined. We want to document the
// macro in the same module.
let mut inserted = FxHashSet::default();
for export in self.cx.tcx.module_reexports(CRATE_DEF_ID).unwrap_or(&[]) {
- if let Res::Def(DefKind::Macro(_), def_id) = export.res {
- if let Some(local_def_id) = def_id.as_local() {
- if self.cx.tcx.has_attr(def_id, sym::macro_export) {
- if inserted.insert(def_id) {
- let item = self.cx.tcx.hir().expect_item(local_def_id);
- top_level_module.items.push((item, None, None));
- }
- }
- }
+ if let Res::Def(DefKind::Macro(_), def_id) = export.res &&
+ let Some(local_def_id) = def_id.as_local() &&
+ self.cx.tcx.has_attr(def_id, sym::macro_export) &&
+ inserted.insert(def_id)
+ {
+ let item = self.cx.tcx.hir().expect_item(local_def_id);
+ top_level_module.items.push((item, None, None));
}
}
top_level_module
}
- fn visit_mod_contents(
- &mut self,
- def_id: LocalDefId,
- m: &'tcx hir::Mod<'tcx>,
- name: Symbol,
- parent_id: Option<LocalDefId>,
- ) -> Module<'tcx> {
- let mut om = Module::new(name, def_id, m.spans.inner_span);
+ /// This method will go through the given module items in two passes:
+ /// 1. The items which are not glob imports/reexports.
+ /// 2. The glob imports/reexports.
+ fn visit_mod_contents(&mut self, def_id: LocalDefId, m: &'tcx hir::Mod<'tcx>) {
+ debug!("Going through module {:?}", m);
// Keep track of if there were any private modules in the path.
let orig_inside_public_path = self.inside_public_path;
self.inside_public_path &= self.cx.tcx.local_visibility(def_id).is_public();
+
+ // Reimplementation of `walk_mod` because we need to do it in two passes (explanations in
+ // the second loop):
for &i in m.item_ids {
let item = self.cx.tcx.hir().item(i);
- if matches!(item.kind, hir::ItemKind::Use(_, hir::UseKind::Glob)) {
- continue;
+ if !matches!(item.kind, hir::ItemKind::Use(_, hir::UseKind::Glob)) {
+ self.visit_item(item);
}
- self.visit_item(item, None, &mut om, parent_id);
}
for &i in m.item_ids {
let item = self.cx.tcx.hir().item(i);
// Later passes in rustdoc will de-duplicate by name and kind, so if glob-
// imported items appear last, then they'll be the ones that get discarded.
if matches!(item.kind, hir::ItemKind::Use(_, hir::UseKind::Glob)) {
- self.visit_item(item, None, &mut om, parent_id);
+ self.visit_item(item);
}
}
self.inside_public_path = orig_inside_public_path;
- om
+ debug!("Leaving module {:?}", m);
}
/// Tries to resolve the target of a `pub use` statement and inlines the
res: Res,
renamed: Option<Symbol>,
glob: bool,
- om: &mut Module<'tcx>,
please_inline: bool,
) -> bool {
debug!("maybe_inline_local res: {:?}", res);
}
let tcx = self.cx.tcx;
- let Some(res_did) = res.opt_def_id() else {
+ let Some(ori_res_did) = res.opt_def_id() else {
return false;
};
let use_attrs = tcx.hir().attrs(tcx.hir().local_def_id_to_hir_id(def_id));
// Don't inline `doc(hidden)` imports so they can be stripped at a later stage.
let is_no_inline = use_attrs.lists(sym::doc).has_word(sym::no_inline)
- || tcx.is_doc_hidden(def_id.to_def_id());
+ || use_attrs.lists(sym::doc).has_word(sym::hidden);
// For cross-crate impl inlining we need to know whether items are
// reachable in documentation -- a previously unreachable item can be
// made reachable by cross-crate inlining which we're checking here.
// (this is done here because we need to know this upfront).
- if !res_did.is_local() && !is_no_inline {
- crate::visit_lib::lib_embargo_visit_item(self.cx, res_did);
+ if !ori_res_did.is_local() && !is_no_inline {
+ crate::visit_lib::lib_embargo_visit_item(self.cx, ori_res_did);
return false;
}
- let Some(res_did) = res_did.as_local() else {
+ let Some(res_did) = ori_res_did.as_local() else {
return false;
};
- let is_private = !self
- .cx
- .cache
- .effective_visibilities
- .is_directly_public(self.cx.tcx, res_did.to_def_id());
+ let is_private =
+ !self.cx.cache.effective_visibilities.is_directly_public(self.cx.tcx, ori_res_did);
let is_hidden = inherits_doc_hidden(self.cx.tcx, res_did);
// Only inline if requested or if the item would otherwise be stripped.
let prev = mem::replace(&mut self.inlining, true);
for &i in m.item_ids {
let i = self.cx.tcx.hir().item(i);
- self.visit_item(i, None, om, Some(def_id));
+ self.visit_item_inner(i, None, Some(def_id));
}
self.inlining = prev;
true
}
Node::Item(it) if !glob => {
let prev = mem::replace(&mut self.inlining, true);
- self.visit_item(it, renamed, om, Some(def_id));
+ self.visit_item_inner(it, renamed, Some(def_id));
self.inlining = prev;
true
}
Node::ForeignItem(it) if !glob => {
let prev = mem::replace(&mut self.inlining, true);
- self.visit_foreign_item(it, renamed, om);
+ self.visit_foreign_item_inner(it, renamed);
self.inlining = prev;
true
}
ret
}
- fn visit_item(
+ #[inline]
+ fn add_to_current_mod(
&mut self,
item: &'tcx hir::Item<'_>,
renamed: Option<Symbol>,
- om: &mut Module<'tcx>,
parent_id: Option<LocalDefId>,
) {
+ self.modules.last_mut().unwrap().items.push((item, renamed, parent_id))
+ }
+
+ fn visit_item_inner(
+ &mut self,
+ item: &'tcx hir::Item<'_>,
+ renamed: Option<Symbol>,
+ import_id: Option<LocalDefId>,
+ ) -> bool {
debug!("visiting item {:?}", item);
let name = renamed.unwrap_or(item.ident.name);
+ let tcx = self.cx.tcx;
let def_id = item.owner_id.to_def_id();
- let is_pub = self.cx.tcx.visibility(def_id).is_public();
+ let is_pub = tcx.visibility(def_id).is_public();
if is_pub {
self.store_path(item.owner_id.to_def_id());
match item.kind {
hir::ItemKind::ForeignMod { items, .. } => {
for item in items {
- let item = self.cx.tcx.hir().foreign_item(item.id);
- self.visit_foreign_item(item, None, om);
+ let item = tcx.hir().foreign_item(item.id);
+ self.visit_foreign_item_inner(item, None);
}
}
// If we're inlining, skip private items or item reexported as "_".
continue;
}
- let attrs = self.cx.tcx.hir().attrs(item.hir_id());
+ let attrs =
+ tcx.hir().attrs(tcx.hir().local_def_id_to_hir_id(item.owner_id.def_id));
// If there was a private module in the current path then don't bother inlining
// anything as it will probably be stripped anyway.
res,
ident,
is_glob,
- om,
please_inline,
) {
continue;
}
}
- om.items.push((item, renamed, parent_id))
+ self.add_to_current_mod(item, renamed, import_id);
}
}
hir::ItemKind::Macro(ref macro_def, _) => {
let def_id = item.owner_id.to_def_id();
let is_macro_2_0 = !macro_def.macro_rules;
- let nonexported = !self.cx.tcx.has_attr(def_id, sym::macro_export);
+ let nonexported = !tcx.has_attr(def_id, sym::macro_export);
if is_macro_2_0 || nonexported || self.inlining {
- om.items.push((item, renamed, None));
+ self.add_to_current_mod(item, renamed, None);
}
}
hir::ItemKind::Mod(ref m) => {
- om.mods.push(self.visit_mod_contents(item.owner_id.def_id, m, name, parent_id));
+ self.enter_mod(item.owner_id.def_id, m, name);
}
hir::ItemKind::Fn(..)
| hir::ItemKind::ExternCrate(..)
| hir::ItemKind::OpaqueTy(..)
| hir::ItemKind::Static(..)
| hir::ItemKind::Trait(..)
- | hir::ItemKind::TraitAlias(..) => om.items.push((item, renamed, parent_id)),
+ | hir::ItemKind::TraitAlias(..) => {
+ self.add_to_current_mod(item, renamed, import_id);
+ }
hir::ItemKind::Const(..) => {
// Underscore constants do not correspond to a nameable item and
// so are never useful in documentation.
if name != kw::Underscore {
- om.items.push((item, renamed, parent_id));
+ self.add_to_current_mod(item, renamed, import_id);
}
}
hir::ItemKind::Impl(impl_) => {
// Don't duplicate impls when inlining or if it's implementing a trait, we'll pick
// them up regardless of where they're located.
if !self.inlining && impl_.of_trait.is_none() {
- om.items.push((item, None, None));
+ self.add_to_current_mod(item, None, None);
}
}
}
+ true
}
- fn visit_foreign_item(
+ fn visit_foreign_item_inner(
&mut self,
item: &'tcx hir::ForeignItem<'_>,
renamed: Option<Symbol>,
- om: &mut Module<'tcx>,
) {
// If inlining we only want to include public functions.
if !self.inlining || self.cx.tcx.visibility(item.owner_id).is_public() {
- om.foreigns.push((item, renamed));
+ self.modules.last_mut().unwrap().foreigns.push((item, renamed));
+ }
+ }
+
+ /// This method will create a new module and push it onto the "modules stack" then call
+ /// `visit_mod_contents`. Once done, it'll remove it from the "modules stack" and instead
+ /// add into the list of modules of the current module.
+ fn enter_mod(&mut self, id: LocalDefId, m: &'tcx hir::Mod<'tcx>, name: Symbol) {
+ self.modules.push(Module::new(name, id, m.spans.inner_span));
+
+ self.visit_mod_contents(id, m);
+
+ let last = self.modules.pop().unwrap();
+ self.modules.last_mut().unwrap().mods.push(last);
+ }
+}
+
+// We need to implement this visitor so it'll go everywhere and retrieve items we're interested in
+// such as impl blocks in const blocks.
+impl<'a, 'tcx> Visitor<'tcx> for RustdocVisitor<'a, 'tcx> {
+ type NestedFilter = nested_filter::All;
+
+ fn nested_visit_map(&mut self) -> Self::Map {
+ self.cx.tcx.hir()
+ }
+
+ fn visit_item(&mut self, i: &'tcx hir::Item<'tcx>) {
+ if self.visit_item_inner(i, None, None) {
+ walk_item(self, i);
}
}
+
+ fn visit_mod(&mut self, _: &hir::Mod<'tcx>, _: Span, _: hir::HirId) {
+ // Handled in `visit_item_inner`
+ }
+
+ fn visit_use(&mut self, _: &hir::UsePath<'tcx>, _: hir::HirId) {
+ // Handled in `visit_item_inner`
+ }
+
+ fn visit_path(&mut self, _: &hir::Path<'tcx>, _: hir::HirId) {
+ // Handled in `visit_item_inner`
+ }
+
+ fn visit_label(&mut self, _: &rustc_ast::Label) {
+ // Unneeded.
+ }
+
+ fn visit_infer(&mut self, _: &hir::InferArg) {
+ // Unneeded.
+ }
+
+ fn visit_lifetime(&mut self, _: &hir::Lifetime) {
+ // Unneeded.
+ }
}
["generate", ref base] => (Mode::Generate, PathBuf::from(base)),
["check", ref base] => (Mode::Check, PathBuf::from(base)),
_ => {
- eprintln!("usage: expand-yaml-anchors <source-dir> <dest-dir>");
+ eprintln!("usage: expand-yaml-anchors <generate|check> <base-dir>");
std::process::exit(1);
}
};
protector: None,
}
} else if pointee.is_unpin(*cx.tcx, cx.param_env()) {
- // A regular full mutable reference.
+ // A regular full mutable reference. On `FnEntry` this is `noalias` and `dereferenceable`.
NewPermission::Uniform {
perm: Permission::Unique,
access: Some(AccessKind::Write),
protector,
}
} else {
+ // `!Unpin` dereferences do not get `noalias` nor `dereferenceable`.
NewPermission::Uniform {
perm: Permission::SharedReadWrite,
- // FIXME: We emit `dereferenceable` for `!Unpin` mutable references, so we
- // should do fake accesses here. But then we run into
- // <https://github.com/rust-lang/unsafe-code-guidelines/issues/381>, so for now
- // we don't do that.
access: None,
- protector,
+ protector: None,
}
}
}
}
}
ty::Ref(_, _pointee, Mutability::Not) => {
+ // Shared references. If frozen, these get `noalias` and `dereferenceable`; otherwise neither.
NewPermission::FreezeSensitive {
freeze_perm: Permission::SharedReadOnly,
freeze_access: Some(AccessKind::Read),
}
}
+ fn from_box_ty<'tcx>(
+ ty: Ty<'tcx>,
+ kind: RetagKind,
+ cx: &crate::MiriInterpCx<'_, 'tcx>,
+ ) -> Self {
+ // `ty` is not the `Box` but the field of the Box with this pointer (due to allocator handling).
+ let pointee = ty.builtin_deref(true).unwrap().ty;
+ if pointee.is_unpin(*cx.tcx, cx.param_env()) {
+ // A regular box. On `FnEntry` this is `noalias`, but not `dereferenceable` (hence only
+ // a weak protector).
+ NewPermission::Uniform {
+ perm: Permission::Unique,
+ access: Some(AccessKind::Write),
+ protector: (kind == RetagKind::FnEntry)
+ .then_some(ProtectorKind::WeakProtector),
+ }
+ } else {
+ // `!Unpin` boxes do not get `noalias` nor `dereferenceable`.
+ NewPermission::Uniform {
+ perm: Permission::SharedReadWrite,
+ access: None,
+ protector: None,
+ }
+ }
+ }
+
fn protector(&self) -> Option<ProtectorKind> {
match self {
NewPermission::Uniform { protector, .. } => *protector,
fn visit_box(&mut self, place: &PlaceTy<'tcx, Provenance>) -> InterpResult<'tcx> {
// Boxes get a weak protectors, since they may be deallocated.
- let new_perm = NewPermission::Uniform {
- perm: Permission::Unique,
- access: Some(AccessKind::Write),
- protector: (self.kind == RetagKind::FnEntry)
- .then_some(ProtectorKind::WeakProtector),
- };
+ let new_perm = NewPermission::from_box_ty(place.layout.ty, self.kind, self.ecx);
self.retag_ptr_inplace(place, new_perm, self.retag_cause)
}
+++ /dev/null
-//@error-pattern: /deallocating while item \[SharedReadWrite for .*\] is strongly protected/
-use std::marker::PhantomPinned;
-
-pub struct NotUnpin(i32, PhantomPinned);
-
-fn inner(x: &mut NotUnpin, f: fn(&mut NotUnpin)) {
- // `f` may mutate, but it may not deallocate!
- f(x)
-}
-
-fn main() {
- inner(Box::leak(Box::new(NotUnpin(0, PhantomPinned))), |x| {
- let raw = x as *mut _;
- drop(unsafe { Box::from_raw(raw) });
- });
-}
+++ /dev/null
-error: Undefined Behavior: deallocating while item [SharedReadWrite for <TAG>] is strongly protected by call ID
- --> RUSTLIB/alloc/src/alloc.rs:LL:CC
- |
-LL | unsafe { __rust_dealloc(ptr, layout.size(), layout.align()) }
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ deallocating while item [SharedReadWrite for <TAG>] is strongly protected by call ID
- |
- = help: this indicates a potential bug in the program: it performed an invalid operation, but the Stacked Borrows rules it violated are still experimental
- = help: see https://github.com/rust-lang/unsafe-code-guidelines/blob/master/wip/stacked-borrows.md for further information
- = note: BACKTRACE:
- = note: inside `std::alloc::dealloc` at RUSTLIB/alloc/src/alloc.rs:LL:CC
- = note: inside `<std::alloc::Global as std::alloc::Allocator>::deallocate` at RUSTLIB/alloc/src/alloc.rs:LL:CC
- = note: inside `alloc::alloc::box_free::<NotUnpin, std::alloc::Global>` at RUSTLIB/alloc/src/alloc.rs:LL:CC
- = note: inside `std::ptr::drop_in_place::<std::boxed::Box<NotUnpin>> - shim(Some(std::boxed::Box<NotUnpin>))` at RUSTLIB/core/src/ptr/mod.rs:LL:CC
- = note: inside `std::mem::drop::<std::boxed::Box<NotUnpin>>` at RUSTLIB/core/src/mem/mod.rs:LL:CC
-note: inside closure
- --> $DIR/deallocate_against_protector2.rs:LL:CC
- |
-LL | drop(unsafe { Box::from_raw(raw) });
- | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- = note: inside `<[closure@$DIR/deallocate_against_protector2.rs:LL:CC] as std::ops::FnOnce<(&mut NotUnpin,)>>::call_once - shim` at RUSTLIB/core/src/ops/function.rs:LL:CC
-note: inside `inner`
- --> $DIR/deallocate_against_protector2.rs:LL:CC
- |
-LL | f(x)
- | ^^^^
-note: inside `main`
- --> $DIR/deallocate_against_protector2.rs:LL:CC
- |
-LL | / inner(Box::leak(Box::new(NotUnpin(0, PhantomPinned))), |x| {
-LL | | let raw = x as *mut _;
-LL | | drop(unsafe { Box::from_raw(raw) });
-LL | | });
- | |______^
-
-note: some details are omitted, run with `MIRIFLAGS=-Zmiri-backtrace=full` for a verbose backtrace
-
-error: aborting due to previous error
-
}
}
+fn mk_waker() -> Waker {
+ use std::sync::Arc;
+
+ struct MyWaker;
+ impl Wake for MyWaker {
+ fn wake(self: Arc<Self>) {
+ unimplemented!()
+ }
+ }
+
+ Waker::from(Arc::new(MyWaker))
+}
+
async fn do_stuff() {
(&mut Delay::new(1)).await;
}
}
fn run_fut<T>(fut: impl Future<Output = T>) -> T {
- use std::sync::Arc;
-
- struct MyWaker;
- impl Wake for MyWaker {
- fn wake(self: Arc<Self>) {
- unimplemented!()
- }
- }
-
- let waker = Waker::from(Arc::new(MyWaker));
+ let waker = mk_waker();
let mut context = Context::from_waker(&waker);
let mut pinned = pin!(fut);
}
}
+fn self_referential_box() {
+ let waker = mk_waker();
+ let cx = &mut Context::from_waker(&waker);
+
+ async fn my_fut() -> i32 {
+ let val = 10;
+ let val_ref = &val;
+
+ let _ = Delay::new(1).await;
+
+ *val_ref
+ }
+
+ fn box_poll<F: Future>(
+ mut f: Pin<Box<F>>,
+ cx: &mut Context<'_>,
+ ) -> (Pin<Box<F>>, Poll<F::Output>) {
+ let p = f.as_mut().poll(cx);
+ (f, p)
+ }
+
+ let my_fut = Box::pin(my_fut());
+ let (my_fut, p1) = box_poll(my_fut, cx);
+ assert!(p1.is_pending());
+ let (my_fut, p2) = box_poll(my_fut, cx);
+ assert!(p2.is_ready());
+ drop(my_fut);
+}
+
fn main() {
run_fut(do_stuff());
run_fut(DoStuff::new());
+ self_referential_box();
}
array_casts();
mut_below_shr();
wide_raw_ptr_in_tuple();
+ not_unpin_not_protected();
}
// Make sure that reading from an `&mut` does, like reborrowing to `&`,
// Make sure the fn ptr part of the vtable is still fine.
r.type_id();
}
+
+fn not_unpin_not_protected() {
+ // `&mut !Unpin`, at least for now, does not get `noalias` nor `dereferenceable`, so we also
+ // don't add protectors. (We could, but until we have a better idea for where we want to go with
+ // the self-referntial-generator situation, it does not seem worth the potential trouble.)
+ use std::marker::PhantomPinned;
+
+ pub struct NotUnpin(i32, PhantomPinned);
+
+ fn inner(x: &mut NotUnpin, f: fn(&mut NotUnpin)) {
+ // `f` may mutate, but it may not deallocate!
+ f(x)
+ }
+
+ inner(Box::leak(Box::new(NotUnpin(0, PhantomPinned))), |x| {
+ let raw = x as *mut _;
+ drop(unsafe { Box::from_raw(raw) });
+ });
+}
use std::panic::{catch_unwind, AssertUnwindSafe};
use rustc_ast::token::{BinOpToken, Delimiter, Token, TokenKind};
-use rustc_ast::tokenstream::{Cursor, TokenStream, TokenTree};
+use rustc_ast::tokenstream::{TokenStream, TokenTree, TokenTreeCursor};
use rustc_ast::{ast, ptr};
use rustc_ast_pretty::pprust;
use rustc_span::{
self.buf.clear();
}
- fn add_meta_variable(&mut self, iter: &mut Cursor) -> Option<()> {
+ fn add_meta_variable(&mut self, iter: &mut TokenTreeCursor) -> Option<()> {
match iter.next() {
Some(TokenTree::Token(
Token {
&mut self,
inner: Vec<ParsedMacroArg>,
delim: Delimiter,
- iter: &mut Cursor,
+ iter: &mut TokenTreeCursor,
) -> Option<()> {
let mut buffer = String::new();
let mut first = true;
// Currently we do not attempt to parse any further than that.
#[derive(new)]
struct MacroParser {
- toks: Cursor,
+ toks: TokenTreeCursor,
}
impl MacroParser {
x
}
+// CHECK: align 4 {{i32\*|ptr}} @borrow_mut({{i32\*|ptr}} align 4 %x)
+#[no_mangle]
+pub fn borrow_mut(x: &mut i32) -> &mut i32 {
+ x
+}
+
// CHECK-LABEL: @borrow_call
#[no_mangle]
pub fn borrow_call(x: &i32, f: fn(&i32) -> &i32) -> &i32 {
pub fn readonly_borrow(_: &i32) {
}
+// CHECK: noundef align 4 dereferenceable(4) {{i32\*|ptr}} @readonly_borrow_ret()
+#[no_mangle]
+pub fn readonly_borrow_ret() -> &'static i32 {
+ loop {}
+}
+
// CHECK: @static_borrow({{i32\*|ptr}} noalias noundef readonly align 4 dereferenceable(4) %_1)
// static borrow may be captured
#[no_mangle]
pub fn mutable_borrow(_: &mut i32) {
}
+// CHECK: noundef align 4 dereferenceable(4) {{i32\*|ptr}} @mutable_borrow_ret()
+#[no_mangle]
+pub fn mutable_borrow_ret() -> &'static mut i32 {
+ loop {}
+}
+
#[no_mangle]
-// CHECK: @mutable_notunpin_borrow({{i32\*|ptr}} noundef align 4 dereferenceable(4) %_1)
+// CHECK: @mutable_notunpin_borrow({{i32\*|ptr}} noundef nonnull align 4 %_1)
// This one is *not* `noalias` because it might be self-referential.
+// It is also not `dereferenceable` due to
+// <https://github.com/rust-lang/unsafe-code-guidelines/issues/381>.
pub fn mutable_notunpin_borrow(_: &mut NotUnpin) {
}
x
}
+// CHECK: noundef nonnull align 4 {{i32\*|ptr}} @notunpin_box({{i32\*|ptr}} noundef nonnull align 4 %x)
+#[no_mangle]
+pub fn notunpin_box(x: Box<NotUnpin>) -> Box<NotUnpin> {
+ x
+}
+
// CHECK: @struct_return({{%S\*|ptr}} noalias nocapture noundef sret(%S) dereferenceable(32){{( %0)?}})
#[no_mangle]
pub fn struct_return() -> S {
// CHECK: @trait_box({{\{\}\*|ptr}} noalias noundef nonnull align 1{{( %0)?}}, {{.+}} noalias noundef readonly align {{.*}} dereferenceable({{.*}}){{( %1)?}})
#[no_mangle]
-pub fn trait_box(_: Box<dyn Drop>) {
+pub fn trait_box(_: Box<dyn Drop + Unpin>) {
}
// CHECK: { {{i8\*|ptr}}, {{i8\*|ptr}} } @trait_option({{i8\*|ptr}} noalias noundef align 1 %x.0, {{i8\*|ptr}} %x.1)
#[no_mangle]
-pub fn trait_option(x: Option<Box<dyn Drop>>) -> Option<Box<dyn Drop>> {
+pub fn trait_option(x: Option<Box<dyn Drop + Unpin>>) -> Option<Box<dyn Drop + Unpin>> {
x
}
bb1: {
_4 = move _2; // scope 0 at $DIR/async_await.rs:+0:14: +0:16
_3 = const (); // scope 0 at $DIR/async_await.rs:+0:14: +0:16
- Deinit(_0); // scope 0 at $DIR/async_await.rs:+0:16: +0:16
- ((_0 as Ready).0: ()) = move _3; // scope 0 at $DIR/async_await.rs:+0:16: +0:16
- discriminant(_0) = 0; // scope 0 at $DIR/async_await.rs:+0:16: +0:16
+ _0 = Poll::<()>::Ready(move _3); // scope 0 at $DIR/async_await.rs:+0:16: +0:16
discriminant((*(_1.0: &mut [async fn body@$DIR/async_await.rs:11:14: 11:16]))) = 1; // scope 0 at $DIR/async_await.rs:+0:16: +0:16
return; // scope 0 at $DIR/async_await.rs:+0:16: +0:16
}
StorageLive(_19); // scope 1 at $DIR/async_await.rs:+1:8: +1:14
StorageLive(_20); // scope 1 at $DIR/async_await.rs:+1:8: +1:14
_20 = (); // scope 1 at $DIR/async_await.rs:+1:8: +1:14
- Deinit(_0); // scope 1 at $DIR/async_await.rs:+1:8: +1:14
- discriminant(_0) = 1; // scope 1 at $DIR/async_await.rs:+1:8: +1:14
+ _0 = Poll::<()>::Pending; // scope 1 at $DIR/async_await.rs:+1:8: +1:14
discriminant((*(_1.0: &mut [async fn body@$DIR/async_await.rs:14:18: 17:2]))) = 3; // scope 1 at $DIR/async_await.rs:+1:8: +1:14
return; // scope 1 at $DIR/async_await.rs:+1:8: +1:14
}
StorageLive(_35); // scope 4 at $DIR/async_await.rs:+2:8: +2:14
StorageLive(_36); // scope 4 at $DIR/async_await.rs:+2:8: +2:14
_36 = (); // scope 4 at $DIR/async_await.rs:+2:8: +2:14
- Deinit(_0); // scope 4 at $DIR/async_await.rs:+2:8: +2:14
- discriminant(_0) = 1; // scope 4 at $DIR/async_await.rs:+2:8: +2:14
+ _0 = Poll::<()>::Pending; // scope 4 at $DIR/async_await.rs:+2:8: +2:14
discriminant((*(_1.0: &mut [async fn body@$DIR/async_await.rs:14:18: 17:2]))) = 4; // scope 4 at $DIR/async_await.rs:+2:8: +2:14
return; // scope 4 at $DIR/async_await.rs:+2:8: +2:14
}
}
bb26: {
- Deinit(_0); // scope 0 at $DIR/async_await.rs:+3:2: +3:2
- ((_0 as Ready).0: ()) = move _37; // scope 0 at $DIR/async_await.rs:+3:2: +3:2
- discriminant(_0) = 0; // scope 0 at $DIR/async_await.rs:+3:2: +3:2
+ _0 = Poll::<()>::Ready(move _37); // scope 0 at $DIR/async_await.rs:+3:2: +3:2
discriminant((*(_1.0: &mut [async fn body@$DIR/async_await.rs:14:18: 17:2]))) = 1; // scope 0 at $DIR/async_await.rs:+3:2: +3:2
return; // scope 0 at $DIR/async_await.rs:+3:2: +3:2
}
bb3: {
StorageDead(_9); // scope 0 at $DIR/combine_clone_of_primitives.rs:10:15: 10:16
- Deinit(_0); // scope 0 at $DIR/combine_clone_of_primitives.rs:+0:10: +0:15
- (_0.0: T) = move _2; // scope 0 at $DIR/combine_clone_of_primitives.rs:+0:10: +0:15
- (_0.1: u64) = move _5; // scope 0 at $DIR/combine_clone_of_primitives.rs:+0:10: +0:15
- (_0.2: [f32; 3]) = move _8; // scope 0 at $DIR/combine_clone_of_primitives.rs:+0:10: +0:15
+ _0 = MyThing::<T> { v: move _2, i: move _5, a: move _8 }; // scope 0 at $DIR/combine_clone_of_primitives.rs:+0:10: +0:15
StorageDead(_8); // scope 0 at $DIR/combine_clone_of_primitives.rs:+0:14: +0:15
StorageDead(_5); // scope 0 at $DIR/combine_clone_of_primitives.rs:+0:14: +0:15
StorageDead(_2); // scope 0 at $DIR/combine_clone_of_primitives.rs:+0:14: +0:15
alloc18 (size: 48, align: 4) {
0x00 │ 00 00 00 00 __ __ __ __ ╾─alloc5──╼ 00 00 00 00 │ ....░░░░╾──╼....
- 0x10 │ 00 00 00 00 __ __ __ __ ╾─alloc9──╼ 02 00 00 00 │ ....░░░░╾──╼....
- 0x20 │ 01 00 00 00 2a 00 00 00 ╾─alloc14─╼ 03 00 00 00 │ ....*...╾──╼....
+ 0x10 │ 00 00 00 00 __ __ __ __ ╾─alloc8──╼ 02 00 00 00 │ ....░░░░╾──╼....
+ 0x20 │ 01 00 00 00 2a 00 00 00 ╾─alloc13─╼ 03 00 00 00 │ ....*...╾──╼....
}
alloc5 (size: 0, align: 4) {}
-alloc9 (size: 16, align: 4) {
- ╾─alloc8──╼ 03 00 00 00 ╾─alloc10─╼ 03 00 00 00 │ ╾──╼....╾──╼....
+alloc8 (size: 16, align: 4) {
+ ╾─alloc9──╼ 03 00 00 00 ╾─alloc10─╼ 03 00 00 00 │ ╾──╼....╾──╼....
}
-alloc8 (size: 3, align: 1) {
+alloc9 (size: 3, align: 1) {
66 6f 6f │ foo
}
62 61 72 │ bar
}
-alloc14 (size: 24, align: 4) {
- 0x00 │ ╾─alloc13─╼ 03 00 00 00 ╾─alloc15─╼ 03 00 00 00 │ ╾──╼....╾──╼....
+alloc13 (size: 24, align: 4) {
+ 0x00 │ ╾─alloc14─╼ 03 00 00 00 ╾─alloc15─╼ 03 00 00 00 │ ╾──╼....╾──╼....
0x10 │ ╾─alloc16─╼ 04 00 00 00 │ ╾──╼....
}
-alloc13 (size: 3, align: 1) {
+alloc14 (size: 3, align: 1) {
6d 65 68 │ meh
}
alloc18 (size: 72, align: 8) {
0x00 │ 00 00 00 00 __ __ __ __ ╾───────alloc5────────╼ │ ....░░░░╾──────╼
0x10 │ 00 00 00 00 00 00 00 00 00 00 00 00 __ __ __ __ │ ............░░░░
- 0x20 │ ╾───────alloc9────────╼ 02 00 00 00 00 00 00 00 │ ╾──────╼........
- 0x30 │ 01 00 00 00 2a 00 00 00 ╾───────alloc14───────╼ │ ....*...╾──────╼
+ 0x20 │ ╾───────alloc8────────╼ 02 00 00 00 00 00 00 00 │ ╾──────╼........
+ 0x30 │ 01 00 00 00 2a 00 00 00 ╾───────alloc13───────╼ │ ....*...╾──────╼
0x40 │ 03 00 00 00 00 00 00 00 │ ........
}
alloc5 (size: 0, align: 8) {}
-alloc9 (size: 32, align: 8) {
- 0x00 │ ╾───────alloc8────────╼ 03 00 00 00 00 00 00 00 │ ╾──────╼........
+alloc8 (size: 32, align: 8) {
+ 0x00 │ ╾───────alloc9────────╼ 03 00 00 00 00 00 00 00 │ ╾──────╼........
0x10 │ ╾───────alloc10───────╼ 03 00 00 00 00 00 00 00 │ ╾──────╼........
}
-alloc8 (size: 3, align: 1) {
+alloc9 (size: 3, align: 1) {
66 6f 6f │ foo
}
62 61 72 │ bar
}
-alloc14 (size: 48, align: 8) {
- 0x00 │ ╾───────alloc13───────╼ 03 00 00 00 00 00 00 00 │ ╾──────╼........
+alloc13 (size: 48, align: 8) {
+ 0x00 │ ╾───────alloc14───────╼ 03 00 00 00 00 00 00 00 │ ╾──────╼........
0x10 │ ╾───────alloc15───────╼ 03 00 00 00 00 00 00 00 │ ╾──────╼........
0x20 │ ╾───────alloc16───────╼ 04 00 00 00 00 00 00 00 │ ╾──────╼........
}
-alloc13 (size: 3, align: 1) {
+alloc14 (size: 3, align: 1) {
6d 65 68 │ meh
}
}
bb0: {
- StorageLive(_1); // scope 0 at $DIR/const_debuginfo.rs:+1:9: +1:10
_1 = const 1_u8; // scope 0 at $DIR/const_debuginfo.rs:+1:13: +1:16
- StorageLive(_2); // scope 1 at $DIR/const_debuginfo.rs:+2:9: +2:10
_2 = const 2_u8; // scope 1 at $DIR/const_debuginfo.rs:+2:13: +2:16
- StorageLive(_3); // scope 2 at $DIR/const_debuginfo.rs:+3:9: +3:10
_3 = const 3_u8; // scope 2 at $DIR/const_debuginfo.rs:+3:13: +3:16
StorageLive(_4); // scope 3 at $DIR/const_debuginfo.rs:+4:9: +4:12
StorageLive(_5); // scope 3 at $DIR/const_debuginfo.rs:+4:15: +4:20
StorageLive(_14); // scope 5 at $DIR/const_debuginfo.rs:+8:9: +8:10
StorageLive(_15); // scope 5 at $DIR/const_debuginfo.rs:+8:9: +8:10
StorageLive(_16); // scope 5 at $DIR/const_debuginfo.rs:+8:9: +8:10
- Deinit(_14); // scope 5 at $DIR/const_debuginfo.rs:+8:13: +8:34
- Deinit(_15); // scope 5 at $DIR/const_debuginfo.rs:+8:13: +8:34
- Deinit(_16); // scope 5 at $DIR/const_debuginfo.rs:+8:13: +8:34
_14 = const true; // scope 5 at $DIR/const_debuginfo.rs:+8:13: +8:34
_15 = const false; // scope 5 at $DIR/const_debuginfo.rs:+8:13: +8:34
_16 = const 123_u32; // scope 5 at $DIR/const_debuginfo.rs:+8:13: +8:34
StorageLive(_10); // scope 6 at $DIR/const_debuginfo.rs:+10:9: +10:10
- Deinit(_10); // scope 6 at $DIR/const_debuginfo.rs:+10:13: +10:24
- ((_10 as Some).0: u16) = const 99_u16; // scope 6 at $DIR/const_debuginfo.rs:+10:13: +10:24
- discriminant(_10) = 1; // scope 6 at $DIR/const_debuginfo.rs:+10:13: +10:24
- StorageLive(_17); // scope 7 at $DIR/const_debuginfo.rs:+12:9: +12:10
- StorageLive(_18); // scope 7 at $DIR/const_debuginfo.rs:+12:9: +12:10
- Deinit(_17); // scope 7 at $DIR/const_debuginfo.rs:+12:13: +12:35
- Deinit(_18); // scope 7 at $DIR/const_debuginfo.rs:+12:13: +12:35
+ _10 = Option::<u16>::Some(const 99_u16); // scope 6 at $DIR/const_debuginfo.rs:+10:13: +10:24
_17 = const 32_u32; // scope 7 at $DIR/const_debuginfo.rs:+12:13: +12:35
_18 = const 32_u32; // scope 7 at $DIR/const_debuginfo.rs:+12:13: +12:35
StorageLive(_11); // scope 8 at $DIR/const_debuginfo.rs:+13:9: +13:10
- StorageLive(_12); // scope 8 at $DIR/const_debuginfo.rs:+13:13: +13:16
- _12 = const 32_u32; // scope 8 at $DIR/const_debuginfo.rs:+13:13: +13:16
- StorageLive(_13); // scope 8 at $DIR/const_debuginfo.rs:+13:19: +13:22
- _13 = const 32_u32; // scope 8 at $DIR/const_debuginfo.rs:+13:19: +13:22
_11 = const 64_u32; // scope 8 at $DIR/const_debuginfo.rs:+13:13: +13:22
- StorageDead(_13); // scope 8 at $DIR/const_debuginfo.rs:+13:21: +13:22
- StorageDead(_12); // scope 8 at $DIR/const_debuginfo.rs:+13:21: +13:22
StorageDead(_11); // scope 8 at $DIR/const_debuginfo.rs:+14:1: +14:2
- StorageDead(_17); // scope 7 at $DIR/const_debuginfo.rs:+14:1: +14:2
- StorageDead(_18); // scope 7 at $DIR/const_debuginfo.rs:+14:1: +14:2
StorageDead(_10); // scope 6 at $DIR/const_debuginfo.rs:+14:1: +14:2
StorageDead(_14); // scope 5 at $DIR/const_debuginfo.rs:+14:1: +14:2
StorageDead(_15); // scope 5 at $DIR/const_debuginfo.rs:+14:1: +14:2
bb0: {
StorageLive(_1); // scope 0 at $DIR/const_goto_storage.rs:+1:9: +1:12
- StorageLive(_2); // scope 0 at $DIR/const_goto_storage.rs:+1:21: +1:23
-- Deinit(_2); // scope 0 at $DIR/const_goto_storage.rs:+1:21: +1:23
+- _2 = (); // scope 0 at $DIR/const_goto_storage.rs:+1:21: +1:23
- StorageLive(_3); // scope 0 at $DIR/const_goto_storage.rs:+2:15: +6:10
- StorageLive(_4); // scope 0 at $DIR/const_goto_storage.rs:+2:18: +2:76
- StorageLive(_5); // scope 0 at $DIR/const_goto_storage.rs:+2:21: +2:52
--- /dev/null
+- // MIR for `foo` before ConstProp
++ // MIR for `foo` after ConstProp
+
+ fn foo(_1: u8) -> () {
+ debug x => _1; // in scope 0 at $DIR/aggregate.rs:+0:8: +0:9
+ let mut _0: (); // return place in scope 0 at $DIR/aggregate.rs:+0:15: +0:15
+ let _2: i32; // in scope 0 at $DIR/aggregate.rs:+2:9: +2:14
+ let mut _3: i32; // in scope 0 at $DIR/aggregate.rs:+2:17: +2:25
+ let mut _4: (i32, u8); // in scope 0 at $DIR/aggregate.rs:+2:17: +2:23
+ let mut _5: u8; // in scope 0 at $DIR/aggregate.rs:+2:21: +2:22
+ let mut _7: i32; // in scope 0 at $DIR/aggregate.rs:+3:18: +3:26
+ let mut _8: (u8, i32); // in scope 0 at $DIR/aggregate.rs:+3:18: +3:24
+ let mut _9: u8; // in scope 0 at $DIR/aggregate.rs:+3:19: +3:20
+ scope 1 {
+ debug first => _2; // in scope 1 at $DIR/aggregate.rs:+2:9: +2:14
+ let _6: i32; // in scope 1 at $DIR/aggregate.rs:+3:9: +3:15
+ scope 2 {
+ debug second => _6; // in scope 2 at $DIR/aggregate.rs:+3:9: +3:15
+ }
+ }
+
+ bb0: {
+ StorageLive(_2); // scope 0 at $DIR/aggregate.rs:+2:9: +2:14
+ StorageLive(_3); // scope 0 at $DIR/aggregate.rs:+2:17: +2:25
+ StorageLive(_4); // scope 0 at $DIR/aggregate.rs:+2:17: +2:23
+ StorageLive(_5); // scope 0 at $DIR/aggregate.rs:+2:21: +2:22
+ _5 = _1; // scope 0 at $DIR/aggregate.rs:+2:21: +2:22
+ _4 = (const 0_i32, move _5); // scope 0 at $DIR/aggregate.rs:+2:17: +2:23
+ StorageDead(_5); // scope 0 at $DIR/aggregate.rs:+2:22: +2:23
+- _3 = (_4.0: i32); // scope 0 at $DIR/aggregate.rs:+2:17: +2:25
+- _2 = Add(move _3, const 1_i32); // scope 0 at $DIR/aggregate.rs:+2:17: +2:29
++ _3 = const 0_i32; // scope 0 at $DIR/aggregate.rs:+2:17: +2:25
++ _2 = const 1_i32; // scope 0 at $DIR/aggregate.rs:+2:17: +2:29
+ StorageDead(_3); // scope 0 at $DIR/aggregate.rs:+2:28: +2:29
+ StorageDead(_4); // scope 0 at $DIR/aggregate.rs:+2:29: +2:30
+ StorageLive(_6); // scope 1 at $DIR/aggregate.rs:+3:9: +3:15
+ StorageLive(_7); // scope 1 at $DIR/aggregate.rs:+3:18: +3:26
+ StorageLive(_8); // scope 1 at $DIR/aggregate.rs:+3:18: +3:24
+ StorageLive(_9); // scope 1 at $DIR/aggregate.rs:+3:19: +3:20
+ _9 = _1; // scope 1 at $DIR/aggregate.rs:+3:19: +3:20
+ _8 = (move _9, const 1_i32); // scope 1 at $DIR/aggregate.rs:+3:18: +3:24
+ StorageDead(_9); // scope 1 at $DIR/aggregate.rs:+3:23: +3:24
+- _7 = (_8.1: i32); // scope 1 at $DIR/aggregate.rs:+3:18: +3:26
+- _6 = Add(move _7, const 2_i32); // scope 1 at $DIR/aggregate.rs:+3:18: +3:30
++ _7 = const 1_i32; // scope 1 at $DIR/aggregate.rs:+3:18: +3:26
++ _6 = const 3_i32; // scope 1 at $DIR/aggregate.rs:+3:18: +3:30
+ StorageDead(_7); // scope 1 at $DIR/aggregate.rs:+3:29: +3:30
+ StorageDead(_8); // scope 1 at $DIR/aggregate.rs:+3:30: +3:31
+ _0 = const (); // scope 0 at $DIR/aggregate.rs:+0:15: +4:2
+ StorageDead(_6); // scope 1 at $DIR/aggregate.rs:+4:1: +4:2
+ StorageDead(_2); // scope 0 at $DIR/aggregate.rs:+4:1: +4:2
+ return; // scope 0 at $DIR/aggregate.rs:+4:2: +4:2
+ }
+ }
+
--- /dev/null
+// MIR for `foo` after PreCodegen
+
+fn foo(_1: u8) -> () {
+ debug x => _1; // in scope 0 at $DIR/aggregate.rs:+0:8: +0:9
+ let mut _0: (); // return place in scope 0 at $DIR/aggregate.rs:+0:15: +0:15
+ let _2: i32; // in scope 0 at $DIR/aggregate.rs:+2:9: +2:14
+ let mut _3: i32; // in scope 0 at $DIR/aggregate.rs:+2:17: +2:25
+ let mut _4: (i32, u8); // in scope 0 at $DIR/aggregate.rs:+2:17: +2:23
+ let mut _5: u8; // in scope 0 at $DIR/aggregate.rs:+2:21: +2:22
+ let mut _7: i32; // in scope 0 at $DIR/aggregate.rs:+3:18: +3:26
+ let mut _8: (u8, i32); // in scope 0 at $DIR/aggregate.rs:+3:18: +3:24
+ let mut _9: u8; // in scope 0 at $DIR/aggregate.rs:+3:19: +3:20
+ scope 1 {
+ debug first => _2; // in scope 1 at $DIR/aggregate.rs:+2:9: +2:14
+ let _6: i32; // in scope 1 at $DIR/aggregate.rs:+3:9: +3:15
+ scope 2 {
+ debug second => _6; // in scope 2 at $DIR/aggregate.rs:+3:9: +3:15
+ }
+ }
+
+ bb0: {
+ StorageLive(_2); // scope 0 at $DIR/aggregate.rs:+2:9: +2:14
+ StorageLive(_3); // scope 0 at $DIR/aggregate.rs:+2:17: +2:25
+ StorageLive(_4); // scope 0 at $DIR/aggregate.rs:+2:17: +2:23
+ StorageLive(_5); // scope 0 at $DIR/aggregate.rs:+2:21: +2:22
+ _5 = _1; // scope 0 at $DIR/aggregate.rs:+2:21: +2:22
+ _4 = (const 0_i32, move _5); // scope 0 at $DIR/aggregate.rs:+2:17: +2:23
+ StorageDead(_5); // scope 0 at $DIR/aggregate.rs:+2:22: +2:23
+ _3 = const 0_i32; // scope 0 at $DIR/aggregate.rs:+2:17: +2:25
+ _2 = const 1_i32; // scope 0 at $DIR/aggregate.rs:+2:17: +2:29
+ StorageDead(_3); // scope 0 at $DIR/aggregate.rs:+2:28: +2:29
+ StorageDead(_4); // scope 0 at $DIR/aggregate.rs:+2:29: +2:30
+ StorageLive(_6); // scope 1 at $DIR/aggregate.rs:+3:9: +3:15
+ StorageLive(_7); // scope 1 at $DIR/aggregate.rs:+3:18: +3:26
+ StorageLive(_8); // scope 1 at $DIR/aggregate.rs:+3:18: +3:24
+ StorageLive(_9); // scope 1 at $DIR/aggregate.rs:+3:19: +3:20
+ _9 = _1; // scope 1 at $DIR/aggregate.rs:+3:19: +3:20
+ _8 = (move _9, const 1_i32); // scope 1 at $DIR/aggregate.rs:+3:18: +3:24
+ StorageDead(_9); // scope 1 at $DIR/aggregate.rs:+3:23: +3:24
+ _7 = const 1_i32; // scope 1 at $DIR/aggregate.rs:+3:18: +3:26
+ _6 = const 3_i32; // scope 1 at $DIR/aggregate.rs:+3:18: +3:30
+ StorageDead(_7); // scope 1 at $DIR/aggregate.rs:+3:29: +3:30
+ StorageDead(_8); // scope 1 at $DIR/aggregate.rs:+3:30: +3:31
+ _0 = const (); // scope 0 at $DIR/aggregate.rs:+0:15: +4:2
+ StorageDead(_6); // scope 1 at $DIR/aggregate.rs:+4:1: +4:2
+ StorageDead(_2); // scope 0 at $DIR/aggregate.rs:+4:1: +4:2
+ return; // scope 0 at $DIR/aggregate.rs:+4:2: +4:2
+ }
+}
fn main() -> () {
let mut _0: (); // return place in scope 0 at $DIR/aggregate.rs:+0:11: +0:11
- let _1: i32; // in scope 0 at $DIR/aggregate.rs:+1:9: +1:10
- let mut _2: i32; // in scope 0 at $DIR/aggregate.rs:+1:13: +1:24
- let mut _3: (i32, i32, i32); // in scope 0 at $DIR/aggregate.rs:+1:13: +1:22
+ let _1: u8; // in scope 0 at $DIR/aggregate.rs:+1:9: +1:10
+ let mut _2: u8; // in scope 0 at $DIR/aggregate.rs:+1:13: +1:24
+ let mut _3: (i32, u8, i32); // in scope 0 at $DIR/aggregate.rs:+1:13: +1:22
+ let _4: (); // in scope 0 at $DIR/aggregate.rs:+2:5: +2:11
+ let mut _5: u8; // in scope 0 at $DIR/aggregate.rs:+2:9: +2:10
scope 1 {
debug x => _1; // in scope 1 at $DIR/aggregate.rs:+1:9: +1:10
}
StorageLive(_1); // scope 0 at $DIR/aggregate.rs:+1:9: +1:10
StorageLive(_2); // scope 0 at $DIR/aggregate.rs:+1:13: +1:24
StorageLive(_3); // scope 0 at $DIR/aggregate.rs:+1:13: +1:22
- Deinit(_3); // scope 0 at $DIR/aggregate.rs:+1:13: +1:22
- (_3.0: i32) = const 0_i32; // scope 0 at $DIR/aggregate.rs:+1:13: +1:22
- (_3.1: i32) = const 1_i32; // scope 0 at $DIR/aggregate.rs:+1:13: +1:22
- (_3.2: i32) = const 2_i32; // scope 0 at $DIR/aggregate.rs:+1:13: +1:22
-- _2 = (_3.1: i32); // scope 0 at $DIR/aggregate.rs:+1:13: +1:24
-- _1 = Add(move _2, const 0_i32); // scope 0 at $DIR/aggregate.rs:+1:13: +1:28
-+ _2 = const 1_i32; // scope 0 at $DIR/aggregate.rs:+1:13: +1:24
-+ _1 = const 1_i32; // scope 0 at $DIR/aggregate.rs:+1:13: +1:28
+ _3 = (const 0_i32, const 1_u8, const 2_i32); // scope 0 at $DIR/aggregate.rs:+1:13: +1:22
+- _2 = (_3.1: u8); // scope 0 at $DIR/aggregate.rs:+1:13: +1:24
+- _1 = Add(move _2, const 0_u8); // scope 0 at $DIR/aggregate.rs:+1:13: +1:28
++ _2 = const 1_u8; // scope 0 at $DIR/aggregate.rs:+1:13: +1:24
++ _1 = const 1_u8; // scope 0 at $DIR/aggregate.rs:+1:13: +1:28
StorageDead(_2); // scope 0 at $DIR/aggregate.rs:+1:27: +1:28
StorageDead(_3); // scope 0 at $DIR/aggregate.rs:+1:28: +1:29
- _0 = const (); // scope 0 at $DIR/aggregate.rs:+0:11: +2:2
- StorageDead(_1); // scope 0 at $DIR/aggregate.rs:+2:1: +2:2
- return; // scope 0 at $DIR/aggregate.rs:+2:2: +2:2
+ StorageLive(_4); // scope 1 at $DIR/aggregate.rs:+2:5: +2:11
+ StorageLive(_5); // scope 1 at $DIR/aggregate.rs:+2:9: +2:10
+- _5 = _1; // scope 1 at $DIR/aggregate.rs:+2:9: +2:10
++ _5 = const 1_u8; // scope 1 at $DIR/aggregate.rs:+2:9: +2:10
+ _4 = foo(move _5) -> bb1; // scope 1 at $DIR/aggregate.rs:+2:5: +2:11
+ // mir::Constant
+ // + span: $DIR/aggregate.rs:8:5: 8:8
+ // + literal: Const { ty: fn(u8) {foo}, val: Value(<ZST>) }
+ }
+
+ bb1: {
+ StorageDead(_5); // scope 1 at $DIR/aggregate.rs:+2:10: +2:11
+ StorageDead(_4); // scope 1 at $DIR/aggregate.rs:+2:11: +2:12
+ _0 = const (); // scope 0 at $DIR/aggregate.rs:+0:11: +3:2
+ StorageDead(_1); // scope 0 at $DIR/aggregate.rs:+3:1: +3:2
+ return; // scope 0 at $DIR/aggregate.rs:+3:2: +3:2
}
}
fn main() -> () {
let mut _0: (); // return place in scope 0 at $DIR/aggregate.rs:+0:11: +0:11
- let _1: i32; // in scope 0 at $DIR/aggregate.rs:+1:9: +1:10
- let mut _2: i32; // in scope 0 at $DIR/aggregate.rs:+1:13: +1:24
- let mut _3: (i32, i32, i32); // in scope 0 at $DIR/aggregate.rs:+1:13: +1:22
+ let _1: u8; // in scope 0 at $DIR/aggregate.rs:+1:9: +1:10
+ let mut _2: u8; // in scope 0 at $DIR/aggregate.rs:+1:13: +1:24
+ let mut _3: (i32, u8, i32); // in scope 0 at $DIR/aggregate.rs:+1:13: +1:22
+ let _4: (); // in scope 0 at $DIR/aggregate.rs:+2:5: +2:11
+ let mut _5: u8; // in scope 0 at $DIR/aggregate.rs:+2:9: +2:10
scope 1 {
debug x => _1; // in scope 1 at $DIR/aggregate.rs:+1:9: +1:10
}
StorageLive(_1); // scope 0 at $DIR/aggregate.rs:+1:9: +1:10
StorageLive(_2); // scope 0 at $DIR/aggregate.rs:+1:13: +1:24
StorageLive(_3); // scope 0 at $DIR/aggregate.rs:+1:13: +1:22
- Deinit(_3); // scope 0 at $DIR/aggregate.rs:+1:13: +1:22
- (_3.0: i32) = const 0_i32; // scope 0 at $DIR/aggregate.rs:+1:13: +1:22
- (_3.1: i32) = const 1_i32; // scope 0 at $DIR/aggregate.rs:+1:13: +1:22
- (_3.2: i32) = const 2_i32; // scope 0 at $DIR/aggregate.rs:+1:13: +1:22
- _2 = const 1_i32; // scope 0 at $DIR/aggregate.rs:+1:13: +1:24
- _1 = const 1_i32; // scope 0 at $DIR/aggregate.rs:+1:13: +1:28
+ _3 = (const 0_i32, const 1_u8, const 2_i32); // scope 0 at $DIR/aggregate.rs:+1:13: +1:22
+ _2 = const 1_u8; // scope 0 at $DIR/aggregate.rs:+1:13: +1:24
+ _1 = const 1_u8; // scope 0 at $DIR/aggregate.rs:+1:13: +1:28
StorageDead(_2); // scope 0 at $DIR/aggregate.rs:+1:27: +1:28
StorageDead(_3); // scope 0 at $DIR/aggregate.rs:+1:28: +1:29
- _0 = const (); // scope 0 at $DIR/aggregate.rs:+0:11: +2:2
- StorageDead(_1); // scope 0 at $DIR/aggregate.rs:+2:1: +2:2
- return; // scope 0 at $DIR/aggregate.rs:+2:2: +2:2
+ StorageLive(_4); // scope 1 at $DIR/aggregate.rs:+2:5: +2:11
+ StorageLive(_5); // scope 1 at $DIR/aggregate.rs:+2:9: +2:10
+ _5 = const 1_u8; // scope 1 at $DIR/aggregate.rs:+2:9: +2:10
+ _4 = foo(move _5) -> bb1; // scope 1 at $DIR/aggregate.rs:+2:5: +2:11
+ // mir::Constant
+ // + span: $DIR/aggregate.rs:8:5: 8:8
+ // + literal: Const { ty: fn(u8) {foo}, val: Value(<ZST>) }
+ }
+
+ bb1: {
+ StorageDead(_5); // scope 1 at $DIR/aggregate.rs:+2:10: +2:11
+ StorageDead(_4); // scope 1 at $DIR/aggregate.rs:+2:11: +2:12
+ _0 = const (); // scope 0 at $DIR/aggregate.rs:+0:11: +3:2
+ StorageDead(_1); // scope 0 at $DIR/aggregate.rs:+3:1: +3:2
+ return; // scope 0 at $DIR/aggregate.rs:+3:2: +3:2
}
}
// EMIT_MIR aggregate.main.PreCodegen.after.mir
fn main() {
let x = (0, 1, 2).1 + 0;
+ foo(x);
+}
+
+// EMIT_MIR aggregate.foo.ConstProp.diff
+// EMIT_MIR aggregate.foo.PreCodegen.after.mir
+fn foo(x: u8) {
+ // Verify that we still propagate if part of the aggregate is not known.
+ let first = (0, x).0 + 1;
+ let second = (x, 1).1 + 2;
}
}
bb0: {
- StorageLive(_1); // scope 0 at $DIR/bad_op_mod_by_zero.rs:+1:9: +1:10
_1 = const 0_i32; // scope 0 at $DIR/bad_op_mod_by_zero.rs:+1:13: +1:14
StorageLive(_2); // scope 1 at $DIR/bad_op_mod_by_zero.rs:+2:9: +2:11
- _4 = Eq(_1, const 0_i32); // scope 1 at $DIR/bad_op_mod_by_zero.rs:+2:14: +2:19
StorageLive(_1); // scope 0 at $DIR/discriminant.rs:+1:9: +1:10
StorageLive(_2); // scope 0 at $DIR/discriminant.rs:+1:13: +1:64
StorageLive(_3); // scope 2 at $DIR/discriminant.rs:+1:34: +1:44
- Deinit(_3); // scope 2 at $DIR/discriminant.rs:+1:34: +1:44
- ((_3 as Some).0: bool) = const true; // scope 2 at $DIR/discriminant.rs:+1:34: +1:44
- discriminant(_3) = 1; // scope 2 at $DIR/discriminant.rs:+1:34: +1:44
+- _3 = Option::<bool>::Some(const true); // scope 2 at $DIR/discriminant.rs:+1:34: +1:44
- _4 = discriminant(_3); // scope 2 at $DIR/discriminant.rs:+1:21: +1:31
- switchInt(move _4) -> [1: bb1, otherwise: bb3]; // scope 2 at $DIR/discriminant.rs:+1:21: +1:31
++ _3 = const Option::<bool>::Some(true); // scope 2 at $DIR/discriminant.rs:+1:34: +1:44
++ // mir::Constant
++ // + span: $DIR/discriminant.rs:12:34: 12:44
++ // + literal: Const { ty: Option<bool>, val: Value(Scalar(0x01)) }
+ _4 = const 1_isize; // scope 2 at $DIR/discriminant.rs:+1:21: +1:31
+ switchInt(const 1_isize) -> [1: bb1, otherwise: bb3]; // scope 2 at $DIR/discriminant.rs:+1:21: +1:31
}
bb1: {
- switchInt(((_3 as Some).0: bool)) -> [0: bb3, otherwise: bb2]; // scope 2 at $DIR/discriminant.rs:+1:21: +1:31
+- switchInt(((_3 as Some).0: bool)) -> [0: bb3, otherwise: bb2]; // scope 2 at $DIR/discriminant.rs:+1:21: +1:31
++ switchInt(const true) -> [0: bb3, otherwise: bb2]; // scope 2 at $DIR/discriminant.rs:+1:21: +1:31
}
bb2: {
StorageLive(_1); // scope 0 at $DIR/discriminant.rs:+1:9: +1:10
StorageLive(_2); // scope 0 at $DIR/discriminant.rs:+1:13: +1:64
StorageLive(_3); // scope 2 at $DIR/discriminant.rs:+1:34: +1:44
- Deinit(_3); // scope 2 at $DIR/discriminant.rs:+1:34: +1:44
- ((_3 as Some).0: bool) = const true; // scope 2 at $DIR/discriminant.rs:+1:34: +1:44
- discriminant(_3) = 1; // scope 2 at $DIR/discriminant.rs:+1:34: +1:44
+- _3 = Option::<bool>::Some(const true); // scope 2 at $DIR/discriminant.rs:+1:34: +1:44
- _4 = discriminant(_3); // scope 2 at $DIR/discriminant.rs:+1:21: +1:31
- switchInt(move _4) -> [1: bb1, otherwise: bb3]; // scope 2 at $DIR/discriminant.rs:+1:21: +1:31
++ _3 = const Option::<bool>::Some(true); // scope 2 at $DIR/discriminant.rs:+1:34: +1:44
++ // mir::Constant
++ // + span: $DIR/discriminant.rs:12:34: 12:44
++ // + literal: Const { ty: Option<bool>, val: Value(Scalar(0x01)) }
+ _4 = const 1_isize; // scope 2 at $DIR/discriminant.rs:+1:21: +1:31
+ switchInt(const 1_isize) -> [1: bb1, otherwise: bb3]; // scope 2 at $DIR/discriminant.rs:+1:21: +1:31
}
bb1: {
- switchInt(((_3 as Some).0: bool)) -> [0: bb3, otherwise: bb2]; // scope 2 at $DIR/discriminant.rs:+1:21: +1:31
+- switchInt(((_3 as Some).0: bool)) -> [0: bb3, otherwise: bb2]; // scope 2 at $DIR/discriminant.rs:+1:21: +1:31
++ switchInt(const true) -> [0: bb3, otherwise: bb2]; // scope 2 at $DIR/discriminant.rs:+1:21: +1:31
}
bb2: {
bb0: {
StorageLive(_1); // scope 0 at $DIR/invalid_constant.rs:+6:9: +6:22
StorageLive(_2); // scope 2 at $DIR/invalid_constant.rs:+6:34: +6:63
- Deinit(_2); // scope 2 at $DIR/invalid_constant.rs:+6:34: +6:63
- (_2.0: u32) = const 1114113_u32; // scope 2 at $DIR/invalid_constant.rs:+6:34: +6:63
+ _2 = InvalidChar { int: const 1114113_u32 }; // scope 2 at $DIR/invalid_constant.rs:+6:34: +6:63
- _1 = (_2.1: char); // scope 2 at $DIR/invalid_constant.rs:+6:34: +6:67
+ _1 = const {transmute(0x00110001): char}; // scope 2 at $DIR/invalid_constant.rs:+6:34: +6:67
StorageDead(_2); // scope 0 at $DIR/invalid_constant.rs:+6:69: +6:70
StorageLive(_3); // scope 1 at $DIR/invalid_constant.rs:+13:9: +13:21
StorageLive(_4); // scope 1 at $DIR/invalid_constant.rs:+13:25: +13:59
StorageLive(_5); // scope 4 at $DIR/invalid_constant.rs:+13:34: +13:55
- Deinit(_5); // scope 4 at $DIR/invalid_constant.rs:+13:34: +13:55
- (_5.0: u32) = const 4_u32; // scope 4 at $DIR/invalid_constant.rs:+13:34: +13:55
+ _5 = InvalidTag { int: const 4_u32 }; // scope 4 at $DIR/invalid_constant.rs:+13:34: +13:55
- _4 = (_5.1: E); // scope 4 at $DIR/invalid_constant.rs:+13:34: +13:57
- _3 = [move _4]; // scope 1 at $DIR/invalid_constant.rs:+13:24: +13:60
+ _4 = const Scalar(0x00000004): E; // scope 4 at $DIR/invalid_constant.rs:+13:34: +13:57
let mut _0: (); // return place in scope 0 at $DIR/issue_66971.rs:+0:11: +0:11
let _1: (); // in scope 0 at $DIR/issue_66971.rs:+1:5: +1:23
let mut _2: ((), u8, u8); // in scope 0 at $DIR/issue_66971.rs:+1:12: +1:22
+ let mut _3: (); // in scope 0 at $DIR/issue_66971.rs:+1:13: +1:15
bb0: {
StorageLive(_1); // scope 0 at $DIR/issue_66971.rs:+1:5: +1:23
StorageLive(_2); // scope 0 at $DIR/issue_66971.rs:+1:12: +1:22
- Deinit(_2); // scope 0 at $DIR/issue_66971.rs:+1:12: +1:22
- (_2.1: u8) = const 0_u8; // scope 0 at $DIR/issue_66971.rs:+1:12: +1:22
- (_2.2: u8) = const 0_u8; // scope 0 at $DIR/issue_66971.rs:+1:12: +1:22
+ StorageLive(_3); // scope 0 at $DIR/issue_66971.rs:+1:13: +1:15
+ _2 = (move _3, const 0_u8, const 0_u8); // scope 0 at $DIR/issue_66971.rs:+1:12: +1:22
+ StorageDead(_3); // scope 0 at $DIR/issue_66971.rs:+1:21: +1:22
_1 = encode(move _2) -> bb1; // scope 0 at $DIR/issue_66971.rs:+1:5: +1:23
// mir::Constant
// + span: $DIR/issue_66971.rs:17:5: 17:11
StorageLive(_1); // scope 0 at $DIR/issue_67019.rs:+1:5: +1:20
StorageLive(_2); // scope 0 at $DIR/issue_67019.rs:+1:10: +1:19
StorageLive(_3); // scope 0 at $DIR/issue_67019.rs:+1:11: +1:17
- Deinit(_3); // scope 0 at $DIR/issue_67019.rs:+1:11: +1:17
- (_3.0: u8) = const 1_u8; // scope 0 at $DIR/issue_67019.rs:+1:11: +1:17
- (_3.1: u8) = const 2_u8; // scope 0 at $DIR/issue_67019.rs:+1:11: +1:17
- Deinit(_2); // scope 0 at $DIR/issue_67019.rs:+1:10: +1:19
-- (_2.0: (u8, u8)) = move _3; // scope 0 at $DIR/issue_67019.rs:+1:10: +1:19
-+ (_2.0: (u8, u8)) = const (1_u8, 2_u8); // scope 0 at $DIR/issue_67019.rs:+1:10: +1:19
+- _3 = (const 1_u8, const 2_u8); // scope 0 at $DIR/issue_67019.rs:+1:11: +1:17
++ _3 = const (1_u8, 2_u8); // scope 0 at $DIR/issue_67019.rs:+1:11: +1:17
+ _2 = (move _3,); // scope 0 at $DIR/issue_67019.rs:+1:10: +1:19
StorageDead(_3); // scope 0 at $DIR/issue_67019.rs:+1:18: +1:19
_1 = test(move _2) -> bb1; // scope 0 at $DIR/issue_67019.rs:+1:5: +1:20
// mir::Constant
fn main() -> () {
let mut _0: (); // return place in scope 0 at $DIR/mutable_variable_aggregate.rs:+0:11: +0:11
- let mut _1: (i32, i32); // in scope 0 at $DIR/mutable_variable_aggregate.rs:+1:9: +1:14
+ let mut _3: i32; // in scope 0 at $DIR/mutable_variable_aggregate.rs:+1:9: +1:14
+ let mut _4: i32; // in scope 0 at $DIR/mutable_variable_aggregate.rs:+1:9: +1:14
scope 1 {
- debug x => _1; // in scope 1 at $DIR/mutable_variable_aggregate.rs:+1:9: +1:14
- let _2: (i32, i32); // in scope 1 at $DIR/mutable_variable_aggregate.rs:+3:9: +3:10
+ debug x => (i32, i32){ .0 => _3, .1 => _4, }; // in scope 1 at $DIR/mutable_variable_aggregate.rs:+1:9: +1:14
+ let _1: i32; // in scope 1 at $DIR/mutable_variable_aggregate.rs:+3:9: +3:10
+ let _2: i32; // in scope 1 at $DIR/mutable_variable_aggregate.rs:+3:9: +3:10
scope 2 {
- debug y => _2; // in scope 2 at $DIR/mutable_variable_aggregate.rs:+3:9: +3:10
+ debug y => (i32, i32){ .0 => _3, .1 => _2, }; // in scope 2 at $DIR/mutable_variable_aggregate.rs:+3:9: +3:10
}
}
bb0: {
- StorageLive(_1); // scope 0 at $DIR/mutable_variable_aggregate.rs:+1:9: +1:14
- Deinit(_1); // scope 0 at $DIR/mutable_variable_aggregate.rs:+1:17: +1:25
- (_1.0: i32) = const 42_i32; // scope 0 at $DIR/mutable_variable_aggregate.rs:+1:17: +1:25
- (_1.1: i32) = const 43_i32; // scope 0 at $DIR/mutable_variable_aggregate.rs:+1:17: +1:25
- (_1.1: i32) = const 99_i32; // scope 1 at $DIR/mutable_variable_aggregate.rs:+2:5: +2:13
+ StorageLive(_4); // scope 0 at $DIR/mutable_variable_aggregate.rs:+1:9: +1:14
+ _3 = const 42_i32; // scope 0 at $DIR/mutable_variable_aggregate.rs:+1:17: +1:25
+ _4 = const 43_i32; // scope 0 at $DIR/mutable_variable_aggregate.rs:+1:17: +1:25
+ _4 = const 99_i32; // scope 1 at $DIR/mutable_variable_aggregate.rs:+2:5: +2:13
StorageLive(_2); // scope 1 at $DIR/mutable_variable_aggregate.rs:+3:9: +3:10
-- _2 = _1; // scope 1 at $DIR/mutable_variable_aggregate.rs:+3:13: +3:14
-+ _2 = const (42_i32, 99_i32); // scope 1 at $DIR/mutable_variable_aggregate.rs:+3:13: +3:14
+- _2 = _4; // scope 1 at $DIR/mutable_variable_aggregate.rs:+3:13: +3:14
++ _2 = const 99_i32; // scope 1 at $DIR/mutable_variable_aggregate.rs:+3:13: +3:14
StorageDead(_2); // scope 1 at $DIR/mutable_variable_aggregate.rs:+4:1: +4:2
- StorageDead(_1); // scope 0 at $DIR/mutable_variable_aggregate.rs:+4:1: +4:2
+ StorageDead(_4); // scope 0 at $DIR/mutable_variable_aggregate.rs:+4:1: +4:2
return; // scope 0 at $DIR/mutable_variable_aggregate.rs:+4:2: +4:2
}
}
let _2: &mut (i32, i32); // in scope 1 at $DIR/mutable_variable_aggregate_mut_ref.rs:+2:9: +2:10
scope 2 {
debug z => _2; // in scope 2 at $DIR/mutable_variable_aggregate_mut_ref.rs:+2:9: +2:10
- let _3: (i32, i32); // in scope 2 at $DIR/mutable_variable_aggregate_mut_ref.rs:+4:9: +4:10
+ let _3: i32; // in scope 2 at $DIR/mutable_variable_aggregate_mut_ref.rs:+4:9: +4:10
+ let _4: i32; // in scope 2 at $DIR/mutable_variable_aggregate_mut_ref.rs:+4:9: +4:10
scope 3 {
- debug y => _3; // in scope 3 at $DIR/mutable_variable_aggregate_mut_ref.rs:+4:9: +4:10
+ debug y => (i32, i32){ .0 => _3, .1 => _4, }; // in scope 3 at $DIR/mutable_variable_aggregate_mut_ref.rs:+4:9: +4:10
}
}
}
bb0: {
StorageLive(_1); // scope 0 at $DIR/mutable_variable_aggregate_mut_ref.rs:+1:9: +1:14
- Deinit(_1); // scope 0 at $DIR/mutable_variable_aggregate_mut_ref.rs:+1:17: +1:25
- (_1.0: i32) = const 42_i32; // scope 0 at $DIR/mutable_variable_aggregate_mut_ref.rs:+1:17: +1:25
- (_1.1: i32) = const 43_i32; // scope 0 at $DIR/mutable_variable_aggregate_mut_ref.rs:+1:17: +1:25
+ _1 = (const 42_i32, const 43_i32); // scope 0 at $DIR/mutable_variable_aggregate_mut_ref.rs:+1:17: +1:25
StorageLive(_2); // scope 1 at $DIR/mutable_variable_aggregate_mut_ref.rs:+2:9: +2:10
_2 = &mut _1; // scope 1 at $DIR/mutable_variable_aggregate_mut_ref.rs:+2:13: +2:19
((*_2).1: i32) = const 99_i32; // scope 2 at $DIR/mutable_variable_aggregate_mut_ref.rs:+3:5: +3:13
StorageLive(_3); // scope 2 at $DIR/mutable_variable_aggregate_mut_ref.rs:+4:9: +4:10
- _3 = _1; // scope 2 at $DIR/mutable_variable_aggregate_mut_ref.rs:+4:13: +4:14
+ StorageLive(_4); // scope 2 at $DIR/mutable_variable_aggregate_mut_ref.rs:+4:9: +4:10
+ _3 = (_1.0: i32); // scope 2 at $DIR/mutable_variable_aggregate_mut_ref.rs:+4:13: +4:14
+ _4 = (_1.1: i32); // scope 2 at $DIR/mutable_variable_aggregate_mut_ref.rs:+4:13: +4:14
StorageDead(_3); // scope 2 at $DIR/mutable_variable_aggregate_mut_ref.rs:+5:1: +5:2
+ StorageDead(_4); // scope 2 at $DIR/mutable_variable_aggregate_mut_ref.rs:+5:1: +5:2
StorageDead(_2); // scope 1 at $DIR/mutable_variable_aggregate_mut_ref.rs:+5:1: +5:2
StorageDead(_1); // scope 0 at $DIR/mutable_variable_aggregate_mut_ref.rs:+5:1: +5:2
return; // scope 0 at $DIR/mutable_variable_aggregate_mut_ref.rs:+5:2: +5:2
debug y => _3; // in scope 3 at $DIR/mutable_variable_unprop_assign.rs:+4:9: +4:10
let _4: i32; // in scope 3 at $DIR/mutable_variable_unprop_assign.rs:+5:9: +5:10
scope 4 {
- debug z => _4; // in scope 4 at $DIR/mutable_variable_unprop_assign.rs:+5:9: +5:10
+ debug z => _5; // in scope 4 at $DIR/mutable_variable_unprop_assign.rs:+5:9: +5:10
}
}
}
}
bb1: {
- StorageLive(_5); // scope 1 at $DIR/mutable_variable_unprop_assign.rs:+2:9: +2:14
StorageLive(_6); // scope 1 at $DIR/mutable_variable_unprop_assign.rs:+2:9: +2:14
- Deinit(_5); // scope 1 at $DIR/mutable_variable_unprop_assign.rs:+2:29: +2:35
- Deinit(_6); // scope 1 at $DIR/mutable_variable_unprop_assign.rs:+2:29: +2:35
_5 = const 1_i32; // scope 1 at $DIR/mutable_variable_unprop_assign.rs:+2:29: +2:35
_6 = const 2_i32; // scope 1 at $DIR/mutable_variable_unprop_assign.rs:+2:29: +2:35
StorageLive(_2); // scope 2 at $DIR/mutable_variable_unprop_assign.rs:+3:11: +3:12
StorageDead(_2); // scope 2 at $DIR/mutable_variable_unprop_assign.rs:+3:11: +3:12
StorageLive(_3); // scope 2 at $DIR/mutable_variable_unprop_assign.rs:+4:9: +4:10
_3 = _6; // scope 2 at $DIR/mutable_variable_unprop_assign.rs:+4:13: +4:16
- StorageLive(_4); // scope 3 at $DIR/mutable_variable_unprop_assign.rs:+5:9: +5:10
-- _4 = _5; // scope 3 at $DIR/mutable_variable_unprop_assign.rs:+5:13: +5:16
-+ _4 = const 1_i32; // scope 3 at $DIR/mutable_variable_unprop_assign.rs:+5:13: +5:16
- StorageDead(_4); // scope 3 at $DIR/mutable_variable_unprop_assign.rs:+6:1: +6:2
StorageDead(_3); // scope 2 at $DIR/mutable_variable_unprop_assign.rs:+6:1: +6:2
- StorageDead(_5); // scope 1 at $DIR/mutable_variable_unprop_assign.rs:+6:1: +6:2
StorageDead(_6); // scope 1 at $DIR/mutable_variable_unprop_assign.rs:+6:1: +6:2
StorageDead(_1); // scope 0 at $DIR/mutable_variable_unprop_assign.rs:+6:1: +6:2
return; // scope 0 at $DIR/mutable_variable_unprop_assign.rs:+6:2: +6:2
debug y => _3; // in scope 2 at $DIR/optimizes_into_variable.rs:+2:9: +2:10
let _8: u32; // in scope 2 at $DIR/optimizes_into_variable.rs:+3:9: +3:10
scope 3 {
- debug z => _8; // in scope 3 at $DIR/optimizes_into_variable.rs:+3:9: +3:10
+ debug z => _9; // in scope 3 at $DIR/optimizes_into_variable.rs:+3:9: +3:10
}
}
}
+ _3 = const 3_i32; // scope 1 at $DIR/optimizes_into_variable.rs:+2:13: +2:34
StorageDead(_5); // scope 1 at $DIR/optimizes_into_variable.rs:+2:34: +2:35
StorageDead(_4); // scope 1 at $DIR/optimizes_into_variable.rs:+2:34: +2:35
- StorageLive(_8); // scope 2 at $DIR/optimizes_into_variable.rs:+3:9: +3:10
- StorageLive(_9); // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
- Deinit(_9); // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
_9 = const 42_u32; // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
-- _8 = _9; // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:38
-+ _8 = const 42_u32; // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:38
- StorageDead(_9); // scope 2 at $DIR/optimizes_into_variable.rs:+3:38: +3:39
- StorageDead(_8); // scope 2 at $DIR/optimizes_into_variable.rs:+4:1: +4:2
StorageDead(_3); // scope 1 at $DIR/optimizes_into_variable.rs:+4:1: +4:2
StorageDead(_1); // scope 0 at $DIR/optimizes_into_variable.rs:+4:1: +4:2
return; // scope 0 at $DIR/optimizes_into_variable.rs:+4:2: +4:2
debug y => _3; // in scope 2 at $DIR/optimizes_into_variable.rs:+2:9: +2:10
let _8: u32; // in scope 2 at $DIR/optimizes_into_variable.rs:+3:9: +3:10
scope 3 {
- debug z => _8; // in scope 3 at $DIR/optimizes_into_variable.rs:+3:9: +3:10
+ debug z => _9; // in scope 3 at $DIR/optimizes_into_variable.rs:+3:9: +3:10
}
}
}
+ _3 = const 3_i32; // scope 1 at $DIR/optimizes_into_variable.rs:+2:13: +2:34
StorageDead(_5); // scope 1 at $DIR/optimizes_into_variable.rs:+2:34: +2:35
StorageDead(_4); // scope 1 at $DIR/optimizes_into_variable.rs:+2:34: +2:35
- StorageLive(_8); // scope 2 at $DIR/optimizes_into_variable.rs:+3:9: +3:10
- StorageLive(_9); // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
- Deinit(_9); // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
_9 = const 42_u32; // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
-- _8 = _9; // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:38
-+ _8 = const 42_u32; // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:38
- StorageDead(_9); // scope 2 at $DIR/optimizes_into_variable.rs:+3:38: +3:39
- StorageDead(_8); // scope 2 at $DIR/optimizes_into_variable.rs:+4:1: +4:2
StorageDead(_3); // scope 1 at $DIR/optimizes_into_variable.rs:+4:1: +4:2
StorageDead(_1); // scope 0 at $DIR/optimizes_into_variable.rs:+4:1: +4:2
return; // scope 0 at $DIR/optimizes_into_variable.rs:+4:2: +4:2
fn main() -> () {
let mut _0: (); // return place in scope 0 at $DIR/optimizes_into_variable.rs:+0:11: +0:11
let _1: i32; // in scope 0 at $DIR/optimizes_into_variable.rs:+1:9: +1:10
+ let mut _3: u32; // in scope 0 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
scope 1 {
debug x => _1; // in scope 1 at $DIR/optimizes_into_variable.rs:+1:9: +1:10
let _2: i32; // in scope 1 at $DIR/optimizes_into_variable.rs:+2:9: +2:10
scope 2 {
debug y => _2; // in scope 2 at $DIR/optimizes_into_variable.rs:+2:9: +2:10
- let _3: u32; // in scope 2 at $DIR/optimizes_into_variable.rs:+3:9: +3:10
scope 3 {
debug z => _3; // in scope 3 at $DIR/optimizes_into_variable.rs:+3:9: +3:10
}
bb0: {
StorageLive(_1); // scope 0 at $DIR/optimizes_into_variable.rs:+1:9: +1:10
StorageLive(_2); // scope 1 at $DIR/optimizes_into_variable.rs:+2:9: +2:10
- StorageLive(_3); // scope 2 at $DIR/optimizes_into_variable.rs:+3:9: +3:10
- StorageDead(_3); // scope 2 at $DIR/optimizes_into_variable.rs:+4:1: +4:2
StorageDead(_2); // scope 1 at $DIR/optimizes_into_variable.rs:+4:1: +4:2
StorageDead(_1); // scope 0 at $DIR/optimizes_into_variable.rs:+4:1: +4:2
return; // scope 0 at $DIR/optimizes_into_variable.rs:+4:2: +4:2
fn main() -> () {
let mut _0: (); // return place in scope 0 at $DIR/optimizes_into_variable.rs:+0:11: +0:11
let _1: i32; // in scope 0 at $DIR/optimizes_into_variable.rs:+1:9: +1:10
+ let mut _3: u32; // in scope 0 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
scope 1 {
debug x => _1; // in scope 1 at $DIR/optimizes_into_variable.rs:+1:9: +1:10
let _2: i32; // in scope 1 at $DIR/optimizes_into_variable.rs:+2:9: +2:10
scope 2 {
debug y => _2; // in scope 2 at $DIR/optimizes_into_variable.rs:+2:9: +2:10
- let _3: u32; // in scope 2 at $DIR/optimizes_into_variable.rs:+3:9: +3:10
scope 3 {
debug z => _3; // in scope 3 at $DIR/optimizes_into_variable.rs:+3:9: +3:10
}
bb0: {
StorageLive(_1); // scope 0 at $DIR/optimizes_into_variable.rs:+1:9: +1:10
StorageLive(_2); // scope 1 at $DIR/optimizes_into_variable.rs:+2:9: +2:10
- StorageLive(_3); // scope 2 at $DIR/optimizes_into_variable.rs:+3:9: +3:10
- StorageDead(_3); // scope 2 at $DIR/optimizes_into_variable.rs:+4:1: +4:2
StorageDead(_2); // scope 1 at $DIR/optimizes_into_variable.rs:+4:1: +4:2
StorageDead(_1); // scope 0 at $DIR/optimizes_into_variable.rs:+4:1: +4:2
return; // scope 0 at $DIR/optimizes_into_variable.rs:+4:2: +4:2
StorageDead(_4); // scope 1 at $DIR/optimizes_into_variable.rs:+2:34: +2:35
StorageLive(_8); // scope 2 at $DIR/optimizes_into_variable.rs:+3:9: +3:10
- StorageLive(_9); // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
-- Deinit(_9); // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
-- (_9.0: u32) = const 12_u32; // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
-- (_9.1: u32) = const 42_u32; // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
+- _9 = Point { x: const 12_u32, y: const 42_u32 }; // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
- _8 = (_9.1: u32); // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:38
- StorageDead(_9); // scope 2 at $DIR/optimizes_into_variable.rs:+3:38: +3:39
+ StorageLive(_10); // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
+ StorageLive(_11); // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
-+ Deinit(_10); // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
-+ Deinit(_11); // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
++ nop; // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
+ _10 = const 12_u32; // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
+ _11 = const 42_u32; // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
++ nop; // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
+ _8 = _11; // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:38
+ StorageDead(_10); // scope 2 at $DIR/optimizes_into_variable.rs:+3:38: +3:39
+ StorageDead(_11); // scope 2 at $DIR/optimizes_into_variable.rs:+3:38: +3:39
++ nop; // scope 2 at $DIR/optimizes_into_variable.rs:+3:38: +3:39
nop; // scope 0 at $DIR/optimizes_into_variable.rs:+0:11: +4:2
StorageDead(_8); // scope 2 at $DIR/optimizes_into_variable.rs:+4:1: +4:2
StorageDead(_3); // scope 1 at $DIR/optimizes_into_variable.rs:+4:1: +4:2
StorageDead(_4); // scope 1 at $DIR/optimizes_into_variable.rs:+2:34: +2:35
StorageLive(_8); // scope 2 at $DIR/optimizes_into_variable.rs:+3:9: +3:10
- StorageLive(_9); // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
-- Deinit(_9); // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
-- (_9.0: u32) = const 12_u32; // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
-- (_9.1: u32) = const 42_u32; // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
+- _9 = Point { x: const 12_u32, y: const 42_u32 }; // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
- _8 = (_9.1: u32); // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:38
- StorageDead(_9); // scope 2 at $DIR/optimizes_into_variable.rs:+3:38: +3:39
+ StorageLive(_10); // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
+ StorageLive(_11); // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
-+ Deinit(_10); // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
-+ Deinit(_11); // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
++ nop; // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
+ _10 = const 12_u32; // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
+ _11 = const 42_u32; // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
++ nop; // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
+ _8 = _11; // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:38
+ StorageDead(_10); // scope 2 at $DIR/optimizes_into_variable.rs:+3:38: +3:39
+ StorageDead(_11); // scope 2 at $DIR/optimizes_into_variable.rs:+3:38: +3:39
++ nop; // scope 2 at $DIR/optimizes_into_variable.rs:+3:38: +3:39
nop; // scope 0 at $DIR/optimizes_into_variable.rs:+0:11: +4:2
StorageDead(_8); // scope 2 at $DIR/optimizes_into_variable.rs:+4:1: +4:2
StorageDead(_3); // scope 1 at $DIR/optimizes_into_variable.rs:+4:1: +4:2
fn main() -> () {
let mut _0: (); // return place in scope 0 at $DIR/optimizes_into_variable.rs:+0:11: +0:11
let _1: i32; // in scope 0 at $DIR/optimizes_into_variable.rs:+1:9: +1:10
+ let mut _3: u32; // in scope 0 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
scope 1 {
debug x => _1; // in scope 1 at $DIR/optimizes_into_variable.rs:+1:9: +1:10
let _2: i32; // in scope 1 at $DIR/optimizes_into_variable.rs:+2:9: +2:10
scope 2 {
debug y => _2; // in scope 2 at $DIR/optimizes_into_variable.rs:+2:9: +2:10
- let _3: u32; // in scope 2 at $DIR/optimizes_into_variable.rs:+3:9: +3:10
scope 3 {
debug z => _3; // in scope 3 at $DIR/optimizes_into_variable.rs:+3:9: +3:10
}
bb0: {
StorageLive(_1); // scope 0 at $DIR/optimizes_into_variable.rs:+1:9: +1:10
StorageLive(_2); // scope 1 at $DIR/optimizes_into_variable.rs:+2:9: +2:10
- StorageLive(_3); // scope 2 at $DIR/optimizes_into_variable.rs:+3:9: +3:10
- StorageDead(_3); // scope 2 at $DIR/optimizes_into_variable.rs:+4:1: +4:2
StorageDead(_2); // scope 1 at $DIR/optimizes_into_variable.rs:+4:1: +4:2
StorageDead(_1); // scope 0 at $DIR/optimizes_into_variable.rs:+4:1: +4:2
return; // scope 0 at $DIR/optimizes_into_variable.rs:+4:2: +4:2
fn main() -> () {
let mut _0: (); // return place in scope 0 at $DIR/optimizes_into_variable.rs:+0:11: +0:11
let _1: i32; // in scope 0 at $DIR/optimizes_into_variable.rs:+1:9: +1:10
+ let mut _3: u32; // in scope 0 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
scope 1 {
debug x => _1; // in scope 1 at $DIR/optimizes_into_variable.rs:+1:9: +1:10
let _2: i32; // in scope 1 at $DIR/optimizes_into_variable.rs:+2:9: +2:10
scope 2 {
debug y => _2; // in scope 2 at $DIR/optimizes_into_variable.rs:+2:9: +2:10
- let _3: u32; // in scope 2 at $DIR/optimizes_into_variable.rs:+3:9: +3:10
scope 3 {
debug z => _3; // in scope 3 at $DIR/optimizes_into_variable.rs:+3:9: +3:10
}
bb0: {
StorageLive(_1); // scope 0 at $DIR/optimizes_into_variable.rs:+1:9: +1:10
StorageLive(_2); // scope 1 at $DIR/optimizes_into_variable.rs:+2:9: +2:10
- StorageLive(_3); // scope 2 at $DIR/optimizes_into_variable.rs:+3:9: +3:10
- StorageDead(_3); // scope 2 at $DIR/optimizes_into_variable.rs:+4:1: +4:2
StorageDead(_2); // scope 1 at $DIR/optimizes_into_variable.rs:+4:1: +4:2
StorageDead(_1); // scope 0 at $DIR/optimizes_into_variable.rs:+4:1: +4:2
return; // scope 0 at $DIR/optimizes_into_variable.rs:+4:2: +4:2
}
bb0: {
- StorageLive(_1); // scope 0 at $DIR/scalar_literal_propagation.rs:+1:9: +1:10
_1 = const 1_u32; // scope 0 at $DIR/scalar_literal_propagation.rs:+1:13: +1:14
StorageLive(_2); // scope 1 at $DIR/scalar_literal_propagation.rs:+2:5: +2:15
- _2 = consume(_1) -> bb1; // scope 1 at $DIR/scalar_literal_propagation.rs:+2:5: +2:15
}
bb0: {
- StorageLive(_1); // scope 0 at $DIR/tuple_literal_propagation.rs:+1:9: +1:10
- Deinit(_1); // scope 0 at $DIR/tuple_literal_propagation.rs:+1:13: +1:19
- (_1.0: u32) = const 1_u32; // scope 0 at $DIR/tuple_literal_propagation.rs:+1:13: +1:19
- (_1.1: u32) = const 2_u32; // scope 0 at $DIR/tuple_literal_propagation.rs:+1:13: +1:19
+- _1 = (const 1_u32, const 2_u32); // scope 0 at $DIR/tuple_literal_propagation.rs:+1:13: +1:19
++ _1 = const (1_u32, 2_u32); // scope 0 at $DIR/tuple_literal_propagation.rs:+1:13: +1:19
StorageLive(_2); // scope 1 at $DIR/tuple_literal_propagation.rs:+3:5: +3:15
- StorageLive(_3); // scope 1 at $DIR/tuple_literal_propagation.rs:+3:13: +3:14
-- _3 = _1; // scope 1 at $DIR/tuple_literal_propagation.rs:+3:13: +3:14
-+ _3 = const (1_u32, 2_u32); // scope 1 at $DIR/tuple_literal_propagation.rs:+3:13: +3:14
- _2 = consume(move _3) -> bb1; // scope 1 at $DIR/tuple_literal_propagation.rs:+3:5: +3:15
+ _2 = consume(_1) -> bb1; // scope 1 at $DIR/tuple_literal_propagation.rs:+3:5: +3:15
// mir::Constant
// + span: $DIR/tuple_literal_propagation.rs:5:5: 5:12
// + literal: Const { ty: fn((u32, u32)) {consume}, val: Value(<ZST>) }
}
bb1: {
- StorageDead(_3); // scope 1 at $DIR/tuple_literal_propagation.rs:+3:14: +3:15
StorageDead(_2); // scope 1 at $DIR/tuple_literal_propagation.rs:+3:15: +3:16
- StorageDead(_1); // scope 0 at $DIR/tuple_literal_propagation.rs:+4:1: +4:2
return; // scope 0 at $DIR/tuple_literal_propagation.rs:+4:2: +4:2
}
}
bb0: {
StorageLive(_1); // scope 0 at $DIR/const_prop_miscompile.rs:+1:9: +1:14
- Deinit(_1); // scope 0 at $DIR/const_prop_miscompile.rs:+1:17: +1:21
- (_1.0: i32) = const 1_i32; // scope 0 at $DIR/const_prop_miscompile.rs:+1:17: +1:21
+- _1 = (const 1_i32,); // scope 0 at $DIR/const_prop_miscompile.rs:+1:17: +1:21
++ _1 = const (1_i32,); // scope 0 at $DIR/const_prop_miscompile.rs:+1:17: +1:21
StorageLive(_2); // scope 1 at $DIR/const_prop_miscompile.rs:+2:5: +4:6
StorageLive(_3); // scope 2 at $DIR/const_prop_miscompile.rs:+3:10: +3:22
_3 = &raw mut (_1.0: i32); // scope 2 at $DIR/const_prop_miscompile.rs:+3:10: +3:22
bb0: {
StorageLive(_1); // scope 0 at $DIR/const_prop_miscompile.rs:+1:9: +1:14
- Deinit(_1); // scope 0 at $DIR/const_prop_miscompile.rs:+1:17: +1:21
- (_1.0: i32) = const 1_i32; // scope 0 at $DIR/const_prop_miscompile.rs:+1:17: +1:21
+- _1 = (const 1_i32,); // scope 0 at $DIR/const_prop_miscompile.rs:+1:17: +1:21
++ _1 = const (1_i32,); // scope 0 at $DIR/const_prop_miscompile.rs:+1:17: +1:21
StorageLive(_2); // scope 1 at $DIR/const_prop_miscompile.rs:+2:6: +2:14
_2 = &mut (_1.0: i32); // scope 1 at $DIR/const_prop_miscompile.rs:+2:6: +2:14
(*_2) = const 5_i32; // scope 1 at $DIR/const_prop_miscompile.rs:+2:5: +2:18
}
bb1: {
- StorageLive(_2); // scope 1 at $DIR/cycle.rs:+2:9: +2:10
+- StorageLive(_2); // scope 1 at $DIR/cycle.rs:+2:9: +2:10
_2 = _1; // scope 1 at $DIR/cycle.rs:+2:13: +2:14
- StorageLive(_3); // scope 2 at $DIR/cycle.rs:+3:9: +3:10
- _3 = _2; // scope 2 at $DIR/cycle.rs:+3:13: +3:14
}
bb0: {
- StorageLive(_2); // scope 0 at $DIR/dead_stores_79191.rs:+1:9: +1:10
_2 = _1; // scope 0 at $DIR/dead_stores_79191.rs:+1:13: +1:14
_1 = const 5_usize; // scope 1 at $DIR/dead_stores_79191.rs:+2:5: +2:10
_1 = _2; // scope 1 at $DIR/dead_stores_79191.rs:+3:5: +3:10
}
bb0: {
- StorageLive(_2); // scope 0 at $DIR/dead_stores_better.rs:+1:9: +1:10
_2 = _1; // scope 0 at $DIR/dead_stores_better.rs:+1:13: +1:14
_1 = const 5_usize; // scope 1 at $DIR/dead_stores_better.rs:+2:5: +2:10
_1 = _2; // scope 1 at $DIR/dead_stores_better.rs:+3:5: +3:10
--- /dev/null
+- // MIR for `main` before CopyProp
++ // MIR for `main` after CopyProp
+
+ fn main() -> () {
+ let mut _0: (); // return place in scope 0 at $DIR/issue_107511.rs:+0:11: +0:11
+ let mut _1: i32; // in scope 0 at $DIR/issue_107511.rs:+1:9: +1:16
+ let mut _3: std::ops::Range<usize>; // in scope 0 at $DIR/issue_107511.rs:+6:14: +6:24
+ let mut _4: std::ops::Range<usize>; // in scope 0 at $DIR/issue_107511.rs:+6:14: +6:24
+ let mut _5: usize; // in scope 0 at $DIR/issue_107511.rs:+6:17: +6:24
+ let mut _6: &[i32]; // in scope 0 at $DIR/issue_107511.rs:+6:17: +6:24
+ let mut _7: &[i32; 4]; // in scope 0 at $DIR/issue_107511.rs:+6:17: +6:24
+ let mut _9: (); // in scope 0 at $DIR/issue_107511.rs:+0:1: +9:2
+ let _10: (); // in scope 0 at $DIR/issue_107511.rs:+6:14: +6:24
+ let mut _11: std::option::Option<usize>; // in scope 0 at $DIR/issue_107511.rs:+6:14: +6:24
+ let mut _12: &mut std::ops::Range<usize>; // in scope 0 at $DIR/issue_107511.rs:+6:14: +6:24
+ let mut _13: &mut std::ops::Range<usize>; // in scope 0 at $DIR/issue_107511.rs:+6:14: +6:24
+ let mut _14: isize; // in scope 0 at $DIR/issue_107511.rs:+6:5: +8:6
+ let mut _15: !; // in scope 0 at $DIR/issue_107511.rs:+6:5: +8:6
+ let mut _17: i32; // in scope 0 at $DIR/issue_107511.rs:+7:16: +7:20
+ let _18: usize; // in scope 0 at $DIR/issue_107511.rs:+7:18: +7:19
+ let mut _19: usize; // in scope 0 at $DIR/issue_107511.rs:+7:16: +7:20
+ let mut _20: bool; // in scope 0 at $DIR/issue_107511.rs:+7:16: +7:20
+ scope 1 {
+ debug sum => _1; // in scope 1 at $DIR/issue_107511.rs:+1:9: +1:16
+ let _2: [i32; 4]; // in scope 1 at $DIR/issue_107511.rs:+2:9: +2:10
+ scope 2 {
+ debug a => _2; // in scope 2 at $DIR/issue_107511.rs:+2:9: +2:10
+ let mut _8: std::ops::Range<usize>; // in scope 2 at $DIR/issue_107511.rs:+6:14: +6:24
+ scope 3 {
+ debug iter => _8; // in scope 3 at $DIR/issue_107511.rs:+6:14: +6:24
+ let _16: usize; // in scope 3 at $DIR/issue_107511.rs:+6:9: +6:10
+ scope 4 {
+ debug i => _16; // in scope 4 at $DIR/issue_107511.rs:+6:9: +6:10
+ }
+ }
+ }
+ }
+
+ bb0: {
+ StorageLive(_1); // scope 0 at $DIR/issue_107511.rs:+1:9: +1:16
+ _1 = const 0_i32; // scope 0 at $DIR/issue_107511.rs:+1:19: +1:20
+ StorageLive(_2); // scope 1 at $DIR/issue_107511.rs:+2:9: +2:10
+ _2 = [const 0_i32, const 10_i32, const 20_i32, const 30_i32]; // scope 1 at $DIR/issue_107511.rs:+2:13: +2:28
+ StorageLive(_3); // scope 2 at $DIR/issue_107511.rs:+6:14: +6:24
+ StorageLive(_4); // scope 2 at $DIR/issue_107511.rs:+6:14: +6:24
+ StorageLive(_5); // scope 2 at $DIR/issue_107511.rs:+6:17: +6:24
+ StorageLive(_6); // scope 2 at $DIR/issue_107511.rs:+6:17: +6:24
+ StorageLive(_7); // scope 2 at $DIR/issue_107511.rs:+6:17: +6:24
+ _7 = &_2; // scope 2 at $DIR/issue_107511.rs:+6:17: +6:24
+ _6 = move _7 as &[i32] (Pointer(Unsize)); // scope 2 at $DIR/issue_107511.rs:+6:17: +6:24
+ StorageDead(_7); // scope 2 at $DIR/issue_107511.rs:+6:17: +6:18
+ _5 = core::slice::<impl [i32]>::len(move _6) -> bb1; // scope 2 at $DIR/issue_107511.rs:+6:17: +6:24
+ // mir::Constant
+ // + span: $DIR/issue_107511.rs:10:19: 10:22
+ // + literal: Const { ty: for<'a> fn(&'a [i32]) -> usize {core::slice::<impl [i32]>::len}, val: Value(<ZST>) }
+ }
+
+ bb1: {
+ StorageDead(_6); // scope 2 at $DIR/issue_107511.rs:+6:23: +6:24
+ _4 = std::ops::Range::<usize> { start: const 0_usize, end: move _5 }; // scope 2 at $DIR/issue_107511.rs:+6:14: +6:24
+ StorageDead(_5); // scope 2 at $DIR/issue_107511.rs:+6:23: +6:24
+ _3 = <std::ops::Range<usize> as IntoIterator>::into_iter(move _4) -> bb2; // scope 2 at $DIR/issue_107511.rs:+6:14: +6:24
+ // mir::Constant
+ // + span: $DIR/issue_107511.rs:10:14: 10:24
+ // + literal: Const { ty: fn(std::ops::Range<usize>) -> <std::ops::Range<usize> as IntoIterator>::IntoIter {<std::ops::Range<usize> as IntoIterator>::into_iter}, val: Value(<ZST>) }
+ }
+
+ bb2: {
+ StorageDead(_4); // scope 2 at $DIR/issue_107511.rs:+6:23: +6:24
+ StorageLive(_8); // scope 2 at $DIR/issue_107511.rs:+6:14: +6:24
+ _8 = move _3; // scope 2 at $DIR/issue_107511.rs:+6:14: +6:24
+ goto -> bb3; // scope 3 at $DIR/issue_107511.rs:+6:5: +8:6
+ }
+
+ bb3: {
+- StorageLive(_10); // scope 3 at $DIR/issue_107511.rs:+6:14: +6:24
+ StorageLive(_11); // scope 3 at $DIR/issue_107511.rs:+6:14: +6:24
+ StorageLive(_12); // scope 3 at $DIR/issue_107511.rs:+6:14: +6:24
+ StorageLive(_13); // scope 3 at $DIR/issue_107511.rs:+6:14: +6:24
+ _13 = &mut _8; // scope 3 at $DIR/issue_107511.rs:+6:14: +6:24
+ _12 = &mut (*_13); // scope 3 at $DIR/issue_107511.rs:+6:14: +6:24
+ _11 = <std::ops::Range<usize> as Iterator>::next(move _12) -> bb4; // scope 3 at $DIR/issue_107511.rs:+6:14: +6:24
+ // mir::Constant
+ // + span: $DIR/issue_107511.rs:10:14: 10:24
+ // + literal: Const { ty: for<'a> fn(&'a mut std::ops::Range<usize>) -> Option<<std::ops::Range<usize> as Iterator>::Item> {<std::ops::Range<usize> as Iterator>::next}, val: Value(<ZST>) }
+ }
+
+ bb4: {
+ StorageDead(_12); // scope 3 at $DIR/issue_107511.rs:+6:23: +6:24
+ _14 = discriminant(_11); // scope 3 at $DIR/issue_107511.rs:+6:14: +6:24
+ switchInt(move _14) -> [0: bb7, 1: bb5, otherwise: bb6]; // scope 3 at $DIR/issue_107511.rs:+6:14: +6:24
+ }
+
+ bb5: {
+- StorageLive(_16); // scope 3 at $DIR/issue_107511.rs:+6:9: +6:10
+ _16 = ((_11 as Some).0: usize); // scope 3 at $DIR/issue_107511.rs:+6:9: +6:10
+ StorageLive(_17); // scope 4 at $DIR/issue_107511.rs:+7:16: +7:20
+- StorageLive(_18); // scope 4 at $DIR/issue_107511.rs:+7:18: +7:19
+- _18 = _16; // scope 4 at $DIR/issue_107511.rs:+7:18: +7:19
+ _19 = Len(_2); // scope 4 at $DIR/issue_107511.rs:+7:16: +7:20
+- _20 = Lt(_18, _19); // scope 4 at $DIR/issue_107511.rs:+7:16: +7:20
+- assert(move _20, "index out of bounds: the length is {} but the index is {}", move _19, _18) -> bb8; // scope 4 at $DIR/issue_107511.rs:+7:16: +7:20
++ _20 = Lt(_16, _19); // scope 4 at $DIR/issue_107511.rs:+7:16: +7:20
++ assert(move _20, "index out of bounds: the length is {} but the index is {}", move _19, _16) -> bb8; // scope 4 at $DIR/issue_107511.rs:+7:16: +7:20
+ }
+
+ bb6: {
+ unreachable; // scope 3 at $DIR/issue_107511.rs:+6:14: +6:24
+ }
+
+ bb7: {
+ _0 = const (); // scope 3 at $DIR/issue_107511.rs:+6:5: +8:6
+ StorageDead(_13); // scope 3 at $DIR/issue_107511.rs:+8:5: +8:6
+ StorageDead(_11); // scope 3 at $DIR/issue_107511.rs:+8:5: +8:6
+- StorageDead(_10); // scope 3 at $DIR/issue_107511.rs:+8:5: +8:6
+ StorageDead(_8); // scope 2 at $DIR/issue_107511.rs:+8:5: +8:6
+ StorageDead(_3); // scope 2 at $DIR/issue_107511.rs:+8:5: +8:6
+ StorageDead(_2); // scope 1 at $DIR/issue_107511.rs:+9:1: +9:2
+ StorageDead(_1); // scope 0 at $DIR/issue_107511.rs:+9:1: +9:2
+ return; // scope 0 at $DIR/issue_107511.rs:+9:2: +9:2
+ }
+
+ bb8: {
+- _17 = _2[_18]; // scope 4 at $DIR/issue_107511.rs:+7:16: +7:20
++ _17 = _2[_16]; // scope 4 at $DIR/issue_107511.rs:+7:16: +7:20
+ _1 = Add(_1, move _17); // scope 4 at $DIR/issue_107511.rs:+7:9: +7:20
+ StorageDead(_17); // scope 4 at $DIR/issue_107511.rs:+7:19: +7:20
+- StorageDead(_18); // scope 4 at $DIR/issue_107511.rs:+7:20: +7:21
+- _10 = const (); // scope 4 at $DIR/issue_107511.rs:+6:25: +8:6
+- StorageDead(_16); // scope 3 at $DIR/issue_107511.rs:+8:5: +8:6
+ StorageDead(_13); // scope 3 at $DIR/issue_107511.rs:+8:5: +8:6
+ StorageDead(_11); // scope 3 at $DIR/issue_107511.rs:+8:5: +8:6
+- StorageDead(_10); // scope 3 at $DIR/issue_107511.rs:+8:5: +8:6
+- _9 = const (); // scope 3 at $DIR/issue_107511.rs:+6:5: +8:6
+ goto -> bb3; // scope 3 at $DIR/issue_107511.rs:+6:5: +8:6
+ }
+ }
+
--- /dev/null
+// unit-test: CopyProp
+
+// EMIT_MIR issue_107511.main.CopyProp.diff
+fn main() {
+ let mut sum = 0;
+ let a = [0, 10, 20, 30];
+
+ // `i` is assigned in a loop. Only removing its `StorageDead` would mean that
+ // execution sees repeated `StorageLive`. This would be UB.
+ for i in 0..a.len() {
+ sum += a[i];
+ }
+}
bb0: {
StorageLive(_1); // scope 0 at $DIR/enum.rs:+1:9: +1:10
- Deinit(_1); // scope 0 at $DIR/enum.rs:+1:13: +1:21
- ((_1 as V1).0: i32) = const 0_i32; // scope 0 at $DIR/enum.rs:+1:13: +1:21
- discriminant(_1) = 0; // scope 0 at $DIR/enum.rs:+1:13: +1:21
+ _1 = E::V1(const 0_i32); // scope 0 at $DIR/enum.rs:+1:13: +1:21
StorageLive(_2); // scope 1 at $DIR/enum.rs:+2:9: +2:10
_3 = discriminant(_1); // scope 1 at $DIR/enum.rs:+2:19: +2:20
switchInt(move _3) -> [0: bb3, 1: bb1, otherwise: bb2]; // scope 1 at $DIR/enum.rs:+2:13: +2:20
}
bb0: {
- StorageLive(_1); // scope 0 at $DIR/inherit_overflow.rs:+3:13: +3:47
_1 = const u8::MAX; // scope 0 at $DIR/inherit_overflow.rs:+3:13: +3:47
- StorageLive(_2); // scope 0 at $DIR/inherit_overflow.rs:+3:13: +3:47
_2 = const 1_u8; // scope 0 at $DIR/inherit_overflow.rs:+3:13: +3:47
_5 = CheckedAdd(const u8::MAX, const 1_u8); // scope 2 at $SRC_DIR/core/src/ops/arith.rs:LL:COL
assert(!move (_5.1: bool), "attempt to compute `{} + {}`, which would overflow", const u8::MAX, const 1_u8) -> bb1; // scope 2 at $SRC_DIR/core/src/ops/arith.rs:LL:COL
bb0: {
StorageLive(_1); // scope 0 at $DIR/repr_transparent.rs:+1:9: +1:10
- Deinit(_1); // scope 0 at $DIR/repr_transparent.rs:+1:13: +1:19
- (_1.0: i32) = const 0_i32; // scope 0 at $DIR/repr_transparent.rs:+1:13: +1:19
+ _1 = I32(const 0_i32); // scope 0 at $DIR/repr_transparent.rs:+1:13: +1:19
StorageLive(_2); // scope 1 at $DIR/repr_transparent.rs:+2:9: +2:10
StorageLive(_3); // scope 1 at $DIR/repr_transparent.rs:+2:17: +2:26
StorageLive(_4); // scope 1 at $DIR/repr_transparent.rs:+2:17: +2:20
+ _3 = const 0_i32; // scope 1 at $DIR/repr_transparent.rs:+2:17: +2:26
StorageDead(_5); // scope 1 at $DIR/repr_transparent.rs:+2:25: +2:26
StorageDead(_4); // scope 1 at $DIR/repr_transparent.rs:+2:25: +2:26
- Deinit(_2); // scope 1 at $DIR/repr_transparent.rs:+2:13: +2:27
-- (_2.0: i32) = move _3; // scope 1 at $DIR/repr_transparent.rs:+2:13: +2:27
-+ (_2.0: i32) = const 0_i32; // scope 1 at $DIR/repr_transparent.rs:+2:13: +2:27
+- _2 = I32(move _3); // scope 1 at $DIR/repr_transparent.rs:+2:13: +2:27
++ _2 = I32(const 0_i32); // scope 1 at $DIR/repr_transparent.rs:+2:13: +2:27
StorageDead(_3); // scope 1 at $DIR/repr_transparent.rs:+2:26: +2:27
_0 = const (); // scope 0 at $DIR/repr_transparent.rs:+0:11: +3:2
StorageDead(_2); // scope 1 at $DIR/repr_transparent.rs:+3:1: +3:2
bb0: {
StorageLive(_1); // scope 0 at $DIR/sibling_ptr.rs:+1:9: +1:14
- Deinit(_1); // scope 0 at $DIR/sibling_ptr.rs:+1:27: +1:33
- (_1.0: u8) = const 0_u8; // scope 0 at $DIR/sibling_ptr.rs:+1:27: +1:33
- (_1.1: u8) = const 0_u8; // scope 0 at $DIR/sibling_ptr.rs:+1:27: +1:33
+ _1 = (const 0_u8, const 0_u8); // scope 0 at $DIR/sibling_ptr.rs:+1:27: +1:33
StorageLive(_2); // scope 1 at $DIR/sibling_ptr.rs:+2:5: +5:6
StorageLive(_3); // scope 2 at $DIR/sibling_ptr.rs:+3:13: +3:14
_3 = &raw mut (_1.0: u8); // scope 2 at $SRC_DIR/core/src/ptr/mod.rs:LL:COL
bb0: {
StorageLive(_1); // scope 0 at $DIR/struct.rs:+1:9: +1:14
- Deinit(_1); // scope 0 at $DIR/struct.rs:+1:17: +1:21
- (_1.0: i32) = const 1_i32; // scope 0 at $DIR/struct.rs:+1:17: +1:21
+ _1 = S(const 1_i32); // scope 0 at $DIR/struct.rs:+1:17: +1:21
StorageLive(_2); // scope 1 at $DIR/struct.rs:+2:9: +2:10
StorageLive(_3); // scope 1 at $DIR/struct.rs:+2:13: +2:16
- _3 = (_1.0: i32); // scope 1 at $DIR/struct.rs:+2:13: +2:16
bb0: {
StorageLive(_1); // scope 0 at $DIR/tuple.rs:+1:9: +1:14
- Deinit(_1); // scope 0 at $DIR/tuple.rs:+1:17: +1:23
- (_1.0: i32) = const 1_i32; // scope 0 at $DIR/tuple.rs:+1:17: +1:23
- (_1.1: i32) = const 2_i32; // scope 0 at $DIR/tuple.rs:+1:17: +1:23
+ _1 = (const 1_i32, const 2_i32); // scope 0 at $DIR/tuple.rs:+1:17: +1:23
StorageLive(_2); // scope 1 at $DIR/tuple.rs:+2:9: +2:10
StorageLive(_3); // scope 1 at $DIR/tuple.rs:+2:13: +2:22
StorageLive(_4); // scope 1 at $DIR/tuple.rs:+2:13: +2:16
- _2 = Add(move _3, const 3_i32); // scope 1 at $DIR/tuple.rs:+2:13: +2:26
+ _2 = const 6_i32; // scope 1 at $DIR/tuple.rs:+2:13: +2:26
StorageDead(_3); // scope 1 at $DIR/tuple.rs:+2:25: +2:26
- Deinit(_1); // scope 2 at $DIR/tuple.rs:+3:5: +3:15
- (_1.0: i32) = const 2_i32; // scope 2 at $DIR/tuple.rs:+3:5: +3:15
- (_1.1: i32) = const 3_i32; // scope 2 at $DIR/tuple.rs:+3:5: +3:15
+ _1 = (const 2_i32, const 3_i32); // scope 2 at $DIR/tuple.rs:+3:5: +3:15
StorageLive(_6); // scope 2 at $DIR/tuple.rs:+4:9: +4:10
StorageLive(_7); // scope 2 at $DIR/tuple.rs:+4:13: +4:22
StorageLive(_8); // scope 2 at $DIR/tuple.rs:+4:13: +4:16
+++ /dev/null
-- // MIR for `bar` before Deaggregator
-+ // MIR for `bar` after Deaggregator
-
- fn bar(_1: usize) -> Baz {
- debug a => _1; // in scope 0 at $DIR/deaggregator_test.rs:+0:8: +0:9
- let mut _0: Baz; // return place in scope 0 at $DIR/deaggregator_test.rs:+0:21: +0:24
- let mut _2: usize; // in scope 0 at $DIR/deaggregator_test.rs:+1:14: +1:15
-
- bb0: {
- StorageLive(_2); // scope 0 at $DIR/deaggregator_test.rs:+1:14: +1:15
- _2 = _1; // scope 0 at $DIR/deaggregator_test.rs:+1:14: +1:15
-- _0 = Baz { x: move _2, y: const 0f32, z: const false }; // scope 0 at $DIR/deaggregator_test.rs:+1:5: +1:35
-+ Deinit(_0); // scope 0 at $DIR/deaggregator_test.rs:+1:5: +1:35
-+ (_0.0: usize) = move _2; // scope 0 at $DIR/deaggregator_test.rs:+1:5: +1:35
-+ (_0.1: f32) = const 0f32; // scope 0 at $DIR/deaggregator_test.rs:+1:5: +1:35
-+ (_0.2: bool) = const false; // scope 0 at $DIR/deaggregator_test.rs:+1:5: +1:35
- StorageDead(_2); // scope 0 at $DIR/deaggregator_test.rs:+1:34: +1:35
- return; // scope 0 at $DIR/deaggregator_test.rs:+2:2: +2:2
- }
- }
-
+++ /dev/null
-// unit-test: Deaggregator
-
-struct Baz {
- x: usize,
- y: f32,
- z: bool,
-}
-
-// EMIT_MIR deaggregator_test.bar.Deaggregator.diff
-fn bar(a: usize) -> Baz {
- Baz { x: a, y: 0.0, z: false }
-}
-
-fn main() {
- // Make sure the function actually gets instantiated.
- bar(0);
-}
+++ /dev/null
-- // MIR for `bar` before Deaggregator
-+ // MIR for `bar` after Deaggregator
-
- fn bar(_1: usize) -> Baz {
- debug a => _1; // in scope 0 at $DIR/deaggregator_test_enum.rs:+0:8: +0:9
- let mut _0: Baz; // return place in scope 0 at $DIR/deaggregator_test_enum.rs:+0:21: +0:24
- let mut _2: usize; // in scope 0 at $DIR/deaggregator_test_enum.rs:+1:19: +1:20
-
- bb0: {
- StorageLive(_2); // scope 0 at $DIR/deaggregator_test_enum.rs:+1:19: +1:20
- _2 = _1; // scope 0 at $DIR/deaggregator_test_enum.rs:+1:19: +1:20
-- _0 = Baz::Foo { x: move _2 }; // scope 0 at $DIR/deaggregator_test_enum.rs:+1:5: +1:22
-+ Deinit(_0); // scope 0 at $DIR/deaggregator_test_enum.rs:+1:5: +1:22
-+ ((_0 as Foo).0: usize) = move _2; // scope 0 at $DIR/deaggregator_test_enum.rs:+1:5: +1:22
-+ discriminant(_0) = 1; // scope 0 at $DIR/deaggregator_test_enum.rs:+1:5: +1:22
- StorageDead(_2); // scope 0 at $DIR/deaggregator_test_enum.rs:+1:21: +1:22
- return; // scope 0 at $DIR/deaggregator_test_enum.rs:+2:2: +2:2
- }
- }
-
+++ /dev/null
-// unit-test: Deaggregator
-
-enum Baz {
- Empty,
- Foo { x: usize },
-}
-
-// EMIT_MIR deaggregator_test_enum.bar.Deaggregator.diff
-fn bar(a: usize) -> Baz {
- Baz::Foo { x: a }
-}
-
-fn main() {
- let x = bar(10);
- match x {
- Baz::Empty => println!("empty"),
- Baz::Foo { x } => println!("{}", x),
- };
-}
+++ /dev/null
-// unit-test: Deaggregator
-// Test that deaggregate fires in more than one basic block
-
-enum Foo {
- A(i32),
- B(i32),
-}
-
-// EMIT_MIR deaggregator_test_enum_2.test1.Deaggregator.diff
-fn test1(x: bool, y: i32) -> Foo {
- if x {
- Foo::A(y)
- } else {
- Foo::B(y)
- }
-}
-
-fn main() {
- // Make sure the function actually gets instantiated.
- test1(false, 0);
-}
+++ /dev/null
-- // MIR for `test1` before Deaggregator
-+ // MIR for `test1` after Deaggregator
-
- fn test1(_1: bool, _2: i32) -> Foo {
- debug x => _1; // in scope 0 at $DIR/deaggregator_test_enum_2.rs:+0:10: +0:11
- debug y => _2; // in scope 0 at $DIR/deaggregator_test_enum_2.rs:+0:19: +0:20
- let mut _0: Foo; // return place in scope 0 at $DIR/deaggregator_test_enum_2.rs:+0:30: +0:33
- let mut _3: bool; // in scope 0 at $DIR/deaggregator_test_enum_2.rs:+1:8: +1:9
- let mut _4: i32; // in scope 0 at $DIR/deaggregator_test_enum_2.rs:+2:16: +2:17
- let mut _5: i32; // in scope 0 at $DIR/deaggregator_test_enum_2.rs:+4:16: +4:17
-
- bb0: {
- StorageLive(_3); // scope 0 at $DIR/deaggregator_test_enum_2.rs:+1:8: +1:9
- _3 = _1; // scope 0 at $DIR/deaggregator_test_enum_2.rs:+1:8: +1:9
- switchInt(move _3) -> [0: bb2, otherwise: bb1]; // scope 0 at $DIR/deaggregator_test_enum_2.rs:+1:8: +1:9
- }
-
- bb1: {
- StorageLive(_4); // scope 0 at $DIR/deaggregator_test_enum_2.rs:+2:16: +2:17
- _4 = _2; // scope 0 at $DIR/deaggregator_test_enum_2.rs:+2:16: +2:17
-- _0 = Foo::A(move _4); // scope 0 at $DIR/deaggregator_test_enum_2.rs:+2:9: +2:18
-+ Deinit(_0); // scope 0 at $DIR/deaggregator_test_enum_2.rs:+2:9: +2:18
-+ ((_0 as A).0: i32) = move _4; // scope 0 at $DIR/deaggregator_test_enum_2.rs:+2:9: +2:18
-+ discriminant(_0) = 0; // scope 0 at $DIR/deaggregator_test_enum_2.rs:+2:9: +2:18
- StorageDead(_4); // scope 0 at $DIR/deaggregator_test_enum_2.rs:+2:17: +2:18
- goto -> bb3; // scope 0 at $DIR/deaggregator_test_enum_2.rs:+1:5: +5:6
- }
-
- bb2: {
- StorageLive(_5); // scope 0 at $DIR/deaggregator_test_enum_2.rs:+4:16: +4:17
- _5 = _2; // scope 0 at $DIR/deaggregator_test_enum_2.rs:+4:16: +4:17
-- _0 = Foo::B(move _5); // scope 0 at $DIR/deaggregator_test_enum_2.rs:+4:9: +4:18
-+ Deinit(_0); // scope 0 at $DIR/deaggregator_test_enum_2.rs:+4:9: +4:18
-+ ((_0 as B).0: i32) = move _5; // scope 0 at $DIR/deaggregator_test_enum_2.rs:+4:9: +4:18
-+ discriminant(_0) = 1; // scope 0 at $DIR/deaggregator_test_enum_2.rs:+4:9: +4:18
- StorageDead(_5); // scope 0 at $DIR/deaggregator_test_enum_2.rs:+4:17: +4:18
- goto -> bb3; // scope 0 at $DIR/deaggregator_test_enum_2.rs:+1:5: +5:6
- }
-
- bb3: {
- StorageDead(_3); // scope 0 at $DIR/deaggregator_test_enum_2.rs:+5:5: +5:6
- return; // scope 0 at $DIR/deaggregator_test_enum_2.rs:+6:2: +6:2
- }
- }
-
+++ /dev/null
-// unit-test: Deaggregator
-// Test that deaggregate fires more than once per block
-
-enum Foo {
- A(i32),
- B,
-}
-
-// EMIT_MIR deaggregator_test_multiple.test.Deaggregator.diff
-fn test(x: i32) -> [Foo; 2] {
- [Foo::A(x), Foo::A(x)]
-}
-
-fn main() {
- // Make sure the function actually gets instantiated.
- test(0);
-}
+++ /dev/null
-- // MIR for `test` before Deaggregator
-+ // MIR for `test` after Deaggregator
-
- fn test(_1: i32) -> [Foo; 2] {
- debug x => _1; // in scope 0 at $DIR/deaggregator_test_multiple.rs:+0:9: +0:10
- let mut _0: [Foo; 2]; // return place in scope 0 at $DIR/deaggregator_test_multiple.rs:+0:20: +0:28
- let mut _2: Foo; // in scope 0 at $DIR/deaggregator_test_multiple.rs:+1:6: +1:15
- let mut _3: i32; // in scope 0 at $DIR/deaggregator_test_multiple.rs:+1:13: +1:14
- let mut _4: Foo; // in scope 0 at $DIR/deaggregator_test_multiple.rs:+1:17: +1:26
- let mut _5: i32; // in scope 0 at $DIR/deaggregator_test_multiple.rs:+1:24: +1:25
-
- bb0: {
- StorageLive(_2); // scope 0 at $DIR/deaggregator_test_multiple.rs:+1:6: +1:15
- StorageLive(_3); // scope 0 at $DIR/deaggregator_test_multiple.rs:+1:13: +1:14
- _3 = _1; // scope 0 at $DIR/deaggregator_test_multiple.rs:+1:13: +1:14
-- _2 = Foo::A(move _3); // scope 0 at $DIR/deaggregator_test_multiple.rs:+1:6: +1:15
-+ Deinit(_2); // scope 0 at $DIR/deaggregator_test_multiple.rs:+1:6: +1:15
-+ ((_2 as A).0: i32) = move _3; // scope 0 at $DIR/deaggregator_test_multiple.rs:+1:6: +1:15
-+ discriminant(_2) = 0; // scope 0 at $DIR/deaggregator_test_multiple.rs:+1:6: +1:15
- StorageDead(_3); // scope 0 at $DIR/deaggregator_test_multiple.rs:+1:14: +1:15
- StorageLive(_4); // scope 0 at $DIR/deaggregator_test_multiple.rs:+1:17: +1:26
- StorageLive(_5); // scope 0 at $DIR/deaggregator_test_multiple.rs:+1:24: +1:25
- _5 = _1; // scope 0 at $DIR/deaggregator_test_multiple.rs:+1:24: +1:25
-- _4 = Foo::A(move _5); // scope 0 at $DIR/deaggregator_test_multiple.rs:+1:17: +1:26
-+ Deinit(_4); // scope 0 at $DIR/deaggregator_test_multiple.rs:+1:17: +1:26
-+ ((_4 as A).0: i32) = move _5; // scope 0 at $DIR/deaggregator_test_multiple.rs:+1:17: +1:26
-+ discriminant(_4) = 0; // scope 0 at $DIR/deaggregator_test_multiple.rs:+1:17: +1:26
- StorageDead(_5); // scope 0 at $DIR/deaggregator_test_multiple.rs:+1:25: +1:26
- _0 = [move _2, move _4]; // scope 0 at $DIR/deaggregator_test_multiple.rs:+1:5: +1:27
- StorageDead(_4); // scope 0 at $DIR/deaggregator_test_multiple.rs:+1:26: +1:27
- StorageDead(_2); // scope 0 at $DIR/deaggregator_test_multiple.rs:+1:26: +1:27
- return; // scope 0 at $DIR/deaggregator_test_multiple.rs:+2:2: +2:2
- }
- }
-
_4 = _1; // scope 0 at $DIR/early_otherwise_branch.rs:+1:12: +1:13
StorageLive(_5); // scope 0 at $DIR/early_otherwise_branch.rs:+1:15: +1:16
_5 = _2; // scope 0 at $DIR/early_otherwise_branch.rs:+1:15: +1:16
- Deinit(_3); // scope 0 at $DIR/early_otherwise_branch.rs:+1:11: +1:17
- (_3.0: std::option::Option<u32>) = move _4; // scope 0 at $DIR/early_otherwise_branch.rs:+1:11: +1:17
- (_3.1: std::option::Option<u32>) = move _5; // scope 0 at $DIR/early_otherwise_branch.rs:+1:11: +1:17
+ _3 = (move _4, move _5); // scope 0 at $DIR/early_otherwise_branch.rs:+1:11: +1:17
StorageDead(_5); // scope 0 at $DIR/early_otherwise_branch.rs:+1:16: +1:17
StorageDead(_4); // scope 0 at $DIR/early_otherwise_branch.rs:+1:16: +1:17
_7 = discriminant((_3.0: std::option::Option<u32>)); // scope 0 at $DIR/early_otherwise_branch.rs:+1:11: +1:17
_4 = _1; // scope 0 at $DIR/early_otherwise_branch.rs:+1:12: +1:13
StorageLive(_5); // scope 0 at $DIR/early_otherwise_branch.rs:+1:15: +1:16
_5 = _2; // scope 0 at $DIR/early_otherwise_branch.rs:+1:15: +1:16
- Deinit(_3); // scope 0 at $DIR/early_otherwise_branch.rs:+1:11: +1:17
- (_3.0: std::option::Option<u32>) = move _4; // scope 0 at $DIR/early_otherwise_branch.rs:+1:11: +1:17
- (_3.1: std::option::Option<u32>) = move _5; // scope 0 at $DIR/early_otherwise_branch.rs:+1:11: +1:17
+ _3 = (move _4, move _5); // scope 0 at $DIR/early_otherwise_branch.rs:+1:11: +1:17
StorageDead(_5); // scope 0 at $DIR/early_otherwise_branch.rs:+1:16: +1:17
StorageDead(_4); // scope 0 at $DIR/early_otherwise_branch.rs:+1:16: +1:17
_8 = discriminant((_3.0: std::option::Option<u32>)); // scope 0 at $DIR/early_otherwise_branch.rs:+1:11: +1:17
_4 = _1; // scope 0 at $DIR/early_otherwise_branch.rs:+1:12: +1:13
StorageLive(_5); // scope 0 at $DIR/early_otherwise_branch.rs:+1:15: +1:16
_5 = _2; // scope 0 at $DIR/early_otherwise_branch.rs:+1:15: +1:16
- Deinit(_3); // scope 0 at $DIR/early_otherwise_branch.rs:+1:11: +1:17
- (_3.0: std::option::Option<u32>) = move _4; // scope 0 at $DIR/early_otherwise_branch.rs:+1:11: +1:17
- (_3.1: std::option::Option<bool>) = move _5; // scope 0 at $DIR/early_otherwise_branch.rs:+1:11: +1:17
+ _3 = (move _4, move _5); // scope 0 at $DIR/early_otherwise_branch.rs:+1:11: +1:17
StorageDead(_5); // scope 0 at $DIR/early_otherwise_branch.rs:+1:16: +1:17
StorageDead(_4); // scope 0 at $DIR/early_otherwise_branch.rs:+1:16: +1:17
_7 = discriminant((_3.0: std::option::Option<u32>)); // scope 0 at $DIR/early_otherwise_branch.rs:+1:11: +1:17
_6 = _2; // scope 0 at $DIR/early_otherwise_branch_3_element_tuple.rs:+1:15: +1:16
StorageLive(_7); // scope 0 at $DIR/early_otherwise_branch_3_element_tuple.rs:+1:18: +1:19
_7 = _3; // scope 0 at $DIR/early_otherwise_branch_3_element_tuple.rs:+1:18: +1:19
- Deinit(_4); // scope 0 at $DIR/early_otherwise_branch_3_element_tuple.rs:+1:11: +1:20
- (_4.0: std::option::Option<u32>) = move _5; // scope 0 at $DIR/early_otherwise_branch_3_element_tuple.rs:+1:11: +1:20
- (_4.1: std::option::Option<u32>) = move _6; // scope 0 at $DIR/early_otherwise_branch_3_element_tuple.rs:+1:11: +1:20
- (_4.2: std::option::Option<u32>) = move _7; // scope 0 at $DIR/early_otherwise_branch_3_element_tuple.rs:+1:11: +1:20
+ _4 = (move _5, move _6, move _7); // scope 0 at $DIR/early_otherwise_branch_3_element_tuple.rs:+1:11: +1:20
StorageDead(_7); // scope 0 at $DIR/early_otherwise_branch_3_element_tuple.rs:+1:19: +1:20
StorageDead(_6); // scope 0 at $DIR/early_otherwise_branch_3_element_tuple.rs:+1:19: +1:20
StorageDead(_5); // scope 0 at $DIR/early_otherwise_branch_3_element_tuple.rs:+1:19: +1:20
_5 = _1; // scope 0 at $DIR/early_otherwise_branch_68867.rs:+5:15: +5:16
StorageLive(_6); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+5:18: +5:23
_6 = _2; // scope 0 at $DIR/early_otherwise_branch_68867.rs:+5:18: +5:23
- Deinit(_4); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+5:14: +5:24
- (_4.0: &ViewportPercentageLength) = move _5; // scope 0 at $DIR/early_otherwise_branch_68867.rs:+5:14: +5:24
- (_4.1: &ViewportPercentageLength) = move _6; // scope 0 at $DIR/early_otherwise_branch_68867.rs:+5:14: +5:24
+ _4 = (move _5, move _6); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+5:14: +5:24
StorageDead(_6); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+5:23: +5:24
StorageDead(_5); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+5:23: +5:24
_34 = deref_copy (_4.0: &ViewportPercentageLength); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+5:14: +5:24
bb2: {
StorageLive(_33); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+10:25: +10:27
- Deinit(_33); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+10:25: +10:27
- Deinit(_0); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+10:21: +10:28
- ((_0 as Err).0: ()) = move _33; // scope 0 at $DIR/early_otherwise_branch_68867.rs:+10:21: +10:28
- discriminant(_0) = 1; // scope 0 at $DIR/early_otherwise_branch_68867.rs:+10:21: +10:28
+ _33 = (); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+10:25: +10:27
+ _0 = Result::<ViewportPercentageLength, ()>::Err(move _33); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+10:21: +10:28
StorageDead(_33); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+10:27: +10:28
StorageDead(_3); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+11:6: +11:7
StorageDead(_4); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+12:1: +12:2
_14 = Add(move _15, move _16); // scope 1 at $DIR/early_otherwise_branch_68867.rs:+6:38: +6:49
StorageDead(_16); // scope 1 at $DIR/early_otherwise_branch_68867.rs:+6:48: +6:49
StorageDead(_15); // scope 1 at $DIR/early_otherwise_branch_68867.rs:+6:48: +6:49
- Deinit(_3); // scope 1 at $DIR/early_otherwise_branch_68867.rs:+6:35: +6:50
- ((_3 as Vw).0: f32) = move _14; // scope 1 at $DIR/early_otherwise_branch_68867.rs:+6:35: +6:50
- discriminant(_3) = 0; // scope 1 at $DIR/early_otherwise_branch_68867.rs:+6:35: +6:50
+ _3 = ViewportPercentageLength::Vw(move _14); // scope 1 at $DIR/early_otherwise_branch_68867.rs:+6:35: +6:50
StorageDead(_14); // scope 1 at $DIR/early_otherwise_branch_68867.rs:+6:49: +6:50
StorageDead(_13); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+6:49: +6:50
StorageDead(_12); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+6:49: +6:50
_19 = Add(move _20, move _21); // scope 2 at $DIR/early_otherwise_branch_68867.rs:+7:38: +7:49
StorageDead(_21); // scope 2 at $DIR/early_otherwise_branch_68867.rs:+7:48: +7:49
StorageDead(_20); // scope 2 at $DIR/early_otherwise_branch_68867.rs:+7:48: +7:49
- Deinit(_3); // scope 2 at $DIR/early_otherwise_branch_68867.rs:+7:35: +7:50
- ((_3 as Vh).0: f32) = move _19; // scope 2 at $DIR/early_otherwise_branch_68867.rs:+7:35: +7:50
- discriminant(_3) = 1; // scope 2 at $DIR/early_otherwise_branch_68867.rs:+7:35: +7:50
+ _3 = ViewportPercentageLength::Vh(move _19); // scope 2 at $DIR/early_otherwise_branch_68867.rs:+7:35: +7:50
StorageDead(_19); // scope 2 at $DIR/early_otherwise_branch_68867.rs:+7:49: +7:50
StorageDead(_18); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+7:49: +7:50
StorageDead(_17); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+7:49: +7:50
_24 = Add(move _25, move _26); // scope 3 at $DIR/early_otherwise_branch_68867.rs:+8:44: +8:55
StorageDead(_26); // scope 3 at $DIR/early_otherwise_branch_68867.rs:+8:54: +8:55
StorageDead(_25); // scope 3 at $DIR/early_otherwise_branch_68867.rs:+8:54: +8:55
- Deinit(_3); // scope 3 at $DIR/early_otherwise_branch_68867.rs:+8:39: +8:56
- ((_3 as Vmin).0: f32) = move _24; // scope 3 at $DIR/early_otherwise_branch_68867.rs:+8:39: +8:56
- discriminant(_3) = 2; // scope 3 at $DIR/early_otherwise_branch_68867.rs:+8:39: +8:56
+ _3 = ViewportPercentageLength::Vmin(move _24); // scope 3 at $DIR/early_otherwise_branch_68867.rs:+8:39: +8:56
StorageDead(_24); // scope 3 at $DIR/early_otherwise_branch_68867.rs:+8:55: +8:56
StorageDead(_23); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+8:55: +8:56
StorageDead(_22); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+8:55: +8:56
_29 = Add(move _30, move _31); // scope 4 at $DIR/early_otherwise_branch_68867.rs:+9:44: +9:55
StorageDead(_31); // scope 4 at $DIR/early_otherwise_branch_68867.rs:+9:54: +9:55
StorageDead(_30); // scope 4 at $DIR/early_otherwise_branch_68867.rs:+9:54: +9:55
- Deinit(_3); // scope 4 at $DIR/early_otherwise_branch_68867.rs:+9:39: +9:56
- ((_3 as Vmax).0: f32) = move _29; // scope 4 at $DIR/early_otherwise_branch_68867.rs:+9:39: +9:56
- discriminant(_3) = 3; // scope 4 at $DIR/early_otherwise_branch_68867.rs:+9:39: +9:56
+ _3 = ViewportPercentageLength::Vmax(move _29); // scope 4 at $DIR/early_otherwise_branch_68867.rs:+9:39: +9:56
StorageDead(_29); // scope 4 at $DIR/early_otherwise_branch_68867.rs:+9:55: +9:56
StorageDead(_28); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+9:55: +9:56
StorageDead(_27); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+9:55: +9:56
}
bb10: {
- Deinit(_0); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+5:5: +11:7
- ((_0 as Ok).0: ViewportPercentageLength) = move _3; // scope 0 at $DIR/early_otherwise_branch_68867.rs:+5:5: +11:7
- discriminant(_0) = 0; // scope 0 at $DIR/early_otherwise_branch_68867.rs:+5:5: +11:7
+ _0 = Result::<ViewportPercentageLength, ()>::Ok(move _3); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+5:5: +11:7
StorageDead(_3); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+11:6: +11:7
StorageDead(_4); // scope 0 at $DIR/early_otherwise_branch_68867.rs:+12:1: +12:2
goto -> bb11; // scope 0 at $DIR/early_otherwise_branch_68867.rs:+12:2: +12:2
_4 = _1; // scope 0 at $DIR/early_otherwise_branch_noopt.rs:+1:12: +1:13
StorageLive(_5); // scope 0 at $DIR/early_otherwise_branch_noopt.rs:+1:15: +1:16
_5 = _2; // scope 0 at $DIR/early_otherwise_branch_noopt.rs:+1:15: +1:16
- Deinit(_3); // scope 0 at $DIR/early_otherwise_branch_noopt.rs:+1:11: +1:17
- (_3.0: std::option::Option<u32>) = move _4; // scope 0 at $DIR/early_otherwise_branch_noopt.rs:+1:11: +1:17
- (_3.1: std::option::Option<u32>) = move _5; // scope 0 at $DIR/early_otherwise_branch_noopt.rs:+1:11: +1:17
+ _3 = (move _4, move _5); // scope 0 at $DIR/early_otherwise_branch_noopt.rs:+1:11: +1:17
StorageDead(_5); // scope 0 at $DIR/early_otherwise_branch_noopt.rs:+1:16: +1:17
StorageDead(_4); // scope 0 at $DIR/early_otherwise_branch_noopt.rs:+1:16: +1:17
_8 = discriminant((_3.0: std::option::Option<u32>)); // scope 0 at $DIR/early_otherwise_branch_noopt.rs:+1:11: +1:17
}
bb2: {
- Deinit(_6); // scope 1 at $DIR/funky_arms.rs:+10:17: +10:41
- discriminant(_6) = 1; // scope 1 at $DIR/funky_arms.rs:+10:17: +10:41
+- _6 = MinusPlus; // scope 1 at $DIR/funky_arms.rs:+10:17: +10:41
++ _6 = const MinusPlus; // scope 1 at $DIR/funky_arms.rs:+10:17: +10:41
++ // mir::Constant
++ // + span: $DIR/funky_arms.rs:21:17: 21:41
++ // + literal: Const { ty: Sign, val: Value(Scalar(0x01)) }
goto -> bb4; // scope 1 at $DIR/funky_arms.rs:+10:17: +10:41
}
bb3: {
- Deinit(_6); // scope 1 at $DIR/funky_arms.rs:+9:18: +9:38
- discriminant(_6) = 0; // scope 1 at $DIR/funky_arms.rs:+9:18: +9:38
+- _6 = Minus; // scope 1 at $DIR/funky_arms.rs:+9:18: +9:38
++ _6 = const Minus; // scope 1 at $DIR/funky_arms.rs:+9:18: +9:38
++ // mir::Constant
++ // + span: $DIR/funky_arms.rs:20:18: 20:38
++ // + literal: Const { ty: Sign, val: Value(Scalar(0x00)) }
goto -> bb4; // scope 1 at $DIR/funky_arms.rs:+9:18: +9:38
}
}
bb6: {
- StorageLive(_10); // scope 3 at $DIR/funky_arms.rs:+13:17: +13:26
_10 = ((_7 as Some).0: usize); // scope 3 at $DIR/funky_arms.rs:+13:17: +13:26
StorageLive(_11); // scope 3 at $DIR/funky_arms.rs:+15:43: +15:46
_11 = &mut (*_1); // scope 3 at $DIR/funky_arms.rs:+15:43: +15:46
StorageLive(_6); // scope 1 at $DIR/generator_tiny.rs:+3:13: +3:18
StorageLive(_7); // scope 1 at $DIR/generator_tiny.rs:+3:13: +3:18
_7 = (); // scope 1 at $DIR/generator_tiny.rs:+3:13: +3:18
- Deinit(_0); // scope 1 at $DIR/generator_tiny.rs:+3:13: +3:18
- ((_0 as Yielded).0: ()) = move _7; // scope 1 at $DIR/generator_tiny.rs:+3:13: +3:18
- discriminant(_0) = 0; // scope 1 at $DIR/generator_tiny.rs:+3:13: +3:18
+ _0 = GeneratorState::<(), ()>::Yielded(move _7); // scope 1 at $DIR/generator_tiny.rs:+3:13: +3:18
discriminant((*(_1.0: &mut [generator@$DIR/generator_tiny.rs:19:16: 19:24]))) = 3; // scope 1 at $DIR/generator_tiny.rs:+3:13: +3:18
return; // scope 1 at $DIR/generator_tiny.rs:+3:13: +3:18
}
StorageLive(_3); // scope 0 at $DIR/cycle.rs:+1:5: +1:6
_3 = &_1; // scope 0 at $DIR/cycle.rs:+1:5: +1:6
StorageLive(_4); // scope 0 at $DIR/cycle.rs:+1:5: +1:8
- Deinit(_4); // scope 0 at $DIR/cycle.rs:+1:5: +1:8
+ _4 = (); // scope 0 at $DIR/cycle.rs:+1:5: +1:8
_2 = <impl Fn() as Fn<()>>::call(move _3, move _4) -> [return: bb1, unwind: bb3]; // scope 0 at $DIR/cycle.rs:+1:5: +1:8
// mir::Constant
// + span: $DIR/cycle.rs:6:5: 6:6
bb0: {
StorageLive(_3); // scope 0 at $DIR/inline_closure.rs:+1:9: +1:10
- Deinit(_3); // scope 0 at $DIR/inline_closure.rs:+1:13: +1:24
+ _3 = [closure@foo::<T>::{closure#0}]; // scope 0 at $DIR/inline_closure.rs:+1:13: +1:24
+ // closure
+ // + def_id: DefId(0:6 ~ inline_closure[92ba]::foo::{closure#0})
+ // + substs: [
+ // T,
+ // i8,
+ // extern "rust-call" fn((i32, i32)) -> i32,
+ // (),
+ // ]
StorageLive(_4); // scope 1 at $DIR/inline_closure.rs:+2:5: +2:6
_4 = &_3; // scope 1 at $DIR/inline_closure.rs:+2:5: +2:6
StorageLive(_5); // scope 1 at $DIR/inline_closure.rs:+2:5: +2:12
_6 = _2; // scope 1 at $DIR/inline_closure.rs:+2:7: +2:8
StorageLive(_7); // scope 1 at $DIR/inline_closure.rs:+2:10: +2:11
_7 = _2; // scope 1 at $DIR/inline_closure.rs:+2:10: +2:11
- Deinit(_5); // scope 1 at $DIR/inline_closure.rs:+2:5: +2:12
- (_5.0: i32) = move _6; // scope 1 at $DIR/inline_closure.rs:+2:5: +2:12
- (_5.1: i32) = move _7; // scope 1 at $DIR/inline_closure.rs:+2:5: +2:12
+ _5 = (move _6, move _7); // scope 1 at $DIR/inline_closure.rs:+2:5: +2:12
StorageLive(_8); // scope 1 at $DIR/inline_closure.rs:+2:5: +2:12
_8 = move (_5.0: i32); // scope 1 at $DIR/inline_closure.rs:+2:5: +2:12
StorageLive(_9); // scope 1 at $DIR/inline_closure.rs:+2:5: +2:12
bb0: {
StorageLive(_3); // scope 0 at $DIR/inline_closure_borrows_arg.rs:+1:9: +1:10
- Deinit(_3); // scope 0 at $DIR/inline_closure_borrows_arg.rs:+1:13: +4:6
+ _3 = [closure@foo::<T>::{closure#0}]; // scope 0 at $DIR/inline_closure_borrows_arg.rs:+1:13: +4:6
+ // closure
+ // + def_id: DefId(0:6 ~ inline_closure_borrows_arg[96e9]::foo::{closure#0})
+ // + substs: [
+ // T,
+ // i8,
+ // for<'a, 'b> extern "rust-call" fn((&'a i32, &'b i32)) -> i32,
+ // (),
+ // ]
StorageLive(_4); // scope 1 at $DIR/inline_closure_borrows_arg.rs:+5:5: +5:6
_4 = &_3; // scope 1 at $DIR/inline_closure_borrows_arg.rs:+5:5: +5:6
StorageLive(_5); // scope 1 at $DIR/inline_closure_borrows_arg.rs:+5:5: +5:12
_6 = &(*_2); // scope 1 at $DIR/inline_closure_borrows_arg.rs:+5:7: +5:8
StorageLive(_7); // scope 1 at $DIR/inline_closure_borrows_arg.rs:+5:10: +5:11
_7 = &(*_2); // scope 1 at $DIR/inline_closure_borrows_arg.rs:+5:10: +5:11
- Deinit(_5); // scope 1 at $DIR/inline_closure_borrows_arg.rs:+5:5: +5:12
- (_5.0: &i32) = move _6; // scope 1 at $DIR/inline_closure_borrows_arg.rs:+5:5: +5:12
- (_5.1: &i32) = move _7; // scope 1 at $DIR/inline_closure_borrows_arg.rs:+5:5: +5:12
+ _5 = (move _6, move _7); // scope 1 at $DIR/inline_closure_borrows_arg.rs:+5:5: +5:12
StorageLive(_8); // scope 1 at $DIR/inline_closure_borrows_arg.rs:+5:5: +5:12
_8 = move (_5.0: &i32); // scope 1 at $DIR/inline_closure_borrows_arg.rs:+5:5: +5:12
StorageLive(_9); // scope 1 at $DIR/inline_closure_borrows_arg.rs:+5:5: +5:12
_4 = &_2; // scope 0 at $DIR/inline_closure_captures.rs:+1:13: +1:24
StorageLive(_5); // scope 0 at $DIR/inline_closure_captures.rs:+1:13: +1:24
_5 = &_1; // scope 0 at $DIR/inline_closure_captures.rs:+1:13: +1:24
- Deinit(_3); // scope 0 at $DIR/inline_closure_captures.rs:+1:13: +1:24
- (_3.0: &i32) = move _4; // scope 0 at $DIR/inline_closure_captures.rs:+1:13: +1:24
- (_3.1: &T) = move _5; // scope 0 at $DIR/inline_closure_captures.rs:+1:13: +1:24
+ _3 = [closure@foo::<T>::{closure#0}] { q: move _4, t: move _5 }; // scope 0 at $DIR/inline_closure_captures.rs:+1:13: +1:24
+ // closure
+ // + def_id: DefId(0:6 ~ inline_closure_captures[8bc0]::foo::{closure#0})
+ // + substs: [
+ // T,
+ // i8,
+ // extern "rust-call" fn((i32,)) -> (i32, T),
+ // (&i32, &T),
+ // ]
StorageDead(_5); // scope 0 at $DIR/inline_closure_captures.rs:+1:16: +1:17
StorageDead(_4); // scope 0 at $DIR/inline_closure_captures.rs:+1:16: +1:17
StorageLive(_6); // scope 1 at $DIR/inline_closure_captures.rs:+2:5: +2:6
StorageLive(_7); // scope 1 at $DIR/inline_closure_captures.rs:+2:5: +2:9
StorageLive(_8); // scope 1 at $DIR/inline_closure_captures.rs:+2:7: +2:8
_8 = _2; // scope 1 at $DIR/inline_closure_captures.rs:+2:7: +2:8
- Deinit(_7); // scope 1 at $DIR/inline_closure_captures.rs:+2:5: +2:9
- (_7.0: i32) = move _8; // scope 1 at $DIR/inline_closure_captures.rs:+2:5: +2:9
+ _7 = (move _8,); // scope 1 at $DIR/inline_closure_captures.rs:+2:5: +2:9
StorageLive(_9); // scope 1 at $DIR/inline_closure_captures.rs:+2:5: +2:9
_9 = move (_7.0: i32); // scope 1 at $DIR/inline_closure_captures.rs:+2:5: +2:9
StorageLive(_10); // scope 2 at $DIR/inline_closure_captures.rs:+1:19: +1:20
StorageLive(_11); // scope 2 at $DIR/inline_closure_captures.rs:+1:22: +1:23
_13 = deref_copy ((*_6).1: &T); // scope 2 at $DIR/inline_closure_captures.rs:+1:22: +1:23
_11 = (*_13); // scope 2 at $DIR/inline_closure_captures.rs:+1:22: +1:23
- Deinit(_0); // scope 2 at $DIR/inline_closure_captures.rs:+1:18: +1:24
- (_0.0: i32) = move _10; // scope 2 at $DIR/inline_closure_captures.rs:+1:18: +1:24
- (_0.1: T) = move _11; // scope 2 at $DIR/inline_closure_captures.rs:+1:18: +1:24
+ _0 = (move _10, move _11); // scope 2 at $DIR/inline_closure_captures.rs:+1:18: +1:24
StorageDead(_11); // scope 2 at $DIR/inline_closure_captures.rs:+1:23: +1:24
StorageDead(_10); // scope 2 at $DIR/inline_closure_captures.rs:+1:23: +1:24
StorageDead(_9); // scope 1 at $DIR/inline_closure_captures.rs:+2:5: +2:9
+ StorageDead(_6); // scope 2 at $DIR/inline_diverging.rs:28:15: 28:16
+ StorageLive(_8); // scope 3 at $DIR/inline_diverging.rs:29:6: 29:7
+ _8 = move _3; // scope 3 at $DIR/inline_diverging.rs:29:6: 29:7
-+ Deinit(_1); // scope 3 at $DIR/inline_diverging.rs:29:5: 29:11
-+ (_1.0: !) = move _8; // scope 3 at $DIR/inline_diverging.rs:29:5: 29:11
-+ (_1.1: !) = move _9; // scope 3 at $DIR/inline_diverging.rs:29:5: 29:11
++ _1 = (move _8, move _9); // scope 3 at $DIR/inline_diverging.rs:29:5: 29:11
+ StorageDead(_8); // scope 3 at $DIR/inline_diverging.rs:29:10: 29:11
+ StorageDead(_3); // scope 1 at $DIR/inline_diverging.rs:30:1: 30:2
+ drop(_2) -> bb2; // scope 1 at $DIR/inline_diverging.rs:30:1: 30:2
- }
-
- bb1: {
-+ Deinit(_4); // scope 2 at $DIR/inline_generator.rs:15:5: 15:41
-+ discriminant(_4) = 0; // scope 2 at $DIR/inline_generator.rs:15:5: 15:41
++ _4 = [generator@$DIR/inline_generator.rs:15:5: 15:8 (#0)]; // scope 2 at $DIR/inline_generator.rs:15:5: 15:41
++ // generator
++ // + def_id: DefId(0:7 ~ inline_generator[ea31]::g::{closure#0})
++ // + substs: [
++ // bool,
++ // i32,
++ // bool,
++ // {bool, i32},
++ // (),
++ // ]
++ // + movability: Movable
_3 = &mut _4; // scope 0 at $DIR/inline_generator.rs:+1:23: +1:31
- _2 = Pin::<&mut [generator@$DIR/inline_generator.rs:15:5: 15:8]>::new(move _3) -> [return: bb2, unwind: bb4]; // scope 0 at $DIR/inline_generator.rs:+1:14: +1:32
- // mir::Constant
+ _5 = move _3; // scope 4 at $SRC_DIR/core/src/pin.rs:LL:COL
+ StorageLive(_6); // scope 5 at $SRC_DIR/core/src/pin.rs:LL:COL
+ _6 = move _5; // scope 5 at $SRC_DIR/core/src/pin.rs:LL:COL
-+ Deinit(_2); // scope 5 at $SRC_DIR/core/src/pin.rs:LL:COL
-+ (_2.0: &mut [generator@$DIR/inline_generator.rs:15:5: 15:8]) = move _6; // scope 5 at $SRC_DIR/core/src/pin.rs:LL:COL
++ _2 = Pin::<&mut [generator@$DIR/inline_generator.rs:15:5: 15:8]> { pointer: move _6 }; // scope 5 at $SRC_DIR/core/src/pin.rs:LL:COL
+ StorageDead(_6); // scope 5 at $SRC_DIR/core/src/pin.rs:LL:COL
+ StorageDead(_5); // scope 4 at $SRC_DIR/core/src/pin.rs:LL:COL
StorageDead(_3); // scope 0 at $DIR/inline_generator.rs:+1:31: +1:32
+ }
+
+ bb6: {
-+ Deinit(_1); // scope 6 at $DIR/inline_generator.rs:15:11: 15:39
-+ ((_1 as Yielded).0: i32) = move _8; // scope 6 at $DIR/inline_generator.rs:15:11: 15:39
-+ discriminant(_1) = 0; // scope 6 at $DIR/inline_generator.rs:15:11: 15:39
++ _1 = GeneratorState::<i32, bool>::Yielded(move _8); // scope 6 at $DIR/inline_generator.rs:15:11: 15:39
+ _11 = deref_copy (_2.0: &mut [generator@$DIR/inline_generator.rs:15:5: 15:8]); // scope 6 at $DIR/inline_generator.rs:15:11: 15:39
+ discriminant((*_11)) = 3; // scope 6 at $DIR/inline_generator.rs:15:11: 15:39
+ goto -> bb1; // scope 0 at $DIR/inline_generator.rs:15:11: 15:39
+ bb7: {
+ StorageLive(_8); // scope 6 at $DIR/inline_generator.rs:15:5: 15:41
+ StorageDead(_8); // scope 6 at $DIR/inline_generator.rs:15:38: 15:39
-+ Deinit(_1); // scope 6 at $DIR/inline_generator.rs:15:41: 15:41
-+ ((_1 as Complete).0: bool) = _7; // scope 6 at $DIR/inline_generator.rs:15:41: 15:41
-+ discriminant(_1) = 1; // scope 6 at $DIR/inline_generator.rs:15:41: 15:41
++ _1 = GeneratorState::<i32, bool>::Complete(_7); // scope 6 at $DIR/inline_generator.rs:15:41: 15:41
+ _12 = deref_copy (_2.0: &mut [generator@$DIR/inline_generator.rs:15:5: 15:8]); // scope 6 at $DIR/inline_generator.rs:15:41: 15:41
+ discriminant((*_12)) = 1; // scope 6 at $DIR/inline_generator.rs:15:41: 15:41
+ goto -> bb1; // scope 0 at $DIR/inline_generator.rs:15:41: 15:41
+ // + span: $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
+ // + user_ty: UserType(0)
+ // + literal: Const { ty: alloc::raw_vec::RawVec<u32>, val: Unevaluated(alloc::raw_vec::RawVec::<T>::NEW, [u32], None) }
-+ Deinit(_9); // scope 3 at $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
-+ (_9.0: alloc::raw_vec::RawVec<u32>) = move _10; // scope 3 at $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
-+ (_9.1: usize) = const 0_usize; // scope 3 at $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
++ _9 = Vec::<u32> { buf: move _10, len: const 0_usize }; // scope 3 at $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
+ StorageDead(_10); // scope 3 at $SRC_DIR/alloc/src/vec/mod.rs:LL:COL
+ (*_8) = move _9; // scope 0 at $DIR/inline_into_box_place.rs:+1:33: +1:43
+ StorageDead(_9); // scope 0 at $DIR/inline_into_box_place.rs:+1:33: +1:43
bb0: {
StorageLive(_1); // scope 0 at $DIR/issue_76997_inline_scopes_parenting.rs:+1:9: +1:10
- Deinit(_1); // scope 0 at $DIR/issue_76997_inline_scopes_parenting.rs:+1:13: +1:33
+ _1 = [closure@$DIR/issue_76997_inline_scopes_parenting.rs:5:13: 5:16]; // scope 0 at $DIR/issue_76997_inline_scopes_parenting.rs:+1:13: +1:33
+ // closure
+ // + def_id: DefId(0:4 ~ issue_76997_inline_scopes_parenting[bc59]::main::{closure#0})
+ // + substs: [
+ // i8,
+ // extern "rust-call" fn(((),)),
+ // (),
+ // ]
StorageLive(_2); // scope 1 at $DIR/issue_76997_inline_scopes_parenting.rs:+2:5: +2:6
_2 = &_1; // scope 1 at $DIR/issue_76997_inline_scopes_parenting.rs:+2:5: +2:6
StorageLive(_3); // scope 1 at $DIR/issue_76997_inline_scopes_parenting.rs:+2:5: +2:10
StorageLive(_4); // scope 1 at $DIR/issue_76997_inline_scopes_parenting.rs:+2:7: +2:9
- Deinit(_4); // scope 1 at $DIR/issue_76997_inline_scopes_parenting.rs:+2:7: +2:9
- Deinit(_3); // scope 1 at $DIR/issue_76997_inline_scopes_parenting.rs:+2:5: +2:10
- (_3.0: ()) = move _4; // scope 1 at $DIR/issue_76997_inline_scopes_parenting.rs:+2:5: +2:10
+ _4 = (); // scope 1 at $DIR/issue_76997_inline_scopes_parenting.rs:+2:7: +2:9
+ _3 = (move _4,); // scope 1 at $DIR/issue_76997_inline_scopes_parenting.rs:+2:5: +2:10
StorageLive(_5); // scope 1 at $DIR/issue_76997_inline_scopes_parenting.rs:+2:5: +2:10
_5 = move (_3.0: ()); // scope 1 at $DIR/issue_76997_inline_scopes_parenting.rs:+2:5: +2:10
StorageLive(_6); // scope 2 at $DIR/issue_76997_inline_scopes_parenting.rs:+1:23: +1:24
bb1: {
_3 = &_4; // scope 0 at $DIR/issue_78442.rs:+4:5: +4:15
StorageLive(_5); // scope 0 at $DIR/issue_78442.rs:+4:5: +4:17
- Deinit(_5); // scope 0 at $DIR/issue_78442.rs:+4:5: +4:17
+ _5 = (); // scope 0 at $DIR/issue_78442.rs:+4:5: +4:17
- _2 = <fn() {foo} as Fn<()>>::call(move _3, move _5) -> [return: bb2, unwind: bb4]; // scope 0 at $DIR/issue_78442.rs:+4:5: +4:17
- // mir::Constant
- // + span: $DIR/issue_78442.rs:11:5: 11:15
bb1: {
_3 = &_4; // scope 0 at $DIR/issue_78442.rs:+4:5: +4:15
StorageLive(_5); // scope 0 at $DIR/issue_78442.rs:+4:5: +4:17
- Deinit(_5); // scope 0 at $DIR/issue_78442.rs:+4:5: +4:17
+ _5 = (); // scope 0 at $DIR/issue_78442.rs:+4:5: +4:17
- _2 = <impl Fn() as Fn<()>>::call(move _3, move _5) -> [return: bb2, unwind: bb4]; // scope 0 at $DIR/issue_78442.rs:+4:5: +4:17
+ _2 = <fn() {foo} as Fn<()>>::call(move _3, move _5) -> [return: bb2, unwind: bb4]; // scope 0 at $DIR/issue_78442.rs:+4:5: +4:17
// mir::Constant
bb0: {
StorageLive(_2); // scope 0 at $DIR/issue_101973.rs:+1:5: +1:65
StorageLive(_3); // scope 0 at $DIR/issue_101973.rs:+1:5: +1:58
- StorageLive(_4); // scope 0 at $DIR/issue_101973.rs:+1:5: +1:17
StorageLive(_12); // scope 2 at $DIR/issue_101973.rs:7:12: 7:27
StorageLive(_13); // scope 2 at $DIR/issue_101973.rs:7:12: 7:20
_14 = CheckedShr(_1, const 0_i32); // scope 2 at $DIR/issue_101973.rs:7:12: 7:20
StorageDead(_13); // scope 2 at $DIR/issue_101973.rs:7:26: 7:27
_4 = BitOr(const 0_u32, move _12); // scope 2 at $DIR/issue_101973.rs:7:5: 7:27
StorageDead(_12); // scope 2 at $DIR/issue_101973.rs:7:26: 7:27
- StorageLive(_6); // scope 0 at $DIR/issue_101973.rs:+1:31: +1:57
StorageLive(_7); // scope 0 at $DIR/issue_101973.rs:+1:31: +1:52
StorageLive(_8); // scope 0 at $DIR/issue_101973.rs:+1:32: +1:45
_10 = CheckedShr(_1, const 8_i32); // scope 0 at $DIR/issue_101973.rs:+1:32: +1:45
bb0: {
StorageLive(_2); // scope 0 at $DIR/issue_76432.rs:+1:9: +1:10
- StorageLive(_4); // scope 0 at $DIR/issue_76432.rs:+1:19: +1:29
StorageLive(_5); // scope 0 at $DIR/issue_76432.rs:+1:20: +1:29
_5 = [_1, _1, _1]; // scope 0 at $DIR/issue_76432.rs:+1:20: +1:29
_4 = &_5; // scope 0 at $DIR/issue_76432.rs:+1:19: +1:29
bb7: {
StorageDead(_6); // scope 4 at $DIR/issue_75439.rs:+5:35: +5:36
- Deinit(_0); // scope 3 at $DIR/issue_75439.rs:+5:9: +5:39
- ((_0 as Some).0: [u8; 4]) = move _5; // scope 3 at $DIR/issue_75439.rs:+5:9: +5:39
- discriminant(_0) = 1; // scope 3 at $DIR/issue_75439.rs:+5:9: +5:39
+ _0 = Option::<[u8; 4]>::Some(move _5); // scope 3 at $DIR/issue_75439.rs:+5:9: +5:39
StorageDead(_5); // scope 3 at $DIR/issue_75439.rs:+5:38: +5:39
StorageDead(_4); // scope 1 at $DIR/issue_75439.rs:+6:5: +6:6
goto -> bb9; // scope 1 at $DIR/issue_75439.rs:+4:5: +8:6
}
bb8: {
- Deinit(_0); // scope 1 at $DIR/issue_75439.rs:+7:9: +7:13
- discriminant(_0) = 0; // scope 1 at $DIR/issue_75439.rs:+7:9: +7:13
+ _0 = Option::<[u8; 4]>::None; // scope 1 at $DIR/issue_75439.rs:+7:9: +7:13
goto -> bb9; // scope 1 at $DIR/issue_75439.rs:+4:5: +8:6
}
bb0: {
StorageLive(_1); // scope 0 at $DIR/lower_intrinsics.rs:+1:9: +1:12
- Deinit(_1); // scope 0 at $DIR/lower_intrinsics.rs:+1:15: +1:17
+ _1 = (); // scope 0 at $DIR/lower_intrinsics.rs:+1:15: +1:17
StorageLive(_2); // scope 1 at $DIR/lower_intrinsics.rs:+2:9: +2:16
- Deinit(_2); // scope 1 at $DIR/lower_intrinsics.rs:+2:19: +2:21
+ _2 = (); // scope 1 at $DIR/lower_intrinsics.rs:+2:19: +2:21
StorageLive(_3); // scope 3 at $DIR/lower_intrinsics.rs:+4:9: +4:95
StorageLive(_4); // scope 3 at $DIR/lower_intrinsics.rs:+4:29: +4:59
StorageLive(_5); // scope 3 at $DIR/lower_intrinsics.rs:+4:29: +4:45
- _3 = const false; // scope 4 at $DIR/matches_reduce_branches.rs:+16:13: +16:22
- _4 = const false; // scope 4 at $DIR/matches_reduce_branches.rs:+17:13: +17:22
- _5 = const true; // scope 4 at $DIR/matches_reduce_branches.rs:+18:13: +18:21
-- Deinit(_6); // scope 4 at $DIR/matches_reduce_branches.rs:+19:13: +19:15
+- _6 = (); // scope 4 at $DIR/matches_reduce_branches.rs:+19:13: +19:15
- goto -> bb3; // scope 4 at $DIR/matches_reduce_branches.rs:+19:13: +19:15
- }
-
+ _3 = Eq(_11, const 7_i32); // scope 4 at $DIR/matches_reduce_branches.rs:+9:13: +9:21
_4 = const false; // scope 4 at $DIR/matches_reduce_branches.rs:+10:13: +10:22
_5 = const true; // scope 4 at $DIR/matches_reduce_branches.rs:+11:13: +11:21
- Deinit(_6); // scope 4 at $DIR/matches_reduce_branches.rs:+12:13: +12:15
+ _6 = (); // scope 4 at $DIR/matches_reduce_branches.rs:+12:13: +12:15
- goto -> bb3; // scope 4 at $DIR/matches_reduce_branches.rs:+12:13: +12:15
- }
-
_9 = _4; // scope 4 at $DIR/matches_reduce_branches.rs:+23:12: +23:13
StorageLive(_10); // scope 4 at $DIR/matches_reduce_branches.rs:+23:15: +23:16
_10 = _5; // scope 4 at $DIR/matches_reduce_branches.rs:+23:15: +23:16
- Deinit(_0); // scope 4 at $DIR/matches_reduce_branches.rs:+23:5: +23:17
- (_0.0: bool) = move _7; // scope 4 at $DIR/matches_reduce_branches.rs:+23:5: +23:17
- (_0.1: bool) = move _8; // scope 4 at $DIR/matches_reduce_branches.rs:+23:5: +23:17
- (_0.2: bool) = move _9; // scope 4 at $DIR/matches_reduce_branches.rs:+23:5: +23:17
- (_0.3: bool) = move _10; // scope 4 at $DIR/matches_reduce_branches.rs:+23:5: +23:17
+ _0 = (move _7, move _8, move _9, move _10); // scope 4 at $DIR/matches_reduce_branches.rs:+23:5: +23:17
StorageDead(_10); // scope 4 at $DIR/matches_reduce_branches.rs:+23:16: +23:17
StorageDead(_9); // scope 4 at $DIR/matches_reduce_branches.rs:+23:16: +23:17
StorageDead(_8); // scope 4 at $DIR/matches_reduce_branches.rs:+23:16: +23:17
- }
-
- bb4: {
- Deinit(_0); // scope 0 at $DIR/matches_reduce_branches.rs:+2:9: +2:11
+ _0 = (); // scope 0 at $DIR/matches_reduce_branches.rs:+2:9: +2:11
- goto -> bb6; // scope 0 at $DIR/matches_reduce_branches.rs:+1:5: +3:6
+ goto -> bb3; // scope 0 at $DIR/matches_reduce_branches.rs:+1:5: +3:6
}
bb0: {
StorageLive(_1); // scope 0 at $DIR/matches_reduce_branches.rs:+1:9: +1:12
StorageLive(_2); // scope 0 at $DIR/matches_reduce_branches.rs:+1:21: +1:23
- Deinit(_2); // scope 0 at $DIR/matches_reduce_branches.rs:+1:21: +1:23
+ _2 = (); // scope 0 at $DIR/matches_reduce_branches.rs:+1:21: +1:23
StorageLive(_3); // scope 0 at $DIR/matches_reduce_branches.rs:+2:15: +6:10
StorageLive(_4); // scope 0 at $DIR/matches_reduce_branches.rs:+2:18: +2:76
StorageLive(_5); // scope 0 at $DIR/matches_reduce_branches.rs:+2:21: +2:52
StorageLive(_1); // scope 0 at $DIR/packed_struct_drop_aligned.rs:+1:9: +1:14
StorageLive(_2); // scope 0 at $DIR/packed_struct_drop_aligned.rs:+1:24: +1:42
StorageLive(_3); // scope 0 at $DIR/packed_struct_drop_aligned.rs:+1:32: +1:41
- Deinit(_3); // scope 0 at $DIR/packed_struct_drop_aligned.rs:+1:32: +1:41
- (_3.0: usize) = const 0_usize; // scope 0 at $DIR/packed_struct_drop_aligned.rs:+1:32: +1:41
- Deinit(_2); // scope 0 at $DIR/packed_struct_drop_aligned.rs:+1:24: +1:42
- (_2.0: Droppy) = move _3; // scope 0 at $DIR/packed_struct_drop_aligned.rs:+1:24: +1:42
+ _3 = Droppy(const 0_usize); // scope 0 at $DIR/packed_struct_drop_aligned.rs:+1:32: +1:41
+ _2 = Aligned(move _3); // scope 0 at $DIR/packed_struct_drop_aligned.rs:+1:24: +1:42
StorageDead(_3); // scope 0 at $DIR/packed_struct_drop_aligned.rs:+1:41: +1:42
- Deinit(_1); // scope 0 at $DIR/packed_struct_drop_aligned.rs:+1:17: +1:43
- (_1.0: Aligned) = move _2; // scope 0 at $DIR/packed_struct_drop_aligned.rs:+1:17: +1:43
+ _1 = Packed(move _2); // scope 0 at $DIR/packed_struct_drop_aligned.rs:+1:17: +1:43
StorageDead(_2); // scope 0 at $DIR/packed_struct_drop_aligned.rs:+1:42: +1:43
StorageLive(_4); // scope 1 at $DIR/packed_struct_drop_aligned.rs:+2:11: +2:29
StorageLive(_5); // scope 1 at $DIR/packed_struct_drop_aligned.rs:+2:19: +2:28
- Deinit(_5); // scope 1 at $DIR/packed_struct_drop_aligned.rs:+2:19: +2:28
- (_5.0: usize) = const 0_usize; // scope 1 at $DIR/packed_struct_drop_aligned.rs:+2:19: +2:28
- Deinit(_4); // scope 1 at $DIR/packed_struct_drop_aligned.rs:+2:11: +2:29
- (_4.0: Droppy) = move _5; // scope 1 at $DIR/packed_struct_drop_aligned.rs:+2:11: +2:29
+ _5 = Droppy(const 0_usize); // scope 1 at $DIR/packed_struct_drop_aligned.rs:+2:19: +2:28
+ _4 = Aligned(move _5); // scope 1 at $DIR/packed_struct_drop_aligned.rs:+2:11: +2:29
StorageDead(_5); // scope 1 at $DIR/packed_struct_drop_aligned.rs:+2:28: +2:29
StorageLive(_6); // scope 1 at $DIR/packed_struct_drop_aligned.rs:+2:5: +2:8
_6 = move (_1.0: Aligned); // scope 1 at $DIR/packed_struct_drop_aligned.rs:+2:5: +2:8
_1 = const 0_i32; // scope 0 at $DIR/remove_storage_markers.rs:+1:19: +1:20
- StorageLive(_2); // scope 1 at $DIR/remove_storage_markers.rs:+2:14: +2:19
- StorageLive(_3); // scope 1 at $DIR/remove_storage_markers.rs:+2:14: +2:19
- Deinit(_3); // scope 1 at $DIR/remove_storage_markers.rs:+2:14: +2:19
- (_3.0: i32) = const 0_i32; // scope 1 at $DIR/remove_storage_markers.rs:+2:14: +2:19
- (_3.1: i32) = const 10_i32; // scope 1 at $DIR/remove_storage_markers.rs:+2:14: +2:19
+ _3 = std::ops::Range::<i32> { start: const 0_i32, end: const 10_i32 }; // scope 1 at $DIR/remove_storage_markers.rs:+2:14: +2:19
_2 = <std::ops::Range<i32> as IntoIterator>::into_iter(move _3) -> bb1; // scope 1 at $DIR/remove_storage_markers.rs:+2:14: +2:19
// mir::Constant
// + span: $DIR/remove_storage_markers.rs:10:14: 10:19
fn get_union() -> Foo {
let mut _0: Foo; // return place in scope 0 at $DIR/remove_zsts.rs:+0:19: +0:22
+ let mut _1: (); // in scope 0 at $DIR/remove_zsts.rs:+1:14: +1:16
bb0: {
- Deinit(_0); // scope 0 at $DIR/remove_zsts.rs:+1:5: +1:18
+ StorageLive(_1); // scope 0 at $DIR/remove_zsts.rs:+1:14: +1:16
+ _0 = Foo { x: move _1 }; // scope 0 at $DIR/remove_zsts.rs:+1:5: +1:18
+ StorageDead(_1); // scope 0 at $DIR/remove_zsts.rs:+1:17: +1:18
return; // scope 0 at $DIR/remove_zsts.rs:+2:2: +2:2
}
}
bb0: {
StorageLive(_1); // scope 0 at $DIR/remove_zsts.rs:+1:14: +1:16
-- Deinit(_1); // scope 0 at $DIR/remove_zsts.rs:+1:14: +1:16
+- _1 = (); // scope 0 at $DIR/remove_zsts.rs:+1:14: +1:16
+ nop; // scope 0 at $DIR/remove_zsts.rs:+1:14: +1:16
- Deinit(_0); // scope 0 at $DIR/remove_zsts.rs:+1:5: +1:18
-- (_0.0: ()) = move _1; // scope 0 at $DIR/remove_zsts.rs:+1:5: +1:18
-+ nop; // scope 0 at $DIR/remove_zsts.rs:+1:5: +1:18
+ _0 = Foo { x: move _1 }; // scope 0 at $DIR/remove_zsts.rs:+1:5: +1:18
StorageDead(_1); // scope 0 at $DIR/remove_zsts.rs:+1:17: +1:18
return; // scope 0 at $DIR/remove_zsts.rs:+2:2: +2:2
}
// + literal: Const { ty: &usize, val: Unevaluated(array_casts, [], Some(promoted[0])) }
Retag(_35); // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
_18 = &(*_35); // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- Deinit(_13); // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- (_13.0: &usize) = move _14; // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- (_13.1: &usize) = move _18; // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+ _13 = (move _14, move _18); // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
Retag(_13); // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
StorageDead(_18); // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
StorageDead(_14); // scope 5 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
bb3: {
StorageLive(_27); // scope 7 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- Deinit(_27); // scope 7 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- discriminant(_27) = 0; // scope 7 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+ _27 = core::panicking::AssertKind::Eq; // scope 7 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
StorageLive(_28); // scope 8 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
StorageLive(_29); // scope 8 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
_29 = move _27; // scope 8 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
_33 = &(*_21); // scope 8 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
_32 = &(*_33); // scope 8 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
StorageLive(_34); // scope 8 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- Deinit(_34); // scope 8 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
- discriminant(_34) = 0; // scope 8 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
+ _34 = Option::<Arguments<'_>>::None; // scope 8 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
Retag(_34); // scope 8 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
_28 = core::panicking::assert_failed::<usize, usize>(move _29, move _30, move _32, move _34); // scope 8 at $SRC_DIR/core/src/macros/mod.rs:LL:COL
// mir::Constant
StorageLive(_3); // scope 1 at $DIR/retag.rs:+3:13: +3:14
StorageLive(_4); // scope 1 at $DIR/retag.rs:+3:17: +3:36
StorageLive(_5); // scope 1 at $DIR/retag.rs:+3:17: +3:24
- Deinit(_5); // scope 1 at $DIR/retag.rs:+3:17: +3:24
- (_5.0: i32) = const 0_i32; // scope 1 at $DIR/retag.rs:+3:17: +3:24
+ _5 = Test(const 0_i32); // scope 1 at $DIR/retag.rs:+3:17: +3:24
_4 = &_5; // scope 1 at $DIR/retag.rs:+3:17: +3:36
StorageLive(_6); // scope 1 at $DIR/retag.rs:+3:29: +3:35
StorageLive(_7); // scope 1 at $DIR/retag.rs:+3:29: +3:35
StorageDead(_2); // scope 1 at $DIR/retag.rs:+8:5: +8:6
StorageLive(_13); // scope 1 at $DIR/retag.rs:+11:9: +11:10
StorageLive(_14); // scope 1 at $DIR/retag.rs:+11:31: +14:6
- Deinit(_14); // scope 1 at $DIR/retag.rs:+11:31: +14:6
+ _14 = [closure@main::{closure#0}]; // scope 1 at $DIR/retag.rs:+11:31: +14:6
+ // closure
+ // + def_id: DefId(0:14 ~ retag[4622]::main::{closure#0})
+ // + substs: [
+ // i8,
+ // for<'a> extern "rust-call" fn((&'a i32,)) -> &'a i32,
+ // (),
+ // ]
Retag(_14); // scope 1 at $DIR/retag.rs:+11:31: +14:6
_13 = move _14 as for<'a> fn(&'a i32) -> &'a i32 (Pointer(ClosureFnPointer(Normal))); // scope 1 at $DIR/retag.rs:+11:31: +14:6
StorageDead(_14); // scope 1 at $DIR/retag.rs:+11:47: +11:48
StorageLive(_19); // scope 7 at $DIR/retag.rs:+18:5: +18:24
StorageLive(_20); // scope 7 at $DIR/retag.rs:+18:5: +18:24
StorageLive(_21); // scope 7 at $DIR/retag.rs:+18:5: +18:12
- Deinit(_21); // scope 7 at $DIR/retag.rs:+18:5: +18:12
- (_21.0: i32) = const 0_i32; // scope 7 at $DIR/retag.rs:+18:5: +18:12
+ _21 = Test(const 0_i32); // scope 7 at $DIR/retag.rs:+18:5: +18:12
_20 = &_21; // scope 7 at $DIR/retag.rs:+18:5: +18:24
StorageLive(_22); // scope 7 at $DIR/retag.rs:+18:21: +18:23
StorageLive(_23); // scope 7 at $DIR/retag.rs:+18:21: +18:23
StorageLive(_4); // scope 0 at $DIR/separate_const_switch.rs:+1:8: +1:9
_4 = _1; // scope 0 at $DIR/separate_const_switch.rs:+1:8: +1:9
_10 = discriminant(_4); // scope 5 at $SRC_DIR/core/src/result.rs:LL:COL
-- switchInt(move _10) -> [0: bb7, 1: bb5, otherwise: bb6]; // scope 5 at $SRC_DIR/core/src/result.rs:LL:COL
-+ switchInt(move _10) -> [0: bb6, 1: bb4, otherwise: bb5]; // scope 5 at $SRC_DIR/core/src/result.rs:LL:COL
+ switchInt(move _10) -> [0: bb7, 1: bb5, otherwise: bb6]; // scope 5 at $SRC_DIR/core/src/result.rs:LL:COL
}
bb1: {
-- StorageDead(_4); // scope 0 at $DIR/separate_const_switch.rs:+1:9: +1:10
-- _5 = discriminant(_3); // scope 0 at $DIR/separate_const_switch.rs:+1:8: +1:10
-- switchInt(move _5) -> [0: bb2, 1: bb4, otherwise: bb3]; // scope 0 at $DIR/separate_const_switch.rs:+1:8: +1:10
-- }
--
-- bb2: {
+ StorageDead(_4); // scope 0 at $DIR/separate_const_switch.rs:+1:9: +1:10
+ _5 = discriminant(_3); // scope 0 at $DIR/separate_const_switch.rs:+1:8: +1:10
+ switchInt(move _5) -> [0: bb2, 1: bb4, otherwise: bb3]; // scope 0 at $DIR/separate_const_switch.rs:+1:8: +1:10
+ }
+
+ bb2: {
StorageLive(_9); // scope 0 at $DIR/separate_const_switch.rs:+1:8: +1:10
_9 = ((_3 as Continue).0: i32); // scope 0 at $DIR/separate_const_switch.rs:+1:8: +1:10
_2 = _9; // scope 4 at $DIR/separate_const_switch.rs:+1:8: +1:10
StorageDead(_9); // scope 0 at $DIR/separate_const_switch.rs:+1:9: +1:10
- Deinit(_0); // scope 0 at $DIR/separate_const_switch.rs:+1:5: +1:11
- ((_0 as Ok).0: i32) = move _2; // scope 0 at $DIR/separate_const_switch.rs:+1:5: +1:11
- discriminant(_0) = 0; // scope 0 at $DIR/separate_const_switch.rs:+1:5: +1:11
+ _0 = Result::<i32, i32>::Ok(move _2); // scope 0 at $DIR/separate_const_switch.rs:+1:5: +1:11
StorageDead(_2); // scope 0 at $DIR/separate_const_switch.rs:+1:10: +1:11
StorageDead(_3); // scope 0 at $DIR/separate_const_switch.rs:+2:1: +2:2
return; // scope 0 at $DIR/separate_const_switch.rs:+2:2: +2:2
}
-- bb3: {
-+ bb2: {
+ bb3: {
unreachable; // scope 0 at $DIR/separate_const_switch.rs:+1:8: +1:10
}
-- bb4: {
-+ bb3: {
+ bb4: {
StorageLive(_6); // scope 0 at $DIR/separate_const_switch.rs:+1:9: +1:10
_6 = ((_3 as Break).0: std::result::Result<std::convert::Infallible, i32>); // scope 0 at $DIR/separate_const_switch.rs:+1:9: +1:10
StorageLive(_8); // scope 2 at $DIR/separate_const_switch.rs:+1:9: +1:10
_18 = move _16; // scope 9 at $SRC_DIR/core/src/result.rs:LL:COL
_17 = move _18; // scope 10 at $SRC_DIR/core/src/convert/mod.rs:LL:COL
StorageDead(_18); // scope 9 at $SRC_DIR/core/src/result.rs:LL:COL
- Deinit(_0); // scope 9 at $SRC_DIR/core/src/result.rs:LL:COL
- ((_0 as Err).0: i32) = move _17; // scope 9 at $SRC_DIR/core/src/result.rs:LL:COL
- discriminant(_0) = 1; // scope 9 at $SRC_DIR/core/src/result.rs:LL:COL
+ _0 = Result::<i32, i32>::Err(move _17); // scope 9 at $SRC_DIR/core/src/result.rs:LL:COL
StorageDead(_17); // scope 9 at $SRC_DIR/core/src/result.rs:LL:COL
StorageDead(_16); // scope 8 at $SRC_DIR/core/src/result.rs:LL:COL
StorageDead(_8); // scope 2 at $DIR/separate_const_switch.rs:+1:9: +1:10
return; // scope 0 at $DIR/separate_const_switch.rs:+2:2: +2:2
}
-- bb5: {
-+ bb4: {
+ bb5: {
StorageLive(_13); // scope 5 at $SRC_DIR/core/src/result.rs:LL:COL
_13 = move ((_4 as Err).0: i32); // scope 5 at $SRC_DIR/core/src/result.rs:LL:COL
StorageLive(_14); // scope 7 at $SRC_DIR/core/src/result.rs:LL:COL
StorageLive(_15); // scope 7 at $SRC_DIR/core/src/result.rs:LL:COL
_15 = move _13; // scope 7 at $SRC_DIR/core/src/result.rs:LL:COL
- Deinit(_14); // scope 7 at $SRC_DIR/core/src/result.rs:LL:COL
- ((_14 as Err).0: i32) = move _15; // scope 7 at $SRC_DIR/core/src/result.rs:LL:COL
- discriminant(_14) = 1; // scope 7 at $SRC_DIR/core/src/result.rs:LL:COL
+ _14 = Result::<Infallible, i32>::Err(move _15); // scope 7 at $SRC_DIR/core/src/result.rs:LL:COL
StorageDead(_15); // scope 7 at $SRC_DIR/core/src/result.rs:LL:COL
- Deinit(_3); // scope 7 at $SRC_DIR/core/src/result.rs:LL:COL
- ((_3 as Break).0: std::result::Result<std::convert::Infallible, i32>) = move _14; // scope 7 at $SRC_DIR/core/src/result.rs:LL:COL
- discriminant(_3) = 1; // scope 7 at $SRC_DIR/core/src/result.rs:LL:COL
+ _3 = ControlFlow::<Result<Infallible, i32>, i32>::Break(move _14); // scope 7 at $SRC_DIR/core/src/result.rs:LL:COL
StorageDead(_14); // scope 7 at $SRC_DIR/core/src/result.rs:LL:COL
StorageDead(_13); // scope 5 at $SRC_DIR/core/src/result.rs:LL:COL
-- goto -> bb1; // scope 5 at $SRC_DIR/core/src/result.rs:LL:COL
-+ StorageDead(_4); // scope 0 at $DIR/separate_const_switch.rs:+1:9: +1:10
-+ _5 = discriminant(_3); // scope 0 at $DIR/separate_const_switch.rs:+1:8: +1:10
-+ switchInt(move _5) -> [0: bb1, 1: bb3, otherwise: bb2]; // scope 0 at $DIR/separate_const_switch.rs:+1:8: +1:10
+ goto -> bb1; // scope 5 at $SRC_DIR/core/src/result.rs:LL:COL
}
-- bb6: {
-+ bb5: {
+ bb6: {
unreachable; // scope 5 at $SRC_DIR/core/src/result.rs:LL:COL
}
-- bb7: {
-+ bb6: {
+ bb7: {
StorageLive(_11); // scope 5 at $SRC_DIR/core/src/result.rs:LL:COL
_11 = move ((_4 as Ok).0: i32); // scope 5 at $SRC_DIR/core/src/result.rs:LL:COL
StorageLive(_12); // scope 6 at $SRC_DIR/core/src/result.rs:LL:COL
_12 = move _11; // scope 6 at $SRC_DIR/core/src/result.rs:LL:COL
- Deinit(_3); // scope 6 at $SRC_DIR/core/src/result.rs:LL:COL
- ((_3 as Continue).0: i32) = move _12; // scope 6 at $SRC_DIR/core/src/result.rs:LL:COL
- discriminant(_3) = 0; // scope 6 at $SRC_DIR/core/src/result.rs:LL:COL
+ _3 = ControlFlow::<Result<Infallible, i32>, i32>::Continue(move _12); // scope 6 at $SRC_DIR/core/src/result.rs:LL:COL
StorageDead(_12); // scope 6 at $SRC_DIR/core/src/result.rs:LL:COL
StorageDead(_11); // scope 5 at $SRC_DIR/core/src/result.rs:LL:COL
-- goto -> bb1; // scope 5 at $SRC_DIR/core/src/result.rs:LL:COL
-+ StorageDead(_4); // scope 0 at $DIR/separate_const_switch.rs:+1:9: +1:10
-+ _5 = discriminant(_3); // scope 0 at $DIR/separate_const_switch.rs:+1:8: +1:10
-+ switchInt(move _5) -> [0: bb1, 1: bb3, otherwise: bb2]; // scope 0 at $DIR/separate_const_switch.rs:+1:8: +1:10
+ goto -> bb1; // scope 5 at $SRC_DIR/core/src/result.rs:LL:COL
}
}
_6 = ((_1 as Err).0: usize); // scope 0 at $DIR/separate_const_switch.rs:+8:17: +8:18
StorageLive(_7); // scope 2 at $DIR/separate_const_switch.rs:+8:42: +8:43
_7 = _6; // scope 2 at $DIR/separate_const_switch.rs:+8:42: +8:43
- Deinit(_2); // scope 2 at $DIR/separate_const_switch.rs:+8:23: +8:44
- ((_2 as Break).0: usize) = move _7; // scope 2 at $DIR/separate_const_switch.rs:+8:23: +8:44
- discriminant(_2) = 1; // scope 2 at $DIR/separate_const_switch.rs:+8:23: +8:44
+ _2 = ControlFlow::<usize, i32>::Break(move _7); // scope 2 at $DIR/separate_const_switch.rs:+8:23: +8:44
StorageDead(_7); // scope 2 at $DIR/separate_const_switch.rs:+8:43: +8:44
StorageDead(_6); // scope 0 at $DIR/separate_const_switch.rs:+8:43: +8:44
-- goto -> bb4; // scope 0 at $DIR/separate_const_switch.rs:+8:43: +8:44
-+ _8 = discriminant(_2); // scope 0 at $DIR/separate_const_switch.rs:+5:11: +10:6
-+ switchInt(move _8) -> [0: bb6, 1: bb4, otherwise: bb5]; // scope 0 at $DIR/separate_const_switch.rs:+5:5: +10:6
+ goto -> bb4; // scope 0 at $DIR/separate_const_switch.rs:+8:43: +8:44
}
bb2: {
_4 = ((_1 as Ok).0: i32); // scope 0 at $DIR/separate_const_switch.rs:+7:16: +7:17
StorageLive(_5); // scope 1 at $DIR/separate_const_switch.rs:+7:44: +7:45
_5 = _4; // scope 1 at $DIR/separate_const_switch.rs:+7:44: +7:45
- Deinit(_2); // scope 1 at $DIR/separate_const_switch.rs:+7:22: +7:46
- ((_2 as Continue).0: i32) = move _5; // scope 1 at $DIR/separate_const_switch.rs:+7:22: +7:46
- discriminant(_2) = 0; // scope 1 at $DIR/separate_const_switch.rs:+7:22: +7:46
+ _2 = ControlFlow::<usize, i32>::Continue(move _5); // scope 1 at $DIR/separate_const_switch.rs:+7:22: +7:46
StorageDead(_5); // scope 1 at $DIR/separate_const_switch.rs:+7:45: +7:46
StorageDead(_4); // scope 0 at $DIR/separate_const_switch.rs:+7:45: +7:46
-- goto -> bb4; // scope 0 at $DIR/separate_const_switch.rs:+7:45: +7:46
-- }
--
-- bb4: {
+ goto -> bb4; // scope 0 at $DIR/separate_const_switch.rs:+7:45: +7:46
+ }
+
+ bb4: {
_8 = discriminant(_2); // scope 0 at $DIR/separate_const_switch.rs:+5:11: +10:6
-- switchInt(move _8) -> [0: bb7, 1: bb5, otherwise: bb6]; // scope 0 at $DIR/separate_const_switch.rs:+5:5: +10:6
-+ switchInt(move _8) -> [0: bb6, 1: bb4, otherwise: bb5]; // scope 0 at $DIR/separate_const_switch.rs:+5:5: +10:6
+ switchInt(move _8) -> [0: bb7, 1: bb5, otherwise: bb6]; // scope 0 at $DIR/separate_const_switch.rs:+5:5: +10:6
}
-- bb5: {
-+ bb4: {
+ bb5: {
StorageLive(_11); // scope 0 at $DIR/separate_const_switch.rs:+12:28: +12:29
_11 = ((_2 as Break).0: usize); // scope 0 at $DIR/separate_const_switch.rs:+12:28: +12:29
- Deinit(_0); // scope 4 at $DIR/separate_const_switch.rs:+12:34: +12:38
- discriminant(_0) = 0; // scope 4 at $DIR/separate_const_switch.rs:+12:34: +12:38
+ _0 = Option::<i32>::None; // scope 4 at $DIR/separate_const_switch.rs:+12:34: +12:38
StorageDead(_11); // scope 0 at $DIR/separate_const_switch.rs:+12:37: +12:38
-- goto -> bb8; // scope 0 at $DIR/separate_const_switch.rs:+12:37: +12:38
-+ goto -> bb7; // scope 0 at $DIR/separate_const_switch.rs:+12:37: +12:38
+ goto -> bb8; // scope 0 at $DIR/separate_const_switch.rs:+12:37: +12:38
}
-- bb6: {
-+ bb5: {
+ bb6: {
unreachable; // scope 0 at $DIR/separate_const_switch.rs:+5:11: +10:6
}
-- bb7: {
-+ bb6: {
+ bb7: {
StorageLive(_9); // scope 0 at $DIR/separate_const_switch.rs:+11:31: +11:32
_9 = ((_2 as Continue).0: i32); // scope 0 at $DIR/separate_const_switch.rs:+11:31: +11:32
StorageLive(_10); // scope 3 at $DIR/separate_const_switch.rs:+11:42: +11:43
_10 = _9; // scope 3 at $DIR/separate_const_switch.rs:+11:42: +11:43
- Deinit(_0); // scope 3 at $DIR/separate_const_switch.rs:+11:37: +11:44
- ((_0 as Some).0: i32) = move _10; // scope 3 at $DIR/separate_const_switch.rs:+11:37: +11:44
- discriminant(_0) = 1; // scope 3 at $DIR/separate_const_switch.rs:+11:37: +11:44
+ _0 = Option::<i32>::Some(move _10); // scope 3 at $DIR/separate_const_switch.rs:+11:37: +11:44
StorageDead(_10); // scope 3 at $DIR/separate_const_switch.rs:+11:43: +11:44
StorageDead(_9); // scope 0 at $DIR/separate_const_switch.rs:+11:43: +11:44
-- goto -> bb8; // scope 0 at $DIR/separate_const_switch.rs:+11:43: +11:44
-+ goto -> bb7; // scope 0 at $DIR/separate_const_switch.rs:+11:43: +11:44
+ goto -> bb8; // scope 0 at $DIR/separate_const_switch.rs:+11:43: +11:44
}
-- bb8: {
-+ bb7: {
+ bb8: {
StorageDead(_2); // scope 0 at $DIR/separate_const_switch.rs:+14:1: +14:2
return; // scope 0 at $DIR/separate_const_switch.rs:+14:2: +14:2
}
debug slf => _1; // in scope 1 at $DIR/simple_option_map_e2e.rs:2:17: 2:20
debug f => _2; // in scope 1 at $DIR/simple_option_map_e2e.rs:2:33: 2:34
let mut _3: isize; // in scope 1 at $DIR/simple_option_map_e2e.rs:7:9: 7:16
- let mut _4: i32; // in scope 1 at $DIR/simple_option_map_e2e.rs:7:25: 7:29
+ let _4: i32; // in scope 1 at $DIR/simple_option_map_e2e.rs:7:14: 7:15
let mut _5: i32; // in scope 1 at $DIR/simple_option_map_e2e.rs:7:25: 7:29
scope 2 {
- debug x => _5; // in scope 2 at $DIR/simple_option_map_e2e.rs:7:14: 7:15
+ debug x => _4; // in scope 2 at $DIR/simple_option_map_e2e.rs:7:14: 7:15
scope 3 (inlined ezmap::{closure#0}) { // at $DIR/simple_option_map_e2e.rs:7:25: 7:29
- debug n => _5; // in scope 3 at $DIR/simple_option_map_e2e.rs:+1:13: +1:14
+ debug n => _4; // in scope 3 at $DIR/simple_option_map_e2e.rs:+1:13: +1:14
}
}
}
}
bb1: {
- Deinit(_0); // scope 1 at $DIR/simple_option_map_e2e.rs:8:17: 8:21
- discriminant(_0) = 0; // scope 1 at $DIR/simple_option_map_e2e.rs:8:17: 8:21
+ _0 = Option::<i32>::None; // scope 1 at $DIR/simple_option_map_e2e.rs:8:17: 8:21
goto -> bb4; // scope 1 at $DIR/simple_option_map_e2e.rs:8:17: 8:21
}
}
bb3: {
- _5 = move ((_1 as Some).0: i32); // scope 1 at $DIR/simple_option_map_e2e.rs:7:14: 7:15
- StorageLive(_4); // scope 2 at $DIR/simple_option_map_e2e.rs:7:25: 7:29
- _4 = Add(_5, const 1_i32); // scope 3 at $DIR/simple_option_map_e2e.rs:+1:16: +1:21
- Deinit(_0); // scope 2 at $DIR/simple_option_map_e2e.rs:7:20: 7:30
- ((_0 as Some).0: i32) = move _4; // scope 2 at $DIR/simple_option_map_e2e.rs:7:20: 7:30
- discriminant(_0) = 1; // scope 2 at $DIR/simple_option_map_e2e.rs:7:20: 7:30
- StorageDead(_4); // scope 2 at $DIR/simple_option_map_e2e.rs:7:29: 7:30
+ _4 = move ((_1 as Some).0: i32); // scope 1 at $DIR/simple_option_map_e2e.rs:7:14: 7:15
+ StorageLive(_5); // scope 2 at $DIR/simple_option_map_e2e.rs:7:25: 7:29
+ _5 = Add(_4, const 1_i32); // scope 3 at $DIR/simple_option_map_e2e.rs:+1:16: +1:21
+ _0 = Option::<i32>::Some(move _5); // scope 2 at $DIR/simple_option_map_e2e.rs:7:20: 7:30
+ StorageDead(_5); // scope 2 at $DIR/simple_option_map_e2e.rs:7:29: 7:30
goto -> bb4; // scope 1 at $DIR/simple_option_map_e2e.rs:10:1: 10:2
}
bb0: {
- StorageLive(_1); // scope 0 at $DIR/simplify_locals.rs:+2:13: +2:17
-- Deinit(_1); // scope 0 at $DIR/simplify_locals.rs:+2:13: +2:17
-- discriminant(_1) = 0; // scope 0 at $DIR/simplify_locals.rs:+2:13: +2:17
+- _1 = E::A; // scope 0 at $DIR/simplify_locals.rs:+2:13: +2:17
- StorageDead(_1); // scope 0 at $DIR/simplify_locals.rs:+2:17: +2:18
_0 = const (); // scope 0 at $DIR/simplify_locals.rs:+0:9: +3:2
return; // scope 0 at $DIR/simplify_locals.rs:+3:2: +3:2
bb0: {
- StorageLive(_1); // scope 0 at $DIR/simplify_locals.rs:+2:22: +2:26
-- Deinit(_1); // scope 0 at $DIR/simplify_locals.rs:+2:22: +2:26
-- discriminant(_1) = 1; // scope 0 at $DIR/simplify_locals.rs:+2:22: +2:26
+- _1 = E::B; // scope 0 at $DIR/simplify_locals.rs:+2:22: +2:26
- StorageLive(_2); // scope 0 at $DIR/simplify_locals.rs:+2:5: +2:17
- StorageLive(_3); // scope 0 at $DIR/simplify_locals.rs:+2:11: +2:15
-- Deinit(_3); // scope 0 at $DIR/simplify_locals.rs:+2:11: +2:15
-- discriminant(_3) = 0; // scope 0 at $DIR/simplify_locals.rs:+2:11: +2:15
-- Deinit(_2); // scope 0 at $DIR/simplify_locals.rs:+2:6: +2:16
-- (_2.0: i32) = const 10_i32; // scope 0 at $DIR/simplify_locals.rs:+2:6: +2:16
-- (_2.1: E) = move _3; // scope 0 at $DIR/simplify_locals.rs:+2:6: +2:16
+- _3 = E::A; // scope 0 at $DIR/simplify_locals.rs:+2:11: +2:15
+- _2 = (const 10_i32, move _3); // scope 0 at $DIR/simplify_locals.rs:+2:6: +2:16
- StorageDead(_3); // scope 0 at $DIR/simplify_locals.rs:+2:15: +2:16
- (_2.1: E) = move _1; // scope 0 at $DIR/simplify_locals.rs:+2:5: +2:26
- StorageDead(_1); // scope 0 at $DIR/simplify_locals.rs:+2:25: +2:26
bb0: {
StorageLive(_1); // scope 1 at $DIR/simplify_locals_fixedpoint.rs:+1:30: +1:69
StorageLive(_2); // scope 1 at $DIR/simplify_locals_fixedpoint.rs:+1:31: +1:49
- Deinit(_2); // scope 1 at $DIR/simplify_locals_fixedpoint.rs:+1:31: +1:49
- discriminant(_2) = 0; // scope 1 at $DIR/simplify_locals_fixedpoint.rs:+1:31: +1:49
+ _2 = Option::<u8>::None; // scope 1 at $DIR/simplify_locals_fixedpoint.rs:+1:31: +1:49
StorageLive(_3); // scope 1 at $DIR/simplify_locals_fixedpoint.rs:+1:51: +1:68
- Deinit(_3); // scope 1 at $DIR/simplify_locals_fixedpoint.rs:+1:51: +1:68
- discriminant(_3) = 0; // scope 1 at $DIR/simplify_locals_fixedpoint.rs:+1:51: +1:68
- Deinit(_1); // scope 1 at $DIR/simplify_locals_fixedpoint.rs:+1:30: +1:69
- (_1.0: std::option::Option<u8>) = move _2; // scope 1 at $DIR/simplify_locals_fixedpoint.rs:+1:30: +1:69
- (_1.1: std::option::Option<T>) = move _3; // scope 1 at $DIR/simplify_locals_fixedpoint.rs:+1:30: +1:69
+ _3 = Option::<T>::None; // scope 1 at $DIR/simplify_locals_fixedpoint.rs:+1:51: +1:68
+ _1 = (move _2, move _3); // scope 1 at $DIR/simplify_locals_fixedpoint.rs:+1:30: +1:69
StorageDead(_3); // scope 1 at $DIR/simplify_locals_fixedpoint.rs:+1:68: +1:69
StorageDead(_2); // scope 1 at $DIR/simplify_locals_fixedpoint.rs:+1:68: +1:69
_5 = discriminant((_1.0: std::option::Option<u8>)); // scope 1 at $DIR/simplify_locals_fixedpoint.rs:+1:12: +1:27
bb0: {
- StorageLive(_1); // scope 0 at $DIR/simplify_locals_removes_unused_consts.rs:+1:20: +1:28
- StorageLive(_2); // scope 0 at $DIR/simplify_locals_removes_unused_consts.rs:+1:21: +1:23
-- Deinit(_2); // scope 0 at $DIR/simplify_locals_removes_unused_consts.rs:+1:21: +1:23
+- _2 = (); // scope 0 at $DIR/simplify_locals_removes_unused_consts.rs:+1:21: +1:23
- StorageLive(_3); // scope 0 at $DIR/simplify_locals_removes_unused_consts.rs:+1:25: +1:27
-- Deinit(_3); // scope 0 at $DIR/simplify_locals_removes_unused_consts.rs:+1:25: +1:27
-- Deinit(_1); // scope 0 at $DIR/simplify_locals_removes_unused_consts.rs:+1:20: +1:28
-- (_1.0: ()) = move _2; // scope 0 at $DIR/simplify_locals_removes_unused_consts.rs:+1:20: +1:28
-- (_1.1: ()) = move _3; // scope 0 at $DIR/simplify_locals_removes_unused_consts.rs:+1:20: +1:28
+- _3 = (); // scope 0 at $DIR/simplify_locals_removes_unused_consts.rs:+1:25: +1:27
+- _1 = (move _2, move _3); // scope 0 at $DIR/simplify_locals_removes_unused_consts.rs:+1:20: +1:28
- StorageDead(_3); // scope 0 at $DIR/simplify_locals_removes_unused_consts.rs:+1:27: +1:28
- StorageDead(_2); // scope 0 at $DIR/simplify_locals_removes_unused_consts.rs:+1:27: +1:28
- StorageDead(_1); // scope 0 at $DIR/simplify_locals_removes_unused_consts.rs:+1:28: +1:29
- StorageLive(_4); // scope 1 at $DIR/simplify_locals_removes_unused_consts.rs:+2:5: +2:22
- StorageLive(_5); // scope 1 at $DIR/simplify_locals_removes_unused_consts.rs:+2:13: +2:21
- StorageLive(_6); // scope 1 at $DIR/simplify_locals_removes_unused_consts.rs:+2:14: +2:16
-- Deinit(_6); // scope 1 at $DIR/simplify_locals_removes_unused_consts.rs:+2:14: +2:16
+- _6 = (); // scope 1 at $DIR/simplify_locals_removes_unused_consts.rs:+2:14: +2:16
- StorageLive(_7); // scope 1 at $DIR/simplify_locals_removes_unused_consts.rs:+2:18: +2:20
-- Deinit(_7); // scope 1 at $DIR/simplify_locals_removes_unused_consts.rs:+2:18: +2:20
-- Deinit(_5); // scope 1 at $DIR/simplify_locals_removes_unused_consts.rs:+2:13: +2:21
-- (_5.0: ()) = move _6; // scope 1 at $DIR/simplify_locals_removes_unused_consts.rs:+2:13: +2:21
-- (_5.1: ()) = move _7; // scope 1 at $DIR/simplify_locals_removes_unused_consts.rs:+2:13: +2:21
+- _7 = (); // scope 1 at $DIR/simplify_locals_removes_unused_consts.rs:+2:18: +2:20
+- _5 = (move _6, move _7); // scope 1 at $DIR/simplify_locals_removes_unused_consts.rs:+2:13: +2:21
- StorageDead(_7); // scope 1 at $DIR/simplify_locals_removes_unused_consts.rs:+2:20: +2:21
- StorageDead(_6); // scope 1 at $DIR/simplify_locals_removes_unused_consts.rs:+2:20: +2:21
- _4 = use_zst(move _5) -> bb1; // scope 1 at $DIR/simplify_locals_removes_unused_consts.rs:+2:5: +2:22
+ StorageLive(_1); // scope 1 at $DIR/simplify_locals_removes_unused_consts.rs:+2:5: +2:22
+ StorageLive(_2); // scope 1 at $DIR/simplify_locals_removes_unused_consts.rs:+2:13: +2:21
+ StorageLive(_3); // scope 1 at $DIR/simplify_locals_removes_unused_consts.rs:+2:14: +2:16
-+ Deinit(_3); // scope 1 at $DIR/simplify_locals_removes_unused_consts.rs:+2:14: +2:16
++ _3 = (); // scope 1 at $DIR/simplify_locals_removes_unused_consts.rs:+2:14: +2:16
+ StorageLive(_4); // scope 1 at $DIR/simplify_locals_removes_unused_consts.rs:+2:18: +2:20
-+ Deinit(_4); // scope 1 at $DIR/simplify_locals_removes_unused_consts.rs:+2:18: +2:20
-+ Deinit(_2); // scope 1 at $DIR/simplify_locals_removes_unused_consts.rs:+2:13: +2:21
-+ (_2.0: ()) = move _3; // scope 1 at $DIR/simplify_locals_removes_unused_consts.rs:+2:13: +2:21
-+ (_2.1: ()) = move _4; // scope 1 at $DIR/simplify_locals_removes_unused_consts.rs:+2:13: +2:21
++ _4 = (); // scope 1 at $DIR/simplify_locals_removes_unused_consts.rs:+2:18: +2:20
++ _2 = (move _3, move _4); // scope 1 at $DIR/simplify_locals_removes_unused_consts.rs:+2:13: +2:21
+ StorageDead(_4); // scope 1 at $DIR/simplify_locals_removes_unused_consts.rs:+2:20: +2:21
+ StorageDead(_3); // scope 1 at $DIR/simplify_locals_removes_unused_consts.rs:+2:20: +2:21
+ _1 = use_zst(move _2) -> bb1; // scope 1 at $DIR/simplify_locals_removes_unused_consts.rs:+2:5: +2:22
- StorageLive(_9); // scope 1 at $DIR/simplify_locals_removes_unused_consts.rs:+4:12: +4:34
- StorageLive(_10); // scope 1 at $DIR/simplify_locals_removes_unused_consts.rs:+4:12: +4:30
- StorageLive(_11); // scope 1 at $DIR/simplify_locals_removes_unused_consts.rs:+4:12: +4:28
-- Deinit(_11); // scope 1 at $DIR/simplify_locals_removes_unused_consts.rs:+4:12: +4:28
-- (_11.0: u8) = const 40_u8; // scope 1 at $DIR/simplify_locals_removes_unused_consts.rs:+4:12: +4:28
+- _11 = Temp { x: const 40_u8 }; // scope 1 at $DIR/simplify_locals_removes_unused_consts.rs:+4:12: +4:28
- _10 = (_11.0: u8); // scope 1 at $DIR/simplify_locals_removes_unused_consts.rs:+4:12: +4:30
- _9 = Add(move _10, const 2_u8); // scope 1 at $DIR/simplify_locals_removes_unused_consts.rs:+4:12: +4:34
- StorageDead(_10); // scope 1 at $DIR/simplify_locals_removes_unused_consts.rs:+4:33: +4:34
+ StorageLive(_6); // scope 1 at $DIR/simplify_locals_removes_unused_consts.rs:+4:12: +4:34
+ StorageLive(_7); // scope 1 at $DIR/simplify_locals_removes_unused_consts.rs:+4:12: +4:30
+ StorageLive(_8); // scope 1 at $DIR/simplify_locals_removes_unused_consts.rs:+4:12: +4:28
-+ Deinit(_8); // scope 1 at $DIR/simplify_locals_removes_unused_consts.rs:+4:12: +4:28
-+ (_8.0: u8) = const 40_u8; // scope 1 at $DIR/simplify_locals_removes_unused_consts.rs:+4:12: +4:28
++ _8 = Temp { x: const 40_u8 }; // scope 1 at $DIR/simplify_locals_removes_unused_consts.rs:+4:12: +4:28
+ _7 = (_8.0: u8); // scope 1 at $DIR/simplify_locals_removes_unused_consts.rs:+4:12: +4:30
+ _6 = Add(move _7, const 2_u8); // scope 1 at $DIR/simplify_locals_removes_unused_consts.rs:+4:12: +4:34
+ StorageDead(_7); // scope 1 at $DIR/simplify_locals_removes_unused_consts.rs:+4:33: +4:34
_3 = move ((_1 as Some).0: std::boxed::Box<()>); // scope 0 at $DIR/simplify_locals_removes_unused_discriminant_reads.rs:+3:14: +3:15
StorageLive(_4); // scope 1 at $DIR/simplify_locals_removes_unused_discriminant_reads.rs:+3:25: +3:26
_4 = move _3; // scope 1 at $DIR/simplify_locals_removes_unused_discriminant_reads.rs:+3:25: +3:26
- Deinit(_0); // scope 1 at $DIR/simplify_locals_removes_unused_discriminant_reads.rs:+3:20: +3:27
- ((_0 as Some).0: std::boxed::Box<()>) = move _4; // scope 1 at $DIR/simplify_locals_removes_unused_discriminant_reads.rs:+3:20: +3:27
- discriminant(_0) = 1; // scope 1 at $DIR/simplify_locals_removes_unused_discriminant_reads.rs:+3:20: +3:27
+ _0 = Option::<Box<()>>::Some(move _4); // scope 1 at $DIR/simplify_locals_removes_unused_discriminant_reads.rs:+3:20: +3:27
StorageDead(_4); // scope 1 at $DIR/simplify_locals_removes_unused_discriminant_reads.rs:+3:26: +3:27
StorageDead(_3); // scope 0 at $DIR/simplify_locals_removes_unused_discriminant_reads.rs:+3:26: +3:27
goto -> bb4; // scope 0 at $DIR/simplify_locals_removes_unused_discriminant_reads.rs:+3:26: +3:27
}
bb3: {
- Deinit(_0); // scope 0 at $DIR/simplify_locals_removes_unused_discriminant_reads.rs:+2:17: +2:21
- discriminant(_0) = 0; // scope 0 at $DIR/simplify_locals_removes_unused_discriminant_reads.rs:+2:17: +2:21
+ _0 = Option::<Box<()>>::None; // scope 0 at $DIR/simplify_locals_removes_unused_discriminant_reads.rs:+2:17: +2:21
goto -> bb4; // scope 0 at $DIR/simplify_locals_removes_unused_discriminant_reads.rs:+2:17: +2:21
}
}
bb0: {
- StorageLive(_2); // scope 0 at $DIR/simplify_match.rs:+1:17: +1:18
_2 = const false; // scope 0 at $DIR/simplify_match.rs:+1:21: +1:26
- switchInt(_2) -> [0: bb1, otherwise: bb2]; // scope 0 at $DIR/simplify_match.rs:+1:5: +1:31
+ switchInt(const false) -> [0: bb1, otherwise: bb2]; // scope 0 at $DIR/simplify_match.rs:+1:5: +1:31
}
bb0: {
- StorageLive(_3); // scope 0 at $DIR/slice_filter.rs:+0:27: +0:28
+- StorageLive(_3); // scope 0 at $DIR/slice_filter.rs:+0:27: +0:28
_25 = deref_copy (*_2); // scope 0 at $DIR/slice_filter.rs:+0:27: +0:28
_3 = &((*_25).0: usize); // scope 0 at $DIR/slice_filter.rs:+0:27: +0:28
- StorageLive(_4); // scope 0 at $DIR/slice_filter.rs:+0:30: +0:31
+- StorageLive(_4); // scope 0 at $DIR/slice_filter.rs:+0:30: +0:31
_26 = deref_copy (*_2); // scope 0 at $DIR/slice_filter.rs:+0:30: +0:31
_4 = &((*_26).1: usize); // scope 0 at $DIR/slice_filter.rs:+0:30: +0:31
- StorageLive(_5); // scope 0 at $DIR/slice_filter.rs:+0:33: +0:34
+- StorageLive(_5); // scope 0 at $DIR/slice_filter.rs:+0:33: +0:34
_27 = deref_copy (*_2); // scope 0 at $DIR/slice_filter.rs:+0:33: +0:34
_5 = &((*_27).2: usize); // scope 0 at $DIR/slice_filter.rs:+0:33: +0:34
- StorageLive(_6); // scope 0 at $DIR/slice_filter.rs:+0:36: +0:37
+- StorageLive(_6); // scope 0 at $DIR/slice_filter.rs:+0:36: +0:37
_28 = deref_copy (*_2); // scope 0 at $DIR/slice_filter.rs:+0:36: +0:37
_6 = &((*_28).3: usize); // scope 0 at $DIR/slice_filter.rs:+0:36: +0:37
StorageLive(_7); // scope 1 at $DIR/slice_filter.rs:+0:40: +0:56
}
bb0: {
- StorageLive(_3); // scope 0 at $DIR/slice_filter.rs:+0:27: +0:28
_25 = deref_copy (*_2); // scope 0 at $DIR/slice_filter.rs:+0:27: +0:28
_3 = &((*_25).0: usize); // scope 0 at $DIR/slice_filter.rs:+0:27: +0:28
- StorageLive(_4); // scope 0 at $DIR/slice_filter.rs:+0:30: +0:31
_26 = deref_copy (*_2); // scope 0 at $DIR/slice_filter.rs:+0:30: +0:31
_4 = &((*_26).1: usize); // scope 0 at $DIR/slice_filter.rs:+0:30: +0:31
- StorageLive(_5); // scope 0 at $DIR/slice_filter.rs:+0:33: +0:34
_27 = deref_copy (*_2); // scope 0 at $DIR/slice_filter.rs:+0:33: +0:34
_5 = &((*_27).2: usize); // scope 0 at $DIR/slice_filter.rs:+0:33: +0:34
- StorageLive(_6); // scope 0 at $DIR/slice_filter.rs:+0:36: +0:37
_28 = deref_copy (*_2); // scope 0 at $DIR/slice_filter.rs:+0:36: +0:37
_6 = &((*_28).3: usize); // scope 0 at $DIR/slice_filter.rs:+0:36: +0:37
- StorageLive(_7); // scope 1 at $DIR/slice_filter.rs:+0:40: +0:56
}
bb0: {
- StorageLive(_3); // scope 0 at $DIR/slice_filter.rs:+0:29: +0:30
+- StorageLive(_3); // scope 0 at $DIR/slice_filter.rs:+0:29: +0:30
_21 = deref_copy (*_2); // scope 0 at $DIR/slice_filter.rs:+0:29: +0:30
_3 = ((*_21).0: usize); // scope 0 at $DIR/slice_filter.rs:+0:29: +0:30
- StorageLive(_4); // scope 0 at $DIR/slice_filter.rs:+0:32: +0:33
+- StorageLive(_4); // scope 0 at $DIR/slice_filter.rs:+0:32: +0:33
_22 = deref_copy (*_2); // scope 0 at $DIR/slice_filter.rs:+0:32: +0:33
_4 = ((*_22).1: usize); // scope 0 at $DIR/slice_filter.rs:+0:32: +0:33
- StorageLive(_5); // scope 0 at $DIR/slice_filter.rs:+0:35: +0:36
+- StorageLive(_5); // scope 0 at $DIR/slice_filter.rs:+0:35: +0:36
_23 = deref_copy (*_2); // scope 0 at $DIR/slice_filter.rs:+0:35: +0:36
_5 = ((*_23).2: usize); // scope 0 at $DIR/slice_filter.rs:+0:35: +0:36
- StorageLive(_6); // scope 0 at $DIR/slice_filter.rs:+0:38: +0:39
+- StorageLive(_6); // scope 0 at $DIR/slice_filter.rs:+0:38: +0:39
_24 = deref_copy (*_2); // scope 0 at $DIR/slice_filter.rs:+0:38: +0:39
_6 = ((*_24).3: usize); // scope 0 at $DIR/slice_filter.rs:+0:38: +0:39
StorageLive(_7); // scope 1 at $DIR/slice_filter.rs:+0:42: +0:58
}
bb0: {
- StorageLive(_3); // scope 0 at $DIR/slice_filter.rs:+0:29: +0:30
_13 = deref_copy (*_2); // scope 0 at $DIR/slice_filter.rs:+0:29: +0:30
_3 = ((*_13).0: usize); // scope 0 at $DIR/slice_filter.rs:+0:29: +0:30
- StorageLive(_4); // scope 0 at $DIR/slice_filter.rs:+0:32: +0:33
_14 = deref_copy (*_2); // scope 0 at $DIR/slice_filter.rs:+0:32: +0:33
_4 = ((*_14).1: usize); // scope 0 at $DIR/slice_filter.rs:+0:32: +0:33
- StorageLive(_5); // scope 0 at $DIR/slice_filter.rs:+0:35: +0:36
_15 = deref_copy (*_2); // scope 0 at $DIR/slice_filter.rs:+0:35: +0:36
_5 = ((*_15).2: usize); // scope 0 at $DIR/slice_filter.rs:+0:35: +0:36
- StorageLive(_6); // scope 0 at $DIR/slice_filter.rs:+0:38: +0:39
_16 = deref_copy (*_2); // scope 0 at $DIR/slice_filter.rs:+0:38: +0:39
_6 = ((*_16).3: usize); // scope 0 at $DIR/slice_filter.rs:+0:38: +0:39
- StorageLive(_7); // scope 1 at $DIR/slice_filter.rs:+0:42: +0:58
--- /dev/null
+- // MIR for `copies` before ScalarReplacementOfAggregates
++ // MIR for `copies` after ScalarReplacementOfAggregates
+
+ fn copies(_1: Foo) -> () {
+ debug x => _1; // in scope 0 at $DIR/sroa.rs:+0:11: +0:12
+ let mut _0: (); // return place in scope 0 at $DIR/sroa.rs:+0:19: +0:19
+ let _2: Foo; // in scope 0 at $DIR/sroa.rs:+1:9: +1:10
++ let _11: u8; // in scope 0 at $DIR/sroa.rs:+1:9: +1:10
++ let _12: (); // in scope 0 at $DIR/sroa.rs:+1:9: +1:10
++ let _13: &str; // in scope 0 at $DIR/sroa.rs:+1:9: +1:10
++ let _14: std::option::Option<isize>; // in scope 0 at $DIR/sroa.rs:+1:9: +1:10
+ scope 1 {
+- debug y => _2; // in scope 1 at $DIR/sroa.rs:+1:9: +1:10
++ debug y => Foo{ .0 => _11, .1 => _12, .2 => _13, .3 => _14, }; // in scope 1 at $DIR/sroa.rs:+1:9: +1:10
+ let _3: u8; // in scope 1 at $DIR/sroa.rs:+2:9: +2:10
+ scope 2 {
+ debug t => _3; // in scope 2 at $DIR/sroa.rs:+2:9: +2:10
+ let _4: &str; // in scope 2 at $DIR/sroa.rs:+3:9: +3:10
+ scope 3 {
+ debug u => _4; // in scope 3 at $DIR/sroa.rs:+3:9: +3:10
+ let _5: Foo; // in scope 3 at $DIR/sroa.rs:+4:9: +4:10
++ let _7: u8; // in scope 3 at $DIR/sroa.rs:+4:9: +4:10
++ let _8: (); // in scope 3 at $DIR/sroa.rs:+4:9: +4:10
++ let _9: &str; // in scope 3 at $DIR/sroa.rs:+4:9: +4:10
++ let _10: std::option::Option<isize>; // in scope 3 at $DIR/sroa.rs:+4:9: +4:10
+ scope 4 {
+- debug z => _5; // in scope 4 at $DIR/sroa.rs:+4:9: +4:10
++ debug z => Foo{ .0 => _7, .1 => _8, .2 => _9, .3 => _10, }; // in scope 4 at $DIR/sroa.rs:+4:9: +4:10
+ let _6: (); // in scope 4 at $DIR/sroa.rs:+5:9: +5:10
+ scope 5 {
+ debug a => _6; // in scope 5 at $DIR/sroa.rs:+5:9: +5:10
+ }
+ }
+ }
+ }
+ }
+
+ bb0: {
+- StorageLive(_2); // scope 0 at $DIR/sroa.rs:+1:9: +1:10
+- _2 = _1; // scope 0 at $DIR/sroa.rs:+1:13: +1:14
++ StorageLive(_11); // scope 0 at $DIR/sroa.rs:+1:9: +1:10
++ StorageLive(_12); // scope 0 at $DIR/sroa.rs:+1:9: +1:10
++ StorageLive(_13); // scope 0 at $DIR/sroa.rs:+1:9: +1:10
++ StorageLive(_14); // scope 0 at $DIR/sroa.rs:+1:9: +1:10
++ nop; // scope 0 at $DIR/sroa.rs:+1:9: +1:10
++ _11 = (_1.0: u8); // scope 0 at $DIR/sroa.rs:+1:13: +1:14
++ _12 = (_1.1: ()); // scope 0 at $DIR/sroa.rs:+1:13: +1:14
++ _13 = (_1.2: &str); // scope 0 at $DIR/sroa.rs:+1:13: +1:14
++ _14 = (_1.3: std::option::Option<isize>); // scope 0 at $DIR/sroa.rs:+1:13: +1:14
++ nop; // scope 0 at $DIR/sroa.rs:+1:13: +1:14
+ StorageLive(_3); // scope 1 at $DIR/sroa.rs:+2:9: +2:10
+- _3 = (_2.0: u8); // scope 1 at $DIR/sroa.rs:+2:13: +2:16
++ _3 = _11; // scope 1 at $DIR/sroa.rs:+2:13: +2:16
+ StorageLive(_4); // scope 2 at $DIR/sroa.rs:+3:9: +3:10
+- _4 = (_2.2: &str); // scope 2 at $DIR/sroa.rs:+3:13: +3:16
+- StorageLive(_5); // scope 3 at $DIR/sroa.rs:+4:9: +4:10
+- _5 = _2; // scope 3 at $DIR/sroa.rs:+4:13: +4:14
++ _4 = _13; // scope 2 at $DIR/sroa.rs:+3:13: +3:16
++ StorageLive(_7); // scope 3 at $DIR/sroa.rs:+4:9: +4:10
++ StorageLive(_8); // scope 3 at $DIR/sroa.rs:+4:9: +4:10
++ StorageLive(_9); // scope 3 at $DIR/sroa.rs:+4:9: +4:10
++ StorageLive(_10); // scope 3 at $DIR/sroa.rs:+4:9: +4:10
++ nop; // scope 3 at $DIR/sroa.rs:+4:9: +4:10
++ _7 = _11; // scope 3 at $DIR/sroa.rs:+4:13: +4:14
++ _8 = _12; // scope 3 at $DIR/sroa.rs:+4:13: +4:14
++ _9 = _13; // scope 3 at $DIR/sroa.rs:+4:13: +4:14
++ _10 = _14; // scope 3 at $DIR/sroa.rs:+4:13: +4:14
++ nop; // scope 3 at $DIR/sroa.rs:+4:13: +4:14
+ StorageLive(_6); // scope 4 at $DIR/sroa.rs:+5:9: +5:10
+- _6 = (_5.1: ()); // scope 4 at $DIR/sroa.rs:+5:13: +5:16
++ _6 = _8; // scope 4 at $DIR/sroa.rs:+5:13: +5:16
+ _0 = const (); // scope 0 at $DIR/sroa.rs:+0:19: +6:2
+ StorageDead(_6); // scope 4 at $DIR/sroa.rs:+6:1: +6:2
+- StorageDead(_5); // scope 3 at $DIR/sroa.rs:+6:1: +6:2
++ StorageDead(_7); // scope 3 at $DIR/sroa.rs:+6:1: +6:2
++ StorageDead(_8); // scope 3 at $DIR/sroa.rs:+6:1: +6:2
++ StorageDead(_9); // scope 3 at $DIR/sroa.rs:+6:1: +6:2
++ StorageDead(_10); // scope 3 at $DIR/sroa.rs:+6:1: +6:2
++ nop; // scope 3 at $DIR/sroa.rs:+6:1: +6:2
+ StorageDead(_4); // scope 2 at $DIR/sroa.rs:+6:1: +6:2
+ StorageDead(_3); // scope 1 at $DIR/sroa.rs:+6:1: +6:2
+- StorageDead(_2); // scope 0 at $DIR/sroa.rs:+6:1: +6:2
++ StorageDead(_11); // scope 0 at $DIR/sroa.rs:+6:1: +6:2
++ StorageDead(_12); // scope 0 at $DIR/sroa.rs:+6:1: +6:2
++ StorageDead(_13); // scope 0 at $DIR/sroa.rs:+6:1: +6:2
++ StorageDead(_14); // scope 0 at $DIR/sroa.rs:+6:1: +6:2
++ nop; // scope 0 at $DIR/sroa.rs:+6:1: +6:2
+ return; // scope 0 at $DIR/sroa.rs:+6:2: +6:2
+ }
+ }
+
StorageLive(_1); // scope 0 at $DIR/sroa.rs:+1:5: +1:32
StorageLive(_2); // scope 0 at $DIR/sroa.rs:+1:5: +1:30
StorageLive(_3); // scope 0 at $DIR/sroa.rs:+1:7: +1:13
- Deinit(_3); // scope 0 at $DIR/sroa.rs:+1:7: +1:13
- (_3.0: usize) = const 0_usize; // scope 0 at $DIR/sroa.rs:+1:7: +1:13
+ _3 = Tag(const 0_usize); // scope 0 at $DIR/sroa.rs:+1:7: +1:13
StorageLive(_4); // scope 0 at $DIR/sroa.rs:+1:15: +1:21
- Deinit(_4); // scope 0 at $DIR/sroa.rs:+1:15: +1:21
- (_4.0: usize) = const 1_usize; // scope 0 at $DIR/sroa.rs:+1:15: +1:21
+ _4 = Tag(const 1_usize); // scope 0 at $DIR/sroa.rs:+1:15: +1:21
StorageLive(_5); // scope 0 at $DIR/sroa.rs:+1:23: +1:29
- Deinit(_5); // scope 0 at $DIR/sroa.rs:+1:23: +1:29
- (_5.0: usize) = const 2_usize; // scope 0 at $DIR/sroa.rs:+1:23: +1:29
- Deinit(_2); // scope 0 at $DIR/sroa.rs:+1:5: +1:30
- (_2.0: Tag) = move _3; // scope 0 at $DIR/sroa.rs:+1:5: +1:30
- (_2.1: Tag) = move _4; // scope 0 at $DIR/sroa.rs:+1:5: +1:30
- (_2.2: Tag) = move _5; // scope 0 at $DIR/sroa.rs:+1:5: +1:30
+ _5 = Tag(const 2_usize); // scope 0 at $DIR/sroa.rs:+1:23: +1:29
+ _2 = S(move _3, move _4, move _5); // scope 0 at $DIR/sroa.rs:+1:5: +1:30
StorageDead(_5); // scope 0 at $DIR/sroa.rs:+1:29: +1:30
StorageDead(_4); // scope 0 at $DIR/sroa.rs:+1:29: +1:30
StorageDead(_3); // scope 0 at $DIR/sroa.rs:+1:29: +1:30
StorageLive(_2); // scope 1 at $DIR/sroa.rs:+1:22: +1:29
StorageLive(_3); // scope 1 at $DIR/sroa.rs:+1:27: +1:28
_3 = _1; // scope 1 at $DIR/sroa.rs:+1:27: +1:28
- Deinit(_2); // scope 1 at $DIR/sroa.rs:+1:22: +1:29
- ((_2 as Some).0: usize) = move _3; // scope 1 at $DIR/sroa.rs:+1:22: +1:29
- discriminant(_2) = 1; // scope 1 at $DIR/sroa.rs:+1:22: +1:29
+ _2 = Option::<usize>::Some(move _3); // scope 1 at $DIR/sroa.rs:+1:22: +1:29
StorageDead(_3); // scope 1 at $DIR/sroa.rs:+1:28: +1:29
_4 = discriminant(_2); // scope 1 at $DIR/sroa.rs:+1:12: +1:19
switchInt(move _4) -> [1: bb1, otherwise: bb2]; // scope 1 at $DIR/sroa.rs:+1:12: +1:19
StorageLive(_5); // scope 0 at $DIR/sroa.rs:+2:34: +2:37
_5 = g() -> bb1; // scope 0 at $DIR/sroa.rs:+2:34: +2:37
// mir::Constant
- // + span: $DIR/sroa.rs:78:34: 78:35
+ // + span: $DIR/sroa.rs:73:34: 73:35
// + literal: Const { ty: fn() -> u32 {g}, val: Value(<ZST>) }
}
bb1: {
- Deinit(_4); // scope 0 at $DIR/sroa.rs:+2:8: +2:39
- (_4.0: u32) = const 1_u32; // scope 0 at $DIR/sroa.rs:+2:8: +2:39
- (_4.1: u32) = const 2_u32; // scope 0 at $DIR/sroa.rs:+2:8: +2:39
- (_4.2: u32) = move _5; // scope 0 at $DIR/sroa.rs:+2:8: +2:39
+ _4 = Escaping { a: const 1_u32, b: const 2_u32, c: move _5 }; // scope 0 at $DIR/sroa.rs:+2:8: +2:39
StorageDead(_5); // scope 0 at $DIR/sroa.rs:+2:38: +2:39
_3 = &(_4.0: u32); // scope 0 at $DIR/sroa.rs:+2:7: +2:41
_2 = &raw const (*_3); // scope 0 at $DIR/sroa.rs:+2:7: +2:41
_1 = f(move _2) -> bb2; // scope 0 at $DIR/sroa.rs:+2:5: +2:42
// mir::Constant
- // + span: $DIR/sroa.rs:78:5: 78:6
+ // + span: $DIR/sroa.rs:73:5: 73:6
// + literal: Const { ty: fn(*const u32) {f}, val: Value(<ZST>) }
}
+ StorageLive(_9); // scope 0 at $DIR/sroa.rs:+1:30: +1:70
+ StorageLive(_10); // scope 0 at $DIR/sroa.rs:+1:30: +1:70
+ StorageLive(_11); // scope 0 at $DIR/sroa.rs:+1:30: +1:70
++ nop; // scope 0 at $DIR/sroa.rs:+1:30: +1:70
StorageLive(_6); // scope 0 at $DIR/sroa.rs:+1:45: +1:47
- Deinit(_6); // scope 0 at $DIR/sroa.rs:+1:45: +1:47
+ _6 = (); // scope 0 at $DIR/sroa.rs:+1:45: +1:47
StorageLive(_7); // scope 0 at $DIR/sroa.rs:+1:60: +1:68
- Deinit(_7); // scope 0 at $DIR/sroa.rs:+1:60: +1:68
- ((_7 as Some).0: isize) = const -4_isize; // scope 0 at $DIR/sroa.rs:+1:60: +1:68
- discriminant(_7) = 1; // scope 0 at $DIR/sroa.rs:+1:60: +1:68
-- Deinit(_5); // scope 0 at $DIR/sroa.rs:+1:30: +1:70
-- (_5.0: u8) = const 5_u8; // scope 0 at $DIR/sroa.rs:+1:30: +1:70
-- (_5.1: ()) = move _6; // scope 0 at $DIR/sroa.rs:+1:30: +1:70
-- (_5.2: &str) = const "a"; // scope 0 at $DIR/sroa.rs:+1:30: +1:70
-+ Deinit(_8); // scope 0 at $DIR/sroa.rs:+1:30: +1:70
-+ Deinit(_9); // scope 0 at $DIR/sroa.rs:+1:30: +1:70
-+ Deinit(_10); // scope 0 at $DIR/sroa.rs:+1:30: +1:70
-+ Deinit(_11); // scope 0 at $DIR/sroa.rs:+1:30: +1:70
+ _7 = Option::<isize>::Some(const -4_isize); // scope 0 at $DIR/sroa.rs:+1:60: +1:68
+- _5 = Foo { a: const 5_u8, b: move _6, c: const "a", d: move _7 }; // scope 0 at $DIR/sroa.rs:+1:30: +1:70
+ _8 = const 5_u8; // scope 0 at $DIR/sroa.rs:+1:30: +1:70
+ _9 = move _6; // scope 0 at $DIR/sroa.rs:+1:30: +1:70
+ _10 = const "a"; // scope 0 at $DIR/sroa.rs:+1:30: +1:70
// mir::Constant
- // + span: $DIR/sroa.rs:57:52: 57:55
+ // + span: $DIR/sroa.rs:53:52: 53:55
// + literal: Const { ty: &str, val: Value(Slice(..)) }
-- (_5.3: std::option::Option<isize>) = move _7; // scope 0 at $DIR/sroa.rs:+1:30: +1:70
+ _11 = move _7; // scope 0 at $DIR/sroa.rs:+1:30: +1:70
++ nop; // scope 0 at $DIR/sroa.rs:+1:30: +1:70
StorageDead(_7); // scope 0 at $DIR/sroa.rs:+1:69: +1:70
StorageDead(_6); // scope 0 at $DIR/sroa.rs:+1:69: +1:70
StorageLive(_1); // scope 0 at $DIR/sroa.rs:+1:15: +1:16
+ StorageDead(_9); // scope 0 at $DIR/sroa.rs:+1:70: +1:71
+ StorageDead(_10); // scope 0 at $DIR/sroa.rs:+1:70: +1:71
+ StorageDead(_11); // scope 0 at $DIR/sroa.rs:+1:70: +1:71
++ nop; // scope 0 at $DIR/sroa.rs:+1:70: +1:71
_0 = const (); // scope 0 at $DIR/sroa.rs:+0:15: +6:2
StorageDead(_4); // scope 0 at $DIR/sroa.rs:+6:1: +6:2
StorageDead(_3); // scope 0 at $DIR/sroa.rs:+6:1: +6:2
--- /dev/null
+- // MIR for `ref_copies` before ScalarReplacementOfAggregates
++ // MIR for `ref_copies` after ScalarReplacementOfAggregates
+
+ fn ref_copies(_1: &Foo) -> () {
+ debug x => _1; // in scope 0 at $DIR/sroa.rs:+0:15: +0:16
+ let mut _0: (); // return place in scope 0 at $DIR/sroa.rs:+0:24: +0:24
+ let _2: Foo; // in scope 0 at $DIR/sroa.rs:+1:9: +1:10
++ let _5: u8; // in scope 0 at $DIR/sroa.rs:+1:9: +1:10
++ let _6: (); // in scope 0 at $DIR/sroa.rs:+1:9: +1:10
++ let _7: &str; // in scope 0 at $DIR/sroa.rs:+1:9: +1:10
++ let _8: std::option::Option<isize>; // in scope 0 at $DIR/sroa.rs:+1:9: +1:10
+ scope 1 {
+- debug y => _2; // in scope 1 at $DIR/sroa.rs:+1:9: +1:10
++ debug y => Foo{ .0 => _5, .1 => _6, .2 => _7, .3 => _8, }; // in scope 1 at $DIR/sroa.rs:+1:9: +1:10
+ let _3: u8; // in scope 1 at $DIR/sroa.rs:+2:9: +2:10
+ scope 2 {
+ debug t => _3; // in scope 2 at $DIR/sroa.rs:+2:9: +2:10
+ let _4: &str; // in scope 2 at $DIR/sroa.rs:+3:9: +3:10
+ scope 3 {
+ debug u => _4; // in scope 3 at $DIR/sroa.rs:+3:9: +3:10
+ }
+ }
+ }
+
+ bb0: {
+- StorageLive(_2); // scope 0 at $DIR/sroa.rs:+1:9: +1:10
+- _2 = (*_1); // scope 0 at $DIR/sroa.rs:+1:13: +1:15
++ StorageLive(_5); // scope 0 at $DIR/sroa.rs:+1:9: +1:10
++ StorageLive(_6); // scope 0 at $DIR/sroa.rs:+1:9: +1:10
++ StorageLive(_7); // scope 0 at $DIR/sroa.rs:+1:9: +1:10
++ StorageLive(_8); // scope 0 at $DIR/sroa.rs:+1:9: +1:10
++ nop; // scope 0 at $DIR/sroa.rs:+1:9: +1:10
++ _5 = ((*_1).0: u8); // scope 0 at $DIR/sroa.rs:+1:13: +1:15
++ _6 = ((*_1).1: ()); // scope 0 at $DIR/sroa.rs:+1:13: +1:15
++ _7 = ((*_1).2: &str); // scope 0 at $DIR/sroa.rs:+1:13: +1:15
++ _8 = ((*_1).3: std::option::Option<isize>); // scope 0 at $DIR/sroa.rs:+1:13: +1:15
++ nop; // scope 0 at $DIR/sroa.rs:+1:13: +1:15
+ StorageLive(_3); // scope 1 at $DIR/sroa.rs:+2:9: +2:10
+- _3 = (_2.0: u8); // scope 1 at $DIR/sroa.rs:+2:13: +2:16
++ _3 = _5; // scope 1 at $DIR/sroa.rs:+2:13: +2:16
+ StorageLive(_4); // scope 2 at $DIR/sroa.rs:+3:9: +3:10
+- _4 = (_2.2: &str); // scope 2 at $DIR/sroa.rs:+3:13: +3:16
++ _4 = _7; // scope 2 at $DIR/sroa.rs:+3:13: +3:16
+ _0 = const (); // scope 0 at $DIR/sroa.rs:+0:24: +4:2
+ StorageDead(_4); // scope 2 at $DIR/sroa.rs:+4:1: +4:2
+ StorageDead(_3); // scope 1 at $DIR/sroa.rs:+4:1: +4:2
+- StorageDead(_2); // scope 0 at $DIR/sroa.rs:+4:1: +4:2
++ StorageDead(_5); // scope 0 at $DIR/sroa.rs:+4:1: +4:2
++ StorageDead(_6); // scope 0 at $DIR/sroa.rs:+4:1: +4:2
++ StorageDead(_7); // scope 0 at $DIR/sroa.rs:+4:1: +4:2
++ StorageDead(_8); // scope 0 at $DIR/sroa.rs:+4:1: +4:2
++ nop; // scope 0 at $DIR/sroa.rs:+4:1: +4:2
+ return; // scope 0 at $DIR/sroa.rs:+4:2: +4:2
+ }
+ }
+
fn drop(&mut self) {}
}
-// EMIT_MIR sroa.dropping.ScalarReplacementOfAggregates.diff
pub fn dropping() {
S(Tag(0), Tag(1), Tag(2)).1;
}
-// EMIT_MIR sroa.enums.ScalarReplacementOfAggregates.diff
pub fn enums(a: usize) -> usize {
if let Some(a) = Some(a) { a } else { 0 }
}
-// EMIT_MIR sroa.structs.ScalarReplacementOfAggregates.diff
pub fn structs(a: f32) -> f32 {
struct U {
_foo: usize,
U { _foo: 0, a }.a
}
-// EMIT_MIR sroa.unions.ScalarReplacementOfAggregates.diff
pub fn unions(a: f32) -> u32 {
union Repr {
f: f32,
unsafe { Repr { f: a }.u }
}
+#[derive(Copy, Clone)]
struct Foo {
a: u8,
b: (),
3
}
-// EMIT_MIR sroa.flat.ScalarReplacementOfAggregates.diff
pub fn flat() {
let Foo { a, b, c, d } = Foo { a: 5, b: (), c: "a", d: Some(-4) };
let _ = a;
println!("{}", unsafe { *a.add(2) });
}
-// EMIT_MIR sroa.escaping.ScalarReplacementOfAggregates.diff
pub fn escaping() {
// Verify this struct is not flattened.
f(&Escaping { a: 1, b: 2, c: g() }.a);
}
+fn copies(x: Foo) {
+ let y = x;
+ let t = y.a;
+ let u = y.c;
+ let z = y;
+ let a = z.b;
+}
+
+fn ref_copies(x: &Foo) {
+ let y = *x;
+ let t = y.a;
+ let u = y.c;
+}
+
fn main() {
dropping();
enums(5);
unions(5.);
flat();
escaping();
+ copies(Foo { a: 5, b: (), c: "a", d: Some(-4) });
+ ref_copies(&Foo { a: 5, b: (), c: "a", d: Some(-4) });
}
+
+// EMIT_MIR sroa.dropping.ScalarReplacementOfAggregates.diff
+// EMIT_MIR sroa.enums.ScalarReplacementOfAggregates.diff
+// EMIT_MIR sroa.structs.ScalarReplacementOfAggregates.diff
+// EMIT_MIR sroa.unions.ScalarReplacementOfAggregates.diff
+// EMIT_MIR sroa.flat.ScalarReplacementOfAggregates.diff
+// EMIT_MIR sroa.escaping.ScalarReplacementOfAggregates.diff
+// EMIT_MIR sroa.copies.ScalarReplacementOfAggregates.diff
+// EMIT_MIR sroa.ref_copies.ScalarReplacementOfAggregates.diff
- StorageLive(_2); // scope 0 at $DIR/sroa.rs:+6:5: +6:21
+ StorageLive(_4); // scope 0 at $DIR/sroa.rs:+6:5: +6:21
+ StorageLive(_5); // scope 0 at $DIR/sroa.rs:+6:5: +6:21
++ nop; // scope 0 at $DIR/sroa.rs:+6:5: +6:21
StorageLive(_3); // scope 0 at $DIR/sroa.rs:+6:18: +6:19
_3 = _1; // scope 0 at $DIR/sroa.rs:+6:18: +6:19
-- Deinit(_2); // scope 0 at $DIR/sroa.rs:+6:5: +6:21
-- (_2.0: usize) = const 0_usize; // scope 0 at $DIR/sroa.rs:+6:5: +6:21
-- (_2.1: f32) = move _3; // scope 0 at $DIR/sroa.rs:+6:5: +6:21
-+ Deinit(_4); // scope 0 at $DIR/sroa.rs:+6:5: +6:21
-+ Deinit(_5); // scope 0 at $DIR/sroa.rs:+6:5: +6:21
+- _2 = U { _foo: const 0_usize, a: move _3 }; // scope 0 at $DIR/sroa.rs:+6:5: +6:21
+ _4 = const 0_usize; // scope 0 at $DIR/sroa.rs:+6:5: +6:21
+ _5 = move _3; // scope 0 at $DIR/sroa.rs:+6:5: +6:21
++ nop; // scope 0 at $DIR/sroa.rs:+6:5: +6:21
StorageDead(_3); // scope 0 at $DIR/sroa.rs:+6:20: +6:21
- _0 = (_2.1: f32); // scope 0 at $DIR/sroa.rs:+6:5: +6:23
- StorageDead(_2); // scope 0 at $DIR/sroa.rs:+7:1: +7:2
+ _0 = _5; // scope 0 at $DIR/sroa.rs:+6:5: +6:23
+ StorageDead(_4); // scope 0 at $DIR/sroa.rs:+7:1: +7:2
+ StorageDead(_5); // scope 0 at $DIR/sroa.rs:+7:1: +7:2
++ nop; // scope 0 at $DIR/sroa.rs:+7:1: +7:2
return; // scope 0 at $DIR/sroa.rs:+7:2: +7:2
}
}
StorageLive(_2); // scope 1 at $DIR/sroa.rs:+5:14: +5:27
StorageLive(_3); // scope 1 at $DIR/sroa.rs:+5:24: +5:25
_3 = _1; // scope 1 at $DIR/sroa.rs:+5:24: +5:25
- Deinit(_2); // scope 1 at $DIR/sroa.rs:+5:14: +5:27
- (_2.0: f32) = move _3; // scope 1 at $DIR/sroa.rs:+5:14: +5:27
+ _2 = Repr { f: move _3 }; // scope 1 at $DIR/sroa.rs:+5:14: +5:27
StorageDead(_3); // scope 1 at $DIR/sroa.rs:+5:26: +5:27
_0 = (_2.1: u32); // scope 1 at $DIR/sroa.rs:+5:14: +5:29
StorageDead(_2); // scope 0 at $DIR/sroa.rs:+6:1: +6:2
bb0: {
StorageLive(_2); // scope 0 at $DIR/try_identity_e2e.rs:+2:15: +7:10
_3 = discriminant(_1); // scope 0 at $DIR/try_identity_e2e.rs:+3:19: +3:20
- switchInt(move _3) -> [0: bb2, 1: bb1, otherwise: bb4]; // scope 0 at $DIR/try_identity_e2e.rs:+3:13: +3:20
+ switchInt(move _3) -> [0: bb2, 1: bb1, otherwise: bb5]; // scope 0 at $DIR/try_identity_e2e.rs:+3:13: +3:20
}
bb1: {
- StorageLive(_5); // scope 0 at $DIR/try_identity_e2e.rs:+5:21: +5:22
_5 = move ((_1 as Err).0: E); // scope 0 at $DIR/try_identity_e2e.rs:+5:21: +5:22
- Deinit(_2); // scope 2 at $DIR/try_identity_e2e.rs:+5:27: +5:48
- ((_2 as Break).0: E) = move _5; // scope 2 at $DIR/try_identity_e2e.rs:+5:27: +5:48
- discriminant(_2) = 1; // scope 2 at $DIR/try_identity_e2e.rs:+5:27: +5:48
- _6 = discriminant(_2); // scope 0 at $DIR/try_identity_e2e.rs:+2:15: +7:10
- switchInt(move _6) -> [0: bb5, 1: bb3, otherwise: bb4]; // scope 0 at $DIR/try_identity_e2e.rs:+2:9: +7:10
+ _2 = ControlFlow::<E, T>::Break(move _5); // scope 2 at $DIR/try_identity_e2e.rs:+5:27: +5:48
+ goto -> bb3; // scope 0 at $DIR/try_identity_e2e.rs:+5:47: +5:48
}
bb2: {
- StorageLive(_4); // scope 0 at $DIR/try_identity_e2e.rs:+4:20: +4:21
_4 = move ((_1 as Ok).0: T); // scope 0 at $DIR/try_identity_e2e.rs:+4:20: +4:21
- Deinit(_2); // scope 1 at $DIR/try_identity_e2e.rs:+4:26: +4:50
- ((_2 as Continue).0: T) = move _4; // scope 1 at $DIR/try_identity_e2e.rs:+4:26: +4:50
- discriminant(_2) = 0; // scope 1 at $DIR/try_identity_e2e.rs:+4:26: +4:50
- _6 = discriminant(_2); // scope 0 at $DIR/try_identity_e2e.rs:+2:15: +7:10
- switchInt(move _6) -> [0: bb5, 1: bb3, otherwise: bb4]; // scope 0 at $DIR/try_identity_e2e.rs:+2:9: +7:10
+ _2 = ControlFlow::<E, T>::Continue(move _4); // scope 1 at $DIR/try_identity_e2e.rs:+4:26: +4:50
+ goto -> bb3; // scope 0 at $DIR/try_identity_e2e.rs:+4:49: +4:50
}
bb3: {
- StorageLive(_8); // scope 0 at $DIR/try_identity_e2e.rs:+9:32: +9:33
+ _6 = discriminant(_2); // scope 0 at $DIR/try_identity_e2e.rs:+2:15: +7:10
+ switchInt(move _6) -> [0: bb6, 1: bb4, otherwise: bb5]; // scope 0 at $DIR/try_identity_e2e.rs:+2:9: +7:10
+ }
+
+ bb4: {
_8 = move ((_2 as Break).0: E); // scope 0 at $DIR/try_identity_e2e.rs:+9:32: +9:33
- Deinit(_0); // scope 4 at $DIR/try_identity_e2e.rs:+9:45: +9:51
- ((_0 as Err).0: E) = move _8; // scope 4 at $DIR/try_identity_e2e.rs:+9:45: +9:51
- discriminant(_0) = 1; // scope 4 at $DIR/try_identity_e2e.rs:+9:45: +9:51
+ _0 = Result::<T, E>::Err(move _8); // scope 4 at $DIR/try_identity_e2e.rs:+9:45: +9:51
StorageDead(_2); // scope 0 at $DIR/try_identity_e2e.rs:+12:1: +12:2
return; // scope 0 at $DIR/try_identity_e2e.rs:+12:1: +12:2
}
- bb4: {
+ bb5: {
unreachable; // scope 0 at $DIR/try_identity_e2e.rs:+2:15: +7:10
}
- bb5: {
- StorageLive(_7); // scope 0 at $DIR/try_identity_e2e.rs:+8:35: +8:36
+ bb6: {
_7 = move ((_2 as Continue).0: T); // scope 0 at $DIR/try_identity_e2e.rs:+8:35: +8:36
- Deinit(_0); // scope 0 at $DIR/try_identity_e2e.rs:+1:5: +11:6
- ((_0 as Ok).0: T) = move _7; // scope 0 at $DIR/try_identity_e2e.rs:+1:5: +11:6
- discriminant(_0) = 0; // scope 0 at $DIR/try_identity_e2e.rs:+1:5: +11:6
+ _0 = Result::<T, E>::Ok(move _7); // scope 0 at $DIR/try_identity_e2e.rs:+1:5: +11:6
StorageDead(_2); // scope 0 at $DIR/try_identity_e2e.rs:+12:1: +12:2
return; // scope 0 at $DIR/try_identity_e2e.rs:+12:1: +12:2
}
}
bb1: {
- StorageLive(_4); // scope 0 at $DIR/try_identity_e2e.rs:+4:17: +4:18
_4 = move ((_1 as Err).0: E); // scope 0 at $DIR/try_identity_e2e.rs:+4:17: +4:18
- Deinit(_0); // scope 2 at $DIR/try_identity_e2e.rs:+4:30: +4:36
- ((_0 as Err).0: E) = move _4; // scope 2 at $DIR/try_identity_e2e.rs:+4:30: +4:36
- discriminant(_0) = 1; // scope 2 at $DIR/try_identity_e2e.rs:+4:30: +4:36
+ _0 = Result::<T, E>::Err(move _4); // scope 2 at $DIR/try_identity_e2e.rs:+4:30: +4:36
return; // scope 0 at $DIR/try_identity_e2e.rs:+7:1: +7:2
}
}
bb3: {
- StorageLive(_3); // scope 0 at $DIR/try_identity_e2e.rs:+3:16: +3:17
_3 = move ((_1 as Ok).0: T); // scope 0 at $DIR/try_identity_e2e.rs:+3:16: +3:17
- Deinit(_0); // scope 0 at $DIR/try_identity_e2e.rs:+1:5: +6:6
- ((_0 as Ok).0: T) = move _3; // scope 0 at $DIR/try_identity_e2e.rs:+1:5: +6:6
- discriminant(_0) = 0; // scope 0 at $DIR/try_identity_e2e.rs:+1:5: +6:6
+ _0 = Result::<T, E>::Ok(move _3); // scope 0 at $DIR/try_identity_e2e.rs:+1:5: +6:6
return; // scope 0 at $DIR/try_identity_e2e.rs:+7:1: +7:2
}
}
bb0: {
StorageLive(_1); // scope 0 at $DIR/uninhabited_enum_branching.rs:+1:5: +5:6
StorageLive(_2); // scope 0 at $DIR/uninhabited_enum_branching.rs:+1:11: +1:19
- Deinit(_2); // scope 0 at $DIR/uninhabited_enum_branching.rs:+1:11: +1:19
- discriminant(_2) = 2; // scope 0 at $DIR/uninhabited_enum_branching.rs:+1:11: +1:19
+ _2 = Test1::C; // scope 0 at $DIR/uninhabited_enum_branching.rs:+1:11: +1:19
_3 = discriminant(_2); // scope 0 at $DIR/uninhabited_enum_branching.rs:+1:11: +1:19
switchInt(move _3) -> [2: bb1, otherwise: bb2]; // scope 0 at $DIR/uninhabited_enum_branching.rs:+1:5: +1:19
}
StorageDead(_1); // scope 0 at $DIR/uninhabited_enum_branching.rs:+5:6: +5:7
StorageLive(_6); // scope 0 at $DIR/uninhabited_enum_branching.rs:+7:5: +10:6
StorageLive(_7); // scope 0 at $DIR/uninhabited_enum_branching.rs:+7:11: +7:19
- Deinit(_7); // scope 0 at $DIR/uninhabited_enum_branching.rs:+7:11: +7:19
- discriminant(_7) = 0; // scope 0 at $DIR/uninhabited_enum_branching.rs:+7:11: +7:19
+ _7 = Test2::D; // scope 0 at $DIR/uninhabited_enum_branching.rs:+7:11: +7:19
_8 = discriminant(_7); // scope 0 at $DIR/uninhabited_enum_branching.rs:+7:11: +7:19
switchInt(move _8) -> [4: bb5, 5: bb3, otherwise: bb4]; // scope 0 at $DIR/uninhabited_enum_branching.rs:+7:5: +7:19
}
bb0: {
StorageLive(_1); // scope 0 at $DIR/uninhabited_enum_branching.rs:+1:5: +5:6
StorageLive(_2); // scope 0 at $DIR/uninhabited_enum_branching.rs:+1:11: +1:19
- Deinit(_2); // scope 0 at $DIR/uninhabited_enum_branching.rs:+1:11: +1:19
- discriminant(_2) = 2; // scope 0 at $DIR/uninhabited_enum_branching.rs:+1:11: +1:19
+ _2 = Test1::C; // scope 0 at $DIR/uninhabited_enum_branching.rs:+1:11: +1:19
_3 = discriminant(_2); // scope 0 at $DIR/uninhabited_enum_branching.rs:+1:11: +1:19
- switchInt(move _3) -> [0: bb3, 1: bb4, 2: bb1, otherwise: bb2]; // scope 0 at $DIR/uninhabited_enum_branching.rs:+1:5: +1:19
+ switchInt(move _3) -> [2: bb1, otherwise: bb2]; // scope 0 at $DIR/uninhabited_enum_branching.rs:+1:5: +1:19
StorageDead(_1); // scope 0 at $DIR/uninhabited_enum_branching.rs:+5:6: +5:7
StorageLive(_6); // scope 0 at $DIR/uninhabited_enum_branching.rs:+7:5: +10:6
StorageLive(_7); // scope 0 at $DIR/uninhabited_enum_branching.rs:+7:11: +7:19
- Deinit(_7); // scope 0 at $DIR/uninhabited_enum_branching.rs:+7:11: +7:19
- discriminant(_7) = 0; // scope 0 at $DIR/uninhabited_enum_branching.rs:+7:11: +7:19
+ _7 = Test2::D; // scope 0 at $DIR/uninhabited_enum_branching.rs:+7:11: +7:19
_8 = discriminant(_7); // scope 0 at $DIR/uninhabited_enum_branching.rs:+7:11: +7:19
switchInt(move _8) -> [4: bb8, 5: bb6, otherwise: bb7]; // scope 0 at $DIR/uninhabited_enum_branching.rs:+7:5: +7:19
}
bb0: {
StorageLive(_1); // scope 0 at $DIR/uninhabited_enum_branching2.rs:+1:9: +1:13
StorageLive(_2); // scope 0 at $DIR/uninhabited_enum_branching2.rs:+1:38: +1:46
- Deinit(_2); // scope 0 at $DIR/uninhabited_enum_branching2.rs:+1:38: +1:46
- discriminant(_2) = 2; // scope 0 at $DIR/uninhabited_enum_branching2.rs:+1:38: +1:46
- Deinit(_1); // scope 0 at $DIR/uninhabited_enum_branching2.rs:+1:16: +1:48
- (_1.0: u32) = const 51_u32; // scope 0 at $DIR/uninhabited_enum_branching2.rs:+1:16: +1:48
- (_1.1: Test1) = move _2; // scope 0 at $DIR/uninhabited_enum_branching2.rs:+1:16: +1:48
+ _2 = Test1::C; // scope 0 at $DIR/uninhabited_enum_branching2.rs:+1:38: +1:46
+ _1 = Plop { xx: const 51_u32, test1: move _2 }; // scope 0 at $DIR/uninhabited_enum_branching2.rs:+1:16: +1:48
StorageDead(_2); // scope 0 at $DIR/uninhabited_enum_branching2.rs:+1:47: +1:48
StorageLive(_3); // scope 1 at $DIR/uninhabited_enum_branching2.rs:+3:5: +8:6
StorageLive(_4); // scope 1 at $DIR/uninhabited_enum_branching2.rs:+3:11: +3:22
bb0: {
StorageLive(_1); // scope 0 at $DIR/uninhabited_enum_branching2.rs:+1:9: +1:13
StorageLive(_2); // scope 0 at $DIR/uninhabited_enum_branching2.rs:+1:38: +1:46
- Deinit(_2); // scope 0 at $DIR/uninhabited_enum_branching2.rs:+1:38: +1:46
- discriminant(_2) = 2; // scope 0 at $DIR/uninhabited_enum_branching2.rs:+1:38: +1:46
- Deinit(_1); // scope 0 at $DIR/uninhabited_enum_branching2.rs:+1:16: +1:48
- (_1.0: u32) = const 51_u32; // scope 0 at $DIR/uninhabited_enum_branching2.rs:+1:16: +1:48
- (_1.1: Test1) = move _2; // scope 0 at $DIR/uninhabited_enum_branching2.rs:+1:16: +1:48
+ _2 = Test1::C; // scope 0 at $DIR/uninhabited_enum_branching2.rs:+1:38: +1:46
+ _1 = Plop { xx: const 51_u32, test1: move _2 }; // scope 0 at $DIR/uninhabited_enum_branching2.rs:+1:16: +1:48
StorageDead(_2); // scope 0 at $DIR/uninhabited_enum_branching2.rs:+1:47: +1:48
StorageLive(_3); // scope 1 at $DIR/uninhabited_enum_branching2.rs:+3:5: +8:6
StorageLive(_4); // scope 1 at $DIR/uninhabited_enum_branching2.rs:+3:11: +3:22
let mut _0: Test; // return place in scope 0 at $DIR/unusual_item_types.rs:+0:5: +0:6
bb0: {
- Deinit(_0); // scope 0 at $DIR/unusual_item_types.rs:+0:5: +0:6
- ((_0 as X).0: usize) = move _1; // scope 0 at $DIR/unusual_item_types.rs:+0:5: +0:6
- discriminant(_0) = 0; // scope 0 at $DIR/unusual_item_types.rs:+0:5: +0:6
+ _0 = Test::X(move _1); // scope 0 at $DIR/unusual_item_types.rs:+0:5: +0:6
return; // scope 0 at $DIR/unusual_item_types.rs:+0:5: +0:6
}
}
StorageLive(_1); // scope 0 at $DIR/while_let_loops.rs:+1:9: +1:15
_1 = const 0_i32; // scope 0 at $DIR/while_let_loops.rs:+1:18: +1:19
StorageLive(_2); // scope 2 at $DIR/while_let_loops.rs:+2:28: +2:32
- Deinit(_2); // scope 2 at $DIR/while_let_loops.rs:+2:28: +2:32
- discriminant(_2) = 0; // scope 2 at $DIR/while_let_loops.rs:+2:28: +2:32
+ _2 = Option::<u32>::None; // scope 2 at $DIR/while_let_loops.rs:+2:28: +2:32
- _3 = discriminant(_2); // scope 2 at $DIR/while_let_loops.rs:+2:15: +2:25
- switchInt(move _3) -> [1: bb1, otherwise: bb3]; // scope 2 at $DIR/while_let_loops.rs:+2:15: +2:25
+ _3 = const 0_isize; // scope 2 at $DIR/while_let_loops.rs:+2:15: +2:25
pub mod doc_block_table {
pub trait DocBlockTableTrait {
- fn func();
+ fn foo();
}
/// Struct doc.
/// | header1 | header2 |
/// |--------------------------|--------------------------|
/// | Lorem Ipsum, Lorem Ipsum | Lorem Ipsum, Lorem Ipsum |
- fn func() {
+ fn foo() {
println!();
}
}
///
/// </sub>
pub mod codeblock_sub {}
+pub mod search_results {
+
+ pub struct SearchResults {
+ pub foo: i32,
+ }
+
+ #[macro_export]
+ macro_rules! foo {
+ () => {};
+ }
+
+}
--- /dev/null
+// exact-check
+
+// https://github.com/rust-lang/rust/issues/103357
+const QUERY = 'regex';
+
+const EXPECTED = {
+ 'others': [],
+ 'in_args': [],
+ 'returned': [],
+};
{ 'path': 'std', 'name': 'println' },
{ 'path': 'std', 'name': 'eprint' },
{ 'path': 'std', 'name': 'eprintln' },
- { 'path': 'std::pin', 'name': 'pin' },
- { 'path': 'std::future', 'name': 'join' },
- { 'path': 'std', 'name': 'line' },
- { 'path': 'std', 'name': 'write' },
],
};
-// exact-check
-
const QUERY = [
'StructItem',
'StructFieldItem',
'others': [
{ 'path': 'module_substring::Sig', 'name': 'pc' },
{ 'path': 'module_substring::Si', 'name': 'pc' },
- { 'path': 'module_substring::Si', 'name': 'pa' },
],
};
-// check-pass
// normalize-stderr-test: "`.*`" -> "`DEF_ID`"
// normalize-stdout-test: "`.*`" -> "`DEF_ID`"
// edition:2018
pub async fn f() -> impl std::fmt::Debug {
- // rustdoc doesn't care that this is infinitely sized
#[derive(Debug)]
- enum E {
+ enum E { //~ ERROR
This(E),
Unit,
}
--- /dev/null
+error[E0072]: recursive type `DEF_ID` has infinite size
+ --> $DIR/infinite-recursive-type-impl-trait-return.rs:7:5
+ |
+LL | enum E {
+ | ^^^^^^
+LL | This(E),
+ | - recursive without indirection
+ |
+help: insert some indirection (e.g., a `DEF_ID`) to break the cycle
+ |
+LL | This(Box<E>),
+ | ++++ +
+
+error: aborting due to previous error
+
+For more information about this error, try `DEF_ID`.
-// check-pass
-
fn f() -> impl Sized {
- // rustdoc doesn't care that this is infinitely sized
- enum E {
+ enum E { //~ ERROR
V(E),
}
unimplemented!()
--- /dev/null
+error[E0072]: recursive type `f::E` has infinite size
+ --> $DIR/infinite-recursive-type-impl-trait.rs:2:5
+ |
+LL | enum E {
+ | ^^^^^^
+LL | V(E),
+ | - recursive without indirection
+ |
+help: insert some indirection (e.g., a `Box`, `Rc`, or `&`) to break the cycle
+ |
+LL | V(Box<E>),
+ | ++++ +
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0072`.
fn bar(&self);
fn foo(&mut self) {}
}
+
+pub trait Bar {
+ fn bar(&self);
+ fn foo1(&mut self) {}
+ fn foo2(&mut self) {}
+}
+
+pub trait Baz {
+ fn bar1(&self);
+ fn bar2(&self);
+ fn foo(&mut self) {}
+}
<code>pub trait Write {
+ // Required methods
fn <a href="#tymethod.poll_write" class="fn">poll_write</a>(<br />        self: <a class="enum" href="{{channel}}/core/option/enum.Option.html" title="enum core::option::Option">Option</a><<a class="struct" href="{{channel}}/alloc/string/struct.String.html" title="struct alloc::string::String">String</a>>,<br />        cx: &mut <a class="enum" href="{{channel}}/core/option/enum.Option.html" title="enum core::option::Option">Option</a><<a class="struct" href="{{channel}}/alloc/string/struct.String.html" title="struct alloc::string::String">String</a>>,<br />        buf: &mut [<a class="primitive" href="{{channel}}/std/primitive.usize.html">usize</a>]<br />    ) -> <a class="enum" href="{{channel}}/core/option/enum.Option.html" title="enum core::option::Option">Option</a><<a class="enum" href="{{channel}}/core/result/enum.Result.html" title="enum core::result::Result">Result</a><<a class="primitive" href="{{channel}}/std/primitive.usize.html">usize</a>, <a class="struct" href="struct.Error.html" title="struct foo::Error">Error</a>>>;
<span class="item-spacer" /> fn <a href="#tymethod.poll_flush" class="fn">poll_flush</a>(<br />        self: <a class="enum" href="{{channel}}/core/option/enum.Option.html" title="enum core::option::Option">Option</a><<a class="struct" href="{{channel}}/alloc/string/struct.String.html" title="struct alloc::string::String">String</a>>,<br />        cx: &mut <a class="enum" href="{{channel}}/core/option/enum.Option.html" title="enum core::option::Option">Option</a><<a class="struct" href="{{channel}}/alloc/string/struct.String.html" title="struct alloc::string::String">String</a>><br />    ) -> <a class="enum" href="{{channel}}/core/option/enum.Option.html" title="enum core::option::Option">Option</a><<a class="enum" href="{{channel}}/core/result/enum.Result.html" title="enum core::result::Result">Result</a><<a class="primitive" href="{{channel}}/std/primitive.unit.html">()</a>, <a class="struct" href="struct.Error.html" title="struct foo::Error">Error</a>>>;
<span class="item-spacer" /> fn <a href="#tymethod.poll_close" class="fn">poll_close</a>(<br />        self: <a class="enum" href="{{channel}}/core/option/enum.Option.html" title="enum core::option::Option">Option</a><<a class="struct" href="{{channel}}/alloc/string/struct.String.html" title="struct alloc::string::String">String</a>>,<br />        cx: &mut <a class="enum" href="{{channel}}/core/option/enum.Option.html" title="enum core::option::Option">Option</a><<a class="struct" href="{{channel}}/alloc/string/struct.String.html" title="struct alloc::string::String">String</a>><br />    ) -> <a class="enum" href="{{channel}}/core/option/enum.Option.html" title="enum core::option::Option">Option</a><<a class="enum" href="{{channel}}/core/result/enum.Result.html" title="enum core::result::Result">Result</a><<a class="primitive" href="{{channel}}/std/primitive.unit.html">()</a>, <a class="struct" href="struct.Error.html" title="struct foo::Error">Error</a>>>;
+ // Provided method
fn <a href="#method.poll_write_vectored" class="fn">poll_write_vectored</a>(<br />        self: <a class="enum" href="{{channel}}/core/option/enum.Option.html" title="enum core::option::Option">Option</a><<a class="struct" href="{{channel}}/alloc/string/struct.String.html" title="struct alloc::string::String">String</a>>,<br />        cx: &mut <a class="enum" href="{{channel}}/core/option/enum.Option.html" title="enum core::option::Option">Option</a><<a class="struct" href="{{channel}}/alloc/string/struct.String.html" title="struct alloc::string::String">String</a>>,<br />        bufs: &[<a class="primitive" href="{{channel}}/std/primitive.usize.html">usize</a>]<br />    ) -> <a class="enum" href="{{channel}}/core/option/enum.Option.html" title="enum core::option::Option">Option</a><<a class="enum" href="{{channel}}/core/result/enum.Result.html" title="enum core::result::Result">Result</a><<a class="primitive" href="{{channel}}/std/primitive.usize.html">usize</a>, <a class="struct" href="struct.Error.html" title="struct foo::Error">Error</a>>> { ... }
-}</code>
\ No newline at end of file
+}</code>
-<script type="text/json" id="notable-traits-data">{"SomeStruct":"<h3>Notable traits for <code><a class=\"struct\" href=\"struct.SomeStruct.html\" title=\"struct doc_notable_trait::SomeStruct\">SomeStruct</a></code></h3><pre><code><span class=\"where fmt-newline\">impl <a class=\"trait\" href=\"trait.SomeTrait.html\" title=\"trait doc_notable_trait::SomeTrait\">SomeTrait</a> for <a class=\"struct\" href=\"struct.SomeStruct.html\" title=\"struct doc_notable_trait::SomeStruct\">SomeStruct</a></span>","Wrapper<Self>":"<h3>Notable traits for <code><a class=\"struct\" href=\"struct.Wrapper.html\" title=\"struct doc_notable_trait::Wrapper\">Wrapper</a>&lt;T&gt;</code></h3><pre><code><span class=\"where fmt-newline\">impl&lt;T:&nbsp;<a class=\"trait\" href=\"trait.SomeTrait.html\" title=\"trait doc_notable_trait::SomeTrait\">SomeTrait</a>&gt; <a class=\"trait\" href=\"trait.SomeTrait.html\" title=\"trait doc_notable_trait::SomeTrait\">SomeTrait</a> for <a class=\"struct\" href=\"struct.Wrapper.html\" title=\"struct doc_notable_trait::Wrapper\">Wrapper</a>&lt;T&gt;</span>"}</script>
\ No newline at end of file
+<script type="text/json" id="notable-traits-data">{"SomeStruct":"<h3>Notable traits for <code><a class=\"struct\" href=\"struct.SomeStruct.html\" title=\"struct doc_notable_trait::SomeStruct\">SomeStruct</a></code></h3><pre><code><span class=\"where fmt-newline\">impl <a class=\"trait\" href=\"trait.SomeTrait.html\" title=\"trait doc_notable_trait::SomeTrait\">SomeTrait</a> for <a class=\"struct\" href=\"struct.SomeStruct.html\" title=\"struct doc_notable_trait::SomeStruct\">SomeStruct</a></span>","Wrapper<Self>":"<h3>Notable traits for <code><a class=\"struct\" href=\"struct.Wrapper.html\" title=\"struct doc_notable_trait::Wrapper\">Wrapper</a>&lt;T&gt;</code></h3><pre><code><span class=\"where fmt-newline\">impl&lt;T: <a class=\"trait\" href=\"trait.SomeTrait.html\" title=\"trait doc_notable_trait::SomeTrait\">SomeTrait</a>&gt; <a class=\"trait\" href=\"trait.SomeTrait.html\" title=\"trait doc_notable_trait::SomeTrait\">SomeTrait</a> for <a class=\"struct\" href=\"struct.Wrapper.html\" title=\"struct doc_notable_trait::Wrapper\">Wrapper</a>&lt;T&gt;</span>"}</script>
\ No newline at end of file
-<script type="text/json" id="notable-traits-data">{"Wrapper<Self>":"<h3>Notable traits for <code><a class=\"struct\" href=\"struct.Wrapper.html\" title=\"struct doc_notable_trait::Wrapper\">Wrapper</a>&lt;T&gt;</code></h3><pre><code><span class=\"where fmt-newline\">impl&lt;T:&nbsp;<a class=\"trait\" href=\"trait.SomeTrait.html\" title=\"trait doc_notable_trait::SomeTrait\">SomeTrait</a>&gt; <a class=\"trait\" href=\"trait.SomeTrait.html\" title=\"trait doc_notable_trait::SomeTrait\">SomeTrait</a> for <a class=\"struct\" href=\"struct.Wrapper.html\" title=\"struct doc_notable_trait::Wrapper\">Wrapper</a>&lt;T&gt;</span>"}</script>
\ No newline at end of file
+<script type="text/json" id="notable-traits-data">{"Wrapper<Self>":"<h3>Notable traits for <code><a class=\"struct\" href=\"struct.Wrapper.html\" title=\"struct doc_notable_trait::Wrapper\">Wrapper</a>&lt;T&gt;</code></h3><pre><code><span class=\"where fmt-newline\">impl&lt;T: <a class=\"trait\" href=\"trait.SomeTrait.html\" title=\"trait doc_notable_trait::SomeTrait\">SomeTrait</a>&gt; <a class=\"trait\" href=\"trait.SomeTrait.html\" title=\"trait doc_notable_trait::SomeTrait\">SomeTrait</a> for <a class=\"struct\" href=\"struct.Wrapper.html\" title=\"struct doc_notable_trait::Wrapper\">Wrapper</a>&lt;T&gt;</span>"}</script>
\ No newline at end of file
--- /dev/null
+// This is a regression test for <https://github.com/rust-lang/rust/issues/106373>.
+// It ensures that the items in the `doc(hidden)` const block don't show up in the
+// generated docs.
+
+// compile-flags: --document-private-items
+
+#![crate_name = "foo"]
+
+// @has 'foo/index.html'
+// @count - '//*[@class="item-table"]//a[@class="struct"]' 2
+// @count - '//*[@class="item-table"]//a[@class="trait"]' 1
+// @count - '//*[@class="item-table"]//a[@class="macro"]' 0
+#[doc(hidden)]
+const _: () = {
+ macro_rules! stry {
+ () => {};
+ }
+
+ struct ShouldBeHidden;
+
+ // @has 'foo/struct.Foo.html'
+ // @!has - '//*[@class="code-header"]' 'impl Bar for Foo'
+ #[doc(hidden)]
+ impl Bar for Foo {
+ fn bar(&self) {
+ struct SHouldAlsoBeHidden;
+ }
+ }
+
+ // @has 'foo/struct.Private.html'
+ // @has - '//*[@id="impl-Bar-for-Private"]/*[@class="code-header"]' 'impl Bar for Private'
+ // @has - '//*[@id="method.bar"]/*[@class="code-header"]' 'fn bar(&self)'
+ impl Bar for Private {
+ fn bar(&self) {}
+ }
+
+ // @has - '//*[@id="impl-Private"]/*[@class="code-header"]' 'impl Private'
+ // @has - '//*[@id="method.tralala"]/*[@class="code-header"]' 'fn tralala()'
+ impl Private {
+ fn tralala() {}
+ }
+};
+
+
+struct Private;
+pub struct Foo;
+
+pub trait Bar {
+ fn bar(&self);
+}
--- /dev/null
+// Regression test for #83026.
+// The goal of this test is to ensure that impl blocks inside
+// const expressions are documented as well.
+
+#![crate_name = "foo"]
+
+// @has 'foo/struct.A.html'
+// @has - '//*[@id="method.new"]/*[@class="code-header"]' 'pub fn new() -> A'
+// @has - '//*[@id="method.bar"]/*[@class="code-header"]' 'pub fn bar(&self)'
+// @has - '//*[@id="method.woo"]/*[@class="code-header"]' 'pub fn woo(&self)'
+// @has - '//*[@id="method.yoo"]/*[@class="code-header"]' 'pub fn yoo()'
+// @has - '//*[@id="method.yuu"]/*[@class="code-header"]' 'pub fn yuu()'
+pub struct A;
+
+const _: () = {
+ impl A {
+ const FOO: () = {
+ impl A {
+ pub fn woo(&self) {}
+ }
+ };
+
+ pub fn new() -> A {
+ A
+ }
+ }
+};
+pub const X: () = {
+ impl A {
+ pub fn bar(&self) {}
+ }
+};
+
+fn foo() {
+ impl A {
+ pub fn yoo() {}
+ }
+ const _: () = {
+ impl A {
+ pub fn yuu() {}
+ }
+ };
+}
extern crate inline_default_methods;
// @has inline_default_methods/trait.Foo.html
-// @has - '//pre[@class="rust item-decl"]' 'fn bar(&self);'
-// @has - '//pre[@class="rust item-decl"]' 'fn foo(&mut self) { ... }'
+// @has - '//pre[@class="rust item-decl"]' '// Required method fn bar(&self);'
+// @has - '//pre[@class="rust item-decl"]' '// Provided method fn foo(&mut self)'
pub use inline_default_methods::Foo;
+
+// @has inline_default_methods/trait.Bar.html
+// @has - '//pre[@class="rust item-decl"]' '// Required method fn bar(&self);'
+// @has - '//pre[@class="rust item-decl"]' '// Provided methods fn foo1(&mut self)'
+// @has - '//pre[@class="rust item-decl"]' 'fn foo2(&mut self)'
+pub use inline_default_methods::Bar;
+
+// @has inline_default_methods/trait.Baz.html
+// @has - '//pre[@class="rust item-decl"]' '// Required methods fn bar1(&self);'
+// @has - '//pre[@class="rust item-decl"]' 'fn bar2(&self);'
+// @has - '//pre[@class="rust item-decl"]' '// Provided method fn foo(&mut self)'
+pub use inline_default_methods::Baz;
extern crate issue_85454;
// @has foo/trait.FromResidual.html
-// @has - '//pre[@class="rust item-decl"]' 'pub trait FromResidual<R = <Self as Try>::Residual> { fn from_residual(residual: R) -> Self; }'
+// @has - '//pre[@class="rust item-decl"]' 'pub trait FromResidual<R = <Self as Try>::Residual> { // Required method fn from_residual(residual: R) -> Self; }'
pub trait FromResidual<R = <Self as Try>::Residual> {
fn from_residual(residual: R) -> Self;
}
pub mod reexport {
// @has foo/reexport/trait.FromResidual.html
- // @has - '//pre[@class="rust item-decl"]' 'pub trait FromResidual<R = <Self as Try>::Residual> { fn from_residual(residual: R) -> Self; }'
+ // @has - '//pre[@class="rust item-decl"]' 'pub trait FromResidual<R = <Self as Try>::Residual> { // Required method fn from_residual(residual: R) -> Self; }'
pub use issue_85454::*;
}
// @has redirect/index.html
// @has - '//code' 'pub use reexp_stripped::Bar'
// @has - '//code/a' 'Bar'
+// @has - '//a[@href="../reexp_stripped/hidden/struct.Bar.html"]' 'Bar'
// @has reexp_stripped/hidden/struct.Bar.html
-// @has - '//p/a' '../../reexp_stripped/struct.Bar.html'
// @has 'reexp_stripped/struct.Bar.html'
+// @has - '//a[@href="struct.Bar.html"]' 'Bar'
#[doc(no_inline)]
pub use reexp_stripped::Bar;
impl Foo for Bar {}
<pre class="rust item-decl"><code>pub trait TraitWhere {
- type <a href="#associatedtype.Item" class="associatedtype">Item</a><'a><br />    <span class="where">where<br />        Self: 'a</span>;
+ type <a href="#associatedtype.Item" class="associatedtype">Item</a><'a><br />       <span class="where">where Self: 'a</span>;
- fn <a href="#method.func" class="fn">func</a>(self)<br />    <span class="where">where<br />        Self: <a class="trait" href="{{channel}}/core/marker/trait.Sized.html" title="trait core::marker::Sized">Sized</a></span>,
- { ... }
-<span class="item-spacer" /> fn <a href="#method.lines" class="fn">lines</a>(self) -> <a class="struct" href="{{channel}}/std/io/struct.Lines.html" title="struct std::io::Lines">Lines</a><Self><br />    <span class="where">where<br />        Self: <a class="trait" href="{{channel}}/core/marker/trait.Sized.html" title="trait core::marker::Sized">Sized</a></span>,
- { ... }
+ // Provided methods
+ fn <a href="#method.func" class="fn">func</a>(self)<br />       <span class="where">where Self: <a class="trait" href="{{channel}}/core/marker/trait.Sized.html" title="trait core::marker::Sized">Sized</a></span> { ... }
+<span class="item-spacer" /> fn <a href="#method.lines" class="fn">lines</a>(self) -> <a class="struct" href="{{channel}}/std/io/struct.Lines.html" title="struct std::io::Lines">Lines</a><Self><br />       <span class="where">where Self: <a class="trait" href="{{channel}}/core/marker/trait.Sized.html" title="trait core::marker::Sized">Sized</a></span> { ... }
+<span class="item-spacer" /> fn <a href="#method.merge" class="fn">merge</a><T>(self, a: T)<br />       <span class="where">where Self: <a class="trait" href="{{channel}}/core/marker/trait.Sized.html" title="trait core::marker::Sized">Sized</a>,<br />             T: <a class="trait" href="{{channel}}/core/marker/trait.Sized.html" title="trait core::marker::Sized">Sized</a></span> { ... }
}</code></pre>
\ No newline at end of file
where
Self: Sized,
{ todo!() }
+
+ fn merge<T>(self, a: T)
+ where
+ Self: Sized,
+ T: Sized,
+ { todo!() }
}
// @has foo/struct.Echo.html '//*[@class="impl"]//h3[@class="code-header"]' \
-<pre class="rust item-decl"><code>pub enum Cow2<'a, B: ?<a class="trait" href="{{channel}}/core/marker/trait.Sized.html" title="trait core::marker::Sized">Sized</a> + <a class="trait" href="trait.ToOwned.html" title="trait foo::ToOwned">ToOwned</a><dyn <a class="trait" href="{{channel}}/core/clone/trait.Clone.html" title="trait core::clone::Clone">Clone</a>> + 'a> {
+<pre class="rust item-decl"><code>pub enum Cow2<'a, B: ?<a class="trait" href="{{channel}}/core/marker/trait.Sized.html" title="trait core::marker::Sized">Sized</a> + <a class="trait" href="trait.ToOwned.html" title="trait foo::ToOwned">ToOwned</a><dyn <a class="trait" href="{{channel}}/core/clone/trait.Clone.html" title="trait core::clone::Clone">Clone</a>> + 'a> {
Borrowed(<a class="primitive" href="{{channel}}/std/primitive.reference.html">&'a B</a>),
Whatever(<a class="primitive" href="{{channel}}/std/primitive.u32.html">u32</a>),
}</code></pre>
\ No newline at end of file
-<pre class="rust item-decl"><code>pub struct Struct2<'a, B: ?<a class="trait" href="{{channel}}/core/marker/trait.Sized.html" title="trait core::marker::Sized">Sized</a> + <a class="trait" href="trait.ToOwned.html" title="trait foo::ToOwned">ToOwned</a><dyn <a class="trait" href="{{channel}}/core/clone/trait.Clone.html" title="trait core::clone::Clone">Clone</a>> + 'a> {
+<pre class="rust item-decl"><code>pub struct Struct2<'a, B: ?<a class="trait" href="{{channel}}/core/marker/trait.Sized.html" title="trait core::marker::Sized">Sized</a> + <a class="trait" href="trait.ToOwned.html" title="trait foo::ToOwned">ToOwned</a><dyn <a class="trait" href="{{channel}}/core/clone/trait.Clone.html" title="trait core::clone::Clone">Clone</a>> + 'a> {
pub a: <a class="primitive" href="{{channel}}/std/primitive.reference.html">&'a B</a>,
pub b: <a class="primitive" href="{{channel}}/std/primitive.u32.html">u32</a>,
}</code></pre>
\ No newline at end of file
<pre class="rust item-decl"><code>pub trait ToOwned<T><span class="where fmt-newline">where<br />    T: <a class="trait" href="{{channel}}/core/clone/trait.Clone.html" title="trait core::clone::Clone">Clone</a>,</span>{
type <a href="#associatedtype.Owned" class="associatedtype">Owned</a>;
+ // Required methods
fn <a href="#tymethod.to_owned" class="fn">to_owned</a>(&self) -> Self::<a class="associatedtype" href="trait.ToOwned.html#associatedtype.Owned" title="type foo::ToOwned::Owned">Owned</a>;
<span class="item-spacer" /> fn <a href="#tymethod.whatever" class="fn">whatever</a>(&self) -> T;
}</code></pre>
\ No newline at end of file
<pre class="rust item-decl"><code>pub trait ToOwned2<T: <a class="trait" href="{{channel}}/core/clone/trait.Clone.html" title="trait core::clone::Clone">Clone</a>> {
type <a href="#associatedtype.Owned" class="associatedtype">Owned</a>;
+ // Required methods
fn <a href="#tymethod.to_owned" class="fn">to_owned</a>(&self) -> Self::<a class="associatedtype" href="trait.ToOwned2.html#associatedtype.Owned" title="type foo::ToOwned2::Owned">Owned</a>;
<span class="item-spacer" /> fn <a href="#tymethod.whatever" class="fn">whatever</a>(&self) -> T;
}</code></pre>
\ No newline at end of file
-<pre class="rust item-decl"><code>pub union Union2<'a, B: ?<a class="trait" href="{{channel}}/core/marker/trait.Sized.html" title="trait core::marker::Sized">Sized</a> + <a class="trait" href="trait.ToOwned.html" title="trait foo::ToOwned">ToOwned</a><dyn <a class="trait" href="{{channel}}/core/clone/trait.Clone.html" title="trait core::clone::Clone">Clone</a>> + 'a> {
+<pre class="rust item-decl"><code>pub union Union2<'a, B: ?<a class="trait" href="{{channel}}/core/marker/trait.Sized.html" title="trait core::marker::Sized">Sized</a> + <a class="trait" href="trait.ToOwned.html" title="trait foo::ToOwned">ToOwned</a><dyn <a class="trait" href="{{channel}}/core/clone/trait.Clone.html" title="trait core::clone::Clone">Clone</a>> + 'a> {
/* private fields */
}</code></pre>
\ No newline at end of file
| |
| arguments to this function are incorrect
|
-note: while checking the return type of the `async fn`
- --> $DIR/dont-suggest-missing-await.rs:7:24
- |
-LL | async fn make_u32() -> u32 {
- | ^^^ checked the `Output` of this `async fn`, found opaque type
= note: expected type `u32`
found opaque type `impl Future<Output = u32>`
note: function defined here
--- /dev/null
+// edition: 2021
+// run-pass
+
+async fn test(_arg: [u8; 16]) {}
+
+async fn use_future(fut: impl std::future::Future<Output = ()>) {
+ fut.await
+}
+
+fn main() {
+ let actual = std::mem::size_of_val(
+ &use_future(use_future(use_future(use_future(use_future(test([0; 16])))))));
+ // Not using an exact number in case it slightly changes over different commits
+ let expected = 550;
+ assert!(actual > expected, "expected: >{expected}, actual: {actual}");
+}
--- /dev/null
+// compile-flags: -Z print-type-sizes --crate-type=lib
+// edition: 2021
+// build-pass
+// ignore-pass
+
+pub async fn test() {
+ let _ = a([0u8; 1024]).await;
+}
+
+pub async fn a<T>(t: T) -> T {
+ b(t).await
+}
+async fn b<T>(t: T) -> T {
+ c(t).await
+}
+async fn c<T>(t: T) -> T {
+ t
+}
--- /dev/null
+print-type-size type: `[async fn body@$DIR/large-arg.rs:6:21: 8:2]`: 3076 bytes, alignment: 1 bytes
+print-type-size discriminant: 1 bytes
+print-type-size variant `Unresumed`: 0 bytes
+print-type-size variant `Suspend0`: 3075 bytes
+print-type-size local `.__awaitee`: 3075 bytes, offset: 0 bytes, alignment: 1 bytes
+print-type-size variant `Returned`: 0 bytes
+print-type-size variant `Panicked`: 0 bytes
+print-type-size type: `[async fn body@$DIR/large-arg.rs:10:30: 12:2]`: 3075 bytes, alignment: 1 bytes
+print-type-size discriminant: 1 bytes
+print-type-size variant `Unresumed`: 1024 bytes
+print-type-size upvar `.t`: 1024 bytes, offset: 0 bytes, alignment: 1 bytes
+print-type-size variant `Suspend0`: 3074 bytes
+print-type-size upvar `.t`: 1024 bytes, offset: 0 bytes, alignment: 1 bytes
+print-type-size local `.__awaitee`: 2050 bytes
+print-type-size variant `Returned`: 1024 bytes
+print-type-size upvar `.t`: 1024 bytes, offset: 0 bytes, alignment: 1 bytes
+print-type-size variant `Panicked`: 1024 bytes
+print-type-size upvar `.t`: 1024 bytes, offset: 0 bytes, alignment: 1 bytes
+print-type-size type: `std::mem::ManuallyDrop<[async fn body@$DIR/large-arg.rs:10:30: 12:2]>`: 3075 bytes, alignment: 1 bytes
+print-type-size field `.value`: 3075 bytes
+print-type-size type: `std::mem::MaybeUninit<[async fn body@$DIR/large-arg.rs:10:30: 12:2]>`: 3075 bytes, alignment: 1 bytes
+print-type-size variant `MaybeUninit`: 3075 bytes
+print-type-size field `.uninit`: 0 bytes
+print-type-size field `.value`: 3075 bytes
+print-type-size type: `[async fn body@$DIR/large-arg.rs:13:26: 15:2]`: 2050 bytes, alignment: 1 bytes
+print-type-size discriminant: 1 bytes
+print-type-size variant `Unresumed`: 1024 bytes
+print-type-size upvar `.t`: 1024 bytes, offset: 0 bytes, alignment: 1 bytes
+print-type-size variant `Suspend0`: 2049 bytes
+print-type-size upvar `.t`: 1024 bytes, offset: 0 bytes, alignment: 1 bytes
+print-type-size local `.__awaitee`: 1025 bytes
+print-type-size variant `Returned`: 1024 bytes
+print-type-size upvar `.t`: 1024 bytes, offset: 0 bytes, alignment: 1 bytes
+print-type-size variant `Panicked`: 1024 bytes
+print-type-size upvar `.t`: 1024 bytes, offset: 0 bytes, alignment: 1 bytes
+print-type-size type: `std::mem::ManuallyDrop<[async fn body@$DIR/large-arg.rs:13:26: 15:2]>`: 2050 bytes, alignment: 1 bytes
+print-type-size field `.value`: 2050 bytes
+print-type-size type: `std::mem::MaybeUninit<[async fn body@$DIR/large-arg.rs:13:26: 15:2]>`: 2050 bytes, alignment: 1 bytes
+print-type-size variant `MaybeUninit`: 2050 bytes
+print-type-size field `.uninit`: 0 bytes
+print-type-size field `.value`: 2050 bytes
+print-type-size type: `[async fn body@$DIR/large-arg.rs:16:26: 18:2]`: 1025 bytes, alignment: 1 bytes
+print-type-size discriminant: 1 bytes
+print-type-size variant `Unresumed`: 1024 bytes
+print-type-size upvar `.t`: 1024 bytes, offset: 0 bytes, alignment: 1 bytes
+print-type-size variant `Returned`: 1024 bytes
+print-type-size upvar `.t`: 1024 bytes, offset: 0 bytes, alignment: 1 bytes
+print-type-size variant `Panicked`: 1024 bytes
+print-type-size upvar `.t`: 1024 bytes, offset: 0 bytes, alignment: 1 bytes
+print-type-size type: `std::mem::ManuallyDrop<[async fn body@$DIR/large-arg.rs:16:26: 18:2]>`: 1025 bytes, alignment: 1 bytes
+print-type-size field `.value`: 1025 bytes
+print-type-size type: `std::mem::MaybeUninit<[async fn body@$DIR/large-arg.rs:16:26: 18:2]>`: 1025 bytes, alignment: 1 bytes
+print-type-size variant `MaybeUninit`: 1025 bytes
+print-type-size field `.uninit`: 0 bytes
+print-type-size field `.value`: 1025 bytes
+print-type-size type: `std::task::Poll<[u8; 1024]>`: 1025 bytes, alignment: 1 bytes
+print-type-size discriminant: 1 bytes
+print-type-size variant `Ready`: 1024 bytes
+print-type-size field `.0`: 1024 bytes
+print-type-size variant `Pending`: 0 bytes
| |
| arguments to this function are incorrect
|
-note: while checking the return type of the `async fn`
- --> $DIR/generator-desc.rs:5:16
- |
-LL | async fn one() {}
- | ^ checked the `Output` of this `async fn`, expected opaque type
-note: while checking the return type of the `async fn`
- --> $DIR/generator-desc.rs:6:16
- |
-LL | async fn two() {}
- | ^ checked the `Output` of this `async fn`, found opaque type
= note: expected opaque type `impl Future<Output = ()>` (opaque type at <$DIR/generator-desc.rs:5:16>)
found opaque type `impl Future<Output = ()>` (opaque type at <$DIR/generator-desc.rs:6:16>)
= help: consider `await`ing on both `Future`s
}
async fn tuple() -> Tuple {
- //~^ NOTE checked the `Output` of this `async fn`, expected opaque type
- //~| NOTE while checking the return type of the `async fn`
- //~| NOTE in this expansion of desugaring of `async` block or function
Tuple(1i32)
}
| ++++++
error[E0277]: the `?` operator can only be applied to values that implement `Try`
- --> $DIR/issue-61076.rs:65:5
+ --> $DIR/issue-61076.rs:62:5
|
LL | t?;
| ^^ the `?` operator cannot be applied to type `T`
| ++++++
error[E0609]: no field `0` on type `impl Future<Output = Tuple>`
- --> $DIR/issue-61076.rs:74:26
+ --> $DIR/issue-61076.rs:71:26
|
LL | let _: i32 = tuple().0;
| ^ field not available in `impl Future`, but it is available in its `Output`
| ++++++
error[E0609]: no field `a` on type `impl Future<Output = Struct>`
- --> $DIR/issue-61076.rs:78:28
+ --> $DIR/issue-61076.rs:75:28
|
LL | let _: i32 = struct_().a;
| ^ field not available in `impl Future`, but it is available in its `Output`
| ++++++
error[E0599]: no method named `method` found for opaque type `impl Future<Output = Struct>` in the current scope
- --> $DIR/issue-61076.rs:82:15
+ --> $DIR/issue-61076.rs:79:15
|
LL | struct_().method();
| ^^^^^^ method not found in `impl Future<Output = Struct>`
| ++++++
error[E0308]: mismatched types
- --> $DIR/issue-61076.rs:91:9
+ --> $DIR/issue-61076.rs:88:9
|
LL | match tuple() {
| ------- this expression has type `impl Future<Output = Tuple>`
LL | Tuple(_) => {}
| ^^^^^^^^ expected opaque type, found `Tuple`
|
-note: while checking the return type of the `async fn`
- --> $DIR/issue-61076.rs:56:21
- |
-LL | async fn tuple() -> Tuple {
- | ^^^^^ checked the `Output` of this `async fn`, expected opaque type
= note: expected opaque type `impl Future<Output = Tuple>`
found struct `Tuple`
help: consider `await`ing on the `Future`
LL | StructAsync { callback }.await;
| ^^^^^^^^ expected `Pin<Box<dyn Future<Output = ()>>>`, found opaque type
|
-note: while checking the return type of the `async fn`
- --> $DIR/issue-98634.rs:24:21
- |
-LL | async fn callback() {}
- | ^ checked the `Output` of this `async fn`, found opaque type
= note: expected struct `Pin<Box<(dyn Future<Output = ()> + 'static)>>`
found opaque type `impl Future<Output = ()>`
note: required by a bound in `StructAsync`
LL | StructAsync { callback }.await;
| ^^^^^^^^^^^^^^^^^^^^^^^^ expected `Pin<Box<dyn Future<Output = ()>>>`, found opaque type
|
-note: while checking the return type of the `async fn`
- --> $DIR/issue-98634.rs:24:21
- |
-LL | async fn callback() {}
- | ^ checked the `Output` of this `async fn`, found opaque type
= note: expected struct `Pin<Box<(dyn Future<Output = ()> + 'static)>>`
found opaque type `impl Future<Output = ()>`
note: required by a bound in `StructAsync`
LL | StructAsync { callback }.await;
| ^^^^^^ expected `Pin<Box<dyn Future<Output = ()>>>`, found opaque type
|
-note: while checking the return type of the `async fn`
- --> $DIR/issue-98634.rs:24:21
- |
-LL | async fn callback() {}
- | ^ checked the `Output` of this `async fn`, found opaque type
= note: expected struct `Pin<Box<(dyn Future<Output = ()> + 'static)>>`
found opaque type `impl Future<Output = ()>`
note: required by a bound in `StructAsync`
| | help: consider borrowing here: `&foo()`
| arguments to this function are incorrect
|
-note: while checking the return type of the `async fn`
- --> $DIR/issue-102206.rs:3:16
- |
-LL | async fn foo() {}
- | ^ checked the `Output` of this `async fn`, found opaque type
= note: expected reference `&_`
found opaque type `impl Future<Output = ()>`
note: function defined here
|
LL | pub const async fn x() {}
| ^^^^^^^^^^^^^^^^^^^^^^
- = note: ...which requires computing whether `impl core::future::future::Future<Output = ()>` is freeze...
- = note: ...which requires evaluating trait selection obligation `impl core::future::future::Future<Output = ()>: core::marker::Freeze`...
+ = note: ...which requires computing whether `x::{opaque#0}` is freeze...
+ = note: ...which requires evaluating trait selection obligation `x::{opaque#0}: core::marker::Freeze`...
= note: ...which again requires computing type of `x::{opaque#0}`, completing the cycle
note: cycle used when checking item types in top-level module
--> $DIR/no-const-async.rs:4:1
| |
| arguments to this function are incorrect
|
-note: while checking the return type of the `async fn`
- --> $DIR/suggest-missing-await-closure.rs:8:24
- |
-LL | async fn make_u32() -> u32 {
- | ^^^ checked the `Output` of this `async fn`, found opaque type
= note: expected type `u32`
found opaque type `impl Future<Output = u32>`
note: function defined here
| |
| arguments to this function are incorrect
|
-note: while checking the return type of the `async fn`
- --> $DIR/suggest-missing-await.rs:5:24
- |
-LL | async fn make_u32() -> u32 {
- | ^^^ checked the `Output` of this `async fn`, found opaque type
= note: expected type `u32`
found opaque type `impl Future<Output = u32>`
note: function defined here
LL | dummy()
| ^^^^^^^ expected `()`, found opaque type
|
-note: while checking the return type of the `async fn`
- --> $DIR/suggest-missing-await.rs:18:18
- |
-LL | async fn dummy() {}
- | ^ checked the `Output` of this `async fn`, found opaque type
= note: expected unit type `()`
found opaque type `impl Future<Output = ()>`
help: consider `await`ing on the `Future`
LL | | };
| |_____- `if` and `else` have incompatible types
|
-note: while checking the return type of the `async fn`
- --> $DIR/suggest-missing-await.rs:18:18
- |
-LL | async fn dummy() {}
- | ^ checked the `Output` of this `async fn`, expected opaque type
= note: expected opaque type `impl Future<Output = ()>`
found unit type `()`
help: consider `await`ing on the `Future`
LL | | };
| |_____- `match` arms have incompatible types
|
-note: while checking the return type of the `async fn`
- --> $DIR/suggest-missing-await.rs:18:18
- |
-LL | async fn dummy() {}
- | ^ checked the `Output` of this `async fn`, expected opaque type
= note: expected opaque type `impl Future<Output = ()>`
found unit type `()`
help: consider `await`ing on the `Future`
LL | () => {}
| ^^ expected opaque type, found `()`
|
-note: while checking the return type of the `async fn`
- --> $DIR/suggest-missing-await.rs:18:18
- |
-LL | async fn dummy() {}
- | ^ checked the `Output` of this `async fn`, expected opaque type
= note: expected opaque type `impl Future<Output = ()>`
found unit type `()`
help: consider `await`ing on the `Future`
LL | Ok(_) => {}
| ^^^^^ expected opaque type, found `Result<_, _>`
|
-note: while checking the return type of the `async fn`
- --> $DIR/suggest-missing-await.rs:57:28
- |
-LL | async fn dummy_result() -> Result<(), ()> {
- | ^^^^^^^^^^^^^^ checked the `Output` of this `async fn`, expected opaque type
= note: expected opaque type `impl Future<Output = Result<(), ()>>`
found enum `Result<_, _>`
help: consider `await`ing on the `Future`
LL | Err(_) => {}
| ^^^^^^ expected opaque type, found `Result<_, _>`
|
-note: while checking the return type of the `async fn`
- --> $DIR/suggest-missing-await.rs:57:28
- |
-LL | async fn dummy_result() -> Result<(), ()> {
- | ^^^^^^^^^^^^^^ checked the `Output` of this `async fn`, expected opaque type
= note: expected opaque type `impl Future<Output = Result<(), ()>>`
found enum `Result<_, _>`
help: consider `await`ing on the `Future`
error[E0594]: cannot assign to `**t1`, which is behind a `&` reference
--> $DIR/borrowck-borrow-mut-base-ptr-in-aliasable-loc.rs:9:5
|
-LL | let t1 = t0;
- | -- consider changing this binding's type to be: `&mut &mut isize`
-LL | let p: &isize = &**t0;
LL | **t1 = 22;
| ^^^^^^^^^ `t1` is a `&` reference, so the data it refers to cannot be written
+ |
+help: consider specifying this binding's type
+ |
+LL | let t1: &mut &mut isize = t0;
+ | +++++++++++++++++
error[E0502]: cannot borrow `**t0` as immutable because it is also borrowed as mutable
--> $DIR/borrowck-borrow-mut-base-ptr-in-aliasable-loc.rs:14:21
fn main() {
let mut test = Vec::new();
let rofl: &Vec<Vec<i32>> = &mut test;
- //~^ NOTE consider changing this binding's type to be
+ //~^ HELP consider changing this binding's type
rofl.push(Vec::new());
//~^ ERROR cannot borrow `*rofl` as mutable, as it is behind a `&` reference
//~| NOTE `rofl` is a `&` reference, so the data it refers to cannot be borrowed as mutable
#[rustfmt::skip]
let x: &usize = &mut{0};
- //~^ NOTE consider changing this binding's type to be
+ //~^ HELP consider changing this binding's type
*x = 1;
//~^ ERROR cannot assign to `*x`, which is behind a `&` reference
//~| NOTE `x` is a `&` reference, so the data it refers to cannot be written
#[rustfmt::skip]
let y: &usize = &mut(0);
- //~^ NOTE consider changing this binding's type to be
+ //~^ HELP consider changing this binding's type
*y = 1;
//~^ ERROR cannot assign to `*y`, which is behind a `&` reference
//~| NOTE `y` is a `&` reference, so the data it refers to cannot be written
error[E0596]: cannot borrow `*rofl` as mutable, as it is behind a `&` reference
--> $DIR/issue-85765.rs:5:5
|
-LL | let rofl: &Vec<Vec<i32>> = &mut test;
- | ---- consider changing this binding's type to be: `&mut Vec<Vec<i32>>`
-LL |
LL | rofl.push(Vec::new());
| ^^^^^^^^^^^^^^^^^^^^^ `rofl` is a `&` reference, so the data it refers to cannot be borrowed as mutable
+ |
+help: consider changing this binding's type
+ |
+LL | let rofl: &mut Vec<Vec<i32>> = &mut test;
+ | ~~~~~~~~~~~~~~~~~~
error[E0594]: cannot assign to `*r`, which is behind a `&` reference
--> $DIR/issue-85765.rs:12:5
error[E0594]: cannot assign to `*x`, which is behind a `&` reference
--> $DIR/issue-85765.rs:19:5
|
-LL | let x: &usize = &mut{0};
- | - consider changing this binding's type to be: `&mut usize`
-LL |
LL | *x = 1;
| ^^^^^^ `x` is a `&` reference, so the data it refers to cannot be written
+ |
+help: consider changing this binding's type
+ |
+LL | let x: &mut usize = &mut{0};
+ | ~~~~~~~~~~
error[E0594]: cannot assign to `*y`, which is behind a `&` reference
--> $DIR/issue-85765.rs:26:5
|
-LL | let y: &usize = &mut(0);
- | - consider changing this binding's type to be: `&mut usize`
-LL |
LL | *y = 1;
| ^^^^^^ `y` is a `&` reference, so the data it refers to cannot be written
+ |
+help: consider changing this binding's type
+ |
+LL | let y: &mut usize = &mut(0);
+ | ~~~~~~~~~~
error: aborting due to 4 previous errors
fn main() {
let client = TestClient;
let inner = client.get_inner_ref();
- //~^ NOTE consider changing this binding's type to be
+ //~^ HELP consider specifying this binding's type
inner.clear();
//~^ ERROR cannot borrow `*inner` as mutable, as it is behind a `&` reference [E0596]
//~| NOTE `inner` is a `&` reference, so the data it refers to cannot be borrowed as mutable
error[E0596]: cannot borrow `*inner` as mutable, as it is behind a `&` reference
--> $DIR/issue-91206.rs:13:5
|
-LL | let inner = client.get_inner_ref();
- | ----- consider changing this binding's type to be: `&mut Vec<usize>`
-LL |
LL | inner.clear();
| ^^^^^^^^^^^^^ `inner` is a `&` reference, so the data it refers to cannot be borrowed as mutable
+ |
+help: consider specifying this binding's type
+ |
+LL | let inner: &mut Vec<usize> = client.get_inner_ref();
+ | +++++++++++++++++
error: aborting due to previous error
error[E0594]: cannot assign to `*foo`, which is behind a `&` reference
--> $DIR/issue-92015.rs:6:5
|
-LL | let foo = Some(&0).unwrap();
- | --- consider changing this binding's type to be: `&mut i32`
LL | *foo = 1;
| ^^^^^^^^ `foo` is a `&` reference, so the data it refers to cannot be written
+ |
+help: consider specifying this binding's type
+ |
+LL | let foo: &mut i32 = Some(&0).unwrap();
+ | ++++++++++
error: aborting due to previous error
--- /dev/null
+// run-rustfix
+#![allow(dead_code, path_statements)]
+fn foo1(s: &str) -> impl Iterator<Item = String> + '_ {
+ None.into_iter()
+ .flat_map(move |()| s.chars().map(move |c| format!("{}{}", c, s)))
+ //~^ ERROR captured variable cannot escape `FnMut` closure body
+ //~| HELP consider adding 'move' keyword before the nested closure
+}
+
+fn foo2(s: &str) -> impl Sized + '_ {
+ move |()| s.chars().map(move |c| format!("{}{}", c, s))
+ //~^ ERROR lifetime may not live long enough
+ //~| HELP consider adding 'move' keyword before the nested closure
+}
+
+pub struct X;
+pub fn foo3<'a>(
+ bar: &'a X,
+) -> impl Iterator<Item = ()> + 'a {
+ Some(()).iter().flat_map(move |()| {
+ Some(()).iter().map(move |()| { bar; }) //~ ERROR captured variable cannot escape
+ //~^ HELP consider adding 'move' keyword before the nested closure
+ })
+}
+
+fn main() {}
+// run-rustfix
+#![allow(dead_code, path_statements)]
fn foo1(s: &str) -> impl Iterator<Item = String> + '_ {
None.into_iter()
.flat_map(move |()| s.chars().map(|c| format!("{}{}", c, s)))
//~| HELP consider adding 'move' keyword before the nested closure
}
+pub struct X;
+pub fn foo3<'a>(
+ bar: &'a X,
+) -> impl Iterator<Item = ()> + 'a {
+ Some(()).iter().flat_map(move |()| {
+ Some(()).iter().map(|()| { bar; }) //~ ERROR captured variable cannot escape
+ //~^ HELP consider adding 'move' keyword before the nested closure
+ })
+}
+
fn main() {}
error: captured variable cannot escape `FnMut` closure body
- --> $DIR/issue-95079-missing-move-in-nested-closure.rs:3:29
+ --> $DIR/issue-95079-missing-move-in-nested-closure.rs:5:29
|
LL | fn foo1(s: &str) -> impl Iterator<Item = String> + '_ {
| - variable defined here
LL | .flat_map(move |()| s.chars().map(|c| format!("{}{}", c, s)))
| - -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| | |
- | | returns a reference to a captured variable which escapes the closure body
+ | | returns a closure that contains a reference to a captured variable, which then escapes the closure body
| | variable captured here
| inferred to be a `FnMut` closure
|
| ++++
error: lifetime may not live long enough
- --> $DIR/issue-95079-missing-move-in-nested-closure.rs:9:15
+ --> $DIR/issue-95079-missing-move-in-nested-closure.rs:11:15
|
LL | move |()| s.chars().map(|c| format!("{}{}", c, s))
| --------- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ returning this value requires that `'1` must outlive `'2`
| | |
- | | return type of closure `Map<Chars<'_>, [closure@$DIR/issue-95079-missing-move-in-nested-closure.rs:9:29: 9:32]>` contains a lifetime `'2`
+ | | return type of closure `Map<Chars<'_>, [closure@$DIR/issue-95079-missing-move-in-nested-closure.rs:11:29: 11:32]>` contains a lifetime `'2`
| lifetime `'1` represents this closure's body
|
= note: closure implements `Fn`, so references to captured variables can't escape the closure
LL | move |()| s.chars().map(move |c| format!("{}{}", c, s))
| ++++
-error: aborting due to 2 previous errors
+error: captured variable cannot escape `FnMut` closure body
+ --> $DIR/issue-95079-missing-move-in-nested-closure.rs:21:9
+ |
+LL | bar: &'a X,
+ | --- variable defined here
+LL | ) -> impl Iterator<Item = ()> + 'a {
+LL | Some(()).iter().flat_map(move |()| {
+ | - inferred to be a `FnMut` closure
+LL | Some(()).iter().map(|()| { bar; })
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^---^^^^
+ | | |
+ | | variable captured here
+ | returns a closure that contains a reference to a captured variable, which then escapes the closure body
+ |
+ = note: `FnMut` closures only have access to their captured variables while they are executing...
+ = note: ...therefore, they cannot allow references to captured variables to escape
+help: consider adding 'move' keyword before the nested closure
+ |
+LL | Some(()).iter().map(move |()| { bar; })
+ | ++++
+
+error: aborting due to 3 previous errors
trait Object: Marker1 {}
// A supertrait marker is illegal...
-impl !Marker1 for dyn Object + Marker2 { } //~ ERROR E0371
+impl !Marker1 for dyn Object + Marker2 {} //~ ERROR E0371
+ //~^ ERROR 0321
// ...and also a direct component.
-impl !Marker2 for dyn Object + Marker2 { } //~ ERROR E0371
-
-// But implementing a marker if it is not present is OK.
-impl !Marker2 for dyn Object {} // OK
+impl !Marker2 for dyn Object + Marker2 {} //~ ERROR E0371
+ //~^ ERROR 0321
// A non-principal trait-object type is orphan even in its crate.
impl !Send for dyn Marker2 {} //~ ERROR E0117
-// And impl'ing a remote marker for a local trait object is forbidden
-// by one of these special orphan-like rules.
+// Implementing a marker for a local trait object is forbidden by a special
+// orphan-like rule.
+impl !Marker2 for dyn Object {} //~ ERROR E0321
impl !Send for dyn Object {} //~ ERROR E0321
impl !Send for dyn Object + Marker2 {} //~ ERROR E0321
-fn main() { }
+// Blanket impl that applies to dyn Object is equally problematic.
+auto trait Marker3 {}
+impl<T: ?Sized> !Marker3 for T {} //~ ERROR E0321
+
+auto trait Marker4 {}
+impl<T> !Marker4 for T {} // okay
+
+fn main() {}
error[E0371]: the object type `(dyn Object + Marker2 + 'static)` automatically implements the trait `Marker1`
--> $DIR/coherence-impl-trait-for-marker-trait-negative.rs:15:1
|
-LL | impl !Marker1 for dyn Object + Marker2 { }
+LL | impl !Marker1 for dyn Object + Marker2 {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `(dyn Object + Marker2 + 'static)` automatically implements trait `Marker1`
+error[E0321]: traits with a default impl, like `Marker1`, cannot be implemented for trait object `(dyn Object + Marker2 + 'static)`
+ --> $DIR/coherence-impl-trait-for-marker-trait-negative.rs:15:1
+ |
+LL | impl !Marker1 for dyn Object + Marker2 {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: a trait object implements `Marker1` if and only if `Marker1` is one of the trait object's trait bounds
+
error[E0371]: the object type `(dyn Object + Marker2 + 'static)` automatically implements the trait `Marker2`
- --> $DIR/coherence-impl-trait-for-marker-trait-negative.rs:17:1
+ --> $DIR/coherence-impl-trait-for-marker-trait-negative.rs:18:1
|
-LL | impl !Marker2 for dyn Object + Marker2 { }
+LL | impl !Marker2 for dyn Object + Marker2 {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `(dyn Object + Marker2 + 'static)` automatically implements trait `Marker2`
+error[E0321]: traits with a default impl, like `Marker2`, cannot be implemented for trait object `(dyn Object + Marker2 + 'static)`
+ --> $DIR/coherence-impl-trait-for-marker-trait-negative.rs:18:1
+ |
+LL | impl !Marker2 for dyn Object + Marker2 {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: a trait object implements `Marker2` if and only if `Marker2` is one of the trait object's trait bounds
+
+error[E0321]: traits with a default impl, like `Marker2`, cannot be implemented for trait object `(dyn Object + 'static)`
+ --> $DIR/coherence-impl-trait-for-marker-trait-negative.rs:26:1
+ |
+LL | impl !Marker2 for dyn Object {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: a trait object implements `Marker2` if and only if `Marker2` is one of the trait object's trait bounds
+
error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
- --> $DIR/coherence-impl-trait-for-marker-trait-negative.rs:23:1
+ --> $DIR/coherence-impl-trait-for-marker-trait-negative.rs:22:1
|
LL | impl !Send for dyn Marker2 {}
| ^^^^^^^^^^^^^^^-----------
LL | impl !Send for dyn Object + Marker2 {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can't implement cross-crate trait with a default impl for non-struct/enum type
-error: aborting due to 5 previous errors
+error[E0321]: traits with a default impl, like `Marker3`, cannot be implemented for generic type `T`
+ --> $DIR/coherence-impl-trait-for-marker-trait-negative.rs:32:1
+ |
+LL | impl<T: ?Sized> !Marker3 for T {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: a trait object implements `Marker3` if and only if `Marker3` is one of the trait object's trait bounds
+
+error: aborting due to 9 previous errors
Some errors have detailed explanations: E0117, E0321, E0371.
For more information about an error, try `rustc --explain E0117`.
trait Object: Marker1 {}
// A supertrait marker is illegal...
-impl Marker1 for dyn Object + Marker2 { } //~ ERROR E0371
+impl Marker1 for dyn Object + Marker2 {} //~ ERROR E0371
+ //~^ ERROR E0321
// ...and also a direct component.
-impl Marker2 for dyn Object + Marker2 { } //~ ERROR E0371
-
-// But implementing a marker if it is not present is OK.
-impl Marker2 for dyn Object {} // OK
+impl Marker2 for dyn Object + Marker2 {} //~ ERROR E0371
+ //~^ ERROR E0321
// A non-principal trait-object type is orphan even in its crate.
unsafe impl Send for dyn Marker2 {} //~ ERROR E0117
-// And impl'ing a remote marker for a local trait object is forbidden
-// by one of these special orphan-like rules.
+// Implementing a marker for a local trait object is forbidden by a special
+// orphan-like rule.
+impl Marker2 for dyn Object {} //~ ERROR E0321
unsafe impl Send for dyn Object {} //~ ERROR E0321
unsafe impl Send for dyn Object + Marker2 {} //~ ERROR E0321
-fn main() { }
+// Blanket impl that applies to dyn Object is equally problematic.
+auto trait Marker3 {}
+impl<T: ?Sized> Marker3 for T {} //~ ERROR E0321
+
+auto trait Marker4 {}
+impl<T> Marker4 for T {} // okay
+
+fn main() {}
error[E0371]: the object type `(dyn Object + Marker2 + 'static)` automatically implements the trait `Marker1`
--> $DIR/coherence-impl-trait-for-marker-trait-positive.rs:15:1
|
-LL | impl Marker1 for dyn Object + Marker2 { }
+LL | impl Marker1 for dyn Object + Marker2 {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `(dyn Object + Marker2 + 'static)` automatically implements trait `Marker1`
+error[E0321]: traits with a default impl, like `Marker1`, cannot be implemented for trait object `(dyn Object + Marker2 + 'static)`
+ --> $DIR/coherence-impl-trait-for-marker-trait-positive.rs:15:1
+ |
+LL | impl Marker1 for dyn Object + Marker2 {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: a trait object implements `Marker1` if and only if `Marker1` is one of the trait object's trait bounds
+
error[E0371]: the object type `(dyn Object + Marker2 + 'static)` automatically implements the trait `Marker2`
- --> $DIR/coherence-impl-trait-for-marker-trait-positive.rs:17:1
+ --> $DIR/coherence-impl-trait-for-marker-trait-positive.rs:18:1
|
-LL | impl Marker2 for dyn Object + Marker2 { }
+LL | impl Marker2 for dyn Object + Marker2 {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `(dyn Object + Marker2 + 'static)` automatically implements trait `Marker2`
+error[E0321]: traits with a default impl, like `Marker2`, cannot be implemented for trait object `(dyn Object + Marker2 + 'static)`
+ --> $DIR/coherence-impl-trait-for-marker-trait-positive.rs:18:1
+ |
+LL | impl Marker2 for dyn Object + Marker2 {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: a trait object implements `Marker2` if and only if `Marker2` is one of the trait object's trait bounds
+
+error[E0321]: traits with a default impl, like `Marker2`, cannot be implemented for trait object `(dyn Object + 'static)`
+ --> $DIR/coherence-impl-trait-for-marker-trait-positive.rs:26:1
+ |
+LL | impl Marker2 for dyn Object {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: a trait object implements `Marker2` if and only if `Marker2` is one of the trait object's trait bounds
+
error[E0117]: only traits defined in the current crate can be implemented for arbitrary types
- --> $DIR/coherence-impl-trait-for-marker-trait-positive.rs:23:1
+ --> $DIR/coherence-impl-trait-for-marker-trait-positive.rs:22:1
|
LL | unsafe impl Send for dyn Marker2 {}
| ^^^^^^^^^^^^^^^^^^^^^-----------
LL | unsafe impl Send for dyn Object + Marker2 {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ can't implement cross-crate trait with a default impl for non-struct/enum type
-error: aborting due to 5 previous errors
+error[E0321]: traits with a default impl, like `Marker3`, cannot be implemented for generic type `T`
+ --> $DIR/coherence-impl-trait-for-marker-trait-positive.rs:32:1
+ |
+LL | impl<T: ?Sized> Marker3 for T {}
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: a trait object implements `Marker3` if and only if `Marker3` is one of the trait object's trait bounds
+
+error: aborting due to 9 previous errors
Some errors have detailed explanations: E0117, E0321, E0371.
For more information about an error, try `rustc --explain E0117`.
--- /dev/null
+// checks that when we relate a `Expr::Binop` we also relate the types of the
+// const arguments.
+#![feature(generic_const_exprs)]
+#![allow(incomplete_features)]
+
+struct Bar<const B: bool>;
+
+const fn make_generic(_: usize, a: bool) -> bool {
+ a
+}
+
+fn foo<const N: usize>() -> Bar<{ make_generic(N, true == false) }> {
+ Bar::<{ make_generic(N, 1_u8 == 0_u8) }>
+ //~^ error: mismatched types
+ //~| error: unconstrained generic constant
+}
+
+fn main() {}
--- /dev/null
+error[E0308]: mismatched types
+ --> $DIR/relate_binop_arg_tys.rs:13:5
+ |
+LL | Bar::<{ make_generic(N, 1_u8 == 0_u8) }>
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `{ make_generic(N, true == false) }`, found `{ make_generic(N, 1_u8 == 0_u8) }`
+ |
+ = note: expected constant `{ make_generic(N, true == false) }`
+ found constant `{ make_generic(N, 1_u8 == 0_u8) }`
+
+error: unconstrained generic constant
+ --> $DIR/relate_binop_arg_tys.rs:13:11
+ |
+LL | Bar::<{ make_generic(N, 1_u8 == 0_u8) }>
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = help: try adding a `where` bound using this expression: `where [(); { make_generic(N, 1_u8 == 0_u8) }]:`
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
--- /dev/null
+// checks that when we relate a `Expr::Cast` we also relate the type of the
+// const argument.
+#![feature(generic_const_exprs)]
+#![allow(incomplete_features)]
+
+fn foo<const N: usize>() -> [(); (true as usize) + N] {
+ [(); (1_u8 as usize) + N]
+ //~^ error: mismatched types
+ //~| error: unconstrained generic constant
+}
+
+fn main() {}
--- /dev/null
+error[E0308]: mismatched types
+ --> $DIR/relate_cast_arg_ty.rs:7:5
+ |
+LL | [(); (1_u8 as usize) + N]
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^ expected `(true as usize) + N`, found `(1_u8 as usize) + N`
+ |
+ = note: expected constant `(true as usize) + N`
+ found constant `(1_u8 as usize) + N`
+
+error: unconstrained generic constant
+ --> $DIR/relate_cast_arg_ty.rs:7:10
+ |
+LL | [(); (1_u8 as usize) + N]
+ | ^^^^^^^^^^^^^^^^^^^
+ |
+ = help: try adding a `where` bound using this expression: `where [(); (1_u8 as usize) + N]:`
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
LL | black_box((S::<i32>::FOO, S::<u32>::FOO));
| ^^^^^^^^^^^^^
-note: erroneous constant used
- --> $DIR/const-err-late.rs:19:31
- |
-LL | black_box((S::<i32>::FOO, S::<u32>::FOO));
- | ^^^^^^^^^^^^^
-
note: erroneous constant used
--> $DIR/const-err-late.rs:19:16
|
LL | black_box((S::<i32>::FOO, S::<u32>::FOO));
| ^^^^^^^^^^^^^
-note: erroneous constant used
- --> $DIR/const-err-late.rs:19:31
- |
-LL | black_box((S::<i32>::FOO, S::<u32>::FOO));
- | ^^^^^^^^^^^^^
-
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0080`.
error[E0277]: the trait bound `B<C>: Copy` is not satisfied
- --> $DIR/deriving-copyclone.rs:31:13
+ --> $DIR/deriving-copyclone.rs:31:26
|
LL | is_copy(B { a: 1, b: C });
- | ------- ^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `B<C>`
+ | ------- ^ the trait `Copy` is not implemented for `B<C>`
| |
| required by a bound introduced by this call
|
= note: this error originates in the derive macro `Copy` (in Nightly builds, run with -Z macro-backtrace for more info)
help: consider borrowing here
|
-LL | is_copy(&B { a: 1, b: C });
- | +
+LL | is_copy(B { a: 1, b: &C });
+ | +
error[E0277]: the trait bound `B<C>: Clone` is not satisfied
- --> $DIR/deriving-copyclone.rs:32:14
+ --> $DIR/deriving-copyclone.rs:32:27
|
LL | is_clone(B { a: 1, b: C });
- | -------- ^^^^^^^^^^^^^^^^ the trait `Clone` is not implemented for `B<C>`
+ | -------- ^ the trait `Clone` is not implemented for `B<C>`
| |
| required by a bound introduced by this call
|
= note: this error originates in the derive macro `Clone` (in Nightly builds, run with -Z macro-backtrace for more info)
help: consider borrowing here
|
-LL | is_clone(&B { a: 1, b: C });
- | +
+LL | is_clone(B { a: 1, b: &C });
+ | +
error[E0277]: the trait bound `B<D>: Copy` is not satisfied
- --> $DIR/deriving-copyclone.rs:35:13
+ --> $DIR/deriving-copyclone.rs:35:26
|
LL | is_copy(B { a: 1, b: D });
- | ------- ^^^^^^^^^^^^^^^^ the trait `Copy` is not implemented for `B<D>`
+ | ------- ^ the trait `Copy` is not implemented for `B<D>`
| |
| required by a bound introduced by this call
|
= note: this error originates in the derive macro `Copy` (in Nightly builds, run with -Z macro-backtrace for more info)
help: consider borrowing here
|
-LL | is_copy(&B { a: 1, b: D });
- | +
+LL | is_copy(B { a: 1, b: &D });
+ | +
error: aborting due to 3 previous errors
impl ::core::fmt::Debug for Point {
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_struct_field2_finish(f, "Point", "x",
- &&self.x, "y", &&self.y)
+ &self.x, "y", &&self.y)
}
}
#[automatically_derived]
impl ::core::fmt::Debug for PackedPoint {
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_struct_field2_finish(f, "PackedPoint",
- "x", &&{ self.x }, "y", &&{ self.y })
+ "x", &{ self.x }, "y", &&{ self.y })
}
}
#[automatically_derived]
impl ::core::cmp::PartialEq for PackedPoint {
#[inline]
fn eq(&self, other: &PackedPoint) -> bool {
- { self.x } == { other.x } && { self.y } == { other.y }
+ ({ self.x }) == ({ other.x }) && ({ self.y }) == ({ other.y })
}
}
#[automatically_derived]
let names: &'static _ =
&["b1", "b2", "b3", "b4", "b5", "b6", "b7", "b8"];
let values: &[&dyn ::core::fmt::Debug] =
- &[&&self.b1, &&self.b2, &&self.b3, &&self.b4, &&self.b5,
- &&self.b6, &&self.b7, &&self.b8];
+ &[&self.b1, &self.b2, &self.b3, &self.b4, &self.b5, &self.b6,
+ &self.b7, &&self.b8];
::core::fmt::Formatter::debug_struct_fields_finish(f, "Big", names,
values)
}
for Generic<T, U> where T::A: ::core::fmt::Debug {
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_struct_field3_finish(f, "Generic", "t",
- &&self.t, "ta", &&self.ta, "u", &&self.u)
+ &self.t, "ta", &self.ta, "u", &&self.u)
}
}
#[automatically_derived]
{
fn fmt(&self, f: &mut ::core::fmt::Formatter) -> ::core::fmt::Result {
::core::fmt::Formatter::debug_tuple_field3_finish(f, "PackedGeneric",
- &&{ self.0 }, &&{ self.1 }, &&{ self.2 })
+ &{ self.0 }, &{ self.1 }, &&{ self.2 })
}
}
#[automatically_derived]
::core::marker::Copy {
#[inline]
fn eq(&self, other: &PackedGeneric<T, U>) -> bool {
- { self.0 } == { other.0 } && { self.1 } == { other.1 } &&
- { self.2 } == { other.2 }
+ ({ self.0 }) == ({ other.0 }) && ({ self.1 }) == ({ other.1 }) &&
+ ({ self.2 }) == ({ other.2 })
}
}
#[automatically_derived]
&__self_0),
Mixed::S { d1: __self_0, d2: __self_1 } =>
::core::fmt::Formatter::debug_struct_field2_finish(f, "S",
- "d1", &__self_0, "d2", &__self_1),
+ "d1", __self_0, "d2", &__self_1),
}
}
}
--> $DIR/issue-46718-struct-pattern-dotdotdot.rs:11:55
|
LL | PersonalityInventory { expressivity: exp, ... } => exp
- | ^^^ help: to omit remaining fields, use one fewer `.`: `..`
+ | ^^^
+ |
+help: to omit remaining fields, use `..`
+ |
+LL | PersonalityInventory { expressivity: exp, .. } => exp
+ | ~~
error: aborting due to previous error
--- /dev/null
+trait T1 {}
+trait T2 {}
+trait T3 {}
+trait T4 {}
+
+impl<B: T2> T1 for Wrapper<B> {}
+
+impl T2 for i32 {}
+impl T3 for i32 {}
+
+impl<A: T3> T2 for Burrito<A> {}
+
+struct Wrapper<W> {
+ value: W,
+}
+
+struct Burrito<F> {
+ filling: F,
+}
+
+fn want<V: T1>(_x: V) {}
+
+fn example<Q>(q: Q) {
+ want(Wrapper { value: Burrito { filling: q } });
+ //~^ ERROR the trait bound `Q: T3` is not satisfied [E0277]
+}
+
+fn main() {}
--- /dev/null
+error[E0277]: the trait bound `Q: T3` is not satisfied
+ --> $DIR/blame-trait-error.rs:24:46
+ |
+LL | want(Wrapper { value: Burrito { filling: q } });
+ | ---- ^ the trait `T3` is not implemented for `Q`
+ | |
+ | required by a bound introduced by this call
+ |
+note: required for `Burrito<Q>` to implement `T2`
+ --> $DIR/blame-trait-error.rs:11:13
+ |
+LL | impl<A: T3> T2 for Burrito<A> {}
+ | -- ^^ ^^^^^^^^^^
+ | |
+ | unsatisfied trait bound introduced here
+note: required for `Wrapper<Burrito<Q>>` to implement `T1`
+ --> $DIR/blame-trait-error.rs:6:13
+ |
+LL | impl<B: T2> T1 for Wrapper<B> {}
+ | -- ^^ ^^^^^^^^^^
+ | |
+ | unsatisfied trait bound introduced here
+note: required by a bound in `want`
+ --> $DIR/blame-trait-error.rs:21:12
+ |
+LL | fn want<V: T1>(_x: V) {}
+ | ^^ required by this bound in `want`
+help: consider restricting type parameter `Q`
+ |
+LL | fn example<Q: T3>(q: Q) {
+ | ++++
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
--- /dev/null
+// This test examines the error spans reported when a generic `impl` fails.
+// For example, if a function wants an `Option<T>` where `T: Copy` but you pass `Some(vec![1, 2])`,
+// then we want to point at the `vec![1, 2]` and not the `Some( ... )` expression.
+
+trait T1 {}
+trait T2 {}
+trait T3 {}
+trait T4 {}
+
+impl T2 for i32 {}
+impl T3 for i32 {}
+
+struct Wrapper<W> {
+ value: W,
+}
+impl<B: T2> T1 for Wrapper<B> {}
+
+struct Burrito<F> {
+ spicy: bool,
+ filling: F,
+}
+impl<A: T3> T2 for Burrito<A> {}
+
+struct BurritoTuple<F>(F);
+impl<C: T3> T2 for BurritoTuple<C> {}
+
+enum BurritoKinds<G> {
+ SmallBurrito { spicy: bool, small_filling: G },
+ LargeBurrito { spicy: bool, large_filling: G },
+ MultiBurrito { first_filling: G, second_filling: G },
+}
+impl<D: T3> T2 for BurritoKinds<D> {}
+
+struct Taco<H>(bool, H);
+impl<E: T3> T2 for Taco<E> {}
+
+enum TacoKinds<H> {
+ OneTaco(bool, H),
+ TwoTacos(bool, H, H),
+}
+impl<F: T3> T2 for TacoKinds<F> {}
+
+struct GenericBurrito<Spiciness, Filling> {
+ spiciness: Spiciness,
+ filling: Filling,
+}
+impl<X, Y: T3> T2 for GenericBurrito<X, Y> {}
+struct NotSpicy;
+
+impl<A: T3, B: T3> T2 for (A, B) {}
+impl<A: T2, B: T2> T1 for (A, B) {}
+
+fn want<V: T1>(_x: V) {}
+
+// Some more-complex examples:
+type AliasBurrito<T> = GenericBurrito<T, T>;
+
+// The following example is fairly confusing. The idea is that we want to "misdirect" the location
+// of the error.
+
+struct Two<A, B> {
+ a: A,
+ b: B,
+}
+
+impl<X, Y: T1, Z> T1 for Two<Two<X, Y>, Z> {}
+
+struct DoubleWrapper<T> {
+ item: Wrapper<T>,
+}
+
+impl<T: T1> T1 for DoubleWrapper<T> {}
+
+fn example<Q>(q: Q) {
+ // In each of the following examples, we expect the error span to point at the 'q' variable,
+ // since the missing constraint is `Q: T3`.
+
+ // Verifies for struct:
+ want(Wrapper { value: Burrito { spicy: false, filling: q } });
+ //~^ ERROR the trait bound `Q: T3` is not satisfied [E0277]
+
+ // Verifies for enum with named fields in variant:
+ want(Wrapper { value: BurritoKinds::SmallBurrito { spicy: true, small_filling: q } });
+ //~^ ERROR the trait bound `Q: T3` is not satisfied [E0277]
+
+ // Verifies for tuple struct:
+ want(Wrapper { value: Taco(false, q) });
+ //~^ ERROR the trait bound `Q: T3` is not satisfied [E0277]
+
+ // Verifies for tuple enum variant:
+ want(Wrapper { value: TacoKinds::OneTaco(false, q) });
+ //~^ ERROR the trait bound `Q: T3` is not satisfied [E0277]
+
+ // Verifies for generic type with multiple parameters:
+ want(Wrapper { value: GenericBurrito { spiciness: NotSpicy, filling: q } });
+ //~^ ERROR the trait bound `Q: T3` is not satisfied [E0277]
+
+ // Verifies for tuple:
+ want((3, q));
+ //~^ ERROR the trait bound `Q: T2` is not satisfied [E0277]
+
+ // Verifies for nested tuple:
+ want(Wrapper { value: (3, q) });
+ //~^ ERROR the trait bound `Q: T3` is not satisfied [E0277]
+
+ // Verifies for nested tuple:
+ want(((3, q), 5));
+ //~^ ERROR the trait bound `Q: T3` is not satisfied [E0277]
+
+ want(DoubleWrapper { item: Wrapper { value: q } });
+ //~^ ERROR the trait bound `Q: T1` is not satisfied [E0277]
+
+ want(DoubleWrapper { item: Wrapper { value: DoubleWrapper { item: Wrapper { value: q } } } });
+ //~^ ERROR the trait bound `Q: T1` is not satisfied [E0277]
+
+ // Verifies for type alias to struct:
+ want(Wrapper { value: AliasBurrito { spiciness: q, filling: q } });
+ //~^ ERROR the trait bound `Q: T3` is not satisfied [E0277]
+
+ want(Two { a: Two { a: (), b: q }, b: () });
+ //~^ ERROR the trait bound `Q: T1` is not satisfied [E0277]
+
+ // We *should* blame the 'q'.
+ // FIXME: Right now, the wrong field is blamed.
+ want(
+ Two { a: Two { a: (), b: Two { a: Two { a: (), b: q }, b: () } }, b: () },
+ //~^ ERROR the trait bound `Q: T1` is not satisfied [E0277]
+ );
+}
+
+fn main() {}
--- /dev/null
+error[E0277]: the trait bound `Q: T3` is not satisfied
+ --> $DIR/blame-trait-error-spans-on-exprs.rs:79:60
+ |
+LL | want(Wrapper { value: Burrito { spicy: false, filling: q } });
+ | ---- required by a bound introduced by this call ^ the trait `T3` is not implemented for `Q`
+ |
+note: required for `Burrito<Q>` to implement `T2`
+ --> $DIR/blame-trait-error-spans-on-exprs.rs:22:13
+ |
+LL | impl<A: T3> T2 for Burrito<A> {}
+ | -- ^^ ^^^^^^^^^^
+ | |
+ | unsatisfied trait bound introduced here
+note: required for `Wrapper<Burrito<Q>>` to implement `T1`
+ --> $DIR/blame-trait-error-spans-on-exprs.rs:16:13
+ |
+LL | impl<B: T2> T1 for Wrapper<B> {}
+ | -- ^^ ^^^^^^^^^^
+ | |
+ | unsatisfied trait bound introduced here
+note: required by a bound in `want`
+ --> $DIR/blame-trait-error-spans-on-exprs.rs:53:12
+ |
+LL | fn want<V: T1>(_x: V) {}
+ | ^^ required by this bound in `want`
+help: consider restricting type parameter `Q`
+ |
+LL | fn example<Q: T3>(q: Q) {
+ | ++++
+
+error[E0277]: the trait bound `Q: T3` is not satisfied
+ --> $DIR/blame-trait-error-spans-on-exprs.rs:83:84
+ |
+LL | want(Wrapper { value: BurritoKinds::SmallBurrito { spicy: true, small_filling: q } });
+ | ---- required by a bound introduced by this call ^ the trait `T3` is not implemented for `Q`
+ |
+note: required for `BurritoKinds<Q>` to implement `T2`
+ --> $DIR/blame-trait-error-spans-on-exprs.rs:32:13
+ |
+LL | impl<D: T3> T2 for BurritoKinds<D> {}
+ | -- ^^ ^^^^^^^^^^^^^^^
+ | |
+ | unsatisfied trait bound introduced here
+note: required for `Wrapper<BurritoKinds<Q>>` to implement `T1`
+ --> $DIR/blame-trait-error-spans-on-exprs.rs:16:13
+ |
+LL | impl<B: T2> T1 for Wrapper<B> {}
+ | -- ^^ ^^^^^^^^^^
+ | |
+ | unsatisfied trait bound introduced here
+note: required by a bound in `want`
+ --> $DIR/blame-trait-error-spans-on-exprs.rs:53:12
+ |
+LL | fn want<V: T1>(_x: V) {}
+ | ^^ required by this bound in `want`
+help: consider restricting type parameter `Q`
+ |
+LL | fn example<Q: T3>(q: Q) {
+ | ++++
+
+error[E0277]: the trait bound `Q: T3` is not satisfied
+ --> $DIR/blame-trait-error-spans-on-exprs.rs:87:39
+ |
+LL | want(Wrapper { value: Taco(false, q) });
+ | ---- ^ the trait `T3` is not implemented for `Q`
+ | |
+ | required by a bound introduced by this call
+ |
+note: required for `Taco<Q>` to implement `T2`
+ --> $DIR/blame-trait-error-spans-on-exprs.rs:35:13
+ |
+LL | impl<E: T3> T2 for Taco<E> {}
+ | -- ^^ ^^^^^^^
+ | |
+ | unsatisfied trait bound introduced here
+note: required for `Wrapper<Taco<Q>>` to implement `T1`
+ --> $DIR/blame-trait-error-spans-on-exprs.rs:16:13
+ |
+LL | impl<B: T2> T1 for Wrapper<B> {}
+ | -- ^^ ^^^^^^^^^^
+ | |
+ | unsatisfied trait bound introduced here
+note: required by a bound in `want`
+ --> $DIR/blame-trait-error-spans-on-exprs.rs:53:12
+ |
+LL | fn want<V: T1>(_x: V) {}
+ | ^^ required by this bound in `want`
+help: consider restricting type parameter `Q`
+ |
+LL | fn example<Q: T3>(q: Q) {
+ | ++++
+
+error[E0277]: the trait bound `Q: T3` is not satisfied
+ --> $DIR/blame-trait-error-spans-on-exprs.rs:91:27
+ |
+LL | want(Wrapper { value: TacoKinds::OneTaco(false, q) });
+ | ---- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `T3` is not implemented for `Q`
+ | |
+ | required by a bound introduced by this call
+ |
+note: required for `TacoKinds<Q>` to implement `T2`
+ --> $DIR/blame-trait-error-spans-on-exprs.rs:41:13
+ |
+LL | impl<F: T3> T2 for TacoKinds<F> {}
+ | -- ^^ ^^^^^^^^^^^^
+ | |
+ | unsatisfied trait bound introduced here
+note: required for `Wrapper<TacoKinds<Q>>` to implement `T1`
+ --> $DIR/blame-trait-error-spans-on-exprs.rs:16:13
+ |
+LL | impl<B: T2> T1 for Wrapper<B> {}
+ | -- ^^ ^^^^^^^^^^
+ | |
+ | unsatisfied trait bound introduced here
+note: required by a bound in `want`
+ --> $DIR/blame-trait-error-spans-on-exprs.rs:53:12
+ |
+LL | fn want<V: T1>(_x: V) {}
+ | ^^ required by this bound in `want`
+help: consider restricting type parameter `Q`
+ |
+LL | fn example<Q: T3>(q: Q) {
+ | ++++
+
+error[E0277]: the trait bound `Q: T3` is not satisfied
+ --> $DIR/blame-trait-error-spans-on-exprs.rs:95:74
+ |
+LL | want(Wrapper { value: GenericBurrito { spiciness: NotSpicy, filling: q } });
+ | ---- required by a bound introduced by this call ^ the trait `T3` is not implemented for `Q`
+ |
+note: required for `GenericBurrito<NotSpicy, Q>` to implement `T2`
+ --> $DIR/blame-trait-error-spans-on-exprs.rs:47:16
+ |
+LL | impl<X, Y: T3> T2 for GenericBurrito<X, Y> {}
+ | -- ^^ ^^^^^^^^^^^^^^^^^^^^
+ | |
+ | unsatisfied trait bound introduced here
+note: required for `Wrapper<GenericBurrito<NotSpicy, Q>>` to implement `T1`
+ --> $DIR/blame-trait-error-spans-on-exprs.rs:16:13
+ |
+LL | impl<B: T2> T1 for Wrapper<B> {}
+ | -- ^^ ^^^^^^^^^^
+ | |
+ | unsatisfied trait bound introduced here
+note: required by a bound in `want`
+ --> $DIR/blame-trait-error-spans-on-exprs.rs:53:12
+ |
+LL | fn want<V: T1>(_x: V) {}
+ | ^^ required by this bound in `want`
+help: consider restricting type parameter `Q`
+ |
+LL | fn example<Q: T3>(q: Q) {
+ | ++++
+
+error[E0277]: the trait bound `Q: T2` is not satisfied
+ --> $DIR/blame-trait-error-spans-on-exprs.rs:99:14
+ |
+LL | want((3, q));
+ | ---- ^ the trait `T2` is not implemented for `Q`
+ | |
+ | required by a bound introduced by this call
+ |
+note: required for `(i32, Q)` to implement `T1`
+ --> $DIR/blame-trait-error-spans-on-exprs.rs:51:20
+ |
+LL | impl<A: T2, B: T2> T1 for (A, B) {}
+ | -- ^^ ^^^^^^
+ | |
+ | unsatisfied trait bound introduced here
+note: required by a bound in `want`
+ --> $DIR/blame-trait-error-spans-on-exprs.rs:53:12
+ |
+LL | fn want<V: T1>(_x: V) {}
+ | ^^ required by this bound in `want`
+help: consider restricting type parameter `Q`
+ |
+LL | fn example<Q: T2>(q: Q) {
+ | ++++
+
+error[E0277]: the trait bound `Q: T3` is not satisfied
+ --> $DIR/blame-trait-error-spans-on-exprs.rs:103:31
+ |
+LL | want(Wrapper { value: (3, q) });
+ | ---- ^ the trait `T3` is not implemented for `Q`
+ | |
+ | required by a bound introduced by this call
+ |
+note: required for `(i32, Q)` to implement `T2`
+ --> $DIR/blame-trait-error-spans-on-exprs.rs:50:20
+ |
+LL | impl<A: T3, B: T3> T2 for (A, B) {}
+ | -- ^^ ^^^^^^
+ | |
+ | unsatisfied trait bound introduced here
+note: required for `Wrapper<(i32, Q)>` to implement `T1`
+ --> $DIR/blame-trait-error-spans-on-exprs.rs:16:13
+ |
+LL | impl<B: T2> T1 for Wrapper<B> {}
+ | -- ^^ ^^^^^^^^^^
+ | |
+ | unsatisfied trait bound introduced here
+note: required by a bound in `want`
+ --> $DIR/blame-trait-error-spans-on-exprs.rs:53:12
+ |
+LL | fn want<V: T1>(_x: V) {}
+ | ^^ required by this bound in `want`
+help: consider restricting type parameter `Q`
+ |
+LL | fn example<Q: T3>(q: Q) {
+ | ++++
+
+error[E0277]: the trait bound `Q: T3` is not satisfied
+ --> $DIR/blame-trait-error-spans-on-exprs.rs:107:15
+ |
+LL | want(((3, q), 5));
+ | ---- ^ the trait `T3` is not implemented for `Q`
+ | |
+ | required by a bound introduced by this call
+ |
+note: required for `(i32, Q)` to implement `T2`
+ --> $DIR/blame-trait-error-spans-on-exprs.rs:50:20
+ |
+LL | impl<A: T3, B: T3> T2 for (A, B) {}
+ | -- ^^ ^^^^^^
+ | |
+ | unsatisfied trait bound introduced here
+note: required for `((i32, Q), i32)` to implement `T1`
+ --> $DIR/blame-trait-error-spans-on-exprs.rs:51:20
+ |
+LL | impl<A: T2, B: T2> T1 for (A, B) {}
+ | -- ^^ ^^^^^^
+ | |
+ | unsatisfied trait bound introduced here
+note: required by a bound in `want`
+ --> $DIR/blame-trait-error-spans-on-exprs.rs:53:12
+ |
+LL | fn want<V: T1>(_x: V) {}
+ | ^^ required by this bound in `want`
+help: consider restricting type parameter `Q`
+ |
+LL | fn example<Q: T3>(q: Q) {
+ | ++++
+
+error[E0277]: the trait bound `Q: T1` is not satisfied
+ --> $DIR/blame-trait-error-spans-on-exprs.rs:110:49
+ |
+LL | want(DoubleWrapper { item: Wrapper { value: q } });
+ | ---- ^ the trait `T1` is not implemented for `Q`
+ | |
+ | required by a bound introduced by this call
+ |
+note: required for `DoubleWrapper<Q>` to implement `T1`
+ --> $DIR/blame-trait-error-spans-on-exprs.rs:72:13
+ |
+LL | impl<T: T1> T1 for DoubleWrapper<T> {}
+ | -- ^^ ^^^^^^^^^^^^^^^^
+ | |
+ | unsatisfied trait bound introduced here
+note: required by a bound in `want`
+ --> $DIR/blame-trait-error-spans-on-exprs.rs:53:12
+ |
+LL | fn want<V: T1>(_x: V) {}
+ | ^^ required by this bound in `want`
+help: consider restricting type parameter `Q`
+ |
+LL | fn example<Q: T1>(q: Q) {
+ | ++++
+
+error[E0277]: the trait bound `Q: T1` is not satisfied
+ --> $DIR/blame-trait-error-spans-on-exprs.rs:113:88
+ |
+LL | want(DoubleWrapper { item: Wrapper { value: DoubleWrapper { item: Wrapper { value: q } } } });
+ | ---- required by a bound introduced by this call ^ the trait `T1` is not implemented for `Q`
+ |
+note: required for `DoubleWrapper<Q>` to implement `T1`
+ --> $DIR/blame-trait-error-spans-on-exprs.rs:72:13
+ |
+LL | impl<T: T1> T1 for DoubleWrapper<T> {}
+ | -- ^^ ^^^^^^^^^^^^^^^^
+ | |
+ | unsatisfied trait bound introduced here
+ = note: 1 redundant requirement hidden
+ = note: required for `DoubleWrapper<DoubleWrapper<Q>>` to implement `T1`
+note: required by a bound in `want`
+ --> $DIR/blame-trait-error-spans-on-exprs.rs:53:12
+ |
+LL | fn want<V: T1>(_x: V) {}
+ | ^^ required by this bound in `want`
+help: consider restricting type parameter `Q`
+ |
+LL | fn example<Q: T1>(q: Q) {
+ | ++++
+
+error[E0277]: the trait bound `Q: T3` is not satisfied
+ --> $DIR/blame-trait-error-spans-on-exprs.rs:117:27
+ |
+LL | want(Wrapper { value: AliasBurrito { spiciness: q, filling: q } });
+ | ---- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `T3` is not implemented for `Q`
+ | |
+ | required by a bound introduced by this call
+ |
+note: required for `GenericBurrito<Q, Q>` to implement `T2`
+ --> $DIR/blame-trait-error-spans-on-exprs.rs:47:16
+ |
+LL | impl<X, Y: T3> T2 for GenericBurrito<X, Y> {}
+ | -- ^^ ^^^^^^^^^^^^^^^^^^^^
+ | |
+ | unsatisfied trait bound introduced here
+note: required for `Wrapper<GenericBurrito<Q, Q>>` to implement `T1`
+ --> $DIR/blame-trait-error-spans-on-exprs.rs:16:13
+ |
+LL | impl<B: T2> T1 for Wrapper<B> {}
+ | -- ^^ ^^^^^^^^^^
+ | |
+ | unsatisfied trait bound introduced here
+note: required by a bound in `want`
+ --> $DIR/blame-trait-error-spans-on-exprs.rs:53:12
+ |
+LL | fn want<V: T1>(_x: V) {}
+ | ^^ required by this bound in `want`
+help: consider restricting type parameter `Q`
+ |
+LL | fn example<Q: T3>(q: Q) {
+ | ++++
+
+error[E0277]: the trait bound `Q: T1` is not satisfied
+ --> $DIR/blame-trait-error-spans-on-exprs.rs:120:35
+ |
+LL | want(Two { a: Two { a: (), b: q }, b: () });
+ | ---- ^ the trait `T1` is not implemented for `Q`
+ | |
+ | required by a bound introduced by this call
+ |
+note: required for `Two<Two<(), Q>, ()>` to implement `T1`
+ --> $DIR/blame-trait-error-spans-on-exprs.rs:66:19
+ |
+LL | impl<X, Y: T1, Z> T1 for Two<Two<X, Y>, Z> {}
+ | -- ^^ ^^^^^^^^^^^^^^^^^
+ | |
+ | unsatisfied trait bound introduced here
+note: required by a bound in `want`
+ --> $DIR/blame-trait-error-spans-on-exprs.rs:53:12
+ |
+LL | fn want<V: T1>(_x: V) {}
+ | ^^ required by this bound in `want`
+help: consider restricting type parameter `Q`
+ |
+LL | fn example<Q: T1>(q: Q) {
+ | ++++
+
+error[E0277]: the trait bound `Q: T1` is not satisfied
+ --> $DIR/blame-trait-error-spans-on-exprs.rs:126:59
+ |
+LL | want(
+ | ---- required by a bound introduced by this call
+LL | Two { a: Two { a: (), b: Two { a: Two { a: (), b: q }, b: () } }, b: () },
+ | ^ the trait `T1` is not implemented for `Q`
+ |
+note: required for `Two<Two<(), Q>, ()>` to implement `T1`
+ --> $DIR/blame-trait-error-spans-on-exprs.rs:66:19
+ |
+LL | impl<X, Y: T1, Z> T1 for Two<Two<X, Y>, Z> {}
+ | -- ^^ ^^^^^^^^^^^^^^^^^
+ | |
+ | unsatisfied trait bound introduced here
+ = note: 1 redundant requirement hidden
+ = note: required for `Two<Two<(), Two<Two<(), Q>, ()>>, ()>` to implement `T1`
+note: required by a bound in `want`
+ --> $DIR/blame-trait-error-spans-on-exprs.rs:53:12
+ |
+LL | fn want<V: T1>(_x: V) {}
+ | ^^ required by this bound in `want`
+help: consider restricting type parameter `Q`
+ |
+LL | fn example<Q: T1>(q: Q) {
+ | ++++
+
+error: aborting due to 13 previous errors
+
+For more information about this error, try `rustc --explain E0277`.
--- /dev/null
+fn main() {
+ let bar = 3;
+ format!("{?:}", bar);
+ //~^ ERROR invalid format string: expected format parameter to occur after `:`
+ format!("{?:bar}");
+ //~^ ERROR invalid format string: expected format parameter to occur after `:`
+ format!("{?:?}", bar);
+ //~^ ERROR invalid format string: expected format parameter to occur after `:`
+ format!("{??}", bar);
+ //~^ ERROR invalid format string: expected `'}'`, found `'?'`
+ format!("{?;bar}");
+ //~^ ERROR invalid format string: expected `'}'`, found `'?'`
+ format!("{?:#?}", bar);
+ //~^ ERROR invalid format string: expected format parameter to occur after `:`
+}
--- /dev/null
+error: invalid format string: expected format parameter to occur after `:`
+ --> $DIR/format-string-wrong-order.rs:3:15
+ |
+LL | format!("{?:}", bar);
+ | ^ expected `?` to occur after `:` in format string
+ |
+ = note: `?` comes after `:`, try `:?` instead
+
+error: invalid format string: expected format parameter to occur after `:`
+ --> $DIR/format-string-wrong-order.rs:5:15
+ |
+LL | format!("{?:bar}");
+ | ^ expected `?` to occur after `:` in format string
+ |
+ = note: `?` comes after `:`, try `bar:?` instead
+
+error: invalid format string: expected format parameter to occur after `:`
+ --> $DIR/format-string-wrong-order.rs:7:15
+ |
+LL | format!("{?:?}", bar);
+ | ^ expected `?` to occur after `:` in format string
+ |
+ = note: `?` comes after `:`, try `:?` instead
+
+error: invalid format string: expected `'}'`, found `'?'`
+ --> $DIR/format-string-wrong-order.rs:9:15
+ |
+LL | format!("{??}", bar);
+ | -^ expected `}` in format string
+ | |
+ | because of this opening brace
+ |
+ = note: if you intended to print `{`, you can escape it using `{{`
+
+error: invalid format string: expected `'}'`, found `'?'`
+ --> $DIR/format-string-wrong-order.rs:11:15
+ |
+LL | format!("{?;bar}");
+ | -^ expected `}` in format string
+ | |
+ | because of this opening brace
+ |
+ = note: if you intended to print `{`, you can escape it using `{{`
+
+error: invalid format string: expected format parameter to occur after `:`
+ --> $DIR/format-string-wrong-order.rs:13:15
+ |
+LL | format!("{?:#?}", bar);
+ | ^ expected `?` to occur after `:` in format string
+ |
+ = note: `?` comes after `:`, try `:?` instead
+
+error: aborting due to 6 previous errors
+
let x = 42;
match x {
0..=73 => {},
- 74..=> {}, //~ ERROR unexpected `=>` after open range
- //~^ ERROR expected one of `=>`, `if`, or `|`, found `>`
+ 74..=> {},
+ //~^ ERROR unexpected `>` after inclusive range
+ //~| NOTE this is parsed as an inclusive range `..=`
}
}
-error: unexpected `=>` after open range
- --> $DIR/half-open-range-pats-inclusive-match-arrow.rs:5:11
+error: unexpected `>` after inclusive range
+ --> $DIR/half-open-range-pats-inclusive-match-arrow.rs:5:14
|
LL | 74..=> {},
- | ^^^
+ | ---^
+ | |
+ | this is parsed as an inclusive range `..=`
|
help: add a space between the pattern and `=>`
|
LL | 74.. => {},
| +
-error: expected one of `=>`, `if`, or `|`, found `>`
- --> $DIR/half-open-range-pats-inclusive-match-arrow.rs:5:14
- |
-LL | 74..=> {},
- | ^ expected one of `=>`, `if`, or `|`
-
-error: aborting due to 2 previous errors
+error: aborting due to previous error
|
LL | send(cycle2().clone());
| ^^^^
- = note: ...which requires evaluating trait selection obligation `impl core::clone::Clone: core::marker::Send`...
+ = note: ...which requires evaluating trait selection obligation `cycle2::{opaque#0}: core::marker::Send`...
note: ...which requires computing type of `cycle2::{opaque#0}`...
--> $DIR/auto-trait-leak.rs:19:16
|
|
LL | send(cycle1().clone());
| ^^^^
- = note: ...which requires evaluating trait selection obligation `impl core::clone::Clone: core::marker::Send`...
+ = note: ...which requires evaluating trait selection obligation `cycle1::{opaque#0}: core::marker::Send`...
= note: ...which again requires computing type of `cycle1::{opaque#0}`, completing the cycle
note: cycle used when checking item types in top-level module
--> $DIR/auto-trait-leak.rs:1:1
| expected `()`, found `u8`
| help: change the parameter type to match the trait: `()`
|
-note: while checking the return type of the `async fn`
- --> $DIR/method-signature-matches.rs:20:25
- |
-LL | async fn owo(_: u8) {}
- | ^ checked the `Output` of this `async fn`, expected opaque type
-note: while checking the return type of the `async fn`
- --> $DIR/method-signature-matches.rs:20:25
- |
-LL | async fn owo(_: u8) {}
- | ^ checked the `Output` of this `async fn`, found opaque type
note: type in trait
--> $DIR/method-signature-matches.rs:16:21
|
| |
| arguments to this function are incorrect
|
-note: while checking the return type of the `async fn`
- --> $DIR/issue-102605.rs:3:19
- |
-LL | async fn foo() -> Result<(), String> {
- | ^^^^^^^^^^^^^^^^^^ checked the `Output` of this `async fn`, found opaque type
= note: expected enum `Result<(), _>`
found opaque type `impl Future<Output = Result<(), String>>`
note: function defined here
LL | t.and_then(|t| -> _ { bar(t) });
| ^^^^^^ expected `Result<_, Error>`, found opaque type
|
-note: while checking the return type of the `async fn`
- --> $DIR/issue-99914.rs:13:23
- |
-LL | async fn bar(t: Okay) {}
- | ^ checked the `Output` of this `async fn`, found opaque type
= note: expected enum `Result<_, Error>`
found opaque type `impl Future<Output = ()>`
help: try wrapping the expression in `Ok`
*foo = 32;
//~^ ERROR cannot assign to `*foo`, which is behind a `&` reference
let bar = foo;
+ //~^ HELP consider specifying this binding's type
*bar = 64;
//~^ ERROR cannot assign to `*bar`, which is behind a `&` reference
}
| ~~~~~~~
error[E0594]: cannot assign to `*bar`, which is behind a `&` reference
- --> $DIR/issue-51515.rs:8:5
+ --> $DIR/issue-51515.rs:9:5
|
-LL | let bar = foo;
- | --- consider changing this binding's type to be: `&mut i32`
LL | *bar = 64;
| ^^^^^^^^^ `bar` is a `&` reference, so the data it refers to cannot be written
+ |
+help: consider specifying this binding's type
+ |
+LL | let bar: &mut i32 = foo;
+ | ++++++++++
error: aborting due to 2 previous errors
--- /dev/null
+pub struct DefaultLifetime<'a, 'b = 'static> {
+ //~^ ERROR unexpected default lifetime parameter
+ _marker: std::marker::PhantomData<&'a &'b ()>,
+}
+
+fn main(){}
--- /dev/null
+error: unexpected default lifetime parameter
+ --> $DIR/issue-107492-default-value-for-lifetime.rs:1:35
+ |
+LL | pub struct DefaultLifetime<'a, 'b = 'static> {
+ | ^^^^^^^^^ lifetime parameters cannot have default values
+
+error: aborting due to previous error
+
--- /dev/null
+// check-pass
+
+#![warn(unused_must_use)]
+#![feature(never_type)]
+
+use std::ops::Add;
+use std::ops::Sub;
+use std::ops::Mul;
+use std::ops::Div;
+use std::ops::Rem;
+
+fn main() {
+ let x = 2_u32;
+ (x.add(4), x.sub(4), x.mul(4), x.div(4), x.rem(4));
+
+ x.add(4); //~ WARN unused return value of `add` that must be used
+
+ x.sub(4); //~ WARN unused return value of `sub` that must be used
+
+ x.mul(4); //~ WARN unused return value of `mul` that must be used
+
+ x.div(4); //~ WARN unused return value of `div` that must be used
+
+ x.rem(4); //~ WARN unused return value of `rem` that must be used
+
+ println!("{}", x);
+}
--- /dev/null
+warning: unused return value of `add` that must be used
+ --> $DIR/issue-103320-must-use-ops.rs:16:5
+ |
+LL | x.add(4);
+ | ^^^^^^^^
+ |
+ = note: this returns the result of the operation, without modifying the original
+note: the lint level is defined here
+ --> $DIR/issue-103320-must-use-ops.rs:3:9
+ |
+LL | #![warn(unused_must_use)]
+ | ^^^^^^^^^^^^^^^
+help: use `let _ = ...` to ignore the resulting value
+ |
+LL | let _ = x.add(4);
+ | +++++++
+
+warning: unused return value of `sub` that must be used
+ --> $DIR/issue-103320-must-use-ops.rs:18:5
+ |
+LL | x.sub(4);
+ | ^^^^^^^^
+ |
+ = note: this returns the result of the operation, without modifying the original
+help: use `let _ = ...` to ignore the resulting value
+ |
+LL | let _ = x.sub(4);
+ | +++++++
+
+warning: unused return value of `mul` that must be used
+ --> $DIR/issue-103320-must-use-ops.rs:20:5
+ |
+LL | x.mul(4);
+ | ^^^^^^^^
+ |
+ = note: this returns the result of the operation, without modifying the original
+help: use `let _ = ...` to ignore the resulting value
+ |
+LL | let _ = x.mul(4);
+ | +++++++
+
+warning: unused return value of `div` that must be used
+ --> $DIR/issue-103320-must-use-ops.rs:22:5
+ |
+LL | x.div(4);
+ | ^^^^^^^^
+ |
+ = note: this returns the result of the operation, without modifying the original
+help: use `let _ = ...` to ignore the resulting value
+ |
+LL | let _ = x.div(4);
+ | +++++++
+
+warning: unused return value of `rem` that must be used
+ --> $DIR/issue-103320-must-use-ops.rs:24:5
+ |
+LL | x.rem(4);
+ | ^^^^^^^^
+ |
+ = note: this returns the result of the operation, without modifying the original
+help: use `let _ = ...` to ignore the resulting value
+ |
+LL | let _ = x.rem(4);
+ | +++++++
+
+warning: 5 warnings emitted
+
--- /dev/null
+#[deny(unused)]
+fn main() {
+ let arr = [0; 10];
+ let _ = arr[(0)]; //~ ERROR unnecessary parentheses around index expression
+ let _ = arr[{0}]; //~ ERROR unnecessary braces around index expression
+ let _ = arr[1 + (0)];
+ let _ = arr[{ let x = 0; x }];
+}
--- /dev/null
+error: unnecessary parentheses around index expression
+ --> $DIR/issue-96606.rs:4:17
+ |
+LL | let _ = arr[(0)];
+ | ^ ^
+ |
+note: the lint level is defined here
+ --> $DIR/issue-96606.rs:1:8
+ |
+LL | #[deny(unused)]
+ | ^^^^^^
+ = note: `#[deny(unused_parens)]` implied by `#[deny(unused)]`
+help: remove these parentheses
+ |
+LL - let _ = arr[(0)];
+LL + let _ = arr[0];
+ |
+
+error: unnecessary braces around index expression
+ --> $DIR/issue-96606.rs:5:17
+ |
+LL | let _ = arr[{0}];
+ | ^ ^
+ |
+ = note: `#[deny(unused_braces)]` implied by `#[deny(unused)]`
+help: remove these braces
+ |
+LL - let _ = arr[{0}];
+LL + let _ = arr[0];
+ |
+
+error: aborting due to 2 previous errors
+
--- /dev/null
+// check that we don't generate a span that points beyond EOF
+
+// error-pattern: unclosed delimiter
+// error-pattern: unclosed delimiter
+// error-pattern: unclosed delimiter
+
+fn a(){{{
--- /dev/null
+error: this file contains an unclosed delimiter
+ --> $DIR/issue-107423-unused-delim-only-one-no-pair.rs:7:11
+ |
+LL | fn a(){{{
+ | --- ^
+ | |||
+ | ||unclosed delimiter
+ | |unclosed delimiter
+ | unclosed delimiter
+
+error: this file contains an unclosed delimiter
+ --> $DIR/issue-107423-unused-delim-only-one-no-pair.rs:7:11
+ |
+LL | fn a(){{{
+ | --- ^
+ | |||
+ | ||unclosed delimiter
+ | |unclosed delimiter
+ | unclosed delimiter
+
+error: this file contains an unclosed delimiter
+ --> $DIR/issue-107423-unused-delim-only-one-no-pair.rs:7:11
+ |
+LL | fn a(){{{
+ | --- ^
+ | |||
+ | ||unclosed delimiter
+ | |unclosed delimiter
+ | unclosed delimiter
+
+error: aborting due to 3 previous errors
+
--- /dev/null
+// check-pass
+
+macro_rules! test_expr {
+ ($expr:expr) => {};
+}
+
+macro_rules! test_ty {
+ ($a:ty | $b:ty) => {};
+}
+
+fn main() {
+ test_expr!(a as fn() -> B | C);
+ // Do not break the `|` operator.
+
+ test_expr!(|_: fn() -> B| C | D);
+ // Do not break `-> Ret` in closure args.
+
+ test_ty!(A | B);
+ // We can't support anon enums in arbitrary positions.
+
+ test_ty!(fn() -> A | B);
+ // Don't break fn ptrs.
+
+ test_ty!(impl Fn() -> A | B);
+ // Don't break parenthesized generics.
+}
+++ /dev/null
-fn foo(x: bool | i32) -> i32 | f64 {
-//~^ ERROR anonymous enums are not supported
-//~| ERROR anonymous enums are not supported
- match x {
- x: i32 => x, //~ ERROR expected
- true => 42.,
- false => 0.333,
- }
-}
-
-fn main() {
- match foo(true) {
- 42: i32 => (), //~ ERROR expected
- _: f64 => (), //~ ERROR expected
- x: i32 => (), //~ ERROR expected
- }
-}
+++ /dev/null
-error: anonymous enums are not supported
- --> $DIR/anon-enums.rs:1:16
- |
-LL | fn foo(x: bool | i32) -> i32 | f64 {
- | ---- ^ ---
- |
- = help: create a named `enum` and use it here instead:
- enum Name {
- Variant1(bool),
- Variant2(i32),
- }
-
-error: anonymous enums are not supported
- --> $DIR/anon-enums.rs:1:30
- |
-LL | fn foo(x: bool | i32) -> i32 | f64 {
- | --- ^ ---
- |
- = help: create a named `enum` and use it here instead:
- enum Name {
- Variant1(i32),
- Variant2(f64),
- }
-
-error: expected one of `@` or `|`, found `:`
- --> $DIR/anon-enums.rs:5:10
- |
-LL | x: i32 => x,
- | ^ --- specifying the type of a pattern isn't supported
- | |
- | expected one of `@` or `|`
- |
-help: maybe write a path separator here
- |
-LL | x::i32 => x,
- | ~~
-
-error: expected one of `...`, `..=`, `..`, or `|`, found `:`
- --> $DIR/anon-enums.rs:13:11
- |
-LL | 42: i32 => (),
- | ^ --- specifying the type of a pattern isn't supported
- | |
- | expected one of `...`, `..=`, `..`, or `|`
-
-error: expected `|`, found `:`
- --> $DIR/anon-enums.rs:14:10
- |
-LL | _: f64 => (),
- | ^ --- specifying the type of a pattern isn't supported
- | |
- | expected `|`
-
-error: expected one of `@` or `|`, found `:`
- --> $DIR/anon-enums.rs:15:10
- |
-LL | x: i32 => (),
- | ^ --- specifying the type of a pattern isn't supported
- | |
- | expected one of `@` or `|`
- |
-help: maybe write a path separator here
- |
-LL | x::i32 => (),
- | ~~
-
-error: aborting due to 6 previous errors
-
+++ /dev/null
-// build-pass
-macro_rules! check_ty {
- ($Z:ty) => { compile_error!("triggered"); };
- ($X:ty | $Y:ty) => { $X };
-}
-
-macro_rules! check {
- ($Z:ty) => { compile_error!("triggered"); };
- ($X:ty | $Y:ty) => { };
-}
-
-check! { i32 | u8 }
-
-fn foo(x: check_ty! { i32 | u8 }) -> check_ty! { i32 | u8 } {
- x
-}
-fn main() {
- let x: check_ty! { i32 | u8 } = 42;
- let _: check_ty! { i32 | u8 } = foo(x);
-}
|
LL | const async unsafe extern "C" fn ff5() {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- = note: ...which requires computing whether `impl core::future::future::Future<Output = ()>` is freeze...
- = note: ...which requires evaluating trait selection obligation `impl core::future::future::Future<Output = ()>: core::marker::Freeze`...
+ = note: ...which requires computing whether `main::ff5::{opaque#0}` is freeze...
+ = note: ...which requires evaluating trait selection obligation `main::ff5::{opaque#0}: core::marker::Freeze`...
= note: ...which again requires computing type of `main::ff5::{opaque#0}`, completing the cycle
note: cycle used when checking item types in top-level module
--> $DIR/fn-header-semantic-fail.rs:5:1
|
LL | const async unsafe extern "C" fn ft5() {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- = note: ...which requires computing whether `impl core::future::future::Future<Output = ()>` is freeze...
- = note: ...which requires evaluating trait selection obligation `impl core::future::future::Future<Output = ()>: core::marker::Freeze`...
+ = note: ...which requires computing whether `main::<impl at $DIR/fn-header-semantic-fail.rs:28:5: 28:17>::ft5::{opaque#0}` is freeze...
+ = note: ...which requires evaluating trait selection obligation `main::<impl at $DIR/fn-header-semantic-fail.rs:28:5: 28:17>::ft5::{opaque#0}: core::marker::Freeze`...
= note: ...which again requires computing type of `main::<impl at $DIR/fn-header-semantic-fail.rs:28:5: 28:17>::ft5::{opaque#0}`, completing the cycle
note: cycle used when checking item types in top-level module
--> $DIR/fn-header-semantic-fail.rs:5:1
|
LL | const async unsafe extern "C" fn fi5() {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
- = note: ...which requires computing whether `impl core::future::future::Future<Output = ()>` is freeze...
- = note: ...which requires evaluating trait selection obligation `impl core::future::future::Future<Output = ()>: core::marker::Freeze`...
+ = note: ...which requires computing whether `main::<impl at $DIR/fn-header-semantic-fail.rs:40:5: 40:11>::fi5::{opaque#0}` is freeze...
+ = note: ...which requires evaluating trait selection obligation `main::<impl at $DIR/fn-header-semantic-fail.rs:40:5: 40:11>::fi5::{opaque#0}: core::marker::Freeze`...
= note: ...which again requires computing type of `main::<impl at $DIR/fn-header-semantic-fail.rs:40:5: 40:11>::fi5::{opaque#0}`, completing the cycle
note: cycle used when checking item types in top-level module
--> $DIR/fn-header-semantic-fail.rs:5:1
--> $DIR/issue-102806.rs:21:22
|
LL | let V3 { z: val, ... } = v;
- | ^^^ help: to omit remaining fields, use one fewer `.`: `..`
+ | ^^^
+ |
+help: to omit remaining fields, use `..`
+ |
+LL | let V3 { z: val, .. } = v;
+ | ~~
error[E0063]: missing fields `x` and `y` in initializer of `V3`
--> $DIR/issue-102806.rs:17:13
--> $DIR/issue-63135.rs:3:8
|
LL | fn i(n{...,f #
- | ^^^ help: to omit remaining fields, use one fewer `.`: `..`
+ | ^^^
+ |
+help: to omit remaining fields, use `..`
+ |
+LL | fn i(n{..,f #
+ | ~~
error: expected `}`, found `,`
--> $DIR/issue-63135.rs:3:11
--- /dev/null
+fn main() {
+ for i in {
+ //~^ ERROR missing expression to iterate on in `for` loop
+ }
+}
--- /dev/null
+error: missing expression to iterate on in `for` loop
+ --> $DIR/missing-expression-in-for-loop.rs:2:14
+ |
+LL | for i in {
+ | ^
+ |
+help: try adding an expression to the `for` loop
+ |
+LL | for i in /* expression */ {
+ | ++++++++++++++++
+
+error: aborting due to previous error
+
enum Foo { Bar }
fn foo(x: impl Iterator<Item = Foo>) {
for <Foo>::Bar in x {}
- //~^ ERROR expected one of `const`, `move`, `static`, `|`
+ //~^ ERROR expected one of `move`, `static`, `|`
//~^^ ERROR `for<...>` binders for closures are experimental
}
-error: expected one of `const`, `move`, `static`, `|`, or `||`, found `::`
+error: expected one of `move`, `static`, `|`, or `||`, found `::`
--> $DIR/recover-quantified-closure.rs:9:14
|
LL | for <Foo>::Bar in x {}
- | ^^ expected one of `const`, `move`, `static`, `|`, or `||`
+ | ^^ expected one of `move`, `static`, `|`, or `||`
error[E0658]: `for<...>` binders for closures are experimental
--> $DIR/recover-quantified-closure.rs:2:5
--- /dev/null
+fn foo(x: bool) -> i32 {
+ match x {
+ x: i32 => x, //~ ERROR expected
+ //~^ ERROR mismatched types
+ true => 42.,
+ false => 0.333,
+ }
+}
+
+fn main() {
+ match foo(true) {
+ 42: i32 => (), //~ ERROR expected
+ _: f64 => (), //~ ERROR expected
+ x: i32 => (), //~ ERROR expected
+ }
+}
--- /dev/null
+error: expected one of `@` or `|`, found `:`
+ --> $DIR/type-ascription-in-pattern.rs:3:10
+ |
+LL | x: i32 => x,
+ | ^ --- specifying the type of a pattern isn't supported
+ | |
+ | expected one of `@` or `|`
+ |
+help: maybe write a path separator here
+ |
+LL | x::i32 => x,
+ | ~~
+
+error: expected one of `...`, `..=`, `..`, or `|`, found `:`
+ --> $DIR/type-ascription-in-pattern.rs:12:11
+ |
+LL | 42: i32 => (),
+ | ^ --- specifying the type of a pattern isn't supported
+ | |
+ | expected one of `...`, `..=`, `..`, or `|`
+
+error: expected `|`, found `:`
+ --> $DIR/type-ascription-in-pattern.rs:13:10
+ |
+LL | _: f64 => (),
+ | ^ --- specifying the type of a pattern isn't supported
+ | |
+ | expected `|`
+
+error: expected one of `@` or `|`, found `:`
+ --> $DIR/type-ascription-in-pattern.rs:14:10
+ |
+LL | x: i32 => (),
+ | ^ --- specifying the type of a pattern isn't supported
+ | |
+ | expected one of `@` or `|`
+ |
+help: maybe write a path separator here
+ |
+LL | x::i32 => (),
+ | ~~
+
+error[E0308]: mismatched types
+ --> $DIR/type-ascription-in-pattern.rs:3:19
+ |
+LL | fn foo(x: bool) -> i32 {
+ | --- expected `i32` because of return type
+LL | match x {
+LL | x: i32 => x,
+ | ^ expected `i32`, found `bool`
+
+error: aborting due to 5 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
print-type-size type: `[async fn body@$DIR/async.rs:8:36: 11:2]`: 16386 bytes, alignment: 1 bytes
print-type-size discriminant: 1 bytes
+print-type-size variant `Unresumed`: 8192 bytes
+print-type-size upvar `.arg`: 8192 bytes, offset: 0 bytes, alignment: 1 bytes
print-type-size variant `Suspend0`: 16385 bytes
print-type-size upvar `.arg`: 8192 bytes, offset: 0 bytes, alignment: 1 bytes
print-type-size local `.arg`: 8192 bytes
print-type-size local `.__awaitee`: 1 bytes
-print-type-size variant `Unresumed`: 8192 bytes
-print-type-size upvar `.arg`: 8192 bytes, offset: 0 bytes, alignment: 1 bytes
print-type-size variant `Returned`: 8192 bytes
print-type-size upvar `.arg`: 8192 bytes, offset: 0 bytes, alignment: 1 bytes
print-type-size variant `Panicked`: 8192 bytes
print-type-size discriminant: 1 bytes
print-type-size variant `Unresumed`: 8192 bytes
print-type-size upvar `.array`: 8192 bytes, offset: 0 bytes, alignment: 1 bytes
+print-type-size variant `Suspend0`: 8192 bytes
+print-type-size upvar `.array`: 8192 bytes, offset: 0 bytes, alignment: 1 bytes
print-type-size variant `Returned`: 8192 bytes
print-type-size upvar `.array`: 8192 bytes, offset: 0 bytes, alignment: 1 bytes
print-type-size variant `Panicked`: 8192 bytes
print-type-size upvar `.array`: 8192 bytes, offset: 0 bytes, alignment: 1 bytes
-print-type-size variant `Suspend0`: 8192 bytes
-print-type-size upvar `.array`: 8192 bytes, offset: 0 bytes, alignment: 1 bytes
print-type-size type: `[generator@$DIR/generator_discr_placement.rs:11:13: 11:15]`: 8 bytes, alignment: 4 bytes
print-type-size discriminant: 1 bytes
+print-type-size variant `Unresumed`: 0 bytes
print-type-size variant `Suspend0`: 7 bytes
print-type-size padding: 3 bytes
print-type-size local `.w`: 4 bytes, alignment: 4 bytes
print-type-size variant `Suspend1`: 7 bytes
print-type-size padding: 3 bytes
print-type-size local `.z`: 4 bytes, alignment: 4 bytes
-print-type-size variant `Unresumed`: 0 bytes
print-type-size variant `Returned`: 0 bytes
print-type-size variant `Panicked`: 0 bytes
// ignore-wasm32
// ignore-sgx no support for proc-macro crate type
// build-pass
+// force-host
+// no-prefer-dynamic
+
#![crate_type = "proc-macro"]
// FIXME: This don't work when crate-type is specified by attribute
--- /dev/null
+fn a() -> _ {
+ //~^ ERROR the placeholder `_` is not allowed within types on item signatures for return types
+ &a
+}
+
+fn main() {}
--- /dev/null
+error[E0121]: the placeholder `_` is not allowed within types on item signatures for return types
+ --> $DIR/no-query-in-printing-during-query-descr.rs:1:11
+ |
+LL | fn a() -> _ {
+ | ^ not allowed in type signatures
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0121`.
--- /dev/null
+// check-pass
+
+#![feature(const_trait_impl, const_closures)]
+#![allow(incomplete_features)]
+
+const fn test() -> impl ~const Fn() {
+ const move || {}
+}
+
+fn main() {}
// check-pass
// compile-flags: -Zhir-stats
// only-x86_64
-// ignore-stage1 FIXME: remove after next bootstrap bump
+
+// Type layouts sometimes change. When that happens, until the next bootstrap
+// bump occurs, stage1 and stage2 will give different outputs for this test.
+// Add an `ignore-stage1` comment marker to work around that problem during
+// that time.
// The aim here is to include at least one of every different type of top-level
// AST/HIR node reported by `-Zhir-stats`.
let foo = Some(Foo::Other);
if let Some(Foo::Bar {_}) = foo {}
- //~^ ERROR expected identifier, found reserved identifier `_`
- //~| ERROR pattern does not mention field `bar` [E0027]
+ //~^ ERROR expected field pattern, found `_`
}
-error: expected identifier, found reserved identifier `_`
+error: expected field pattern, found `_`
--> $DIR/struct-enum-ignoring-field-with-underscore.rs:9:27
|
LL | if let Some(Foo::Bar {_}) = foo {}
- | ^ expected identifier, found reserved identifier
-
-error[E0027]: pattern does not mention field `bar`
- --> $DIR/struct-enum-ignoring-field-with-underscore.rs:9:17
- |
-LL | if let Some(Foo::Bar {_}) = foo {}
- | ^^^^^^^^^^^^ missing field `bar`
- |
-help: include the missing field in the pattern
+ | ^
|
-LL | if let Some(Foo::Bar {_, bar }) = foo {}
- | ~~~~~~~
-help: if you don't care about this missing field, you can explicitly ignore it
+help: to omit remaining fields, use `..`
|
-LL | if let Some(Foo::Bar {_, .. }) = foo {}
- | ~~~~~~
+LL | if let Some(Foo::Bar {..}) = foo {}
+ | ~~
-error: aborting due to 2 previous errors
+error: aborting due to previous error
-For more information about this error, try `rustc --explain E0027`.
--- /dev/null
+// run-rustfix
+
+// Suggest providing a std::ptr::null{,_mut}() to a function that takes in a raw
+// pointer if a literal 0 was provided by the user.
+
+extern "C" {
+ fn foo(ptr: *const u8);
+
+ fn foo_mut(ptr: *mut u8);
+
+ fn usize(ptr: *const usize);
+
+ fn usize_mut(ptr: *mut usize);
+}
+
+fn main() {
+ unsafe {
+ foo(std::ptr::null());
+ //~^ mismatched types [E0308]
+ //~| if you meant to create a null pointer, use `std::ptr::null()`
+ foo_mut(std::ptr::null_mut());
+ //~^ mismatched types [E0308]
+ //~| if you meant to create a null pointer, use `std::ptr::null_mut()`
+ usize(std::ptr::null());
+ //~^ mismatched types [E0308]
+ //~| if you meant to create a null pointer, use `std::ptr::null()`
+ usize_mut(std::ptr::null_mut());
+ //~^ mismatched types [E0308]
+ //~| if you meant to create a null pointer, use `std::ptr::null_mut()`
+ }
+}
--- /dev/null
+// run-rustfix
+
+// Suggest providing a std::ptr::null{,_mut}() to a function that takes in a raw
+// pointer if a literal 0 was provided by the user.
+
+extern "C" {
+ fn foo(ptr: *const u8);
+
+ fn foo_mut(ptr: *mut u8);
+
+ fn usize(ptr: *const usize);
+
+ fn usize_mut(ptr: *mut usize);
+}
+
+fn main() {
+ unsafe {
+ foo(0);
+ //~^ mismatched types [E0308]
+ //~| if you meant to create a null pointer, use `std::ptr::null()`
+ foo_mut(0);
+ //~^ mismatched types [E0308]
+ //~| if you meant to create a null pointer, use `std::ptr::null_mut()`
+ usize(0);
+ //~^ mismatched types [E0308]
+ //~| if you meant to create a null pointer, use `std::ptr::null()`
+ usize_mut(0);
+ //~^ mismatched types [E0308]
+ //~| if you meant to create a null pointer, use `std::ptr::null_mut()`
+ }
+}
--- /dev/null
+error[E0308]: mismatched types
+ --> $DIR/suggest-null-ptr.rs:18:13
+ |
+LL | foo(0);
+ | --- ^ expected `*const u8`, found `usize`
+ | |
+ | arguments to this function are incorrect
+ |
+ = note: expected raw pointer `*const u8`
+ found type `usize`
+note: function defined here
+ --> $DIR/suggest-null-ptr.rs:7:8
+ |
+LL | fn foo(ptr: *const u8);
+ | ^^^
+help: if you meant to create a null pointer, use `std::ptr::null()`
+ |
+LL | foo(std::ptr::null());
+ | ~~~~~~~~~~~~~~~~
+
+error[E0308]: mismatched types
+ --> $DIR/suggest-null-ptr.rs:21:17
+ |
+LL | foo_mut(0);
+ | ------- ^ expected `*mut u8`, found `usize`
+ | |
+ | arguments to this function are incorrect
+ |
+ = note: expected raw pointer `*mut u8`
+ found type `usize`
+note: function defined here
+ --> $DIR/suggest-null-ptr.rs:9:8
+ |
+LL | fn foo_mut(ptr: *mut u8);
+ | ^^^^^^^
+help: if you meant to create a null pointer, use `std::ptr::null_mut()`
+ |
+LL | foo_mut(std::ptr::null_mut());
+ | ~~~~~~~~~~~~~~~~~~~~
+
+error[E0308]: mismatched types
+ --> $DIR/suggest-null-ptr.rs:24:15
+ |
+LL | usize(0);
+ | ----- ^ expected `*const usize`, found `usize`
+ | |
+ | arguments to this function are incorrect
+ |
+ = note: expected raw pointer `*const usize`
+ found type `usize`
+note: function defined here
+ --> $DIR/suggest-null-ptr.rs:11:8
+ |
+LL | fn usize(ptr: *const usize);
+ | ^^^^^
+help: if you meant to create a null pointer, use `std::ptr::null()`
+ |
+LL | usize(std::ptr::null());
+ | ~~~~~~~~~~~~~~~~
+
+error[E0308]: mismatched types
+ --> $DIR/suggest-null-ptr.rs:27:19
+ |
+LL | usize_mut(0);
+ | --------- ^ expected `*mut usize`, found `usize`
+ | |
+ | arguments to this function are incorrect
+ |
+ = note: expected raw pointer `*mut usize`
+ found type `usize`
+note: function defined here
+ --> $DIR/suggest-null-ptr.rs:13:8
+ |
+LL | fn usize_mut(ptr: *mut usize);
+ | ^^^^^^^^^
+help: if you meant to create a null pointer, use `std::ptr::null_mut()`
+ |
+LL | usize_mut(std::ptr::null_mut());
+ | ~~~~~~~~~~~~~~~~~~~~
+
+error: aborting due to 4 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
};
}
-async fn async_dummy() {} //~ NOTE checked the `Output` of this `async fn`, found opaque type
-//~| NOTE while checking the return type of the `async fn`
-//~| NOTE in this expansion of desugaring of `async` block or function
-//~| NOTE checked the `Output` of this `async fn`, expected opaque type
-//~| NOTE while checking the return type of the `async fn`
-//~| NOTE in this expansion of desugaring of `async` block or function
-async fn async_dummy2() {} //~ NOTE checked the `Output` of this `async fn`, found opaque type
-//~| NOTE checked the `Output` of this `async fn`, found opaque type
-//~| NOTE while checking the return type of the `async fn`
-//~| NOTE in this expansion of desugaring of `async` block or function
-//~| NOTE while checking the return type of the `async fn`
-//~| NOTE in this expansion of desugaring of `async` block or function
+async fn async_dummy() {}
+
+async fn async_dummy2() {}
async fn async_extra_semicolon_same() {
let _ = if true {
error[E0308]: `if` and `else` have incompatible types
- --> $DIR/if-then-neeing-semi.rs:37:9
+ --> $DIR/if-then-neeing-semi.rs:28:9
|
LL | let _ = if true {
| _____________-
LL | | };
| |_____- `if` and `else` have incompatible types
|
-note: while checking the return type of the `async fn`
- --> $DIR/if-then-neeing-semi.rs:18:24
- |
-LL | async fn async_dummy() {}
- | ^ checked the `Output` of this `async fn`, found opaque type
= note: expected unit type `()`
found opaque type `impl Future<Output = ()>`
help: consider `await`ing on the `Future`
|
error[E0308]: `if` and `else` have incompatible types
- --> $DIR/if-then-neeing-semi.rs:50:9
+ --> $DIR/if-then-neeing-semi.rs:41:9
|
LL | let _ = if true {
| _____________-
LL | | };
| |_____- `if` and `else` have incompatible types
|
-note: while checking the return type of the `async fn`
- --> $DIR/if-then-neeing-semi.rs:24:25
- |
-LL | async fn async_dummy2() {}
- | ^ checked the `Output` of this `async fn`, found opaque type
= note: expected unit type `()`
found opaque type `impl Future<Output = ()>`
help: consider `await`ing on the `Future`
|
error[E0308]: `if` and `else` have incompatible types
- --> $DIR/if-then-neeing-semi.rs:63:9
+ --> $DIR/if-then-neeing-semi.rs:54:9
|
LL | let _ = if true {
| _____________-
LL | | };
| |_____- `if` and `else` have incompatible types
|
-note: while checking the return type of the `async fn`
- --> $DIR/if-then-neeing-semi.rs:18:24
- |
-LL | async fn async_dummy() {}
- | ^ checked the `Output` of this `async fn`, expected opaque type
-note: while checking the return type of the `async fn`
- --> $DIR/if-then-neeing-semi.rs:24:25
- |
-LL | async fn async_dummy2() {}
- | ^ checked the `Output` of this `async fn`, found opaque type
= note: expected opaque type `impl Future<Output = ()>` (opaque type at <$DIR/if-then-neeing-semi.rs:18:24>)
- found opaque type `impl Future<Output = ()>` (opaque type at <$DIR/if-then-neeing-semi.rs:24:25>)
+ found opaque type `impl Future<Output = ()>` (opaque type at <$DIR/if-then-neeing-semi.rs:20:25>)
= note: distinct uses of `impl Trait` result in different opaque types
help: consider `await`ing on both `Future`s
|
LL | | }
| |_____- `match` arms have incompatible types
|
-note: while checking the return type of the `async fn`
- --> $DIR/auxiliary/issue-81839.rs:6:49
- |
-LL | pub async fn answer_str(&self, _s: &str) -> Test {
- | ^^^^ checked the `Output` of this `async fn`, found opaque type
= note: expected unit type `()`
found opaque type `impl Future<Output = Test>`
};
}
-async fn async_dummy() {} //~ NOTE checked the `Output` of this `async fn`, found opaque type
-//~| NOTE while checking the return type of the `async fn`
-//~| NOTE in this expansion of desugaring of `async` block or function
-//~| NOTE checked the `Output` of this `async fn`, expected opaque type
-//~| NOTE while checking the return type of the `async fn`
-//~| NOTE in this expansion of desugaring of `async` block or function
-async fn async_dummy2() {} //~ NOTE checked the `Output` of this `async fn`, found opaque type
-//~| NOTE checked the `Output` of this `async fn`, found opaque type
-//~| NOTE while checking the return type of the `async fn`
-//~| NOTE in this expansion of desugaring of `async` block or function
-//~| NOTE while checking the return type of the `async fn`
-//~| NOTE in this expansion of desugaring of `async` block or function
+async fn async_dummy() {}
+
+async fn async_dummy2() {}
async fn async_extra_semicolon_same() {
let _ = match true { //~ NOTE `match` arms have incompatible types
error[E0308]: `match` arms have incompatible types
- --> $DIR/match-prev-arm-needing-semi.rs:35:18
+ --> $DIR/match-prev-arm-needing-semi.rs:26:18
|
LL | let _ = match true {
| _____________-
LL | | };
| |_____- `match` arms have incompatible types
|
-note: while checking the return type of the `async fn`
- --> $DIR/match-prev-arm-needing-semi.rs:16:24
- |
-LL | async fn async_dummy() {}
- | ^ checked the `Output` of this `async fn`, found opaque type
= note: expected unit type `()`
found opaque type `impl Future<Output = ()>`
help: consider `await`ing on the `Future`
|
error[E0308]: `match` arms have incompatible types
- --> $DIR/match-prev-arm-needing-semi.rs:48:18
+ --> $DIR/match-prev-arm-needing-semi.rs:39:18
|
LL | let _ = match true {
| _____________-
LL | | };
| |_____- `match` arms have incompatible types
|
-note: while checking the return type of the `async fn`
- --> $DIR/match-prev-arm-needing-semi.rs:22:25
- |
-LL | async fn async_dummy2() {}
- | ^ checked the `Output` of this `async fn`, found opaque type
= note: expected unit type `()`
found opaque type `impl Future<Output = ()>`
help: consider `await`ing on the `Future`
|
error[E0308]: `match` arms have incompatible types
- --> $DIR/match-prev-arm-needing-semi.rs:59:18
+ --> $DIR/match-prev-arm-needing-semi.rs:50:18
|
LL | let _ = match true {
| _____________-
LL | | };
| |_____- `match` arms have incompatible types
|
-note: while checking the return type of the `async fn`
- --> $DIR/match-prev-arm-needing-semi.rs:16:24
- |
-LL | async fn async_dummy() {}
- | ^ checked the `Output` of this `async fn`, expected opaque type
-note: while checking the return type of the `async fn`
- --> $DIR/match-prev-arm-needing-semi.rs:22:25
- |
-LL | async fn async_dummy2() {}
- | ^ checked the `Output` of this `async fn`, found opaque type
= note: expected opaque type `impl Future<Output = ()>` (opaque type at <$DIR/match-prev-arm-needing-semi.rs:16:24>)
- found opaque type `impl Future<Output = ()>` (opaque type at <$DIR/match-prev-arm-needing-semi.rs:22:25>)
+ found opaque type `impl Future<Output = ()>` (opaque type at <$DIR/match-prev-arm-needing-semi.rs:18:25>)
= note: distinct uses of `impl Trait` result in different opaque types
help: consider `await`ing on both `Future`s
|
--- /dev/null
+// run-rustfix
+
+#![allow(unused)]
+
+struct Wrapper<T>(T);
+
+fn bar() -> Wrapper<fn()> { Wrapper(foo) }
+//~^ ERROR the placeholder `_` is not allowed within types on item signatures for return types
+
+fn foo() {}
+
+fn main() {}
--- /dev/null
+// run-rustfix
+
+#![allow(unused)]
+
+struct Wrapper<T>(T);
+
+fn bar() -> _ { Wrapper(foo) }
+//~^ ERROR the placeholder `_` is not allowed within types on item signatures for return types
+
+fn foo() {}
+
+fn main() {}
--- /dev/null
+error[E0121]: the placeholder `_` is not allowed within types on item signatures for return types
+ --> $DIR/suggest-fn-ptr-for-fn-item-in-fn-ret.rs:7:13
+ |
+LL | fn bar() -> _ { Wrapper(foo) }
+ | ^
+ | |
+ | not allowed in type signatures
+ | help: replace with the correct return type: `Wrapper<fn()>`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0121`.
| ^^^^ required by this bound in `is_send`
error[E0277]: `main::TestType` cannot be sent between threads safely
- --> $DIR/negated-auto-traits-error.rs:66:13
+ --> $DIR/negated-auto-traits-error.rs:66:20
|
LL | is_sync(Outer2(TestType));
- | ------- ^^^^^^^^^^^^^^^^ `main::TestType` cannot be sent between threads safely
+ | ------- ^^^^^^^^ `main::TestType` cannot be sent between threads safely
| |
| required by a bound introduced by this call
|
LL | Box::new(test) as AsyncFnPtr;
| ^^^^^^^^^^^^^^ expected `Pin<Box<dyn Future<Output = ()>>>`, found opaque type
|
-note: while checking the return type of the `async fn`
- --> $DIR/issue-98604.rs:5:17
- |
-LL | async fn test() {}
- | ^ checked the `Output` of this `async fn`, found opaque type
= note: expected struct `Pin<Box<(dyn Future<Output = ()> + 'static)>>`
found opaque type `impl Future<Output = ()>`
= note: required for the cast from `fn() -> impl Future<Output = ()> {test}` to the object type `dyn Fn() -> Pin<Box<(dyn Future<Output = ()> + 'static)>>`
--- /dev/null
+// run-rustfix
+
+fn take_str_maybe(_: Option<&str>) { }
+fn main() {
+ let string = String::from("Hello, world");
+
+ let option: Option<String> = Some(string.clone());
+ take_str_maybe(option.as_deref());
+ //~^ ERROR: mismatched types [E0308]
+
+ let option_ref = Some(&string);
+ take_str_maybe(option_ref.map(|x| x.as_str()));
+ //~^ ERROR: mismatched types [E0308]
+
+ let option_ref_ref = option_ref.as_ref();
+ take_str_maybe(option_ref_ref.map(|x| x.as_str()));
+ //~^ ERROR: mismatched types [E0308]
+}
-fn take_str_maybe(x: Option<&str>) -> Option<&str> { None }
+// run-rustfix
+fn take_str_maybe(_: Option<&str>) { }
fn main() {
let string = String::from("Hello, world");
- let option = Some(&string);
+
+ let option: Option<String> = Some(string.clone());
take_str_maybe(option);
//~^ ERROR: mismatched types [E0308]
+
+ let option_ref = Some(&string);
+ take_str_maybe(option_ref);
+ //~^ ERROR: mismatched types [E0308]
+
+ let option_ref_ref = option_ref.as_ref();
+ take_str_maybe(option_ref_ref);
+ //~^ ERROR: mismatched types [E0308]
}
error[E0308]: mismatched types
- --> $DIR/issue-89856.rs:6:20
+ --> $DIR/issue-89856.rs:8:20
|
LL | take_str_maybe(option);
- | -------------- ^^^^^^ expected `Option<&str>`, found `Option<&String>`
+ | -------------- ^^^^^^ expected `Option<&str>`, found `Option<String>`
+ | |
+ | arguments to this function are incorrect
+ |
+ = note: expected enum `Option<&str>`
+ found enum `Option<String>`
+note: function defined here
+ --> $DIR/issue-89856.rs:3:4
+ |
+LL | fn take_str_maybe(_: Option<&str>) { }
+ | ^^^^^^^^^^^^^^ ---------------
+help: try converting the passed type into a `&str`
+ |
+LL | take_str_maybe(option.as_deref());
+ | +++++++++++
+
+error[E0308]: mismatched types
+ --> $DIR/issue-89856.rs:12:20
+ |
+LL | take_str_maybe(option_ref);
+ | -------------- ^^^^^^^^^^ expected `Option<&str>`, found `Option<&String>`
| |
| arguments to this function are incorrect
|
= note: expected enum `Option<&str>`
found enum `Option<&String>`
note: function defined here
- --> $DIR/issue-89856.rs:1:4
+ --> $DIR/issue-89856.rs:3:4
+ |
+LL | fn take_str_maybe(_: Option<&str>) { }
+ | ^^^^^^^^^^^^^^ ---------------
+help: try converting the passed type into a `&str`
+ |
+LL | take_str_maybe(option_ref.map(|x| x.as_str()));
+ | ++++++++++++++++++++
+
+error[E0308]: mismatched types
+ --> $DIR/issue-89856.rs:16:20
+ |
+LL | take_str_maybe(option_ref_ref);
+ | -------------- ^^^^^^^^^^^^^^ expected `Option<&str>`, found `Option<&&String>`
+ | |
+ | arguments to this function are incorrect
+ |
+ = note: expected enum `Option<&str>`
+ found enum `Option<&&String>`
+note: function defined here
+ --> $DIR/issue-89856.rs:3:4
|
-LL | fn take_str_maybe(x: Option<&str>) -> Option<&str> { None }
+LL | fn take_str_maybe(_: Option<&str>) { }
| ^^^^^^^^^^^^^^ ---------------
help: try converting the passed type into a `&str`
|
-LL | take_str_maybe(option.map(|x| &**x));
- | ++++++++++++++
+LL | take_str_maybe(option_ref_ref.map(|x| x.as_str()));
+ | ++++++++++++++++++++
-error: aborting due to previous error
+error: aborting due to 3 previous errors
For more information about this error, try `rustc --explain E0308`.
--- /dev/null
+// edition:2018
+
+async fn hello() { //~ HELP try adding a return type
+ 0
+ //~^ ERROR [E0308]
+}
+
+async fn world() -> () {
+ 0
+ //~^ ERROR [E0308]
+}
+
+async fn suggest_await_in_async_fn_return() {
+ hello()
+ //~^ ERROR mismatched types [E0308]
+ //~| HELP consider `await`ing on the `Future`
+ //~| HELP consider using a semicolon here
+ //~| SUGGESTION .await
+}
+
+fn main() {}
--- /dev/null
+error[E0308]: mismatched types
+ --> $DIR/issue-90027-async-fn-return-suggestion.rs:4:5
+ |
+LL | async fn hello() {
+ | - help: try adding a return type: `-> i32`
+LL | 0
+ | ^ expected `()`, found integer
+
+error[E0308]: mismatched types
+ --> $DIR/issue-90027-async-fn-return-suggestion.rs:9:5
+ |
+LL | async fn world() -> () {
+ | -- expected `()` because of return type
+LL | 0
+ | ^ expected `()`, found integer
+
+error[E0308]: mismatched types
+ --> $DIR/issue-90027-async-fn-return-suggestion.rs:14:5
+ |
+LL | hello()
+ | ^^^^^^^ expected `()`, found opaque type
+ |
+ = note: expected unit type `()`
+ found opaque type `impl Future<Output = ()>`
+help: consider `await`ing on the `Future`
+ |
+LL | hello().await
+ | ++++++
+help: consider using a semicolon here
+ |
+LL | hello();
+ | +
+
+error: aborting due to 3 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
--- /dev/null
+// compile-flags: --crate-type=lib
+// check-pass
+
+// Make sure we don't pass inference variables to uninhabitedness checks in borrowck
+
+struct Command<'s> {
+ session: &'s (),
+ imp: std::convert::Infallible,
+}
+
+fn command(_: &()) -> Command<'_> {
+ unreachable!()
+}
+
+fn with_session<'s>(a: &std::process::Command, b: &'s ()) -> Command<'s> {
+ a.get_program();
+ command(b)
+}
--- /dev/null
+// check-fail
+
+#![feature(auto_traits)]
+#![deny(where_clauses_object_safety)]
+
+auto trait AutoTrait {}
+
+trait Trait {
+ fn static_lifetime_bound(&self) where Self: 'static {}
+
+ fn arg_lifetime_bound<'a>(&self, _arg: &'a ()) where Self: 'a {}
+
+ fn autotrait_bound(&self) where Self: AutoTrait {}
+}
+
+impl Trait for () {}
+
+fn main() {
+ let trait_object = &() as &dyn Trait;
+ trait_object.static_lifetime_bound();
+ trait_object.arg_lifetime_bound(&());
+ trait_object.autotrait_bound(); //~ ERROR: the trait bound `dyn Trait: AutoTrait` is not satisfied
+}
--- /dev/null
+error[E0277]: the trait bound `dyn Trait: AutoTrait` is not satisfied
+ --> $DIR/self-in-where-clause-allowed.rs:22:18
+ |
+LL | trait_object.autotrait_bound();
+ | ^^^^^^^^^^^^^^^ the trait `AutoTrait` is not implemented for `dyn Trait`
+ |
+note: required by a bound in `Trait::autotrait_bound`
+ --> $DIR/self-in-where-clause-allowed.rs:13:43
+ |
+LL | fn autotrait_bound(&self) where Self: AutoTrait {}
+ | ^^^^^^^^^ required by this bound in `Trait::autotrait_bound`
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0277`.
[autolabel."WG-trait-system-refactor"]
trigger_files = [
- "compiler/rustc_trait_selection/src/solve"
+ "compiler/rustc_trait_selection/src/solve",
+ "compiler/rustc_middle/src/traits/solve.rs"
]
[notify-zulip."I-prioritize"]
[mentions."compiler/rustc_error_messages"]
message = "`rustc_error_messages` was changed"
-cc = ["@davidtwco", "@compiler-errors", "@JohnTitor", "@estebank", "@TaKO8Ki"]
+cc = ["@davidtwco", "@compiler-errors", "@JohnTitor", "@TaKO8Ki"]
[mentions."compiler/rustc_errors/src/translation.rs"]
message = "`rustc_errors::translation` was changed"
-cc = ["@davidtwco", "@compiler-errors", "@JohnTitor", "@estebank", "@TaKO8Ki"]
+cc = ["@davidtwco", "@compiler-errors", "@JohnTitor", "@TaKO8Ki"]
[mentions."compiler/rustc_macros/src/diagnostics"]
message = "`rustc_macros::diagnostics` was changed"
-cc = ["@davidtwco", "@compiler-errors", "@JohnTitor", "@estebank", "@TaKO8Ki"]
+cc = ["@davidtwco", "@compiler-errors", "@JohnTitor", "@TaKO8Ki"]
[mentions."compiler/rustc_target/src/spec"]
message = """
[mentions."src/doc/style-guide"]
cc = ["@rust-lang/style"]
+[mentions."Cargo.lock"]
+message = """
+These commits modify the `Cargo.lock` file. Random changes to `Cargo.lock` can be introduced when switching branches and rebasing PRs.
+This was probably unintentional and should be reverted before this PR is merged.
+
+If this was intentional then you can ignore this comment.
+"""
+
[assign]
warn_non_default_branch = true
contributing_url = "https://rustc-dev-guide.rust-lang.org/contributing.html"
[assign.adhoc_groups]
compiler-team = [
"@cjgillot",
- "@estebank",
"@petrochenkov",
"@davidtwco",
"@oli-obk",
diagnostics = [
"@compiler-errors",
"@davidtwco",
- "@estebank",
"@oli-obk",
"@TaKO8Ki",
]
parser = [
"@davidtwco",
- "@estebank",
"@nnethercote",
"@petrochenkov",
]