Turn projections into copies in CopyProp.
The current implementation can leave behind projections that are moved out several times.
This PR widens the check to turn such moves into copies: a move out of a projection of a copy is equivalent to a copy of the original projection.
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>
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",
"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",
#[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.
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" }
predicates
.iter()
.map(|(param, constraint)| (param.name.as_str(), &**constraint, None)),
+ None,
);
}
}
}
}
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 => {}
}
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"
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(
- &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(())
- }
-
- /// Writes the discriminant of the given variant.
- #[instrument(skip(self), level = "debug")]
+ /// Writes the aggregate to the destination.
+ #[instrument(skip(self), level = "trace")]
pub fn write_aggregate(
&mut self,
kind: &mir::AggregateKind<'tcx>,
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() })
}
}
¶m_ty.name.as_str(),
&constraint,
None,
+ None,
);
}
}
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 })
}
}
E0520: include_str!("./error_codes/E0520.md"),
E0521: include_str!("./error_codes/E0521.md"),
E0522: include_str!("./error_codes/E0522.md"),
+E0523: include_str!("./error_codes/E0523.md"),
E0524: include_str!("./error_codes/E0524.md"),
E0525: include_str!("./error_codes/E0525.md"),
E0527: include_str!("./error_codes/E0527.md"),
// E0488, // lifetime of variable does not enclose its declaration
// E0489, // type/lifetime parameter not in scope here
// E0490, // removed: unreachable
- E0523, // two dependencies have same (crate-name, disambiguator) but different SVH
// E0526, // shuffle indices are not constant
// E0540, // multiple rustc_deprecated attributes
// E0548, // replaced with a generic attribute input check
The compiler found multiple library files with the requested crate name.
+```compile_fail
+// aux-build:crateresolve-1.rs
+// aux-build:crateresolve-2.rs
+// aux-build:crateresolve-3.rs
+
+extern crate crateresolve;
+//~^ ERROR multiple candidates for `rlib` dependency `crateresolve` found
+
+fn main() {}
+```
+
This error can occur in several different cases -- for example, when using
`extern crate` or passing `--extern` options without crate paths. It can also be
caused by caching issues with the build directory, in which case `cargo clean`
may help.
+
+In the above example, there are three different library files, all of which
+define the same crate name. Without providing a full path, there is no way for
+the compiler to know which crate it should use.
--- /dev/null
+#### Note: this error code is no longer emitted by the compiler.
+
+The compiler found multiple library files with the requested crate name.
+
+```compile_fail
+// aux-build:crateresolve-1.rs
+// aux-build:crateresolve-2.rs
+// aux-build:crateresolve-3.rs
+
+extern crate crateresolve;
+//~^ ERROR multiple candidates for `rlib` dependency `crateresolve` found
+
+fn main() {}
+```
+
+This error can occur in several different cases -- for example, when using
+`extern crate` or passing `--extern` options without crate paths. It can also be
+caused by caching issues with the build directory, in which case `cargo clean`
+may help.
+
+In the above example, there are three different library files, all of which
+define the same crate name. Without providing a full path, there is no way for
+the compiler to know which crate it should use.
+
+*Note that E0523 has been merged into E0464.*
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
.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
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
#![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)
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" }
bounds.iter().map(|(param, constraint, def_id)| {
(param.as_str(), constraint.as_str(), *def_id)
}),
+ None,
);
err.emit();
}
|| 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);
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 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,
}
}
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,
generics,
diag,
vec![(param.name.as_str(), "Clone", Some(clone_trait_did))].into_iter(),
+ None,
);
} else {
self.suggest_derive(diag, &[(trait_ref.to_predicate(self.tcx), None, None)]);
traits::ImplDerivedObligationCause {
derived,
impl_def_id,
+ impl_def_predicate_index: None,
span,
},
))
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"] }
(ty::Param(p), ty::Alias(ty::Projection, proj)) | (ty::Alias(ty::Projection, proj), ty::Param(p))
if tcx.def_kind(proj.def_id) != DefKind::ImplTraitPlaceholder =>
{
- let generics = tcx.generics_of(body_owner_def_id);
- let p_span = tcx.def_span(generics.type_param(p, tcx).def_id);
+ let p_def_id = tcx
+ .generics_of(body_owner_def_id)
+ .type_param(p, tcx)
+ .def_id;
+ let p_span = tcx.def_span(p_def_id);
if !sp.contains(p_span) {
diag.span_label(p_span, "this type parameter");
}
let hir = tcx.hir();
let mut note = true;
- if let Some(generics) = generics
- .type_param(p, tcx)
- .def_id
+ let parent = p_def_id
.as_local()
- .map(|id| hir.local_def_id_to_hir_id(id))
- .and_then(|id| tcx.hir().find_parent(id))
- .as_ref()
- .and_then(|node| node.generics())
+ .and_then(|id| {
+ let local_id = hir.local_def_id_to_hir_id(id);
+ let generics = tcx.hir().find_parent(local_id)?.generics()?;
+ Some((id, generics))
+ });
+ if let Some((local_id, generics)) = parent
{
// Synthesize the associated type restriction `Add<Output = Expected>`.
// FIXME: extract this logic for use in other diagnostics.
let (trait_ref, assoc_substs) = proj.trait_ref_and_own_substs(tcx);
- let path =
- tcx.def_path_str_with_substs(trait_ref.def_id, trait_ref.substs);
let item_name = tcx.item_name(proj.def_id);
let item_args = self.format_generic_args(assoc_substs);
- let path = if path.ends_with('>') {
- format!(
- "{}, {}{} = {}>",
- &path[..path.len() - 1],
- item_name,
- item_args,
- p
- )
+ // Here, we try to see if there's an existing
+ // trait implementation that matches the one that
+ // we're suggesting to restrict. If so, find the
+ // "end", whether it be at the end of the trait
+ // or the end of the generic arguments.
+ let mut matching_span = None;
+ let mut matched_end_of_args = false;
+ for bound in generics.bounds_for_param(local_id) {
+ let potential_spans = bound
+ .bounds
+ .iter()
+ .find_map(|bound| {
+ let bound_trait_path = bound.trait_ref()?.path;
+ let def_id = bound_trait_path.res.opt_def_id()?;
+ let generic_args = bound_trait_path.segments.iter().last().map(|path| path.args());
+ (def_id == trait_ref.def_id).then_some((bound_trait_path.span, generic_args))
+ });
+
+ if let Some((end_of_trait, end_of_args)) = potential_spans {
+ let args_span = end_of_args.and_then(|args| args.span());
+ matched_end_of_args = args_span.is_some();
+ matching_span = args_span
+ .or_else(|| Some(end_of_trait))
+ .map(|span| span.shrink_to_hi());
+ break;
+ }
+ }
+
+ if matched_end_of_args {
+ // Append suggestion to the end of our args
+ let path = format!(", {}{} = {}",item_name, item_args, p);
+ note = !suggest_constraining_type_param(
+ tcx,
+ generics,
+ diag,
+ &format!("{}", proj.self_ty()),
+ &path,
+ None,
+ matching_span,
+ );
} else {
- format!("{}<{}{} = {}>", path, item_name, item_args, p)
- };
- note = !suggest_constraining_type_param(
- tcx,
- generics,
- diag,
- &format!("{}", proj.self_ty()),
- &path,
- None,
- );
+ // Suggest adding a bound to an existing trait
+ // or if the trait doesn't exist, add the trait
+ // and the suggested bounds.
+ let path = format!("<{}{} = {}>", item_name, item_args, p);
+ note = !suggest_constraining_type_param(
+ tcx,
+ generics,
+ diag,
+ &format!("{}", proj.self_ty()),
+ &path,
+ None,
+ matching_span,
+ );
+ }
}
if note {
diag.note("you might be missing a type parameter or trait bound");
}
}
+ #[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,
},
);
}
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)
//! 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};
for (_, other) in self.cstore.iter_crate_data() {
// Same stable crate id but different SVH
if other.stable_crate_id() == root.stable_crate_id() && other.hash() != root.hash() {
- return Err(CrateError::SymbolConflictsOthers(root.name()));
+ bug!(
+ "Previously returned E0523 here. \
+ See https://github.com/rust-lang/rust/pull/100599 for additional discussion.\
+ root.name() = {}.",
+ root.name()
+ );
}
}
// 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(),
pub crate_name: Symbol,
}
-#[derive(Diagnostic)]
-#[diag(metadata_symbol_conflicts_others, code = "E0523")]
-pub struct SymbolConflictsOthers {
- #[primary_span]
- pub span: Span,
- pub crate_name: Symbol,
-}
-
#[derive(Diagnostic)]
#[diag(metadata_stable_crate_id_collision)]
pub struct StableCrateIdCollision {
//! 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};
ExternLocationNotFile(Symbol, PathBuf),
MultipleCandidates(Symbol, CrateFlavor, Vec<PathBuf>),
SymbolConflictsCurrent(Symbol),
- SymbolConflictsOthers(Symbol),
StableCrateIdCollision(Symbol, Symbol),
DlOpen(String),
DlSym(String),
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 });
- }
- CrateError::SymbolConflictsOthers(root_name) => {
- sess.emit_err(SymbolConflictsOthers { span, crate_name: root_name });
+ sess.emit_err(errors::SymbolConflictsCurrent { 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.
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,
}
}
/// Suggest restricting a type param with a new bound.
+///
+/// If `span_to_replace` is provided, then that span will be replaced with the
+/// `constraint`. If one wasn't provided, then the full bound will be suggested.
pub fn suggest_constraining_type_param(
tcx: TyCtxt<'_>,
generics: &hir::Generics<'_>,
param_name: &str,
constraint: &str,
def_id: Option<DefId>,
+ span_to_replace: Option<Span>,
) -> bool {
suggest_constraining_type_params(
tcx,
generics,
err,
[(param_name, constraint, def_id)].into_iter(),
+ span_to_replace,
)
}
generics: &hir::Generics<'_>,
err: &mut Diagnostic,
param_names_and_constraints: impl Iterator<Item = (&'a str, &'a str, Option<DefId>)>,
+ span_to_replace: Option<Span>,
) -> bool {
let mut grouped = FxHashMap::default();
param_names_and_constraints.for_each(|(param_name, constraint, def_id)| {
let mut suggest_restrict = |span, bound_list_non_empty| {
suggestions.push((
span,
- if bound_list_non_empty {
+ if span_to_replace.is_some() {
+ constraint.clone()
+ } else if bound_list_non_empty {
format!(" + {}", constraint)
} else {
format!(" {}", constraint)
))
};
+ if let Some(span) = span_to_replace {
+ suggest_restrict(span, true);
+ continue;
+ }
+
// When the type parameter has been provided bounds
//
// Message:
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: {:?}) => {:?}",
}
}
-/// 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()) {
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" }
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>,
}
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));
}
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 {
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 {
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),
});
// 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)
.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,
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
}
//! 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,
//! 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"
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);
// 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 {
&& 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 {
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" }
¶m_name,
&constraint,
Some(trait_pred.def_id()),
+ None,
) {
return;
}
param.name.as_str(),
"Clone",
Some(clone_trait),
+ None,
);
}
err.span_suggestion_verbose(
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 {
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;
}
///
/// 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
///
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
//! 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 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)
} else {
let mut br_with_padding = String::with_capacity(6 * indent + 28);
br_with_padding.push_str("<br>");
- for _ in 0..indent + 4 {
+
+ 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 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);
+ clause.truncate(clause.len() - "where ".len());
+
write!(clause, "<span class=\"where\">where{where_preds}</span>")?;
clause
}
/// 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>");
.item-spacer {
width: 100%;
height: 12px;
+ display: block;
}
.out-of-band > span.since {
--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;
-}
}
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;
}
}
{%- for theme in themes -%}
<link rel="stylesheet" disabled href="{{page.root_path|safe}}{{theme}}{{page.resource_suffix}}.css"> {#- -#}
{%- endfor -%}
+ {%- if !layout.default_settings.is_empty() -%}
<script id="default-settings" {# -#}
{% for (k, v) in layout.default_settings %}
data-{{k}}="{{v}}"
{%- endfor -%}
></script> {#- -#}
+ {%- endif -%}
<script src="{{static_root_path|safe}}{{files.storage_js}}"></script> {#- -#}
{%- if page.css_class.contains("crate") -%}
<script defer src="{{page.root_path|safe}}crates{{page.resource_suffix}}.js"></script> {#- -#}
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,
["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) });
+ });
+}
// Error codes that don't yet have a UI test. This list will eventually be removed.
const IGNORE_UI_TEST_CHECK: &[&str] =
- &["E0461", "E0465", "E0476", "E0514", "E0523", "E0554", "E0640", "E0717", "E0729"];
+ &["E0461", "E0465", "E0476", "E0514", "E0554", "E0640", "E0717", "E0729"];
macro_rules! verbose_print {
($verbose:expr, $($fmt:tt)*) => {
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
}
let mut _6: u8; // in scope 0 at $DIR/const_debuginfo.rs:+4:15: +4:16
let mut _7: u8; // in scope 0 at $DIR/const_debuginfo.rs:+4:19: +4:20
let mut _8: u8; // in scope 0 at $DIR/const_debuginfo.rs:+4:23: +4:24
- let mut _14: u32; // in scope 0 at $DIR/const_debuginfo.rs:+13:13: +13:16
- let mut _15: u32; // in scope 0 at $DIR/const_debuginfo.rs:+13:19: +13:22
+ let mut _12: u32; // in scope 0 at $DIR/const_debuginfo.rs:+13:13: +13:16
+ let mut _13: u32; // in scope 0 at $DIR/const_debuginfo.rs:+13:19: +13:22
scope 1 {
- debug x => _1; // in scope 1 at $DIR/const_debuginfo.rs:+1:9: +1:10
+ debug x => const 1_u8; // in scope 1 at $DIR/const_debuginfo.rs:+1:9: +1:10
scope 5 {
- debug s => _9; // in scope 5 at $DIR/const_debuginfo.rs:+6:9: +6:10
+ debug s => const "hello, world!"; // in scope 5 at $DIR/const_debuginfo.rs:+6:9: +6:10
- let _10: (bool, bool, u32); // in scope 5 at $DIR/const_debuginfo.rs:+8:9: +8:10
+ let _14: bool; // in scope 5 at $DIR/const_debuginfo.rs:+8:9: +8:10
+ let _15: bool; // in scope 5 at $DIR/const_debuginfo.rs:+8:9: +8:10
+ let _16: u32; // in scope 5 at $DIR/const_debuginfo.rs:+8:9: +8:10
scope 6 {
- debug f => _10; // in scope 6 at $DIR/const_debuginfo.rs:+8:9: +8:10
- let _11: std::option::Option<u16>; // in scope 6 at $DIR/const_debuginfo.rs:+10:9: +10:10
+ debug f => (bool, bool, u32){ .0 => _14, .1 => _15, .2 => _16, }; // in scope 6 at $DIR/const_debuginfo.rs:+8:9: +8:10
+ let _10: std::option::Option<u16>; // in scope 6 at $DIR/const_debuginfo.rs:+10:9: +10:10
scope 7 {
- debug o => _11; // in scope 7 at $DIR/const_debuginfo.rs:+10:9: +10:10
- let _12: Point; // in scope 7 at $DIR/const_debuginfo.rs:+12:9: +12:10
+ debug o => _10; // in scope 7 at $DIR/const_debuginfo.rs:+10:9: +10:10
+ let _17: u32; // in scope 7 at $DIR/const_debuginfo.rs:+12:9: +12:10
+ let _18: u32; // in scope 7 at $DIR/const_debuginfo.rs:+12:9: +12:10
scope 8 {
- debug p => _12; // in scope 8 at $DIR/const_debuginfo.rs:+12:9: +12:10
- let _13: u32; // in scope 8 at $DIR/const_debuginfo.rs:+13:9: +13:10
+ debug p => Point{ .0 => _17, .1 => _18, }; // in scope 8 at $DIR/const_debuginfo.rs:+12:9: +12:10
+ let _11: u32; // in scope 8 at $DIR/const_debuginfo.rs:+13:9: +13:10
scope 9 {
-- debug a => _13; // in scope 9 at $DIR/const_debuginfo.rs:+13:9: +13:10
+- debug a => _11; // in scope 9 at $DIR/const_debuginfo.rs:+13:9: +13:10
+ debug a => const 64_u32; // in scope 9 at $DIR/const_debuginfo.rs:+13:9: +13:10
}
}
// mir::Constant
// + span: $DIR/const_debuginfo.rs:14:13: 14:28
// + literal: Const { ty: &str, val: Value(Slice(..)) }
- StorageLive(_10); // scope 5 at $DIR/const_debuginfo.rs:+8:9: +8:10
- _10 = (const true, const false, const 123_u32); // scope 5 at $DIR/const_debuginfo.rs:+8:13: +8:34
- StorageLive(_11); // scope 6 at $DIR/const_debuginfo.rs:+10:9: +10:10
- _11 = Option::<u16>::Some(const 99_u16); // scope 6 at $DIR/const_debuginfo.rs:+10:13: +10:24
- StorageLive(_12); // scope 7 at $DIR/const_debuginfo.rs:+12:9: +12:10
- _12 = Point { x: const 32_u32, y: const 32_u32 }; // scope 7 at $DIR/const_debuginfo.rs:+12:13: +12:35
- StorageLive(_13); // scope 8 at $DIR/const_debuginfo.rs:+13:9: +13:10
- StorageLive(_14); // scope 8 at $DIR/const_debuginfo.rs:+13:13: +13:16
- _14 = const 32_u32; // scope 8 at $DIR/const_debuginfo.rs:+13:13: +13:16
- StorageLive(_15); // scope 8 at $DIR/const_debuginfo.rs:+13:19: +13:22
- _15 = const 32_u32; // scope 8 at $DIR/const_debuginfo.rs:+13:19: +13:22
- _13 = const 64_u32; // scope 8 at $DIR/const_debuginfo.rs:+13:13: +13:22
- StorageDead(_15); // scope 8 at $DIR/const_debuginfo.rs:+13:21: +13:22
- StorageDead(_14); // scope 8 at $DIR/const_debuginfo.rs:+13:21: +13:22
- StorageDead(_13); // scope 8 at $DIR/const_debuginfo.rs:+14:1: +14:2
- StorageDead(_12); // scope 7 at $DIR/const_debuginfo.rs:+14:1: +14:2
- StorageDead(_11); // scope 6 at $DIR/const_debuginfo.rs:+14:1: +14:2
- StorageDead(_10); // scope 5 at $DIR/const_debuginfo.rs:+14:1: +14:2
+ 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
+ _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
+ _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
+ _11 = const 64_u32; // scope 8 at $DIR/const_debuginfo.rs:+13:13: +13:22
+ StorageDead(_11); // scope 8 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
+ StorageDead(_16); // scope 5 at $DIR/const_debuginfo.rs:+14:1: +14:2
StorageDead(_9); // scope 4 at $DIR/const_debuginfo.rs:+14:1: +14:2
StorageDead(_4); // scope 3 at $DIR/const_debuginfo.rs:+14:1: +14:2
return; // scope 0 at $DIR/const_debuginfo.rs:+14:2: +14:2
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
-- _1 = (const 42_i32, const 43_i32); // scope 0 at $DIR/mutable_variable_aggregate.rs:+1:17: +1:25
-+ _1 = const (42_i32, 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
}
}
}
_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
fn main() -> () {
let mut _0: (); // return place in scope 0 at $DIR/mutable_variable_unprop_assign.rs:+0:11: +0:11
let _1: i32; // in scope 0 at $DIR/mutable_variable_unprop_assign.rs:+1:9: +1:10
- let mut _3: i32; // in scope 0 at $DIR/mutable_variable_unprop_assign.rs:+3:11: +3:12
+ let mut _2: i32; // in scope 0 at $DIR/mutable_variable_unprop_assign.rs:+3:11: +3:12
scope 1 {
debug a => _1; // in scope 1 at $DIR/mutable_variable_unprop_assign.rs:+1:9: +1:10
- let mut _2: (i32, i32); // in scope 1 at $DIR/mutable_variable_unprop_assign.rs:+2:9: +2:14
+ let mut _5: i32; // in scope 1 at $DIR/mutable_variable_unprop_assign.rs:+2:9: +2:14
+ let mut _6: i32; // in scope 1 at $DIR/mutable_variable_unprop_assign.rs:+2:9: +2:14
scope 2 {
- debug x => _2; // in scope 2 at $DIR/mutable_variable_unprop_assign.rs:+2:9: +2:14
- let _4: i32; // in scope 2 at $DIR/mutable_variable_unprop_assign.rs:+4:9: +4:10
+ debug x => (i32, i32){ .0 => _5, .1 => _6, }; // in scope 2 at $DIR/mutable_variable_unprop_assign.rs:+2:9: +2:14
+ let _3: i32; // in scope 2 at $DIR/mutable_variable_unprop_assign.rs:+4:9: +4:10
scope 3 {
- debug y => _4; // in scope 3 at $DIR/mutable_variable_unprop_assign.rs:+4:9: +4:10
- let _5: i32; // in scope 3 at $DIR/mutable_variable_unprop_assign.rs:+5:9: +5:10
+ 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 => _5; // in scope 4 at $DIR/mutable_variable_unprop_assign.rs:+5:9: +5:10
}
}
bb1: {
- StorageLive(_2); // scope 1 at $DIR/mutable_variable_unprop_assign.rs:+2:9: +2:14
-- _2 = (const 1_i32, const 2_i32); // scope 1 at $DIR/mutable_variable_unprop_assign.rs:+2:29: +2:35
-+ _2 = const (1_i32, 2_i32); // scope 1 at $DIR/mutable_variable_unprop_assign.rs:+2:29: +2:35
- StorageLive(_3); // scope 2 at $DIR/mutable_variable_unprop_assign.rs:+3:11: +3:12
- _3 = _1; // scope 2 at $DIR/mutable_variable_unprop_assign.rs:+3:11: +3:12
- (_2.1: i32) = move _3; // scope 2 at $DIR/mutable_variable_unprop_assign.rs:+3:5: +3:12
- StorageDead(_3); // scope 2 at $DIR/mutable_variable_unprop_assign.rs:+3:11: +3:12
- StorageLive(_4); // scope 2 at $DIR/mutable_variable_unprop_assign.rs:+4:9: +4:10
- _4 = (_2.1: i32); // scope 2 at $DIR/mutable_variable_unprop_assign.rs:+4:13: +4:16
- StorageLive(_5); // scope 3 at $DIR/mutable_variable_unprop_assign.rs:+5:9: +5:10
-- _5 = (_2.0: i32); // scope 3 at $DIR/mutable_variable_unprop_assign.rs:+5:13: +5:16
-+ _5 = const 1_i32; // scope 3 at $DIR/mutable_variable_unprop_assign.rs:+5:13: +5:16
- StorageDead(_5); // scope 3 at $DIR/mutable_variable_unprop_assign.rs:+6:1: +6:2
- StorageDead(_4); // scope 2 at $DIR/mutable_variable_unprop_assign.rs:+6:1: +6:2
- StorageDead(_2); // scope 1 at $DIR/mutable_variable_unprop_assign.rs:+6:1: +6:2
+ StorageLive(_6); // scope 1 at $DIR/mutable_variable_unprop_assign.rs:+2:9: +2:14
+ _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
+ _2 = _1; // scope 2 at $DIR/mutable_variable_unprop_assign.rs:+3:11: +3:12
+ _6 = move _2; // scope 2 at $DIR/mutable_variable_unprop_assign.rs:+3:5: +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
+ StorageDead(_3); // scope 2 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
}
let _5: usize; // in scope 0 at $DIR/optimizes_into_variable.rs:+2:32: +2:33
let mut _6: usize; // in scope 0 at $DIR/optimizes_into_variable.rs:+2:13: +2:34
let mut _7: bool; // in scope 0 at $DIR/optimizes_into_variable.rs:+2:13: +2:34
- let mut _9: Point; // in scope 0 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
+ let mut _9: 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 _3: i32; // in scope 1 at $DIR/optimizes_into_variable.rs:+2:9: +2:10
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
- _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
-+ _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
+ _9 = const 42_u32; // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
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
let _5: usize; // in scope 0 at $DIR/optimizes_into_variable.rs:+2:32: +2:33
let mut _6: usize; // in scope 0 at $DIR/optimizes_into_variable.rs:+2:13: +2:34
let mut _7: bool; // in scope 0 at $DIR/optimizes_into_variable.rs:+2:13: +2:34
- let mut _9: Point; // in scope 0 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
+ let mut _9: 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 _3: i32; // in scope 1 at $DIR/optimizes_into_variable.rs:+2:9: +2:10
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
- _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
-+ _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
+ _9 = const 42_u32; // scope 2 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
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
let mut _6: usize; // in scope 0 at $DIR/optimizes_into_variable.rs:+2:13: +2:34
let mut _7: bool; // in scope 0 at $DIR/optimizes_into_variable.rs:+2:13: +2:34
let mut _9: Point; // in scope 0 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
++ let mut _10: u32; // in scope 0 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
++ let mut _11: 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 _3: i32; // in scope 1 at $DIR/optimizes_into_variable.rs:+2:9: +2:10
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
- _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(_9); // 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
++ 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
let mut _6: usize; // in scope 0 at $DIR/optimizes_into_variable.rs:+2:13: +2:34
let mut _7: bool; // in scope 0 at $DIR/optimizes_into_variable.rs:+2:13: +2:34
let mut _9: Point; // in scope 0 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
++ let mut _10: u32; // in scope 0 at $DIR/optimizes_into_variable.rs:+3:13: +3:36
++ let mut _11: 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 _3: i32; // in scope 1 at $DIR/optimizes_into_variable.rs:+2:9: +2:10
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
- _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(_9); // 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
++ 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
debug x => _1; // in scope 0 at $DIR/simple_option_map_e2e.rs:+0:14: +0:15
let mut _0: std::option::Option<i32>; // return place in scope 0 at $DIR/simple_option_map_e2e.rs:+0:33: +0:44
let mut _2: [closure@$DIR/simple_option_map_e2e.rs:14:12: 14:15]; // in scope 0 at $DIR/simple_option_map_e2e.rs:+1:12: +1:21
- let mut _7: i32; // in scope 0 at $DIR/simple_option_map_e2e.rs:7:25: 7:29
scope 1 (inlined map::<i32, i32, [closure@$DIR/simple_option_map_e2e.rs:14:12: 14:15]>) { // at $DIR/simple_option_map_e2e.rs:14:5: 14:22
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 _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
- let mut _6: (i32,); // in scope 1 at $DIR/simple_option_map_e2e.rs:7:25: 7:29
scope 2 {
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 => _7; // 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
}
}
}
bb0: {
StorageLive(_2); // scope 0 at $DIR/simple_option_map_e2e.rs:+1:12: +1:21
- StorageLive(_4); // scope 0 at $DIR/simple_option_map_e2e.rs:+1:5: +1:22
_3 = discriminant(_1); // scope 1 at $DIR/simple_option_map_e2e.rs:6:11: 6:14
switchInt(move _3) -> [0: bb1, 1: bb3, otherwise: bb2]; // scope 1 at $DIR/simple_option_map_e2e.rs:6:5: 6:14
}
bb3: {
_4 = ((_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
- StorageLive(_6); // scope 2 at $DIR/simple_option_map_e2e.rs:7:25: 7:29
- _6 = (move _4,); // scope 2 at $DIR/simple_option_map_e2e.rs:7:25: 7:29
- StorageLive(_7); // scope 2 at $DIR/simple_option_map_e2e.rs:7:25: 7:29
- _7 = move (_6.0: i32); // scope 2 at $DIR/simple_option_map_e2e.rs:7:25: 7:29
- _5 = Add(_7, const 1_i32); // scope 3 at $DIR/simple_option_map_e2e.rs:+1:16: +1:21
- StorageDead(_7); // scope 2 at $DIR/simple_option_map_e2e.rs:7:25: 7:29
- StorageDead(_6); // scope 2 at $DIR/simple_option_map_e2e.rs:7:28: 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
}
bb4: {
- StorageDead(_4); // scope 0 at $DIR/simple_option_map_e2e.rs:+1:5: +1:22
StorageDead(_2); // scope 0 at $DIR/simple_option_map_e2e.rs:+1:21: +1:22
return; // scope 0 at $DIR/simple_option_map_e2e.rs:+2:2: +2:2
}
--- /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(_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>) }
}
_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>) }
}
let mut _5: Foo; // in scope 0 at $DIR/sroa.rs:+1:30: +1:70
let mut _6: (); // in scope 0 at $DIR/sroa.rs:+1:45: +1:47
let mut _7: std::option::Option<isize>; // in scope 0 at $DIR/sroa.rs:+1:60: +1:68
++ let mut _8: u8; // in scope 0 at $DIR/sroa.rs:+1:30: +1:70
++ let mut _9: (); // in scope 0 at $DIR/sroa.rs:+1:30: +1:70
++ let mut _10: &str; // in scope 0 at $DIR/sroa.rs:+1:30: +1:70
++ let mut _11: std::option::Option<isize>; // in scope 0 at $DIR/sroa.rs:+1:30: +1:70
scope 1 {
debug a => _1; // in scope 1 at $DIR/sroa.rs:+1:15: +1:16
debug b => _2; // in scope 1 at $DIR/sroa.rs:+1:18: +1:19
}
bb0: {
- StorageLive(_5); // scope 0 at $DIR/sroa.rs:+1:30: +1:70
+- StorageLive(_5); // scope 0 at $DIR/sroa.rs:+1:30: +1:70
++ StorageLive(_8); // scope 0 at $DIR/sroa.rs:+1:30: +1:70
++ 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
_6 = (); // scope 0 at $DIR/sroa.rs:+1:45: +1:47
StorageLive(_7); // scope 0 at $DIR/sroa.rs:+1:60: +1:68
_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
+- _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(..)) }
++ _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
- _1 = (_5.0: u8); // scope 0 at $DIR/sroa.rs:+1:15: +1:16
+- _1 = (_5.0: u8); // scope 0 at $DIR/sroa.rs:+1:15: +1:16
++ _1 = _8; // scope 0 at $DIR/sroa.rs:+1:15: +1:16
StorageLive(_2); // scope 0 at $DIR/sroa.rs:+1:18: +1:19
- _2 = (_5.1: ()); // scope 0 at $DIR/sroa.rs:+1:18: +1:19
+- _2 = (_5.1: ()); // scope 0 at $DIR/sroa.rs:+1:18: +1:19
++ _2 = _9; // scope 0 at $DIR/sroa.rs:+1:18: +1:19
StorageLive(_3); // scope 0 at $DIR/sroa.rs:+1:21: +1:22
- _3 = (_5.2: &str); // scope 0 at $DIR/sroa.rs:+1:21: +1:22
+- _3 = (_5.2: &str); // scope 0 at $DIR/sroa.rs:+1:21: +1:22
++ _3 = _10; // scope 0 at $DIR/sroa.rs:+1:21: +1:22
StorageLive(_4); // scope 0 at $DIR/sroa.rs:+1:24: +1:25
- _4 = (_5.3: std::option::Option<isize>); // scope 0 at $DIR/sroa.rs:+1:24: +1:25
- StorageDead(_5); // scope 0 at $DIR/sroa.rs:+1:70: +1:71
+- _4 = (_5.3: std::option::Option<isize>); // scope 0 at $DIR/sroa.rs:+1:24: +1:25
+- StorageDead(_5); // scope 0 at $DIR/sroa.rs:+1:70: +1:71
++ _4 = _11; // scope 0 at $DIR/sroa.rs:+1:24: +1:25
++ StorageDead(_8); // scope 0 at $DIR/sroa.rs:+1:70: +1:71
++ 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
let mut _0: f32; // return place in scope 0 at $DIR/sroa.rs:+0:27: +0:30
let mut _2: structs::U; // in scope 0 at $DIR/sroa.rs:+6:5: +6:21
let mut _3: f32; // in scope 0 at $DIR/sroa.rs:+6:18: +6:19
++ let mut _4: usize; // in scope 0 at $DIR/sroa.rs:+6:5: +6:21
++ let mut _5: f32; // in scope 0 at $DIR/sroa.rs:+6:5: +6:21
bb0: {
- StorageLive(_2); // scope 0 at $DIR/sroa.rs:+6:5: +6:21
+- 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
- _2 = U { _foo: const 0_usize, a: move _3 }; // 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 = (_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
}
}
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' },
],
};
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>
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::*;
}
<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 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
| ^^^^^^^^^^ required by this bound in `UnsafeCopy`
help: consider further restricting this bound
|
-LL | impl<T: Copy + std::ops::Deref + Deref<Target = T>> UnsafeCopy<'_, T> for T {
- | +++++++++++++++++++
+LL | impl<T: Copy + std::ops::Deref<Target = T>> UnsafeCopy<'_, T> for T {
+ | ++++++++++++
error: aborting due to previous error
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 `Unresumed`: 0 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 `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 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 `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
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
+// 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`.
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
--- /dev/null
+// aux-build:crateresolve1-1.rs
+// aux-build:crateresolve1-2.rs
+// aux-build:crateresolve1-3.rs
+
+// normalize-stderr-test: "\.nll/" -> "/"
+// normalize-stderr-test: "\\\?\\" -> ""
+// normalize-stderr-test: "(lib)?crateresolve1-([123])\.[a-z]+" -> "libcrateresolve1-$2.somelib"
+
+// NOTE: This test is duplicated from `tests/ui/crate-loading/crateresolve1.rs`.
+
+extern crate crateresolve1;
+//~^ ERROR multiple candidates for `rlib` dependency `crateresolve1` found
+
+fn main() {}
--- /dev/null
+error[E0464]: multiple candidates for `rlib` dependency `crateresolve1` found
+ --> $DIR/E0523.rs:11:1
+ |
+LL | extern crate crateresolve1;
+ | ^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ |
+ = note: candidate #1: $TEST_BUILD_DIR/error-codes/E0523/auxiliary/libcrateresolve1-1.somelib
+ = note: candidate #2: $TEST_BUILD_DIR/error-codes/E0523/auxiliary/libcrateresolve1-2.somelib
+ = note: candidate #3: $TEST_BUILD_DIR/error-codes/E0523/auxiliary/libcrateresolve1-3.somelib
+
+error: aborting due to previous error
+
+For more information about this error, try `rustc --explain E0464`.
--- /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`.
| ^^^^^^^^^^ required by this bound in `UnsafeCopy::Item`
help: consider further restricting this bound
|
-LL | impl<T: Copy + std::ops::Deref + Deref<Target = T>> UnsafeCopy<T> for T {
- | +++++++++++++++++++
+LL | impl<T: Copy + std::ops::Deref<Target = T>> UnsafeCopy<T> for T {
+ | ++++++++++++
error: aborting due to previous error
struct A<B>(B);
-impl<B> Add for A<B> where B: Add + Add<Output = B> {
+impl<B> Add for A<B> where B: Add<Output = B> {
type Output = Self;
fn add(self, rhs: Self) -> Self {
struct C<B>(B);
-impl<B: Add + Add<Output = B>> Add for C<B> {
+impl<B: Add<Output = B>> Add for C<B> {
type Output = Self;
fn add(self, rhs: Self) -> Self {
struct E<B>(B);
-impl<B: Add + Add<Output = B>> Add for E<B> where B: Add<Output = B> {
+impl<B: Add<Output = B>> Add for E<B> where B: Add<Output = B> {
//~^ ERROR equality constraints are not yet supported in `where` clauses
type Output = Self;
| ^
help: consider further restricting this bound
|
-LL | impl<B> Add for A<B> where B: Add + Add<Output = B> {
- | +++++++++++++++++
+LL | impl<B> Add for A<B> where B: Add<Output = B> {
+ | ++++++++++++
error[E0308]: mismatched types
--> $DIR/missing-bounds.rs:21:14
| ^
help: consider further restricting this bound
|
-LL | impl<B: Add + Add<Output = B>> Add for C<B> {
- | +++++++++++++++++
+LL | impl<B: Add<Output = B>> Add for C<B> {
+ | ++++++++++++
error[E0369]: cannot add `B` to `B`
--> $DIR/missing-bounds.rs:31:21
| ^
help: consider further restricting this bound
|
-LL | impl<B: Add + Add<Output = B>> Add for E<B> where <B as Add>::Output = B {
- | +++++++++++++++++
+LL | impl<B: Add<Output = B>> Add for E<B> where <B as Add>::Output = B {
+ | ++++++++++++
error: aborting due to 5 previous errors
*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
+// 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
+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
+
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
--- /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`.
--- /dev/null
+pub trait TryAdd<Rhs = Self> {
+ type Error;
+ type Output;
+
+ fn try_add(self, rhs: Rhs) -> Result<Self::Output, Self::Error>;
+}
+
+impl<T: TryAdd> TryAdd for Option<T> {
+ type Error = <T as TryAdd>::Error;
+ type Output = Option<<T as TryAdd>::Output>;
+
+ fn try_add(self, rhs: Self) -> Result<Self::Output, Self::Error> {
+ Ok(self) //~ ERROR mismatched types
+ }
+}
+
+struct Other<A>(A);
+
+struct X;
+
+impl<T: TryAdd<Error = X>> TryAdd for Other<T> {
+ type Error = <T as TryAdd>::Error;
+ type Output = Other<<T as TryAdd>::Output>;
+
+ fn try_add(self, rhs: Self) -> Result<Self::Output, Self::Error> {
+ Ok(self) //~ ERROR mismatched types
+ }
+}
+
+fn main() {}
--- /dev/null
+error[E0308]: mismatched types
+ --> $DIR/restrict-existing-type-bounds.rs:13:12
+ |
+LL | impl<T: TryAdd> TryAdd for Option<T> {
+ | - this type parameter
+...
+LL | Ok(self)
+ | -- ^^^^ expected `Option<<T as TryAdd>::Output>`, found `Option<T>`
+ | |
+ | arguments to this enum variant are incorrect
+ |
+ = note: expected enum `Option<<T as TryAdd>::Output>`
+ found enum `Option<T>`
+help: the type constructed contains `Option<T>` due to the type of the argument passed
+ --> $DIR/restrict-existing-type-bounds.rs:13:9
+ |
+LL | Ok(self)
+ | ^^^----^
+ | |
+ | this argument influences the type of `Ok`
+note: tuple variant defined here
+ --> $SRC_DIR/core/src/result.rs:LL:COL
+help: consider further restricting this bound
+ |
+LL | impl<T: TryAdd<Output = T>> TryAdd for Option<T> {
+ | ++++++++++++
+
+error[E0308]: mismatched types
+ --> $DIR/restrict-existing-type-bounds.rs:26:12
+ |
+LL | impl<T: TryAdd<Error = X>> TryAdd for Other<T> {
+ | - this type parameter
+...
+LL | Ok(self)
+ | -- ^^^^ expected `Other<<T as TryAdd>::Output>`, found `Other<T>`
+ | |
+ | arguments to this enum variant are incorrect
+ |
+ = note: expected struct `Other<<T as TryAdd>::Output>`
+ found struct `Other<T>`
+help: the type constructed contains `Other<T>` due to the type of the argument passed
+ --> $DIR/restrict-existing-type-bounds.rs:26:9
+ |
+LL | Ok(self)
+ | ^^^----^
+ | |
+ | this argument influences the type of `Ok`
+note: tuple variant defined here
+ --> $SRC_DIR/core/src/result.rs:LL:COL
+help: consider further restricting this bound
+ |
+LL | impl<T: TryAdd<Error = X, Output = T>> TryAdd for Other<T> {
+ | ++++++++++++
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0308`.
| ^^^^ 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
|
--- /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`.
[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 = """
[assign.adhoc_groups]
compiler-team = [
"@cjgillot",
- "@estebank",
"@petrochenkov",
"@davidtwco",
"@oli-obk",
diagnostics = [
"@compiler-errors",
"@davidtwco",
- "@estebank",
"@oli-obk",
"@TaKO8Ki",
]
parser = [
"@davidtwco",
- "@estebank",
"@nnethercote",
"@petrochenkov",
]