Auto merge of #134499 - jieyouxu:rollup-zmaveur, r=jieyouxu
Rollup of 7 pull requests Successful merges: - #133702 (Variants::Single: do not use invalid VariantIdx for uninhabited enums) - #134427 (ci: remove duplicate task definition) - #134432 (Fix intra doc links not generated inside footnote definitions) - #134437 (reduce compiler `Assemble` complexity) - #134474 (Forbid overwriting types in typeck) - #134477 (move lint_unused_mut into sub-fn) - #134491 (Some destructor/drop related tweaks) r? `@ghost` `@rustbot` modify labels: rollup
This commit is contained in:
commit
3bf62ccc10
59 changed files with 447 additions and 368 deletions
|
@ -206,7 +206,7 @@ impl<'a, Ty> TyAndLayout<'a, Ty> {
|
||||||
let (mut result, mut total) = from_fields_at(*self, Size::ZERO)?;
|
let (mut result, mut total) = from_fields_at(*self, Size::ZERO)?;
|
||||||
|
|
||||||
match &self.variants {
|
match &self.variants {
|
||||||
abi::Variants::Single { .. } => {}
|
abi::Variants::Single { .. } | abi::Variants::Empty => {}
|
||||||
abi::Variants::Multiple { variants, .. } => {
|
abi::Variants::Multiple { variants, .. } => {
|
||||||
// Treat enum variants like union members.
|
// Treat enum variants like union members.
|
||||||
// HACK(eddyb) pretend the `enum` field (discriminant)
|
// HACK(eddyb) pretend the `enum` field (discriminant)
|
||||||
|
|
|
@ -213,8 +213,9 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
|
||||||
&self,
|
&self,
|
||||||
) -> LayoutData<FieldIdx, VariantIdx> {
|
) -> LayoutData<FieldIdx, VariantIdx> {
|
||||||
let dl = self.cx.data_layout();
|
let dl = self.cx.data_layout();
|
||||||
|
// This is also used for uninhabited enums, so we use `Variants::Empty`.
|
||||||
LayoutData {
|
LayoutData {
|
||||||
variants: Variants::Single { index: VariantIdx::new(0) },
|
variants: Variants::Empty,
|
||||||
fields: FieldsShape::Primitive,
|
fields: FieldsShape::Primitive,
|
||||||
backend_repr: BackendRepr::Uninhabited,
|
backend_repr: BackendRepr::Uninhabited,
|
||||||
largest_niche: None,
|
largest_niche: None,
|
||||||
|
@ -1004,8 +1005,8 @@ impl<Cx: HasDataLayout> LayoutCalculator<Cx> {
|
||||||
Variants::Multiple { tag, tag_encoding, tag_field, .. } => {
|
Variants::Multiple { tag, tag_encoding, tag_field, .. } => {
|
||||||
Variants::Multiple { tag, tag_encoding, tag_field, variants: best_layout.variants }
|
Variants::Multiple { tag, tag_encoding, tag_field, variants: best_layout.variants }
|
||||||
}
|
}
|
||||||
Variants::Single { .. } => {
|
Variants::Single { .. } | Variants::Empty => {
|
||||||
panic!("encountered a single-variant enum during multi-variant layout")
|
panic!("encountered a single-variant or empty enum during multi-variant layout")
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
Ok(best_layout.layout)
|
Ok(best_layout.layout)
|
||||||
|
|
|
@ -1504,10 +1504,12 @@ impl BackendRepr {
|
||||||
#[derive(PartialEq, Eq, Hash, Clone, Debug)]
|
#[derive(PartialEq, Eq, Hash, Clone, Debug)]
|
||||||
#[cfg_attr(feature = "nightly", derive(HashStable_Generic))]
|
#[cfg_attr(feature = "nightly", derive(HashStable_Generic))]
|
||||||
pub enum Variants<FieldIdx: Idx, VariantIdx: Idx> {
|
pub enum Variants<FieldIdx: Idx, VariantIdx: Idx> {
|
||||||
|
/// A type with no valid variants. Must be uninhabited.
|
||||||
|
Empty,
|
||||||
|
|
||||||
/// Single enum variants, structs/tuples, unions, and all non-ADTs.
|
/// Single enum variants, structs/tuples, unions, and all non-ADTs.
|
||||||
Single {
|
Single {
|
||||||
/// Always 0 for non-enums/generators.
|
/// Always `0` for types that cannot have multiple variants.
|
||||||
/// For enums without a variant, this is an invalid index!
|
|
||||||
index: VariantIdx,
|
index: VariantIdx,
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -334,35 +334,7 @@ fn do_mir_borrowck<'tcx>(
|
||||||
mbcx.gather_used_muts(temporary_used_locals, unused_mut_locals);
|
mbcx.gather_used_muts(temporary_used_locals, unused_mut_locals);
|
||||||
|
|
||||||
debug!("mbcx.used_mut: {:?}", mbcx.used_mut);
|
debug!("mbcx.used_mut: {:?}", mbcx.used_mut);
|
||||||
let used_mut = std::mem::take(&mut mbcx.used_mut);
|
mbcx.lint_unused_mut();
|
||||||
for local in mbcx.body.mut_vars_and_args_iter().filter(|local| !used_mut.contains(local)) {
|
|
||||||
let local_decl = &mbcx.body.local_decls[local];
|
|
||||||
let lint_root = match &mbcx.body.source_scopes[local_decl.source_info.scope].local_data {
|
|
||||||
ClearCrossCrate::Set(data) => data.lint_root,
|
|
||||||
_ => continue,
|
|
||||||
};
|
|
||||||
|
|
||||||
// Skip over locals that begin with an underscore or have no name
|
|
||||||
match mbcx.local_names[local] {
|
|
||||||
Some(name) => {
|
|
||||||
if name.as_str().starts_with('_') {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
None => continue,
|
|
||||||
}
|
|
||||||
|
|
||||||
let span = local_decl.source_info.span;
|
|
||||||
if span.desugaring_kind().is_some() {
|
|
||||||
// If the `mut` arises as part of a desugaring, we should ignore it.
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
|
|
||||||
let mut_span = tcx.sess.source_map().span_until_non_whitespace(span);
|
|
||||||
|
|
||||||
tcx.emit_node_span_lint(UNUSED_MUT, lint_root, span, VarNeedNotMut { span: mut_span })
|
|
||||||
}
|
|
||||||
|
|
||||||
let tainted_by_errors = mbcx.emit_errors();
|
let tainted_by_errors = mbcx.emit_errors();
|
||||||
|
|
||||||
let result = BorrowCheckResult {
|
let result = BorrowCheckResult {
|
||||||
|
@ -2390,6 +2362,38 @@ impl<'a, 'tcx> MirBorrowckCtxt<'a, '_, 'tcx> {
|
||||||
// `BasicBlocks` computes dominators on-demand and caches them.
|
// `BasicBlocks` computes dominators on-demand and caches them.
|
||||||
self.body.basic_blocks.dominators()
|
self.body.basic_blocks.dominators()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn lint_unused_mut(&self) {
|
||||||
|
let tcx = self.infcx.tcx;
|
||||||
|
let body = self.body;
|
||||||
|
for local in body.mut_vars_and_args_iter().filter(|local| !self.used_mut.contains(local)) {
|
||||||
|
let local_decl = &body.local_decls[local];
|
||||||
|
let lint_root = match &body.source_scopes[local_decl.source_info.scope].local_data {
|
||||||
|
ClearCrossCrate::Set(data) => data.lint_root,
|
||||||
|
_ => continue,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Skip over locals that begin with an underscore or have no name
|
||||||
|
match self.local_names[local] {
|
||||||
|
Some(name) => {
|
||||||
|
if name.as_str().starts_with('_') {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
None => continue,
|
||||||
|
}
|
||||||
|
|
||||||
|
let span = local_decl.source_info.span;
|
||||||
|
if span.desugaring_kind().is_some() {
|
||||||
|
// If the `mut` arises as part of a desugaring, we should ignore it.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut_span = tcx.sess.source_map().span_until_non_whitespace(span);
|
||||||
|
|
||||||
|
tcx.emit_node_span_lint(UNUSED_MUT, lint_root, span, VarNeedNotMut { span: mut_span })
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mod diags {
|
mod diags {
|
||||||
|
|
|
@ -18,6 +18,7 @@ pub(crate) fn codegen_set_discriminant<'tcx>(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
match layout.variants {
|
match layout.variants {
|
||||||
|
Variants::Empty => unreachable!("we already handled uninhabited types"),
|
||||||
Variants::Single { index } => {
|
Variants::Single { index } => {
|
||||||
assert_eq!(index, variant_index);
|
assert_eq!(index, variant_index);
|
||||||
}
|
}
|
||||||
|
@ -85,6 +86,7 @@ pub(crate) fn codegen_get_discriminant<'tcx>(
|
||||||
}
|
}
|
||||||
|
|
||||||
let (tag_scalar, tag_field, tag_encoding) = match &layout.variants {
|
let (tag_scalar, tag_field, tag_encoding) = match &layout.variants {
|
||||||
|
Variants::Empty => unreachable!("we already handled uninhabited types"),
|
||||||
Variants::Single { index } => {
|
Variants::Single { index } => {
|
||||||
let discr_val = layout
|
let discr_val = layout
|
||||||
.ty
|
.ty
|
||||||
|
|
|
@ -212,21 +212,17 @@ pub(super) fn build_enum_type_di_node<'ll, 'tcx>(
|
||||||
),
|
),
|
||||||
|cx, enum_type_di_node| {
|
|cx, enum_type_di_node| {
|
||||||
match enum_type_and_layout.variants {
|
match enum_type_and_layout.variants {
|
||||||
Variants::Single { index: variant_index } => {
|
Variants::Empty => {
|
||||||
if enum_adt_def.variants().is_empty() {
|
// We don't generate any members for uninhabited types.
|
||||||
// Uninhabited enums have Variants::Single. We don't generate
|
return smallvec![];
|
||||||
// any members for them.
|
|
||||||
return smallvec![];
|
|
||||||
}
|
|
||||||
|
|
||||||
build_single_variant_union_fields(
|
|
||||||
cx,
|
|
||||||
enum_adt_def,
|
|
||||||
enum_type_and_layout,
|
|
||||||
enum_type_di_node,
|
|
||||||
variant_index,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
Variants::Single { index: variant_index } => build_single_variant_union_fields(
|
||||||
|
cx,
|
||||||
|
enum_adt_def,
|
||||||
|
enum_type_and_layout,
|
||||||
|
enum_type_di_node,
|
||||||
|
variant_index,
|
||||||
|
),
|
||||||
Variants::Multiple {
|
Variants::Multiple {
|
||||||
tag_encoding: TagEncoding::Direct,
|
tag_encoding: TagEncoding::Direct,
|
||||||
ref variants,
|
ref variants,
|
||||||
|
@ -303,6 +299,7 @@ pub(super) fn build_coroutine_di_node<'ll, 'tcx>(
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
Variants::Single { .. }
|
Variants::Single { .. }
|
||||||
|
| Variants::Empty
|
||||||
| Variants::Multiple { tag_encoding: TagEncoding::Niche { .. }, .. } => {
|
| Variants::Multiple { tag_encoding: TagEncoding::Niche { .. }, .. } => {
|
||||||
bug!(
|
bug!(
|
||||||
"Encountered coroutine with non-direct-tag layout: {:?}",
|
"Encountered coroutine with non-direct-tag layout: {:?}",
|
||||||
|
|
|
@ -392,7 +392,7 @@ fn compute_discriminant_value<'ll, 'tcx>(
|
||||||
variant_index: VariantIdx,
|
variant_index: VariantIdx,
|
||||||
) -> DiscrResult {
|
) -> DiscrResult {
|
||||||
match enum_type_and_layout.layout.variants() {
|
match enum_type_and_layout.layout.variants() {
|
||||||
&Variants::Single { .. } => DiscrResult::NoDiscriminant,
|
&Variants::Single { .. } | &Variants::Empty => DiscrResult::NoDiscriminant,
|
||||||
&Variants::Multiple { tag_encoding: TagEncoding::Direct, .. } => DiscrResult::Value(
|
&Variants::Multiple { tag_encoding: TagEncoding::Direct, .. } => DiscrResult::Value(
|
||||||
enum_type_and_layout.ty.discriminant_for_variant(cx.tcx, variant_index).unwrap().val,
|
enum_type_and_layout.ty.discriminant_for_variant(cx.tcx, variant_index).unwrap().val,
|
||||||
),
|
),
|
||||||
|
|
|
@ -358,8 +358,8 @@ fn build_discr_member_di_node<'ll, 'tcx>(
|
||||||
let containing_scope = enum_or_coroutine_type_di_node;
|
let containing_scope = enum_or_coroutine_type_di_node;
|
||||||
|
|
||||||
match enum_or_coroutine_type_and_layout.layout.variants() {
|
match enum_or_coroutine_type_and_layout.layout.variants() {
|
||||||
// A single-variant enum has no discriminant.
|
// A single-variant or no-variant enum has no discriminant.
|
||||||
&Variants::Single { .. } => None,
|
&Variants::Single { .. } | &Variants::Empty => None,
|
||||||
|
|
||||||
&Variants::Multiple { tag_field, .. } => {
|
&Variants::Multiple { tag_field, .. } => {
|
||||||
let tag_base_type = tag_base_type(cx.tcx, enum_or_coroutine_type_and_layout);
|
let tag_base_type = tag_base_type(cx.tcx, enum_or_coroutine_type_and_layout);
|
||||||
|
|
|
@ -38,7 +38,7 @@ fn uncached_llvm_type<'a, 'tcx>(
|
||||||
if let (&ty::Adt(def, _), &Variants::Single { index }) =
|
if let (&ty::Adt(def, _), &Variants::Single { index }) =
|
||||||
(layout.ty.kind(), &layout.variants)
|
(layout.ty.kind(), &layout.variants)
|
||||||
{
|
{
|
||||||
if def.is_enum() && !def.variants().is_empty() {
|
if def.is_enum() {
|
||||||
write!(&mut name, "::{}", def.variant(index).name).unwrap();
|
write!(&mut name, "::{}", def.variant(index).name).unwrap();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -65,8 +65,8 @@ fn tag_base_type_opt<'tcx>(
|
||||||
});
|
});
|
||||||
|
|
||||||
match enum_type_and_layout.layout.variants() {
|
match enum_type_and_layout.layout.variants() {
|
||||||
// A single-variant enum has no discriminant.
|
// A single-variant or no-variant enum has no discriminant.
|
||||||
Variants::Single { .. } => None,
|
Variants::Single { .. } | Variants::Empty => None,
|
||||||
|
|
||||||
Variants::Multiple { tag_encoding: TagEncoding::Niche { .. }, tag, .. } => {
|
Variants::Multiple { tag_encoding: TagEncoding::Niche { .. }, tag, .. } => {
|
||||||
// Niche tags are always normalized to unsized integers of the correct size.
|
// Niche tags are always normalized to unsized integers of the correct size.
|
||||||
|
|
|
@ -243,6 +243,7 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> {
|
||||||
return bx.cx().const_poison(cast_to);
|
return bx.cx().const_poison(cast_to);
|
||||||
}
|
}
|
||||||
let (tag_scalar, tag_encoding, tag_field) = match self.layout.variants {
|
let (tag_scalar, tag_encoding, tag_field) = match self.layout.variants {
|
||||||
|
Variants::Empty => unreachable!("we already handled uninhabited types"),
|
||||||
Variants::Single { index } => {
|
Variants::Single { index } => {
|
||||||
let discr_val = self
|
let discr_val = self
|
||||||
.layout
|
.layout
|
||||||
|
@ -365,9 +366,9 @@ impl<'a, 'tcx, V: CodegenObject> PlaceRef<'tcx, V> {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
match self.layout.variants {
|
match self.layout.variants {
|
||||||
Variants::Single { index } => {
|
Variants::Empty => unreachable!("we already handled uninhabited types"),
|
||||||
assert_eq!(index, variant_index);
|
Variants::Single { index } => assert_eq!(index, variant_index),
|
||||||
}
|
|
||||||
Variants::Multiple { tag_encoding: TagEncoding::Direct, tag_field, .. } => {
|
Variants::Multiple { tag_encoding: TagEncoding::Direct, tag_field, .. } => {
|
||||||
let ptr = self.project_field(bx, tag_field);
|
let ptr = self.project_field(bx, tag_field);
|
||||||
let to =
|
let to =
|
||||||
|
|
|
@ -44,7 +44,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Read discriminant, return the runtime value as well as the variant index.
|
/// Read discriminant, return the variant index.
|
||||||
/// Can also legally be called on non-enums (e.g. through the discriminant_value intrinsic)!
|
/// Can also legally be called on non-enums (e.g. through the discriminant_value intrinsic)!
|
||||||
///
|
///
|
||||||
/// Will never return an uninhabited variant.
|
/// Will never return an uninhabited variant.
|
||||||
|
@ -65,21 +65,17 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
||||||
// We use "tag" to refer to how the discriminant is encoded in memory, which can be either
|
// 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`).
|
// straight-forward (`TagEncoding::Direct`) or with a niche (`TagEncoding::Niche`).
|
||||||
let (tag_scalar_layout, tag_encoding, tag_field) = match op.layout().variants {
|
let (tag_scalar_layout, tag_encoding, tag_field) = match op.layout().variants {
|
||||||
|
Variants::Empty => {
|
||||||
|
throw_ub!(UninhabitedEnumVariantRead(None));
|
||||||
|
}
|
||||||
Variants::Single { index } => {
|
Variants::Single { index } => {
|
||||||
// Do some extra checks on enums.
|
if op.layout().is_uninhabited() {
|
||||||
if ty.is_enum() {
|
|
||||||
// Hilariously, `Single` is used even for 0-variant enums.
|
|
||||||
// (See https://github.com/rust-lang/rust/issues/89765).
|
|
||||||
if ty.ty_adt_def().unwrap().variants().is_empty() {
|
|
||||||
throw_ub!(UninhabitedEnumVariantRead(index))
|
|
||||||
}
|
|
||||||
// For consistency with `write_discriminant`, and to make sure that
|
// For consistency with `write_discriminant`, and to make sure that
|
||||||
// `project_downcast` cannot fail due to strange layouts, we declare immediate UB
|
// `project_downcast` cannot fail due to strange layouts, we declare immediate UB
|
||||||
// for uninhabited variants.
|
// for uninhabited enums.
|
||||||
if op.layout().for_variant(self, index).is_uninhabited() {
|
throw_ub!(UninhabitedEnumVariantRead(Some(index)));
|
||||||
throw_ub!(UninhabitedEnumVariantRead(index))
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
// Since the type is inhabited, there must be an index.
|
||||||
return interp_ok(index);
|
return interp_ok(index);
|
||||||
}
|
}
|
||||||
Variants::Multiple { tag, ref tag_encoding, tag_field, .. } => {
|
Variants::Multiple { tag, ref tag_encoding, tag_field, .. } => {
|
||||||
|
@ -199,11 +195,13 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
||||||
// `uninhabited_enum_branching` MIR pass. It also ensures consistency with
|
// `uninhabited_enum_branching` MIR pass. It also ensures consistency with
|
||||||
// `write_discriminant`.
|
// `write_discriminant`.
|
||||||
if op.layout().for_variant(self, index).is_uninhabited() {
|
if op.layout().for_variant(self, index).is_uninhabited() {
|
||||||
throw_ub!(UninhabitedEnumVariantRead(index))
|
throw_ub!(UninhabitedEnumVariantRead(Some(index)))
|
||||||
}
|
}
|
||||||
interp_ok(index)
|
interp_ok(index)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Read discriminant, return the user-visible discriminant.
|
||||||
|
/// Can also legally be called on non-enums (e.g. through the discriminant_value intrinsic)!
|
||||||
pub fn discriminant_for_variant(
|
pub fn discriminant_for_variant(
|
||||||
&self,
|
&self,
|
||||||
ty: Ty<'tcx>,
|
ty: Ty<'tcx>,
|
||||||
|
@ -243,6 +241,7 @@ impl<'tcx, M: Machine<'tcx>> InterpCx<'tcx, M> {
|
||||||
}
|
}
|
||||||
|
|
||||||
match layout.variants {
|
match layout.variants {
|
||||||
|
abi::Variants::Empty => unreachable!("we already handled uninhabited types"),
|
||||||
abi::Variants::Single { .. } => {
|
abi::Variants::Single { .. } => {
|
||||||
// The tag of a `Single` enum is like the tag of the niched
|
// The tag of a `Single` enum is like the tag of the niched
|
||||||
// variant: there's no tag as the discriminant is encoded
|
// variant: there's no tag as the discriminant is encoded
|
||||||
|
|
|
@ -302,7 +302,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> {
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Variants::Single { .. } => {}
|
Variants::Single { .. } | Variants::Empty => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Now we know we are projecting to a field, so figure out which one.
|
// Now we know we are projecting to a field, so figure out which one.
|
||||||
|
@ -344,6 +344,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> {
|
||||||
// Inside a variant
|
// Inside a variant
|
||||||
PathElem::Field(def.variant(index).fields[FieldIdx::from_usize(field)].name)
|
PathElem::Field(def.variant(index).fields[FieldIdx::from_usize(field)].name)
|
||||||
}
|
}
|
||||||
|
Variants::Empty => panic!("there is no field in Variants::Empty types"),
|
||||||
Variants::Multiple { .. } => bug!("we handled variants above"),
|
Variants::Multiple { .. } => bug!("we handled variants above"),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1010,7 +1011,7 @@ impl<'rt, 'tcx, M: Machine<'tcx>> ValidityVisitor<'rt, 'tcx, M> {
|
||||||
}
|
}
|
||||||
// Don't forget potential other variants.
|
// Don't forget potential other variants.
|
||||||
match &layout.variants {
|
match &layout.variants {
|
||||||
Variants::Single { .. } => {
|
Variants::Single { .. } | Variants::Empty => {
|
||||||
// Fully handled above.
|
// Fully handled above.
|
||||||
}
|
}
|
||||||
Variants::Multiple { variants, .. } => {
|
Variants::Multiple { variants, .. } => {
|
||||||
|
|
|
@ -218,8 +218,8 @@ pub trait ValueVisitor<'tcx, M: Machine<'tcx>>: Sized {
|
||||||
// recurse with the inner type
|
// recurse with the inner type
|
||||||
self.visit_variant(v, idx, &inner)?;
|
self.visit_variant(v, idx, &inner)?;
|
||||||
}
|
}
|
||||||
// For single-variant layouts, we already did anything there is to do.
|
// For single-variant layouts, we already did everything there is to do.
|
||||||
Variants::Single { .. } => {}
|
Variants::Single { .. } | Variants::Empty => {}
|
||||||
}
|
}
|
||||||
|
|
||||||
interp_ok(())
|
interp_ok(())
|
||||||
|
|
|
@ -155,6 +155,7 @@ fn check_validity_requirement_lax<'tcx>(
|
||||||
}
|
}
|
||||||
|
|
||||||
match &this.variants {
|
match &this.variants {
|
||||||
|
Variants::Empty => return Ok(false),
|
||||||
Variants::Single { .. } => {
|
Variants::Single { .. } => {
|
||||||
// All fields of this single variant have already been checked above, there is nothing
|
// All fields of this single variant have already been checked above, there is nothing
|
||||||
// else to do.
|
// else to do.
|
||||||
|
|
|
@ -129,7 +129,7 @@ fn resolve_block<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, blk: &'tcx h
|
||||||
let mut prev_cx = visitor.cx;
|
let mut prev_cx = visitor.cx;
|
||||||
|
|
||||||
visitor.enter_scope(Scope {
|
visitor.enter_scope(Scope {
|
||||||
id: blk.hir_id.local_id,
|
local_id: blk.hir_id.local_id,
|
||||||
data: ScopeData::Remainder(FirstStatementIndex::new(i)),
|
data: ScopeData::Remainder(FirstStatementIndex::new(i)),
|
||||||
});
|
});
|
||||||
visitor.cx.var_parent = visitor.cx.parent;
|
visitor.cx.var_parent = visitor.cx.parent;
|
||||||
|
@ -154,7 +154,7 @@ fn resolve_block<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, blk: &'tcx h
|
||||||
// the first such subscope, which has the block itself as a
|
// the first such subscope, which has the block itself as a
|
||||||
// parent.
|
// parent.
|
||||||
visitor.enter_scope(Scope {
|
visitor.enter_scope(Scope {
|
||||||
id: blk.hir_id.local_id,
|
local_id: blk.hir_id.local_id,
|
||||||
data: ScopeData::Remainder(FirstStatementIndex::new(i)),
|
data: ScopeData::Remainder(FirstStatementIndex::new(i)),
|
||||||
});
|
});
|
||||||
visitor.cx.var_parent = visitor.cx.parent;
|
visitor.cx.var_parent = visitor.cx.parent;
|
||||||
|
@ -184,7 +184,7 @@ fn resolve_block<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, blk: &'tcx h
|
||||||
visitor
|
visitor
|
||||||
.scope_tree
|
.scope_tree
|
||||||
.backwards_incompatible_scope
|
.backwards_incompatible_scope
|
||||||
.insert(local_id, Scope { id: local_id, data: ScopeData::Node });
|
.insert(local_id, Scope { local_id, data: ScopeData::Node });
|
||||||
}
|
}
|
||||||
visitor.visit_expr(tail_expr);
|
visitor.visit_expr(tail_expr);
|
||||||
}
|
}
|
||||||
|
@ -221,7 +221,7 @@ fn resolve_arm<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, arm: &'tcx hir
|
||||||
}
|
}
|
||||||
|
|
||||||
fn resolve_pat<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, pat: &'tcx hir::Pat<'tcx>) {
|
fn resolve_pat<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, pat: &'tcx hir::Pat<'tcx>) {
|
||||||
visitor.record_child_scope(Scope { id: pat.hir_id.local_id, data: ScopeData::Node });
|
visitor.record_child_scope(Scope { local_id: pat.hir_id.local_id, data: ScopeData::Node });
|
||||||
|
|
||||||
// If this is a binding then record the lifetime of that binding.
|
// If this is a binding then record the lifetime of that binding.
|
||||||
if let PatKind::Binding(..) = pat.kind {
|
if let PatKind::Binding(..) = pat.kind {
|
||||||
|
@ -485,7 +485,7 @@ fn resolve_expr<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, expr: &'tcx h
|
||||||
} else {
|
} else {
|
||||||
ScopeData::IfThen
|
ScopeData::IfThen
|
||||||
};
|
};
|
||||||
visitor.enter_scope(Scope { id: then.hir_id.local_id, data });
|
visitor.enter_scope(Scope { local_id: then.hir_id.local_id, data });
|
||||||
visitor.cx.var_parent = visitor.cx.parent;
|
visitor.cx.var_parent = visitor.cx.parent;
|
||||||
visitor.visit_expr(cond);
|
visitor.visit_expr(cond);
|
||||||
visitor.visit_expr(then);
|
visitor.visit_expr(then);
|
||||||
|
@ -500,7 +500,7 @@ fn resolve_expr<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, expr: &'tcx h
|
||||||
} else {
|
} else {
|
||||||
ScopeData::IfThen
|
ScopeData::IfThen
|
||||||
};
|
};
|
||||||
visitor.enter_scope(Scope { id: then.hir_id.local_id, data });
|
visitor.enter_scope(Scope { local_id: then.hir_id.local_id, data });
|
||||||
visitor.cx.var_parent = visitor.cx.parent;
|
visitor.cx.var_parent = visitor.cx.parent;
|
||||||
visitor.visit_expr(cond);
|
visitor.visit_expr(cond);
|
||||||
visitor.visit_expr(then);
|
visitor.visit_expr(then);
|
||||||
|
@ -516,7 +516,7 @@ fn resolve_expr<'tcx>(visitor: &mut RegionResolutionVisitor<'tcx>, expr: &'tcx h
|
||||||
|
|
||||||
if let hir::ExprKind::Yield(_, source) = &expr.kind {
|
if let hir::ExprKind::Yield(_, source) = &expr.kind {
|
||||||
// Mark this expr's scope and all parent scopes as containing `yield`.
|
// Mark this expr's scope and all parent scopes as containing `yield`.
|
||||||
let mut scope = Scope { id: expr.hir_id.local_id, data: ScopeData::Node };
|
let mut scope = Scope { local_id: expr.hir_id.local_id, data: ScopeData::Node };
|
||||||
loop {
|
loop {
|
||||||
let span = match expr.kind {
|
let span = match expr.kind {
|
||||||
hir::ExprKind::Yield(expr, hir::YieldSource::Await { .. }) => {
|
hir::ExprKind::Yield(expr, hir::YieldSource::Await { .. }) => {
|
||||||
|
@ -803,9 +803,9 @@ impl<'tcx> RegionResolutionVisitor<'tcx> {
|
||||||
// account for the destruction scope representing the scope of
|
// account for the destruction scope representing the scope of
|
||||||
// the destructors that run immediately after it completes.
|
// the destructors that run immediately after it completes.
|
||||||
if self.terminating_scopes.contains(&id) {
|
if self.terminating_scopes.contains(&id) {
|
||||||
self.enter_scope(Scope { id, data: ScopeData::Destruction });
|
self.enter_scope(Scope { local_id: id, data: ScopeData::Destruction });
|
||||||
}
|
}
|
||||||
self.enter_scope(Scope { id, data: ScopeData::Node });
|
self.enter_scope(Scope { local_id: id, data: ScopeData::Node });
|
||||||
}
|
}
|
||||||
|
|
||||||
fn enter_body(&mut self, hir_id: hir::HirId, f: impl FnOnce(&mut Self)) {
|
fn enter_body(&mut self, hir_id: hir::HirId, f: impl FnOnce(&mut Self)) {
|
||||||
|
@ -822,8 +822,8 @@ impl<'tcx> RegionResolutionVisitor<'tcx> {
|
||||||
let outer_pessimistic_yield = mem::replace(&mut self.pessimistic_yield, false);
|
let outer_pessimistic_yield = mem::replace(&mut self.pessimistic_yield, false);
|
||||||
self.terminating_scopes.insert(hir_id.local_id);
|
self.terminating_scopes.insert(hir_id.local_id);
|
||||||
|
|
||||||
self.enter_scope(Scope { id: hir_id.local_id, data: ScopeData::CallSite });
|
self.enter_scope(Scope { local_id: hir_id.local_id, data: ScopeData::CallSite });
|
||||||
self.enter_scope(Scope { id: hir_id.local_id, data: ScopeData::Arguments });
|
self.enter_scope(Scope { local_id: hir_id.local_id, data: ScopeData::Arguments });
|
||||||
|
|
||||||
f(self);
|
f(self);
|
||||||
|
|
||||||
|
|
|
@ -146,18 +146,21 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
debug!("write_ty({:?}, {:?}) in fcx {}", id, self.resolve_vars_if_possible(ty), self.tag());
|
debug!("write_ty({:?}, {:?}) in fcx {}", id, self.resolve_vars_if_possible(ty), self.tag());
|
||||||
let mut typeck = self.typeck_results.borrow_mut();
|
let mut typeck = self.typeck_results.borrow_mut();
|
||||||
let mut node_ty = typeck.node_types_mut();
|
let mut node_ty = typeck.node_types_mut();
|
||||||
if let Some(ty) = node_ty.get(id)
|
|
||||||
&& let Err(e) = ty.error_reported()
|
|
||||||
{
|
|
||||||
// Do not overwrite nodes that were already marked as `{type error}`. This allows us to
|
|
||||||
// silence unnecessary errors from obligations that were set earlier than a type error
|
|
||||||
// was produced, but that is overwritten by later analysis. This happens in particular
|
|
||||||
// for `Sized` obligations introduced in gather_locals. (#117846)
|
|
||||||
self.set_tainted_by_errors(e);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
node_ty.insert(id, ty);
|
if let Some(prev) = node_ty.insert(id, ty) {
|
||||||
|
if prev.references_error() {
|
||||||
|
node_ty.insert(id, prev);
|
||||||
|
} else if !ty.references_error() {
|
||||||
|
// Could change this to a bug, but there's lots of diagnostic code re-lowering
|
||||||
|
// or re-typechecking nodes that were already typecked.
|
||||||
|
// Lots of that diagnostics code relies on subtle effects of re-lowering, so we'll
|
||||||
|
// let it keep doing that and just ensure that compilation won't succeed.
|
||||||
|
self.dcx().span_delayed_bug(
|
||||||
|
self.tcx.hir().span(id),
|
||||||
|
format!("`{prev}` overridden by `{ty}` for {id:?} in {:?}", self.body_id),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if let Err(e) = ty.error_reported() {
|
if let Err(e) = ty.error_reported() {
|
||||||
self.set_tainted_by_errors(e);
|
self.set_tainted_by_errors(e);
|
||||||
|
@ -1104,7 +1107,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
if let Res::Local(hid) = res {
|
if let Res::Local(hid) = res {
|
||||||
let ty = self.local_ty(span, hid);
|
let ty = self.local_ty(span, hid);
|
||||||
let ty = self.normalize(span, ty);
|
let ty = self.normalize(span, ty);
|
||||||
self.write_ty(hir_id, ty);
|
|
||||||
return (ty, res);
|
return (ty, res);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1750,10 +1750,9 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub(in super::super) fn check_decl(&self, decl: Declaration<'tcx>) {
|
pub(in super::super) fn check_decl(&self, decl: Declaration<'tcx>) -> Ty<'tcx> {
|
||||||
// Determine and write the type which we'll check the pattern against.
|
// Determine and write the type which we'll check the pattern against.
|
||||||
let decl_ty = self.local_ty(decl.span, decl.hir_id);
|
let decl_ty = self.local_ty(decl.span, decl.hir_id);
|
||||||
self.write_ty(decl.hir_id, decl_ty);
|
|
||||||
|
|
||||||
// Type check the initializer.
|
// Type check the initializer.
|
||||||
if let Some(ref init) = decl.init {
|
if let Some(ref init) = decl.init {
|
||||||
|
@ -1785,11 +1784,13 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||||
}
|
}
|
||||||
self.diverges.set(previous_diverges);
|
self.diverges.set(previous_diverges);
|
||||||
}
|
}
|
||||||
|
decl_ty
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Type check a `let` statement.
|
/// Type check a `let` statement.
|
||||||
fn check_decl_local(&self, local: &'tcx hir::LetStmt<'tcx>) {
|
fn check_decl_local(&self, local: &'tcx hir::LetStmt<'tcx>) {
|
||||||
self.check_decl(local.into());
|
let ty = self.check_decl(local.into());
|
||||||
|
self.write_ty(local.hir_id, ty);
|
||||||
if local.pat.is_never_pattern() {
|
if local.pat.is_never_pattern() {
|
||||||
self.diverges.set(Diverges::Always {
|
self.diverges.set(Diverges::Always {
|
||||||
span: local.pat.span,
|
span: local.pat.span,
|
||||||
|
|
|
@ -84,23 +84,23 @@ use crate::ty::TyCtxt;
|
||||||
#[derive(Clone, PartialEq, PartialOrd, Eq, Ord, Hash, Copy, TyEncodable, TyDecodable)]
|
#[derive(Clone, PartialEq, PartialOrd, Eq, Ord, Hash, Copy, TyEncodable, TyDecodable)]
|
||||||
#[derive(HashStable)]
|
#[derive(HashStable)]
|
||||||
pub struct Scope {
|
pub struct Scope {
|
||||||
pub id: hir::ItemLocalId,
|
pub local_id: hir::ItemLocalId,
|
||||||
pub data: ScopeData,
|
pub data: ScopeData,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl fmt::Debug for Scope {
|
impl fmt::Debug for Scope {
|
||||||
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||||
match self.data {
|
match self.data {
|
||||||
ScopeData::Node => write!(fmt, "Node({:?})", self.id),
|
ScopeData::Node => write!(fmt, "Node({:?})", self.local_id),
|
||||||
ScopeData::CallSite => write!(fmt, "CallSite({:?})", self.id),
|
ScopeData::CallSite => write!(fmt, "CallSite({:?})", self.local_id),
|
||||||
ScopeData::Arguments => write!(fmt, "Arguments({:?})", self.id),
|
ScopeData::Arguments => write!(fmt, "Arguments({:?})", self.local_id),
|
||||||
ScopeData::Destruction => write!(fmt, "Destruction({:?})", self.id),
|
ScopeData::Destruction => write!(fmt, "Destruction({:?})", self.local_id),
|
||||||
ScopeData::IfThen => write!(fmt, "IfThen({:?})", self.id),
|
ScopeData::IfThen => write!(fmt, "IfThen({:?})", self.local_id),
|
||||||
ScopeData::IfThenRescope => write!(fmt, "IfThen[edition2024]({:?})", self.id),
|
ScopeData::IfThenRescope => write!(fmt, "IfThen[edition2024]({:?})", self.local_id),
|
||||||
ScopeData::Remainder(fsi) => write!(
|
ScopeData::Remainder(fsi) => write!(
|
||||||
fmt,
|
fmt,
|
||||||
"Remainder {{ block: {:?}, first_statement_index: {}}}",
|
"Remainder {{ block: {:?}, first_statement_index: {}}}",
|
||||||
self.id,
|
self.local_id,
|
||||||
fsi.as_u32(),
|
fsi.as_u32(),
|
||||||
),
|
),
|
||||||
}
|
}
|
||||||
|
@ -164,18 +164,8 @@ rustc_index::newtype_index! {
|
||||||
rustc_data_structures::static_assert_size!(ScopeData, 4);
|
rustc_data_structures::static_assert_size!(ScopeData, 4);
|
||||||
|
|
||||||
impl Scope {
|
impl Scope {
|
||||||
/// Returns an item-local ID associated with this scope.
|
|
||||||
///
|
|
||||||
/// N.B., likely to be replaced as API is refined; e.g., pnkfelix
|
|
||||||
/// anticipates `fn entry_node_id` and `fn each_exit_node_id`.
|
|
||||||
pub fn item_local_id(&self) -> hir::ItemLocalId {
|
|
||||||
self.id
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn hir_id(&self, scope_tree: &ScopeTree) -> Option<HirId> {
|
pub fn hir_id(&self, scope_tree: &ScopeTree) -> Option<HirId> {
|
||||||
scope_tree
|
scope_tree.root_body.map(|hir_id| HirId { owner: hir_id.owner, local_id: self.local_id })
|
||||||
.root_body
|
|
||||||
.map(|hir_id| HirId { owner: hir_id.owner, local_id: self.item_local_id() })
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the span of this `Scope`. Note that in general the
|
/// Returns the span of this `Scope`. Note that in general the
|
||||||
|
@ -350,7 +340,7 @@ impl ScopeTree {
|
||||||
|
|
||||||
pub fn record_var_scope(&mut self, var: hir::ItemLocalId, lifetime: Scope) {
|
pub fn record_var_scope(&mut self, var: hir::ItemLocalId, lifetime: Scope) {
|
||||||
debug!("record_var_scope(sub={:?}, sup={:?})", var, lifetime);
|
debug!("record_var_scope(sub={:?}, sup={:?})", var, lifetime);
|
||||||
assert!(var != lifetime.item_local_id());
|
assert!(var != lifetime.local_id);
|
||||||
self.var_map.insert(var, lifetime);
|
self.var_map.insert(var, lifetime);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -359,7 +349,7 @@ impl ScopeTree {
|
||||||
match &candidate_type {
|
match &candidate_type {
|
||||||
RvalueCandidateType::Borrow { lifetime: Some(lifetime), .. }
|
RvalueCandidateType::Borrow { lifetime: Some(lifetime), .. }
|
||||||
| RvalueCandidateType::Pattern { lifetime: Some(lifetime), .. } => {
|
| RvalueCandidateType::Pattern { lifetime: Some(lifetime), .. } => {
|
||||||
assert!(var.local_id != lifetime.item_local_id())
|
assert!(var.local_id != lifetime.local_id)
|
||||||
}
|
}
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
|
|
@ -392,7 +392,7 @@ pub enum UndefinedBehaviorInfo<'tcx> {
|
||||||
/// A discriminant of an uninhabited enum variant is written.
|
/// A discriminant of an uninhabited enum variant is written.
|
||||||
UninhabitedEnumVariantWritten(VariantIdx),
|
UninhabitedEnumVariantWritten(VariantIdx),
|
||||||
/// An uninhabited enum variant is projected.
|
/// An uninhabited enum variant is projected.
|
||||||
UninhabitedEnumVariantRead(VariantIdx),
|
UninhabitedEnumVariantRead(Option<VariantIdx>),
|
||||||
/// Trying to set discriminant to the niched variant, but the value does not match.
|
/// Trying to set discriminant to the niched variant, but the value does not match.
|
||||||
InvalidNichedEnumVariantWritten { enum_ty: Ty<'tcx> },
|
InvalidNichedEnumVariantWritten { enum_ty: Ty<'tcx> },
|
||||||
/// ABI-incompatible argument types.
|
/// ABI-incompatible argument types.
|
||||||
|
|
|
@ -734,21 +734,22 @@ where
|
||||||
let layout = match this.variants {
|
let layout = match this.variants {
|
||||||
Variants::Single { index }
|
Variants::Single { index }
|
||||||
// If all variants but one are uninhabited, the variant layout is the enum layout.
|
// If all variants but one are uninhabited, the variant layout is the enum layout.
|
||||||
if index == variant_index &&
|
if index == variant_index =>
|
||||||
// Don't confuse variants of uninhabited enums with the enum itself.
|
|
||||||
// For more details see https://github.com/rust-lang/rust/issues/69763.
|
|
||||||
this.fields != FieldsShape::Primitive =>
|
|
||||||
{
|
{
|
||||||
this.layout
|
this.layout
|
||||||
}
|
}
|
||||||
|
|
||||||
Variants::Single { index } => {
|
Variants::Single { .. } | Variants::Empty => {
|
||||||
|
// Single-variant and no-variant enums *can* have other variants, but those are
|
||||||
|
// uninhabited. Produce a layout that has the right fields for that variant, so that
|
||||||
|
// the rest of the compiler can project fields etc as usual.
|
||||||
|
|
||||||
let tcx = cx.tcx();
|
let tcx = cx.tcx();
|
||||||
let typing_env = cx.typing_env();
|
let typing_env = cx.typing_env();
|
||||||
|
|
||||||
// Deny calling for_variant more than once for non-Single enums.
|
// Deny calling for_variant more than once for non-Single enums.
|
||||||
if let Ok(original_layout) = tcx.layout_of(typing_env.as_query_input(this.ty)) {
|
if let Ok(original_layout) = tcx.layout_of(typing_env.as_query_input(this.ty)) {
|
||||||
assert_eq!(original_layout.variants, Variants::Single { index });
|
assert_eq!(original_layout.variants, this.variants);
|
||||||
}
|
}
|
||||||
|
|
||||||
let fields = match this.ty.kind() {
|
let fields = match this.ty.kind() {
|
||||||
|
@ -902,6 +903,7 @@ where
|
||||||
),
|
),
|
||||||
|
|
||||||
ty::Coroutine(def_id, args) => match this.variants {
|
ty::Coroutine(def_id, args) => match this.variants {
|
||||||
|
Variants::Empty => unreachable!(),
|
||||||
Variants::Single { index } => TyMaybeWithLayout::Ty(
|
Variants::Single { index } => TyMaybeWithLayout::Ty(
|
||||||
args.as_coroutine()
|
args.as_coroutine()
|
||||||
.state_tys(def_id, tcx)
|
.state_tys(def_id, tcx)
|
||||||
|
@ -927,6 +929,7 @@ where
|
||||||
let field = &def.variant(index).fields[FieldIdx::from_usize(i)];
|
let field = &def.variant(index).fields[FieldIdx::from_usize(i)];
|
||||||
TyMaybeWithLayout::Ty(field.ty(tcx, args))
|
TyMaybeWithLayout::Ty(field.ty(tcx, args))
|
||||||
}
|
}
|
||||||
|
Variants::Empty => panic!("there is no field in Variants::Empty types"),
|
||||||
|
|
||||||
// Discriminant field for enums (where applicable).
|
// Discriminant field for enums (where applicable).
|
||||||
Variants::Multiple { tag, .. } => {
|
Variants::Multiple { tag, .. } => {
|
||||||
|
|
|
@ -35,7 +35,7 @@ impl RvalueScopes {
|
||||||
// if there's one. Static items, for instance, won't
|
// if there's one. Static items, for instance, won't
|
||||||
// have an enclosing scope, hence no scope will be
|
// have an enclosing scope, hence no scope will be
|
||||||
// returned.
|
// returned.
|
||||||
let mut id = Scope { id: expr_id, data: ScopeData::Node };
|
let mut id = Scope { local_id: expr_id, data: ScopeData::Node };
|
||||||
let mut backwards_incompatible = None;
|
let mut backwards_incompatible = None;
|
||||||
|
|
||||||
while let Some(&(p, _)) = region_scope_tree.parent_map.get(&id) {
|
while let Some(&(p, _)) = region_scope_tree.parent_map.get(&id) {
|
||||||
|
@ -60,7 +60,7 @@ impl RvalueScopes {
|
||||||
if backwards_incompatible.is_none() {
|
if backwards_incompatible.is_none() {
|
||||||
backwards_incompatible = region_scope_tree
|
backwards_incompatible = region_scope_tree
|
||||||
.backwards_incompatible_scope
|
.backwards_incompatible_scope
|
||||||
.get(&p.item_local_id())
|
.get(&p.local_id)
|
||||||
.copied();
|
.copied();
|
||||||
}
|
}
|
||||||
id = p
|
id = p
|
||||||
|
@ -76,7 +76,7 @@ impl RvalueScopes {
|
||||||
pub fn record_rvalue_scope(&mut self, var: hir::ItemLocalId, lifetime: Option<Scope>) {
|
pub fn record_rvalue_scope(&mut self, var: hir::ItemLocalId, lifetime: Option<Scope>) {
|
||||||
debug!("record_rvalue_scope(var={var:?}, lifetime={lifetime:?})");
|
debug!("record_rvalue_scope(var={var:?}, lifetime={lifetime:?})");
|
||||||
if let Some(lifetime) = lifetime {
|
if let Some(lifetime) = lifetime {
|
||||||
assert!(var != lifetime.item_local_id());
|
assert!(var != lifetime.local_id);
|
||||||
}
|
}
|
||||||
self.map.insert(var, lifetime);
|
self.map.insert(var, lifetime);
|
||||||
}
|
}
|
||||||
|
|
|
@ -75,11 +75,11 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
LocalInfo::BlockTailTemp(tail_info)
|
LocalInfo::BlockTailTemp(tail_info)
|
||||||
}
|
}
|
||||||
|
|
||||||
_ if let Some(Scope { data: ScopeData::IfThenRescope, id }) =
|
_ if let Some(Scope { data: ScopeData::IfThenRescope, local_id }) =
|
||||||
temp_lifetime.temp_lifetime =>
|
temp_lifetime.temp_lifetime =>
|
||||||
{
|
{
|
||||||
LocalInfo::IfThenRescopeTemp {
|
LocalInfo::IfThenRescopeTemp {
|
||||||
if_then: HirId { owner: this.hir_id.owner, local_id: id },
|
if_then: HirId { owner: this.hir_id.owner, local_id },
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -531,9 +531,9 @@ fn construct_fn<'tcx>(
|
||||||
);
|
);
|
||||||
|
|
||||||
let call_site_scope =
|
let call_site_scope =
|
||||||
region::Scope { id: body.id().hir_id.local_id, data: region::ScopeData::CallSite };
|
region::Scope { local_id: body.id().hir_id.local_id, data: region::ScopeData::CallSite };
|
||||||
let arg_scope =
|
let arg_scope =
|
||||||
region::Scope { id: body.id().hir_id.local_id, data: region::ScopeData::Arguments };
|
region::Scope { local_id: body.id().hir_id.local_id, data: region::ScopeData::Arguments };
|
||||||
let source_info = builder.source_info(span);
|
let source_info = builder.source_info(span);
|
||||||
let call_site_s = (call_site_scope, source_info);
|
let call_site_s = (call_site_scope, source_info);
|
||||||
let _: BlockAnd<()> = builder.in_scope(call_site_s, LintLevel::Inherited, |builder| {
|
let _: BlockAnd<()> = builder.in_scope(call_site_s, LintLevel::Inherited, |builder| {
|
||||||
|
|
|
@ -89,7 +89,7 @@ use rustc_index::{IndexSlice, IndexVec};
|
||||||
use rustc_middle::middle::region;
|
use rustc_middle::middle::region;
|
||||||
use rustc_middle::mir::*;
|
use rustc_middle::mir::*;
|
||||||
use rustc_middle::thir::{ExprId, LintLevel};
|
use rustc_middle::thir::{ExprId, LintLevel};
|
||||||
use rustc_middle::{bug, span_bug, ty};
|
use rustc_middle::{bug, span_bug};
|
||||||
use rustc_session::lint::Level;
|
use rustc_session::lint::Level;
|
||||||
use rustc_span::source_map::Spanned;
|
use rustc_span::source_map::Spanned;
|
||||||
use rustc_span::{DUMMY_SP, Span};
|
use rustc_span::{DUMMY_SP, Span};
|
||||||
|
@ -1119,10 +1119,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
region_scope: region::Scope,
|
region_scope: region::Scope,
|
||||||
local: Local,
|
local: Local,
|
||||||
) {
|
) {
|
||||||
if !self.local_decls[local].ty.has_significant_drop(self.tcx, ty::TypingEnv {
|
if !self.local_decls[local].ty.has_significant_drop(self.tcx, self.typing_env()) {
|
||||||
typing_mode: ty::TypingMode::non_body_analysis(),
|
|
||||||
param_env: self.param_env,
|
|
||||||
}) {
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
for scope in self.scopes.scopes.iter_mut().rev() {
|
for scope in self.scopes.scopes.iter_mut().rev() {
|
||||||
|
|
|
@ -16,7 +16,7 @@ impl<'tcx> Cx<'tcx> {
|
||||||
let block = Block {
|
let block = Block {
|
||||||
targeted_by_break: block.targeted_by_break,
|
targeted_by_break: block.targeted_by_break,
|
||||||
region_scope: region::Scope {
|
region_scope: region::Scope {
|
||||||
id: block.hir_id.local_id,
|
local_id: block.hir_id.local_id,
|
||||||
data: region::ScopeData::Node,
|
data: region::ScopeData::Node,
|
||||||
},
|
},
|
||||||
span: block.span,
|
span: block.span,
|
||||||
|
@ -51,7 +51,7 @@ impl<'tcx> Cx<'tcx> {
|
||||||
let stmt = Stmt {
|
let stmt = Stmt {
|
||||||
kind: StmtKind::Expr {
|
kind: StmtKind::Expr {
|
||||||
scope: region::Scope {
|
scope: region::Scope {
|
||||||
id: hir_id.local_id,
|
local_id: hir_id.local_id,
|
||||||
data: region::ScopeData::Node,
|
data: region::ScopeData::Node,
|
||||||
},
|
},
|
||||||
expr: self.mirror_expr(expr),
|
expr: self.mirror_expr(expr),
|
||||||
|
@ -65,7 +65,7 @@ impl<'tcx> Cx<'tcx> {
|
||||||
}
|
}
|
||||||
hir::StmtKind::Let(local) => {
|
hir::StmtKind::Let(local) => {
|
||||||
let remainder_scope = region::Scope {
|
let remainder_scope = region::Scope {
|
||||||
id: block_id,
|
local_id: block_id,
|
||||||
data: region::ScopeData::Remainder(region::FirstStatementIndex::new(
|
data: region::ScopeData::Remainder(region::FirstStatementIndex::new(
|
||||||
index,
|
index,
|
||||||
)),
|
)),
|
||||||
|
@ -108,7 +108,7 @@ impl<'tcx> Cx<'tcx> {
|
||||||
kind: StmtKind::Let {
|
kind: StmtKind::Let {
|
||||||
remainder_scope,
|
remainder_scope,
|
||||||
init_scope: region::Scope {
|
init_scope: region::Scope {
|
||||||
id: hir_id.local_id,
|
local_id: hir_id.local_id,
|
||||||
data: region::ScopeData::Node,
|
data: region::ScopeData::Node,
|
||||||
},
|
},
|
||||||
pattern,
|
pattern,
|
||||||
|
|
|
@ -45,7 +45,7 @@ impl<'tcx> Cx<'tcx> {
|
||||||
#[instrument(level = "trace", skip(self, hir_expr))]
|
#[instrument(level = "trace", skip(self, hir_expr))]
|
||||||
pub(super) fn mirror_expr_inner(&mut self, hir_expr: &'tcx hir::Expr<'tcx>) -> ExprId {
|
pub(super) fn mirror_expr_inner(&mut self, hir_expr: &'tcx hir::Expr<'tcx>) -> ExprId {
|
||||||
let expr_scope =
|
let expr_scope =
|
||||||
region::Scope { id: hir_expr.hir_id.local_id, data: region::ScopeData::Node };
|
region::Scope { local_id: hir_expr.hir_id.local_id, data: region::ScopeData::Node };
|
||||||
|
|
||||||
trace!(?hir_expr.hir_id, ?hir_expr.span);
|
trace!(?hir_expr.hir_id, ?hir_expr.span);
|
||||||
|
|
||||||
|
@ -814,14 +814,20 @@ impl<'tcx> Cx<'tcx> {
|
||||||
hir::ExprKind::Become(call) => ExprKind::Become { value: self.mirror_expr(call) },
|
hir::ExprKind::Become(call) => ExprKind::Become { value: self.mirror_expr(call) },
|
||||||
hir::ExprKind::Break(dest, ref value) => match dest.target_id {
|
hir::ExprKind::Break(dest, ref value) => match dest.target_id {
|
||||||
Ok(target_id) => ExprKind::Break {
|
Ok(target_id) => ExprKind::Break {
|
||||||
label: region::Scope { id: target_id.local_id, data: region::ScopeData::Node },
|
label: region::Scope {
|
||||||
|
local_id: target_id.local_id,
|
||||||
|
data: region::ScopeData::Node,
|
||||||
|
},
|
||||||
value: value.map(|value| self.mirror_expr(value)),
|
value: value.map(|value| self.mirror_expr(value)),
|
||||||
},
|
},
|
||||||
Err(err) => bug!("invalid loop id for break: {}", err),
|
Err(err) => bug!("invalid loop id for break: {}", err),
|
||||||
},
|
},
|
||||||
hir::ExprKind::Continue(dest) => match dest.target_id {
|
hir::ExprKind::Continue(dest) => match dest.target_id {
|
||||||
Ok(loop_id) => ExprKind::Continue {
|
Ok(loop_id) => ExprKind::Continue {
|
||||||
label: region::Scope { id: loop_id.local_id, data: region::ScopeData::Node },
|
label: region::Scope {
|
||||||
|
local_id: loop_id.local_id,
|
||||||
|
data: region::ScopeData::Node,
|
||||||
|
},
|
||||||
},
|
},
|
||||||
Err(err) => bug!("invalid loop id for continue: {}", err),
|
Err(err) => bug!("invalid loop id for continue: {}", err),
|
||||||
},
|
},
|
||||||
|
@ -831,7 +837,7 @@ impl<'tcx> Cx<'tcx> {
|
||||||
},
|
},
|
||||||
hir::ExprKind::If(cond, then, else_opt) => ExprKind::If {
|
hir::ExprKind::If(cond, then, else_opt) => ExprKind::If {
|
||||||
if_then_scope: region::Scope {
|
if_then_scope: region::Scope {
|
||||||
id: then.hir_id.local_id,
|
local_id: then.hir_id.local_id,
|
||||||
data: {
|
data: {
|
||||||
if expr.span.at_least_rust_2024() {
|
if expr.span.at_least_rust_2024() {
|
||||||
region::ScopeData::IfThenRescope
|
region::ScopeData::IfThenRescope
|
||||||
|
@ -1021,7 +1027,7 @@ impl<'tcx> Cx<'tcx> {
|
||||||
guard: arm.guard.as_ref().map(|g| self.mirror_expr(g)),
|
guard: arm.guard.as_ref().map(|g| self.mirror_expr(g)),
|
||||||
body: self.mirror_expr(arm.body),
|
body: self.mirror_expr(arm.body),
|
||||||
lint_level: LintLevel::Explicit(arm.hir_id),
|
lint_level: LintLevel::Explicit(arm.hir_id),
|
||||||
scope: region::Scope { id: arm.hir_id.local_id, data: region::ScopeData::Node },
|
scope: region::Scope { local_id: arm.hir_id.local_id, data: region::ScopeData::Node },
|
||||||
span: arm.span,
|
span: arm.span,
|
||||||
};
|
};
|
||||||
self.thir.arms.push(arm)
|
self.thir.arms.push(arm)
|
||||||
|
|
|
@ -35,7 +35,6 @@
|
||||||
//! Likewise, applying the optimisation can create a lot of new MIR, so we bound the instruction
|
//! Likewise, applying the optimisation can create a lot of new MIR, so we bound the instruction
|
||||||
//! cost by `MAX_COST`.
|
//! cost by `MAX_COST`.
|
||||||
|
|
||||||
use rustc_abi::{TagEncoding, Variants};
|
|
||||||
use rustc_arena::DroplessArena;
|
use rustc_arena::DroplessArena;
|
||||||
use rustc_const_eval::const_eval::DummyMachine;
|
use rustc_const_eval::const_eval::DummyMachine;
|
||||||
use rustc_const_eval::interpret::{ImmTy, Immediate, InterpCx, OpTy, Projectable};
|
use rustc_const_eval::interpret::{ImmTy, Immediate, InterpCx, OpTy, Projectable};
|
||||||
|
@ -565,31 +564,15 @@ impl<'a, 'tcx> TOFinder<'a, 'tcx> {
|
||||||
StatementKind::SetDiscriminant { box place, variant_index } => {
|
StatementKind::SetDiscriminant { box place, variant_index } => {
|
||||||
let Some(discr_target) = self.map.find_discr(place.as_ref()) else { return };
|
let Some(discr_target) = self.map.find_discr(place.as_ref()) else { return };
|
||||||
let enum_ty = place.ty(self.body, self.tcx).ty;
|
let enum_ty = place.ty(self.body, self.tcx).ty;
|
||||||
// `SetDiscriminant` may be a no-op if the assigned variant is the untagged variant
|
// `SetDiscriminant` guarantees that the discriminant is now `variant_index`.
|
||||||
// of a niche encoding. If we cannot ensure that we write to the discriminant, do
|
// Even if the discriminant write does nothing due to niches, it is UB to set the
|
||||||
// nothing.
|
// discriminant when the data does not encode the desired discriminant.
|
||||||
let Ok(enum_layout) = self.ecx.layout_of(enum_ty) else {
|
let Some(discr) =
|
||||||
|
self.ecx.discriminant_for_variant(enum_ty, *variant_index).discard_err()
|
||||||
|
else {
|
||||||
return;
|
return;
|
||||||
};
|
};
|
||||||
let writes_discriminant = match enum_layout.variants {
|
self.process_immediate(bb, discr_target, discr, state);
|
||||||
Variants::Single { index } => {
|
|
||||||
assert_eq!(index, *variant_index);
|
|
||||||
true
|
|
||||||
}
|
|
||||||
Variants::Multiple { tag_encoding: TagEncoding::Direct, .. } => true,
|
|
||||||
Variants::Multiple {
|
|
||||||
tag_encoding: TagEncoding::Niche { untagged_variant, .. },
|
|
||||||
..
|
|
||||||
} => *variant_index != untagged_variant,
|
|
||||||
};
|
|
||||||
if writes_discriminant {
|
|
||||||
let Some(discr) =
|
|
||||||
self.ecx.discriminant_for_variant(enum_ty, *variant_index).discard_err()
|
|
||||||
else {
|
|
||||||
return;
|
|
||||||
};
|
|
||||||
self.process_immediate(bb, discr_target, discr, state);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
// If we expect `lhs ?= true`, we have an opportunity if we assume `lhs == true`.
|
// If we expect `lhs ?= true`, we have an opportunity if we assume `lhs == true`.
|
||||||
StatementKind::Intrinsic(box NonDivergingIntrinsic::Assume(
|
StatementKind::Intrinsic(box NonDivergingIntrinsic::Assume(
|
||||||
|
|
|
@ -216,7 +216,7 @@ impl EnumSizeOpt {
|
||||||
};
|
};
|
||||||
let layout = tcx.layout_of(typing_env.as_query_input(ty)).ok()?;
|
let layout = tcx.layout_of(typing_env.as_query_input(ty)).ok()?;
|
||||||
let variants = match &layout.variants {
|
let variants = match &layout.variants {
|
||||||
Variants::Single { .. } => return None,
|
Variants::Single { .. } | Variants::Empty => return None,
|
||||||
Variants::Multiple { tag_encoding: TagEncoding::Niche { .. }, .. } => return None,
|
Variants::Multiple { tag_encoding: TagEncoding::Niche { .. }, .. } => return None,
|
||||||
|
|
||||||
Variants::Multiple { variants, .. } if variants.len() <= 1 => return None,
|
Variants::Multiple { variants, .. } if variants.len() <= 1 => return None,
|
||||||
|
|
|
@ -54,6 +54,10 @@ fn variant_discriminants<'tcx>(
|
||||||
tcx: TyCtxt<'tcx>,
|
tcx: TyCtxt<'tcx>,
|
||||||
) -> FxHashSet<u128> {
|
) -> FxHashSet<u128> {
|
||||||
match &layout.variants {
|
match &layout.variants {
|
||||||
|
Variants::Empty => {
|
||||||
|
// Uninhabited, no valid discriminant.
|
||||||
|
FxHashSet::default()
|
||||||
|
}
|
||||||
Variants::Single { index } => {
|
Variants::Single { index } => {
|
||||||
let mut res = FxHashSet::default();
|
let mut res = FxHashSet::default();
|
||||||
res.insert(
|
res.insert(
|
||||||
|
|
|
@ -167,6 +167,7 @@ impl<'tcx> Stable<'tcx> for rustc_abi::Variants<rustc_abi::FieldIdx, rustc_abi::
|
||||||
rustc_abi::Variants::Single { index } => {
|
rustc_abi::Variants::Single { index } => {
|
||||||
VariantsShape::Single { index: index.stable(tables) }
|
VariantsShape::Single { index: index.stable(tables) }
|
||||||
}
|
}
|
||||||
|
rustc_abi::Variants::Empty => VariantsShape::Empty,
|
||||||
rustc_abi::Variants::Multiple { tag, tag_encoding, tag_field, variants } => {
|
rustc_abi::Variants::Multiple { tag, tag_encoding, tag_field, variants } => {
|
||||||
VariantsShape::Multiple {
|
VariantsShape::Multiple {
|
||||||
tag: tag.stable(tables),
|
tag: tag.stable(tables),
|
||||||
|
|
|
@ -116,7 +116,7 @@ where
|
||||||
FieldsShape::Arbitrary { .. } => {
|
FieldsShape::Arbitrary { .. } => {
|
||||||
match arg_layout.variants {
|
match arg_layout.variants {
|
||||||
abi::Variants::Multiple { .. } => return Err(CannotUseFpConv),
|
abi::Variants::Multiple { .. } => return Err(CannotUseFpConv),
|
||||||
abi::Variants::Single { .. } => (),
|
abi::Variants::Single { .. } | abi::Variants::Empty => (),
|
||||||
}
|
}
|
||||||
for i in arg_layout.fields.index_by_increasing_offset() {
|
for i in arg_layout.fields.index_by_increasing_offset() {
|
||||||
let field = arg_layout.field(cx, i);
|
let field = arg_layout.field(cx, i);
|
||||||
|
|
|
@ -122,7 +122,7 @@ where
|
||||||
FieldsShape::Arbitrary { .. } => {
|
FieldsShape::Arbitrary { .. } => {
|
||||||
match arg_layout.variants {
|
match arg_layout.variants {
|
||||||
abi::Variants::Multiple { .. } => return Err(CannotUseFpConv),
|
abi::Variants::Multiple { .. } => return Err(CannotUseFpConv),
|
||||||
abi::Variants::Single { .. } => (),
|
abi::Variants::Single { .. } | abi::Variants::Empty => (),
|
||||||
}
|
}
|
||||||
for i in arg_layout.fields.index_by_increasing_offset() {
|
for i in arg_layout.fields.index_by_increasing_offset() {
|
||||||
let field = arg_layout.field(cx, i);
|
let field = arg_layout.field(cx, i);
|
||||||
|
|
|
@ -65,7 +65,7 @@ where
|
||||||
}
|
}
|
||||||
|
|
||||||
match &layout.variants {
|
match &layout.variants {
|
||||||
abi::Variants::Single { .. } => {}
|
abi::Variants::Single { .. } | abi::Variants::Empty => {}
|
||||||
abi::Variants::Multiple { variants, .. } => {
|
abi::Variants::Multiple { variants, .. } => {
|
||||||
// Treat enum variants like union members.
|
// Treat enum variants like union members.
|
||||||
for variant_idx in variants.indices() {
|
for variant_idx in variants.indices() {
|
||||||
|
|
|
@ -338,16 +338,11 @@ pub(crate) mod rustc {
|
||||||
};
|
};
|
||||||
|
|
||||||
match layout.variants() {
|
match layout.variants() {
|
||||||
|
Variants::Empty => Ok(Self::uninhabited()),
|
||||||
Variants::Single { index } => {
|
Variants::Single { index } => {
|
||||||
// Hilariously, `Single` is used even for 0-variant enums;
|
// `Variants::Single` on enums with variants denotes that
|
||||||
// `index` is just junk in that case.
|
// the enum delegates its layout to the variant at `index`.
|
||||||
if ty.ty_adt_def().unwrap().variants().is_empty() {
|
layout_of_variant(*index, None)
|
||||||
Ok(Self::uninhabited())
|
|
||||||
} else {
|
|
||||||
// `Variants::Single` on enums with variants denotes that
|
|
||||||
// the enum delegates its layout to the variant at `index`.
|
|
||||||
layout_of_variant(*index, None)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
Variants::Multiple { tag, tag_encoding, tag_field, .. } => {
|
Variants::Multiple { tag, tag_encoding, tag_field, .. } => {
|
||||||
// `Variants::Multiple` denotes an enum with multiple
|
// `Variants::Multiple` denotes an enum with multiple
|
||||||
|
@ -500,6 +495,10 @@ pub(crate) mod rustc {
|
||||||
(ty, layout): (Ty<'tcx>, Layout<'tcx>),
|
(ty, layout): (Ty<'tcx>, Layout<'tcx>),
|
||||||
i: FieldIdx,
|
i: FieldIdx,
|
||||||
) -> Ty<'tcx> {
|
) -> Ty<'tcx> {
|
||||||
|
// We cannot use `ty_and_layout_field` to retrieve the field type, since
|
||||||
|
// `ty_and_layout_field` erases regions in the returned type. We must
|
||||||
|
// not erase regions here, since we may need to ultimately emit outlives
|
||||||
|
// obligations as a consequence of the transmutability analysis.
|
||||||
match ty.kind() {
|
match ty.kind() {
|
||||||
ty::Adt(def, args) => {
|
ty::Adt(def, args) => {
|
||||||
match layout.variants {
|
match layout.variants {
|
||||||
|
@ -507,6 +506,7 @@ pub(crate) mod rustc {
|
||||||
let field = &def.variant(index).fields[i];
|
let field = &def.variant(index).fields[i];
|
||||||
field.ty(cx.tcx(), args)
|
field.ty(cx.tcx(), args)
|
||||||
}
|
}
|
||||||
|
Variants::Empty => panic!("there is no field in Variants::Empty types"),
|
||||||
// Discriminant field for enums (where applicable).
|
// Discriminant field for enums (where applicable).
|
||||||
Variants::Multiple { tag, .. } => {
|
Variants::Multiple { tag, .. } => {
|
||||||
assert_eq!(i.as_usize(), 0);
|
assert_eq!(i.as_usize(), 0);
|
||||||
|
|
|
@ -1104,15 +1104,13 @@ fn variant_info_for_adt<'tcx>(
|
||||||
};
|
};
|
||||||
|
|
||||||
match layout.variants {
|
match layout.variants {
|
||||||
|
Variants::Empty => (vec![], None),
|
||||||
|
|
||||||
Variants::Single { index } => {
|
Variants::Single { index } => {
|
||||||
if !adt_def.variants().is_empty() && layout.fields != FieldsShape::Primitive {
|
debug!("print-type-size `{:#?}` variant {}", layout, adt_def.variant(index).name);
|
||||||
debug!("print-type-size `{:#?}` variant {}", layout, adt_def.variant(index).name);
|
let variant_def = &adt_def.variant(index);
|
||||||
let variant_def = &adt_def.variant(index);
|
let fields: Vec<_> = variant_def.fields.iter().map(|f| f.name).collect();
|
||||||
let fields: Vec<_> = variant_def.fields.iter().map(|f| f.name).collect();
|
(vec![build_variant_info(Some(variant_def.name), &fields, layout)], None)
|
||||||
(vec![build_variant_info(Some(variant_def.name), &fields, layout)], None)
|
|
||||||
} else {
|
|
||||||
(vec![], None)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Variants::Multiple { tag, ref tag_encoding, .. } => {
|
Variants::Multiple { tag, ref tag_encoding, .. } => {
|
||||||
|
|
|
@ -241,63 +241,81 @@ pub(super) fn layout_sanity_check<'tcx>(cx: &LayoutCx<'tcx>, layout: &TyAndLayou
|
||||||
|
|
||||||
check_layout_abi(cx, layout);
|
check_layout_abi(cx, layout);
|
||||||
|
|
||||||
if let Variants::Multiple { variants, tag, tag_encoding, .. } = &layout.variants {
|
match &layout.variants {
|
||||||
if let TagEncoding::Niche { niche_start, untagged_variant, niche_variants } = tag_encoding {
|
Variants::Empty => {
|
||||||
let niche_size = tag.size(cx);
|
assert!(layout.is_uninhabited());
|
||||||
assert!(*niche_start <= niche_size.unsigned_int_max());
|
}
|
||||||
for (idx, variant) in variants.iter_enumerated() {
|
Variants::Single { index } => {
|
||||||
// Ensure all inhabited variants are accounted for.
|
if let Some(variants) = layout.ty.variant_range(tcx) {
|
||||||
if !variant.is_uninhabited() {
|
assert!(variants.contains(index));
|
||||||
assert!(idx == *untagged_variant || niche_variants.contains(&idx));
|
} else {
|
||||||
}
|
// Types without variants use `0` as dummy variant index.
|
||||||
|
assert!(index.as_u32() == 0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for variant in variants.iter() {
|
Variants::Multiple { variants, tag, tag_encoding, .. } => {
|
||||||
// No nested "multiple".
|
if let TagEncoding::Niche { niche_start, untagged_variant, niche_variants } =
|
||||||
assert_matches!(variant.variants, Variants::Single { .. });
|
tag_encoding
|
||||||
// Variants should have the same or a smaller size as the full thing,
|
|
||||||
// and same for alignment.
|
|
||||||
if variant.size > layout.size {
|
|
||||||
bug!(
|
|
||||||
"Type with size {} bytes has variant with size {} bytes: {layout:#?}",
|
|
||||||
layout.size.bytes(),
|
|
||||||
variant.size.bytes(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
if variant.align.abi > layout.align.abi {
|
|
||||||
bug!(
|
|
||||||
"Type with alignment {} bytes has variant with alignment {} bytes: {layout:#?}",
|
|
||||||
layout.align.abi.bytes(),
|
|
||||||
variant.align.abi.bytes(),
|
|
||||||
)
|
|
||||||
}
|
|
||||||
// Skip empty variants.
|
|
||||||
if variant.size == Size::ZERO || variant.fields.count() == 0 || variant.is_uninhabited()
|
|
||||||
{
|
{
|
||||||
// These are never actually accessed anyway, so we can skip the coherence check
|
let niche_size = tag.size(cx);
|
||||||
// for them. They also fail that check, since they have
|
assert!(*niche_start <= niche_size.unsigned_int_max());
|
||||||
// `Aggregate`/`Uninhabited` ABI even when the main type is
|
for (idx, variant) in variants.iter_enumerated() {
|
||||||
// `Scalar`/`ScalarPair`. (Note that sometimes, variants with fields have size
|
// Ensure all inhabited variants are accounted for.
|
||||||
// 0, and sometimes, variants without fields have non-0 size.)
|
if !variant.is_uninhabited() {
|
||||||
continue;
|
assert!(idx == *untagged_variant || niche_variants.contains(&idx));
|
||||||
}
|
}
|
||||||
// The top-level ABI and the ABI of the variants should be coherent.
|
}
|
||||||
let scalar_coherent =
|
}
|
||||||
|s1: Scalar, s2: Scalar| s1.size(cx) == s2.size(cx) && s1.align(cx) == s2.align(cx);
|
for variant in variants.iter() {
|
||||||
let abi_coherent = match (layout.backend_repr, variant.backend_repr) {
|
// No nested "multiple".
|
||||||
(BackendRepr::Scalar(s1), BackendRepr::Scalar(s2)) => scalar_coherent(s1, s2),
|
assert_matches!(variant.variants, Variants::Single { .. });
|
||||||
(BackendRepr::ScalarPair(a1, b1), BackendRepr::ScalarPair(a2, b2)) => {
|
// Variants should have the same or a smaller size as the full thing,
|
||||||
scalar_coherent(a1, a2) && scalar_coherent(b1, b2)
|
// and same for alignment.
|
||||||
|
if variant.size > layout.size {
|
||||||
|
bug!(
|
||||||
|
"Type with size {} bytes has variant with size {} bytes: {layout:#?}",
|
||||||
|
layout.size.bytes(),
|
||||||
|
variant.size.bytes(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if variant.align.abi > layout.align.abi {
|
||||||
|
bug!(
|
||||||
|
"Type with alignment {} bytes has variant with alignment {} bytes: {layout:#?}",
|
||||||
|
layout.align.abi.bytes(),
|
||||||
|
variant.align.abi.bytes(),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
// Skip empty variants.
|
||||||
|
if variant.size == Size::ZERO
|
||||||
|
|| variant.fields.count() == 0
|
||||||
|
|| variant.is_uninhabited()
|
||||||
|
{
|
||||||
|
// These are never actually accessed anyway, so we can skip the coherence check
|
||||||
|
// for them. They also fail that check, since they have
|
||||||
|
// `Aggregate`/`Uninhabited` ABI even when the main type is
|
||||||
|
// `Scalar`/`ScalarPair`. (Note that sometimes, variants with fields have size
|
||||||
|
// 0, and sometimes, variants without fields have non-0 size.)
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
// The top-level ABI and the ABI of the variants should be coherent.
|
||||||
|
let scalar_coherent = |s1: Scalar, s2: Scalar| {
|
||||||
|
s1.size(cx) == s2.size(cx) && s1.align(cx) == s2.align(cx)
|
||||||
|
};
|
||||||
|
let abi_coherent = match (layout.backend_repr, variant.backend_repr) {
|
||||||
|
(BackendRepr::Scalar(s1), BackendRepr::Scalar(s2)) => scalar_coherent(s1, s2),
|
||||||
|
(BackendRepr::ScalarPair(a1, b1), BackendRepr::ScalarPair(a2, b2)) => {
|
||||||
|
scalar_coherent(a1, a2) && scalar_coherent(b1, b2)
|
||||||
|
}
|
||||||
|
(BackendRepr::Uninhabited, _) => true,
|
||||||
|
(BackendRepr::Memory { .. }, _) => true,
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
if !abi_coherent {
|
||||||
|
bug!(
|
||||||
|
"Variant ABI is incompatible with top-level ABI:\nvariant={:#?}\nTop-level: {layout:#?}",
|
||||||
|
variant
|
||||||
|
);
|
||||||
}
|
}
|
||||||
(BackendRepr::Uninhabited, _) => true,
|
|
||||||
(BackendRepr::Memory { .. }, _) => true,
|
|
||||||
_ => false,
|
|
||||||
};
|
|
||||||
if !abi_coherent {
|
|
||||||
bug!(
|
|
||||||
"Variant ABI is incompatible with top-level ABI:\nvariant={:#?}\nTop-level: {layout:#?}",
|
|
||||||
variant
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -180,6 +180,9 @@ impl FieldsShape {
|
||||||
|
|
||||||
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)]
|
#[derive(Clone, Debug, PartialEq, Eq, Hash, Serialize)]
|
||||||
pub enum VariantsShape {
|
pub enum VariantsShape {
|
||||||
|
/// A type with no valid variants. Must be uninhabited.
|
||||||
|
Empty,
|
||||||
|
|
||||||
/// Single enum variants, structs/tuples, unions, and all non-ADTs.
|
/// Single enum variants, structs/tuples, unions, and all non-ADTs.
|
||||||
Single { index: VariantIdx },
|
Single { index: VariantIdx },
|
||||||
|
|
||||||
|
|
|
@ -1897,12 +1897,6 @@ impl Step for Assemble {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
let lld_install = if builder.config.lld_enabled {
|
|
||||||
Some(builder.ensure(llvm::Lld { target: target_compiler.host }))
|
|
||||||
} else {
|
|
||||||
None
|
|
||||||
};
|
|
||||||
|
|
||||||
let stage = target_compiler.stage;
|
let stage = target_compiler.stage;
|
||||||
let host = target_compiler.host;
|
let host = target_compiler.host;
|
||||||
let (host_info, dir_name) = if build_compiler.host == host {
|
let (host_info, dir_name) = if build_compiler.host == host {
|
||||||
|
@ -1963,22 +1957,11 @@ impl Step for Assemble {
|
||||||
|
|
||||||
copy_codegen_backends_to_sysroot(builder, build_compiler, target_compiler);
|
copy_codegen_backends_to_sysroot(builder, build_compiler, target_compiler);
|
||||||
|
|
||||||
if let Some(lld_install) = lld_install {
|
if builder.config.lld_enabled {
|
||||||
let src_exe = exe("lld", target_compiler.host);
|
builder.ensure(crate::core::build_steps::tool::LldWrapper {
|
||||||
let dst_exe = exe("rust-lld", target_compiler.host);
|
build_compiler,
|
||||||
builder.copy_link(&lld_install.join("bin").join(src_exe), &libdir_bin.join(dst_exe));
|
target_compiler,
|
||||||
let self_contained_lld_dir = libdir_bin.join("gcc-ld");
|
|
||||||
t!(fs::create_dir_all(&self_contained_lld_dir));
|
|
||||||
let lld_wrapper_exe = builder.ensure(crate::core::build_steps::tool::LldWrapper {
|
|
||||||
compiler: build_compiler,
|
|
||||||
target: target_compiler.host,
|
|
||||||
});
|
});
|
||||||
for name in crate::LLD_FILE_NAMES {
|
|
||||||
builder.copy_link(
|
|
||||||
&lld_wrapper_exe,
|
|
||||||
&self_contained_lld_dir.join(exe(name, target_compiler.host)),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if builder.config.llvm_enabled(target_compiler.host) && builder.config.llvm_tools_enabled {
|
if builder.config.llvm_enabled(target_compiler.host) && builder.config.llvm_tools_enabled {
|
||||||
|
|
|
@ -1,8 +1,8 @@
|
||||||
use std::path::PathBuf;
|
use std::path::PathBuf;
|
||||||
use std::{env, fs};
|
use std::{env, fs};
|
||||||
|
|
||||||
use crate::core::build_steps::compile;
|
|
||||||
use crate::core::build_steps::toolstate::ToolState;
|
use crate::core::build_steps::toolstate::ToolState;
|
||||||
|
use crate::core::build_steps::{compile, llvm};
|
||||||
use crate::core::builder;
|
use crate::core::builder;
|
||||||
use crate::core::builder::{Builder, Cargo as CargoCommand, RunConfig, ShouldRun, Step};
|
use crate::core::builder::{Builder, Cargo as CargoCommand, RunConfig, ShouldRun, Step};
|
||||||
use crate::core::config::TargetSelection;
|
use crate::core::config::TargetSelection;
|
||||||
|
@ -722,21 +722,27 @@ impl Step for Cargo {
|
||||||
|
|
||||||
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
|
#[derive(Debug, Clone, Hash, PartialEq, Eq)]
|
||||||
pub struct LldWrapper {
|
pub struct LldWrapper {
|
||||||
pub compiler: Compiler,
|
pub build_compiler: Compiler,
|
||||||
pub target: TargetSelection,
|
pub target_compiler: Compiler,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Step for LldWrapper {
|
impl Step for LldWrapper {
|
||||||
type Output = PathBuf;
|
type Output = ();
|
||||||
|
|
||||||
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
|
fn should_run(run: ShouldRun<'_>) -> ShouldRun<'_> {
|
||||||
run.never()
|
run.never()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn run(self, builder: &Builder<'_>) -> PathBuf {
|
fn run(self, builder: &Builder<'_>) {
|
||||||
builder.ensure(ToolBuild {
|
if builder.config.dry_run() {
|
||||||
compiler: self.compiler,
|
return;
|
||||||
target: self.target,
|
}
|
||||||
|
|
||||||
|
let target = self.target_compiler.host;
|
||||||
|
|
||||||
|
let executable = builder.ensure(ToolBuild {
|
||||||
|
compiler: self.build_compiler,
|
||||||
|
target,
|
||||||
tool: "lld-wrapper",
|
tool: "lld-wrapper",
|
||||||
mode: Mode::ToolStd,
|
mode: Mode::ToolStd,
|
||||||
path: "src/tools/lld-wrapper",
|
path: "src/tools/lld-wrapper",
|
||||||
|
@ -744,7 +750,22 @@ impl Step for LldWrapper {
|
||||||
extra_features: Vec::new(),
|
extra_features: Vec::new(),
|
||||||
allow_features: "",
|
allow_features: "",
|
||||||
cargo_args: Vec::new(),
|
cargo_args: Vec::new(),
|
||||||
})
|
});
|
||||||
|
|
||||||
|
let libdir_bin = builder.sysroot_target_bindir(self.target_compiler, target);
|
||||||
|
t!(fs::create_dir_all(&libdir_bin));
|
||||||
|
|
||||||
|
let lld_install = builder.ensure(llvm::Lld { target });
|
||||||
|
let src_exe = exe("lld", target);
|
||||||
|
let dst_exe = exe("rust-lld", target);
|
||||||
|
|
||||||
|
builder.copy_link(&lld_install.join("bin").join(src_exe), &libdir_bin.join(dst_exe));
|
||||||
|
let self_contained_lld_dir = libdir_bin.join("gcc-ld");
|
||||||
|
t!(fs::create_dir_all(&self_contained_lld_dir));
|
||||||
|
|
||||||
|
for name in crate::LLD_FILE_NAMES {
|
||||||
|
builder.copy_link(&executable, &self_contained_lld_dir.join(exe(name, target)));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -28,4 +28,6 @@ RUN echo "optimize = false" >> /config/nopt-std-config.toml
|
||||||
|
|
||||||
ENV RUST_CONFIGURE_ARGS --build=i686-unknown-linux-gnu --disable-optimize-tests
|
ENV RUST_CONFIGURE_ARGS --build=i686-unknown-linux-gnu --disable-optimize-tests
|
||||||
ARG SCRIPT_ARG
|
ARG SCRIPT_ARG
|
||||||
ENV SCRIPT=${SCRIPT_ARG}
|
COPY scripts/stage_2_test_set1.sh /scripts/
|
||||||
|
COPY scripts/stage_2_test_set2.sh /scripts/
|
||||||
|
ENV SCRIPT ${SCRIPT_ARG}
|
||||||
|
|
|
@ -25,4 +25,6 @@ RUN sh /scripts/sccache.sh
|
||||||
|
|
||||||
ENV RUST_CONFIGURE_ARGS --build=i686-unknown-linux-gnu
|
ENV RUST_CONFIGURE_ARGS --build=i686-unknown-linux-gnu
|
||||||
ARG SCRIPT_ARG
|
ARG SCRIPT_ARG
|
||||||
ENV SCRIPT=${SCRIPT_ARG}
|
COPY scripts/stage_2_test_set1.sh /scripts/
|
||||||
|
COPY scripts/stage_2_test_set2.sh /scripts/
|
||||||
|
ENV SCRIPT /scripts/${SCRIPT_ARG}
|
||||||
|
|
|
@ -60,9 +60,12 @@ COPY scripts/build-gccjit.sh /scripts/
|
||||||
RUN /scripts/build-gccjit.sh /scripts
|
RUN /scripts/build-gccjit.sh /scripts
|
||||||
|
|
||||||
ARG SCRIPT_ARG
|
ARG SCRIPT_ARG
|
||||||
COPY scripts/add_dummy_commit.sh /tmp/add_dummy_commit.sh
|
|
||||||
COPY scripts/x86_64-gnu-llvm.sh /tmp/x86_64-gnu-llvm.sh
|
COPY scripts/add_dummy_commit.sh /tmp/
|
||||||
COPY scripts/x86_64-gnu-llvm1.sh /tmp/x86_64-gnu-llvm1.sh
|
COPY scripts/x86_64-gnu-llvm.sh /tmp/
|
||||||
COPY scripts/x86_64-gnu-llvm2.sh /tmp/x86_64-gnu-llvm2.sh
|
COPY scripts/x86_64-gnu-llvm2.sh /tmp/
|
||||||
COPY scripts/x86_64-gnu-llvm3.sh /tmp/x86_64-gnu-llvm3.sh
|
COPY scripts/x86_64-gnu-llvm3.sh /tmp/
|
||||||
ENV SCRIPT /tmp/${SCRIPT_ARG}
|
COPY scripts/stage_2_test_set1.sh /tmp/
|
||||||
|
COPY scripts/stage_2_test_set2.sh /tmp/
|
||||||
|
|
||||||
|
ENV SCRIPT "/tmp/add_dummy_commit.sh && /tmp/${SCRIPT_ARG}"
|
||||||
|
|
|
@ -60,9 +60,12 @@ COPY scripts/build-gccjit.sh /scripts/
|
||||||
RUN /scripts/build-gccjit.sh /scripts
|
RUN /scripts/build-gccjit.sh /scripts
|
||||||
|
|
||||||
ARG SCRIPT_ARG
|
ARG SCRIPT_ARG
|
||||||
COPY scripts/add_dummy_commit.sh /tmp/add_dummy_commit.sh
|
|
||||||
COPY scripts/x86_64-gnu-llvm.sh /tmp/x86_64-gnu-llvm.sh
|
COPY scripts/add_dummy_commit.sh /tmp/
|
||||||
COPY scripts/x86_64-gnu-llvm1.sh /tmp/x86_64-gnu-llvm1.sh
|
COPY scripts/x86_64-gnu-llvm.sh /tmp/
|
||||||
COPY scripts/x86_64-gnu-llvm2.sh /tmp/x86_64-gnu-llvm2.sh
|
COPY scripts/x86_64-gnu-llvm2.sh /tmp/
|
||||||
COPY scripts/x86_64-gnu-llvm3.sh /tmp/x86_64-gnu-llvm3.sh
|
COPY scripts/x86_64-gnu-llvm3.sh /tmp/
|
||||||
ENV SCRIPT /tmp/${SCRIPT_ARG}
|
COPY scripts/stage_2_test_set1.sh /tmp/
|
||||||
|
COPY scripts/stage_2_test_set2.sh /tmp/
|
||||||
|
|
||||||
|
ENV SCRIPT "/tmp/add_dummy_commit.sh && /tmp/${SCRIPT_ARG}"
|
||||||
|
|
9
src/ci/docker/scripts/stage_2_test_set1.sh
Executable file
9
src/ci/docker/scripts/stage_2_test_set1.sh
Executable file
|
@ -0,0 +1,9 @@
|
||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
set -ex
|
||||||
|
|
||||||
|
# Run a subset of tests. Used to run tests in parallel in multiple jobs.
|
||||||
|
|
||||||
|
../x.py --stage 2 test \
|
||||||
|
--skip compiler \
|
||||||
|
--skip src
|
|
@ -2,7 +2,7 @@
|
||||||
|
|
||||||
set -ex
|
set -ex
|
||||||
|
|
||||||
/tmp/add_dummy_commit.sh
|
# Run a subset of tests. Used to run tests in parallel in multiple jobs.
|
||||||
|
|
||||||
../x.py --stage 2 test \
|
../x.py --stage 2 test \
|
||||||
--skip tests \
|
--skip tests \
|
|
@ -2,8 +2,6 @@
|
||||||
|
|
||||||
set -ex
|
set -ex
|
||||||
|
|
||||||
/tmp/add_dummy_commit.sh
|
|
||||||
|
|
||||||
# NOTE: intentionally uses all of `x.py`, `x`, and `x.ps1` to make sure they all work on Linux.
|
# NOTE: intentionally uses all of `x.py`, `x`, and `x.ps1` to make sure they all work on Linux.
|
||||||
../x.py --stage 2 test --skip src/tools/tidy
|
../x.py --stage 2 test --skip src/tools/tidy
|
||||||
|
|
||||||
|
|
|
@ -2,13 +2,9 @@
|
||||||
|
|
||||||
set -ex
|
set -ex
|
||||||
|
|
||||||
/tmp/add_dummy_commit.sh
|
|
||||||
|
|
||||||
##### Test stage 2 #####
|
##### Test stage 2 #####
|
||||||
|
|
||||||
../x.py --stage 2 test \
|
/tmp/stage_2_test_set1.sh
|
||||||
--skip compiler \
|
|
||||||
--skip src
|
|
||||||
|
|
||||||
# Run the `mir-opt` tests again but this time for a 32-bit target.
|
# Run the `mir-opt` tests again but this time for a 32-bit target.
|
||||||
# This enforces that tests using `// EMIT_MIR_FOR_EACH_BIT_WIDTH` have
|
# This enforces that tests using `// EMIT_MIR_FOR_EACH_BIT_WIDTH` have
|
||||||
|
|
|
@ -58,22 +58,6 @@ envs:
|
||||||
NO_DEBUG_ASSERTIONS: 1
|
NO_DEBUG_ASSERTIONS: 1
|
||||||
NO_OVERFLOW_CHECKS: 1
|
NO_OVERFLOW_CHECKS: 1
|
||||||
|
|
||||||
# Different set of tests to run tests in parallel in multiple jobs.
|
|
||||||
stage_2_test_set1: &stage_2_test_set1
|
|
||||||
DOCKER_SCRIPT: >-
|
|
||||||
python3 ../x.py --stage 2 test
|
|
||||||
--skip compiler
|
|
||||||
--skip src
|
|
||||||
|
|
||||||
stage_2_test_set2: &stage_2_test_set2
|
|
||||||
DOCKER_SCRIPT: >-
|
|
||||||
python3 ../x.py --stage 2 test
|
|
||||||
--skip tests
|
|
||||||
--skip coverage-map
|
|
||||||
--skip coverage-run
|
|
||||||
--skip library
|
|
||||||
--skip tidyselftest
|
|
||||||
|
|
||||||
production:
|
production:
|
||||||
&production
|
&production
|
||||||
DEPLOY_BUCKET: rust-lang-ci2
|
DEPLOY_BUCKET: rust-lang-ci2
|
||||||
|
@ -234,14 +218,14 @@ auto:
|
||||||
- image: i686-gnu-1
|
- image: i686-gnu-1
|
||||||
env:
|
env:
|
||||||
IMAGE: i686-gnu
|
IMAGE: i686-gnu
|
||||||
<<: *stage_2_test_set1
|
DOCKER_SCRIPT: stage_2_test_set1.sh
|
||||||
<<: *job-linux-4c
|
<<: *job-linux-4c
|
||||||
|
|
||||||
# Skip tests that run in i686-gnu-1
|
# Skip tests that run in i686-gnu-1
|
||||||
- image: i686-gnu-2
|
- image: i686-gnu-2
|
||||||
env:
|
env:
|
||||||
IMAGE: i686-gnu
|
IMAGE: i686-gnu
|
||||||
<<: *stage_2_test_set2
|
DOCKER_SCRIPT: stage_2_test_set2.sh
|
||||||
<<: *job-linux-4c
|
<<: *job-linux-4c
|
||||||
|
|
||||||
# The i686-gnu-nopt job is split into multiple jobs to run tests in parallel.
|
# The i686-gnu-nopt job is split into multiple jobs to run tests in parallel.
|
||||||
|
@ -249,7 +233,7 @@ auto:
|
||||||
- image: i686-gnu-nopt-1
|
- image: i686-gnu-nopt-1
|
||||||
env:
|
env:
|
||||||
IMAGE: i686-gnu-nopt
|
IMAGE: i686-gnu-nopt
|
||||||
<<: *stage_2_test_set1
|
DOCKER_SCRIPT: /scripts/stage_2_test_set1.sh
|
||||||
<<: *job-linux-4c
|
<<: *job-linux-4c
|
||||||
|
|
||||||
# Skip tests that run in i686-gnu-nopt-1
|
# Skip tests that run in i686-gnu-nopt-1
|
||||||
|
@ -258,12 +242,7 @@ auto:
|
||||||
IMAGE: i686-gnu-nopt
|
IMAGE: i686-gnu-nopt
|
||||||
DOCKER_SCRIPT: >-
|
DOCKER_SCRIPT: >-
|
||||||
python3 ../x.py test --stage 0 --config /config/nopt-std-config.toml library/std &&
|
python3 ../x.py test --stage 0 --config /config/nopt-std-config.toml library/std &&
|
||||||
python3 ../x.py --stage 2 test
|
/scripts/stage_2_test_set2.sh
|
||||||
--skip tests
|
|
||||||
--skip coverage-map
|
|
||||||
--skip coverage-run
|
|
||||||
--skip library
|
|
||||||
--skip tidyselftest
|
|
||||||
<<: *job-linux-4c
|
<<: *job-linux-4c
|
||||||
|
|
||||||
- image: mingw-check
|
- image: mingw-check
|
||||||
|
@ -319,7 +298,7 @@ auto:
|
||||||
env:
|
env:
|
||||||
RUST_BACKTRACE: 1
|
RUST_BACKTRACE: 1
|
||||||
IMAGE: x86_64-gnu-llvm-19
|
IMAGE: x86_64-gnu-llvm-19
|
||||||
DOCKER_SCRIPT: x86_64-gnu-llvm1.sh
|
DOCKER_SCRIPT: stage_2_test_set1.sh
|
||||||
<<: *job-linux-4c
|
<<: *job-linux-4c
|
||||||
|
|
||||||
# Skip tests that run in x86_64-gnu-llvm-19-{1,3}
|
# Skip tests that run in x86_64-gnu-llvm-19-{1,3}
|
||||||
|
@ -345,7 +324,7 @@ auto:
|
||||||
RUST_BACKTRACE: 1
|
RUST_BACKTRACE: 1
|
||||||
READ_ONLY_SRC: "0"
|
READ_ONLY_SRC: "0"
|
||||||
IMAGE: x86_64-gnu-llvm-18
|
IMAGE: x86_64-gnu-llvm-18
|
||||||
DOCKER_SCRIPT: x86_64-gnu-llvm1.sh
|
DOCKER_SCRIPT: stage_2_test_set1.sh
|
||||||
<<: *job-linux-4c
|
<<: *job-linux-4c
|
||||||
|
|
||||||
# Skip tests that run in x86_64-gnu-llvm-18-{1,3}
|
# Skip tests that run in x86_64-gnu-llvm-18-{1,3}
|
||||||
|
|
|
@ -344,35 +344,48 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for CodeBlocks<'_, 'a, I> {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Make headings links with anchor IDs and build up TOC.
|
/// Make headings links with anchor IDs and build up TOC.
|
||||||
struct LinkReplacer<'a, I: Iterator<Item = Event<'a>>> {
|
struct LinkReplacerInner<'a> {
|
||||||
inner: I,
|
|
||||||
links: &'a [RenderedLink],
|
links: &'a [RenderedLink],
|
||||||
shortcut_link: Option<&'a RenderedLink>,
|
shortcut_link: Option<&'a RenderedLink>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct LinkReplacer<'a, I: Iterator<Item = Event<'a>>> {
|
||||||
|
iter: I,
|
||||||
|
inner: LinkReplacerInner<'a>,
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a, I: Iterator<Item = Event<'a>>> LinkReplacer<'a, I> {
|
impl<'a, I: Iterator<Item = Event<'a>>> LinkReplacer<'a, I> {
|
||||||
fn new(iter: I, links: &'a [RenderedLink]) -> Self {
|
fn new(iter: I, links: &'a [RenderedLink]) -> Self {
|
||||||
LinkReplacer { inner: iter, links, shortcut_link: None }
|
LinkReplacer { iter, inner: { LinkReplacerInner { links, shortcut_link: None } } }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'a, I: Iterator<Item = Event<'a>>> Iterator for LinkReplacer<'a, I> {
|
// FIXME: Once we have specialized trait impl (for `Iterator` impl on `LinkReplacer`),
|
||||||
type Item = Event<'a>;
|
// we can remove this type and move back `LinkReplacerInner` fields into `LinkReplacer`.
|
||||||
|
struct SpannedLinkReplacer<'a, I: Iterator<Item = SpannedEvent<'a>>> {
|
||||||
|
iter: I,
|
||||||
|
inner: LinkReplacerInner<'a>,
|
||||||
|
}
|
||||||
|
|
||||||
fn next(&mut self) -> Option<Self::Item> {
|
impl<'a, I: Iterator<Item = SpannedEvent<'a>>> SpannedLinkReplacer<'a, I> {
|
||||||
let mut event = self.inner.next();
|
fn new(iter: I, links: &'a [RenderedLink]) -> Self {
|
||||||
|
SpannedLinkReplacer { iter, inner: { LinkReplacerInner { links, shortcut_link: None } } }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a> LinkReplacerInner<'a> {
|
||||||
|
fn handle_event(&mut self, event: &mut Event<'a>) {
|
||||||
// Replace intra-doc links and remove disambiguators from shortcut links (`[fn@f]`).
|
// Replace intra-doc links and remove disambiguators from shortcut links (`[fn@f]`).
|
||||||
match &mut event {
|
match event {
|
||||||
// This is a shortcut link that was resolved by the broken_link_callback: `[fn@f]`
|
// This is a shortcut link that was resolved by the broken_link_callback: `[fn@f]`
|
||||||
// Remove any disambiguator.
|
// Remove any disambiguator.
|
||||||
Some(Event::Start(Tag::Link {
|
Event::Start(Tag::Link {
|
||||||
// [fn@f] or [fn@f][]
|
// [fn@f] or [fn@f][]
|
||||||
link_type: LinkType::ShortcutUnknown | LinkType::CollapsedUnknown,
|
link_type: LinkType::ShortcutUnknown | LinkType::CollapsedUnknown,
|
||||||
dest_url,
|
dest_url,
|
||||||
title,
|
title,
|
||||||
..
|
..
|
||||||
})) => {
|
}) => {
|
||||||
debug!("saw start of shortcut link to {dest_url} with title {title}");
|
debug!("saw start of shortcut link to {dest_url} with title {title}");
|
||||||
// If this is a shortcut link, it was resolved by the broken_link_callback.
|
// If this is a shortcut link, it was resolved by the broken_link_callback.
|
||||||
// So the URL will already be updated properly.
|
// So the URL will already be updated properly.
|
||||||
|
@ -389,13 +402,13 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for LinkReplacer<'a, I> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// Now that we're done with the shortcut link, don't replace any more text.
|
// Now that we're done with the shortcut link, don't replace any more text.
|
||||||
Some(Event::End(TagEnd::Link)) if self.shortcut_link.is_some() => {
|
Event::End(TagEnd::Link) if self.shortcut_link.is_some() => {
|
||||||
debug!("saw end of shortcut link");
|
debug!("saw end of shortcut link");
|
||||||
self.shortcut_link = None;
|
self.shortcut_link = None;
|
||||||
}
|
}
|
||||||
// Handle backticks in inline code blocks, but only if we're in the middle of a shortcut link.
|
// Handle backticks in inline code blocks, but only if we're in the middle of a shortcut link.
|
||||||
// [`fn@f`]
|
// [`fn@f`]
|
||||||
Some(Event::Code(text)) => {
|
Event::Code(text) => {
|
||||||
trace!("saw code {text}");
|
trace!("saw code {text}");
|
||||||
if let Some(link) = self.shortcut_link {
|
if let Some(link) = self.shortcut_link {
|
||||||
// NOTE: this only replaces if the code block is the *entire* text.
|
// NOTE: this only replaces if the code block is the *entire* text.
|
||||||
|
@ -418,7 +431,7 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for LinkReplacer<'a, I> {
|
||||||
}
|
}
|
||||||
// Replace plain text in links, but only in the middle of a shortcut link.
|
// Replace plain text in links, but only in the middle of a shortcut link.
|
||||||
// [fn@f]
|
// [fn@f]
|
||||||
Some(Event::Text(text)) => {
|
Event::Text(text) => {
|
||||||
trace!("saw text {text}");
|
trace!("saw text {text}");
|
||||||
if let Some(link) = self.shortcut_link {
|
if let Some(link) = self.shortcut_link {
|
||||||
// NOTE: same limitations as `Event::Code`
|
// NOTE: same limitations as `Event::Code`
|
||||||
|
@ -434,7 +447,7 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for LinkReplacer<'a, I> {
|
||||||
}
|
}
|
||||||
// If this is a link, but not a shortcut link,
|
// If this is a link, but not a shortcut link,
|
||||||
// replace the URL, since the broken_link_callback was not called.
|
// replace the URL, since the broken_link_callback was not called.
|
||||||
Some(Event::Start(Tag::Link { dest_url, title, .. })) => {
|
Event::Start(Tag::Link { dest_url, title, .. }) => {
|
||||||
if let Some(link) =
|
if let Some(link) =
|
||||||
self.links.iter().find(|&link| *link.original_text == **dest_url)
|
self.links.iter().find(|&link| *link.original_text == **dest_url)
|
||||||
{
|
{
|
||||||
|
@ -447,12 +460,33 @@ impl<'a, I: Iterator<Item = Event<'a>>> Iterator for LinkReplacer<'a, I> {
|
||||||
// Anything else couldn't have been a valid Rust path, so no need to replace the text.
|
// Anything else couldn't have been a valid Rust path, so no need to replace the text.
|
||||||
_ => {}
|
_ => {}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'a, I: Iterator<Item = Event<'a>>> Iterator for LinkReplacer<'a, I> {
|
||||||
|
type Item = Event<'a>;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
let mut event = self.iter.next();
|
||||||
|
if let Some(ref mut event) = event {
|
||||||
|
self.inner.handle_event(event);
|
||||||
|
}
|
||||||
// Yield the modified event
|
// Yield the modified event
|
||||||
event
|
event
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'a, I: Iterator<Item = SpannedEvent<'a>>> Iterator for SpannedLinkReplacer<'a, I> {
|
||||||
|
type Item = SpannedEvent<'a>;
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
let Some((mut event, range)) = self.iter.next() else { return None };
|
||||||
|
self.inner.handle_event(&mut event);
|
||||||
|
// Yield the modified event
|
||||||
|
Some((event, range))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// Wrap HTML tables into `<div>` to prevent having the doc blocks width being too big.
|
/// Wrap HTML tables into `<div>` to prevent having the doc blocks width being too big.
|
||||||
struct TableWrapper<'a, I: Iterator<Item = Event<'a>>> {
|
struct TableWrapper<'a, I: Iterator<Item = Event<'a>>> {
|
||||||
inner: I,
|
inner: I,
|
||||||
|
@ -1339,9 +1373,9 @@ impl<'a> Markdown<'a> {
|
||||||
|
|
||||||
ids.handle_footnotes(|ids, existing_footnotes| {
|
ids.handle_footnotes(|ids, existing_footnotes| {
|
||||||
let p = HeadingLinks::new(p, None, ids, heading_offset);
|
let p = HeadingLinks::new(p, None, ids, heading_offset);
|
||||||
|
let p = SpannedLinkReplacer::new(p, links);
|
||||||
let p = footnotes::Footnotes::new(p, existing_footnotes);
|
let p = footnotes::Footnotes::new(p, existing_footnotes);
|
||||||
let p = LinkReplacer::new(p.map(|(ev, _)| ev), links);
|
let p = TableWrapper::new(p.map(|(ev, _)| ev));
|
||||||
let p = TableWrapper::new(p);
|
|
||||||
CodeBlocks::new(p, codes, edition, playground)
|
CodeBlocks::new(p, codes, edition, playground)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -605,7 +605,7 @@ pub trait EvalContextExt<'tcx>: crate::MiriInterpCxExt<'tcx> {
|
||||||
// `UnsafeCell` action.
|
// `UnsafeCell` action.
|
||||||
(self.unsafe_cell_action)(v)
|
(self.unsafe_cell_action)(v)
|
||||||
}
|
}
|
||||||
Variants::Single { .. } => {
|
Variants::Single { .. } | Variants::Empty => {
|
||||||
// Proceed further, try to find where exactly that `UnsafeCell`
|
// Proceed further, try to find where exactly that `UnsafeCell`
|
||||||
// is hiding.
|
// is hiding.
|
||||||
self.walk_value(v)
|
self.walk_value(v)
|
||||||
|
|
|
@ -813,7 +813,7 @@ impl Evaluator<'_> {
|
||||||
ProjectionElem::Field(Either::Left(f)) => {
|
ProjectionElem::Field(Either::Left(f)) => {
|
||||||
let layout = self.layout(&prev_ty)?;
|
let layout = self.layout(&prev_ty)?;
|
||||||
let variant_layout = match &layout.variants {
|
let variant_layout = match &layout.variants {
|
||||||
Variants::Single { .. } => &layout,
|
Variants::Single { .. } | Variants::Empty => &layout,
|
||||||
Variants::Multiple { variants, .. } => {
|
Variants::Multiple { variants, .. } => {
|
||||||
&variants[match f.parent {
|
&variants[match f.parent {
|
||||||
hir_def::VariantId::EnumVariantId(it) => {
|
hir_def::VariantId::EnumVariantId(it) => {
|
||||||
|
@ -1638,6 +1638,7 @@ impl Evaluator<'_> {
|
||||||
return Ok(0);
|
return Ok(0);
|
||||||
};
|
};
|
||||||
match &layout.variants {
|
match &layout.variants {
|
||||||
|
Variants::Empty => unreachable!(),
|
||||||
Variants::Single { index } => {
|
Variants::Single { index } => {
|
||||||
let r = self.const_eval_discriminant(self.db.enum_data(e).variants[index.0].0)?;
|
let r = self.const_eval_discriminant(self.db.enum_data(e).variants[index.0].0)?;
|
||||||
Ok(r)
|
Ok(r)
|
||||||
|
@ -1800,7 +1801,7 @@ impl Evaluator<'_> {
|
||||||
}
|
}
|
||||||
let layout = self.layout_adt(adt, subst)?;
|
let layout = self.layout_adt(adt, subst)?;
|
||||||
Ok(match &layout.variants {
|
Ok(match &layout.variants {
|
||||||
Variants::Single { .. } => (layout.size.bytes_usize(), layout, None),
|
Variants::Single { .. } | Variants::Empty => (layout.size.bytes_usize(), layout, None),
|
||||||
Variants::Multiple { variants, tag, tag_encoding, .. } => {
|
Variants::Multiple { variants, tag, tag_encoding, .. } => {
|
||||||
let enum_variant_id = match it {
|
let enum_variant_id = match it {
|
||||||
VariantId::EnumVariantId(it) => it,
|
VariantId::EnumVariantId(it) => it,
|
||||||
|
|
|
@ -334,6 +334,7 @@ pub(crate) fn detect_variant_from_bytes<'a>(
|
||||||
e: EnumId,
|
e: EnumId,
|
||||||
) -> Option<(EnumVariantId, &'a Layout)> {
|
) -> Option<(EnumVariantId, &'a Layout)> {
|
||||||
let (var_id, var_layout) = match &layout.variants {
|
let (var_id, var_layout) = match &layout.variants {
|
||||||
|
hir_def::layout::Variants::Empty => unreachable!(),
|
||||||
hir_def::layout::Variants::Single { index } => {
|
hir_def::layout::Variants::Single { index } => {
|
||||||
(db.enum_data(e).variants[index.0].0, layout)
|
(db.enum_data(e).variants[index.0].0, layout)
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,8 @@
|
||||||
_2 = E::<char>::A;
|
_2 = E::<char>::A;
|
||||||
discriminant(_2) = 1;
|
discriminant(_2) = 1;
|
||||||
_1 = discriminant(_2);
|
_1 = discriminant(_2);
|
||||||
switchInt(copy _1) -> [0: bb1, otherwise: bb2];
|
- switchInt(copy _1) -> [0: bb1, otherwise: bb2];
|
||||||
|
+ goto -> bb2;
|
||||||
}
|
}
|
||||||
|
|
||||||
bb1: {
|
bb1: {
|
||||||
|
|
|
@ -10,7 +10,8 @@
|
||||||
_2 = E::<T>::A;
|
_2 = E::<T>::A;
|
||||||
discriminant(_2) = 1;
|
discriminant(_2) = 1;
|
||||||
_1 = discriminant(_2);
|
_1 = discriminant(_2);
|
||||||
switchInt(copy _1) -> [0: bb1, otherwise: bb2];
|
- switchInt(copy _1) -> [0: bb1, otherwise: bb2];
|
||||||
|
+ goto -> bb2;
|
||||||
}
|
}
|
||||||
|
|
||||||
bb1: {
|
bb1: {
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
// `SetDiscriminant` does not actually write anything if the chosen variant is the untagged variant
|
// `SetDiscriminant` does not actually write anything if the chosen variant is the untagged variant
|
||||||
// of a niche encoding. Verify that we do not thread over this case.
|
// of a niche encoding. However, it is UB to call `SetDiscriminant` with the untagged variant if the
|
||||||
|
// value currently encodes a different variant. Verify that we do correctly thread in this case.
|
||||||
//@ test-mir-pass: JumpThreading
|
//@ test-mir-pass: JumpThreading
|
||||||
|
|
||||||
#![feature(custom_mir)]
|
#![feature(custom_mir)]
|
||||||
|
@ -16,20 +17,21 @@ enum E<T> {
|
||||||
#[custom_mir(dialect = "runtime")]
|
#[custom_mir(dialect = "runtime")]
|
||||||
pub fn f() -> usize {
|
pub fn f() -> usize {
|
||||||
// CHECK-LABEL: fn f(
|
// CHECK-LABEL: fn f(
|
||||||
// CHECK-NOT: goto
|
// CHECK-NOT: switchInt
|
||||||
// CHECK: switchInt(
|
// CHECK: goto
|
||||||
// CHECK-NOT: goto
|
// CHECK-NOT: switchInt
|
||||||
mir! {
|
mir! {
|
||||||
let a: isize;
|
let a: isize;
|
||||||
let e: E<char>;
|
let e: E<char>;
|
||||||
{
|
{
|
||||||
e = E::A;
|
e = E::A;
|
||||||
SetDiscriminant(e, 1);
|
SetDiscriminant(e, 1); // UB!
|
||||||
a = Discriminant(e);
|
a = Discriminant(e);
|
||||||
match a {
|
match a {
|
||||||
0 => bb0,
|
0 => bb0,
|
||||||
_ => bb1,
|
_ => bb1,
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
bb0 = {
|
bb0 = {
|
||||||
RET = 0;
|
RET = 0;
|
||||||
|
@ -46,15 +48,15 @@ pub fn f() -> usize {
|
||||||
#[custom_mir(dialect = "runtime")]
|
#[custom_mir(dialect = "runtime")]
|
||||||
pub fn generic<T>() -> usize {
|
pub fn generic<T>() -> usize {
|
||||||
// CHECK-LABEL: fn generic(
|
// CHECK-LABEL: fn generic(
|
||||||
// CHECK-NOT: goto
|
// CHECK-NOT: switchInt
|
||||||
// CHECK: switchInt(
|
// CHECK: goto
|
||||||
// CHECK-NOT: goto
|
// CHECK-NOT: switchInt
|
||||||
mir! {
|
mir! {
|
||||||
let a: isize;
|
let a: isize;
|
||||||
let e: E<T>;
|
let e: E<T>;
|
||||||
{
|
{
|
||||||
e = E::A;
|
e = E::A;
|
||||||
SetDiscriminant(e, 1);
|
SetDiscriminant(e, 1); // UB!
|
||||||
a = Discriminant(e);
|
a = Discriminant(e);
|
||||||
match a {
|
match a {
|
||||||
0 => bb0,
|
0 => bb0,
|
||||||
|
@ -72,6 +74,7 @@ pub fn generic<T>() -> usize {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// CHECK-LABEL: fn main(
|
||||||
fn main() {
|
fn main() {
|
||||||
assert_eq!(f(), 0);
|
assert_eq!(f(), 0);
|
||||||
assert_eq!(generic::<char>(), 0);
|
assert_eq!(generic::<char>(), 0);
|
||||||
|
|
24
tests/rustdoc/intra-doc/link-in-footnotes-132208.rs
Normal file
24
tests/rustdoc/intra-doc/link-in-footnotes-132208.rs
Normal file
|
@ -0,0 +1,24 @@
|
||||||
|
// Rustdoc has multiple passes and if the footnote pass is run before the link replacer
|
||||||
|
// one, intra doc links are not generated inside footnote definitions. This test
|
||||||
|
// therefore ensures that intra-doc link are correctly generated inside footnote
|
||||||
|
// definitions.
|
||||||
|
//
|
||||||
|
// Regression test for <https://github.com/rust-lang/rust/issues/132208>.
|
||||||
|
|
||||||
|
#![crate_name = "foo"]
|
||||||
|
|
||||||
|
//@ has 'foo/index.html'
|
||||||
|
//@ has - '//*[@class="docblock"]//a[@href="struct.Bar.html"]' 'a'
|
||||||
|
//@ has - '//*[@class="docblock"]//*[@class="footnotes"]//a[@href="struct.Foo.html"]' 'b'
|
||||||
|
|
||||||
|
//! [a]: crate::Bar
|
||||||
|
//! [b]: crate::Foo
|
||||||
|
//!
|
||||||
|
//! link in body: [a]
|
||||||
|
//!
|
||||||
|
//! see footnote[^1]
|
||||||
|
//!
|
||||||
|
//! [^1]: link in footnote: [b]
|
||||||
|
|
||||||
|
pub struct Bar;
|
||||||
|
pub struct Foo;
|
|
@ -1,13 +1,15 @@
|
||||||
error[E0282]: type annotations needed
|
error[E0282]: type annotations needed
|
||||||
--> $DIR/slice-pattern-refutable.rs:14:9
|
--> $DIR/slice-pattern-refutable.rs:14:28
|
||||||
|
|
|
|
||||||
LL | let [a, b, c] = Zeroes.into() else {
|
LL | let [a, b, c] = Zeroes.into() else {
|
||||||
| ^^^^^^^^^
|
| --------- ^^^^
|
||||||
|
| |
|
||||||
|
| type must be known at this point
|
||||||
|
|
|
|
||||||
help: consider giving this pattern a type
|
help: try using a fully qualified path to specify the expected types
|
||||||
|
|
|
|
||||||
LL | let [a, b, c]: /* Type */ = Zeroes.into() else {
|
LL | let [a, b, c] = <Zeroes as Into<T>>::into(Zeroes) else {
|
||||||
| ++++++++++++
|
| ++++++++++++++++++++++++++ ~
|
||||||
|
|
||||||
error[E0282]: type annotations needed
|
error[E0282]: type annotations needed
|
||||||
--> $DIR/slice-pattern-refutable.rs:21:31
|
--> $DIR/slice-pattern-refutable.rs:21:31
|
||||||
|
|
|
@ -1,13 +1,15 @@
|
||||||
error[E0282]: type annotations needed for `&_`
|
error[E0282]: type annotations needed
|
||||||
--> $DIR/slice-patterns-ambiguity.rs:25:9
|
--> $DIR/slice-patterns-ambiguity.rs:25:26
|
||||||
|
|
|
|
||||||
LL | let &[a, b] = Zeroes.into() else {
|
LL | let &[a, b] = Zeroes.into() else {
|
||||||
| ^^^^^^^
|
| ------ ^^^^
|
||||||
|
| |
|
||||||
|
| type must be known at this point
|
||||||
|
|
|
|
||||||
help: consider giving this pattern a type, where the placeholders `_` are specified
|
help: try using a fully qualified path to specify the expected types
|
||||||
|
|
|
|
||||||
LL | let &[a, b]: &_ = Zeroes.into() else {
|
LL | let &[a, b] = <Zeroes as Into<&_>>::into(Zeroes) else {
|
||||||
| ++++
|
| +++++++++++++++++++++++++++ ~
|
||||||
|
|
||||||
error[E0282]: type annotations needed
|
error[E0282]: type annotations needed
|
||||||
--> $DIR/slice-patterns-ambiguity.rs:32:29
|
--> $DIR/slice-patterns-ambiguity.rs:32:29
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue