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" }
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>,
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.*
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
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" }
|| 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,
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"] }
// 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" }
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()
+ );
}
}
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 {
ExternLocationNotFile(Symbol, PathBuf),
MultipleCandidates(Symbol, CrateFlavor, Vec<PathBuf>),
SymbolConflictsCurrent(Symbol),
- SymbolConflictsOthers(Symbol),
StableCrateIdCollision(Symbol, Symbol),
DlOpen(String),
DlSym(String),
CrateError::SymbolConflictsCurrent(root_name) => {
sess.emit_err(errors::SymbolConflictsCurrent { span, crate_name: root_name });
}
- CrateError::SymbolConflictsOthers(root_name) => {
- sess.emit_err(errors::SymbolConflictsOthers { span, crate_name: root_name });
- }
CrateError::StableCrateIdCollision(crate_name0, crate_name1) => {
sess.emit_err(errors::StableCrateIdCollision { span, crate_name0, crate_name1 });
}
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,
}
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: {:?}) => {:?}",
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));
}
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 {
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,
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"
// 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(),
}
}
+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" }
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
///
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
///
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)
/// 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();
--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;
-}
{%- 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 = move ((_1 as Some).0: i32); // scope 1 at $DIR/simple_option_map_e2e.rs:7:14: 7:15
StorageLive(_5); // scope 2 at $DIR/simple_option_map_e2e.rs:7:25: 7:29
- 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
}
}
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[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`.
--- /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
+
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`.
| ^^^^ 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`.