From d0b74ca1fa07b9d163981d2bc1c759c53356f031 Mon Sep 17 00:00:00 2001 From: "Celina G. Val" Date: Wed, 11 May 2022 18:06:38 -0700 Subject: [PATCH] Update the rust toolchain to `nightly-2022-05-03` (#1181) * Update rust toolchain to nightly-2022-05-03 This compiles but regression is failing due to unimplemented statement. * Handle change to Box structure Box now users NonNull instead of raw pointer. * Handle new statement kind Deinit We codegen an assignment to non-det value per documentation. See more information here: - https://github.com/rust-lang/rust/pull/95125 * Fix discriminant computation After the merge, the previous wrapping sub logic was triggering a panic due to u128 -> i64 conversion. There were also other overflow issues when trying to convert the `niche_value` to unsigned. For now, I'm disabling the coversion check which I believe is too strict. We should consider implementing a more flexible check later that can be controlled by the user without affecting the internal compiler codegen. * Address PR comments: - Improve comments. - Remove wrong cast to i64. - Fix statement location. - Create util function to create unsigned type. --- cprover_bindings/src/goto_program/typ.rs | 13 ++ .../codegen_cprover_gotoc/codegen/rvalue.rs | 59 +++++----- .../codegen/statement.rs | 62 ++++++---- .../src/codegen_cprover_gotoc/codegen/typ.rs | 4 +- .../src/codegen_cprover_gotoc/utils/utils.rs | 106 ++++++++--------- kani-driver/src/call_cbmc.rs | 9 +- rust-toolchain.toml | 2 +- tests/expected/dry-run/expected | 2 +- tests/kani/Enum/discriminant_128bits.rs | 18 +++ tests/ui/code-location/expected | 2 +- tools/bookrunner/librustdoc/clean/inline.rs | 2 +- tools/bookrunner/librustdoc/clean/mod.rs | 34 ++---- tools/bookrunner/librustdoc/clean/types.rs | 111 +++++++++--------- tools/bookrunner/librustdoc/clean/utils.rs | 4 +- tools/bookrunner/librustdoc/doctest.rs | 30 ++++- .../librustdoc/formats/item_type.rs | 2 +- 16 files changed, 258 insertions(+), 202 deletions(-) create mode 100644 tests/kani/Enum/discriminant_128bits.rs diff --git a/cprover_bindings/src/goto_program/typ.rs b/cprover_bindings/src/goto_program/typ.rs index 87edaeb686fd..e3da2b62b835 100644 --- a/cprover_bindings/src/goto_program/typ.rs +++ b/cprover_bindings/src/goto_program/typ.rs @@ -973,6 +973,19 @@ impl Type { Pointer { typ: Box::new(self) } } + /// Convert type to its unsigned counterpart if possible. + /// For types that are already unsigned, this will return self. + /// Note: This will expand any typedef. + pub fn to_unsigned(&self) -> Option { + let concrete = self.unwrap_typedef(); + match concrete { + CInteger(CIntType::SSizeT) => Some(CInteger(CIntType::SizeT)), + Signedbv { ref width } => Some(Unsignedbv { width: *width }), + Unsignedbv { .. } => Some(self.clone()), + _ => None, + } + } + pub fn signed_int(w: T) -> Self where T: TryInto, diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/rvalue.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/rvalue.rs index ce51a52cc5f4..416fcdfe1860 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/rvalue.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/rvalue.rs @@ -472,19 +472,27 @@ impl<'tcx> GotocCtx<'tcx> { FieldsShape::Arbitrary { offsets, .. } => offsets[0].bytes_usize(), _ => unreachable!("niche encoding must have arbitrary fields"), }; - let discr_ty = self.codegen_enum_discr_typ(ty); - let discr_ty = self.codegen_ty(discr_ty); - let niche_val = self.codegen_get_niche(e, offset, discr_ty.clone()); - let relative_discr = if *niche_start == 0 { - niche_val - } else { - wrapping_sub(&niche_val, *niche_start) - }; + + // Compute relative discriminant value (`niche_val - niche_start`). + // + // "We remap `niche_start..=niche_start + n` (which may wrap around) to + // (non-wrap-around) `0..=n`, to be able to check whether the discriminant + // corresponds to a niche variant with one comparison." + // https://github.com/rust-lang/rust/blob/fee75fbe11b1fad5d93c723234178b2a329a3c03/compiler/rustc_codegen_ssa/src/mir/place.rs#L247 + // + // Note: niche_variants can only represent values that fit in a u32. + let discr_mir_ty = self.codegen_enum_discr_typ(ty); + let discr_type = self.codegen_ty(discr_mir_ty); + let niche_val = self.codegen_get_niche(e, offset, discr_type.clone()); + let relative_discr = + wrapping_sub(&niche_val, u64::try_from(*niche_start).unwrap()); let relative_max = niche_variants.end().as_u32() - niche_variants.start().as_u32(); - let is_niche = if tag.value == Primitive::Pointer { - discr_ty.null().eq(relative_discr.clone()) + let is_niche = if tag.primitive() == Primitive::Pointer { + tracing::trace!(?tag, "Primitive::Pointer"); + discr_type.null().eq(relative_discr.clone()) } else { + tracing::trace!(?tag, "Not Primitive::Pointer"); relative_discr .clone() .le(Expr::int_constant(relative_max, relative_discr.typ().clone())) @@ -1260,26 +1268,13 @@ impl<'tcx> GotocCtx<'tcx> { /// Perform a wrapping subtraction of an Expr with a constant "expr - constant" /// where "-" is wrapping subtraction, i.e., the result should be interpreted as /// an unsigned value (2's complement). -fn wrapping_sub(expr: &Expr, constant: u128) -> Expr { - // While the wrapping subtraction can be done through a regular subtraction - // and then casting the result to an unsigned, doing so may result in CBMC - // flagging the operation with a signed to unsigned overflow failure (see - // https://github.com/model-checking/kani/issues/356). - // To avoid those overflow failures, the wrapping subtraction operation is - // computed as: - // if expr >= constant { - // // result is positive, so overflow may not occur - // expr - constant - // } else { - // // compute the 2's complement to avoid overflow - // expr - constant + 2^32 - // } - let s64 = Type::signed_int(64); - let expr = expr.clone().cast_to(s64.clone()); - let twos_complement: i64 = u32::MAX as i64 + 1 - i64::try_from(constant).unwrap(); - let constant = Expr::int_constant(constant, s64.clone()); - expr.clone().ge(constant.clone()).ternary( - expr.clone().sub(constant), - expr.plus(Expr::int_constant(twos_complement, s64.clone())), - ) +fn wrapping_sub(expr: &Expr, constant: u64) -> Expr { + if constant == 0 { + // No need to subtract. + expr.clone() + } else { + let unsigned = expr.typ().to_unsigned().unwrap(); + let constant = Expr::int_constant(constant, unsigned.clone()); + expr.clone().cast_to(unsigned).sub(constant) + } } diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/statement.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/statement.rs index 9fdf12914518..fd81d603a900 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/statement.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/statement.rs @@ -603,6 +603,7 @@ impl<'tcx> GotocCtx<'tcx> { pub fn codegen_statement(&mut self, stmt: &Statement<'tcx>) -> Stmt { let _trace_span = info_span!("CodegenStatement", statement = ?stmt).entered(); debug!("handling statement {:?}", stmt); + let location = self.codegen_span(&stmt.source_info.span); match &stmt.kind { StatementKind::Assign(box (l, r)) => { let lty = self.place_ty(l); @@ -617,15 +618,32 @@ impl<'tcx> GotocCtx<'tcx> { // where the reference is implicit. unwrap_or_return_codegen_unimplemented_stmt!(self, self.codegen_place(l)) .goto_expr - .assign(self.codegen_rvalue(r).address_of(), Location::none()) + .assign(self.codegen_rvalue(r).address_of(), location) } else if rty.is_bool() { unwrap_or_return_codegen_unimplemented_stmt!(self, self.codegen_place(l)) .goto_expr - .assign(self.codegen_rvalue(r).cast_to(Type::c_bool()), Location::none()) + .assign(self.codegen_rvalue(r).cast_to(Type::c_bool()), location) } else { unwrap_or_return_codegen_unimplemented_stmt!(self, self.codegen_place(l)) .goto_expr - .assign(self.codegen_rvalue(r), Location::none()) + .assign(self.codegen_rvalue(r), location) + } + } + StatementKind::Deinit(place) => { + // From rustc doc: "This writes `uninit` bytes to the entire place." + // Thus, we assign nondet() value to the entire place. + let dst_mir_ty = self.place_ty(place); + let dst_type = self.codegen_ty(dst_mir_ty); + let layout = self.layout_of(dst_mir_ty); + if layout.is_zst() || dst_type.sizeof_in_bits(&self.symbol_table) == 0 { + // We ignore assignment for all zero size types + // Ignore generators too for now: + // https://github.com/model-checking/kani/issues/416 + Stmt::skip(location) + } else { + unwrap_or_return_codegen_unimplemented_stmt!(self, self.codegen_place(place)) + .goto_expr + .assign(dst_type.nondet(), location) } } StatementKind::SetDiscriminant { place, variant_index } => { @@ -638,16 +656,16 @@ impl<'tcx> GotocCtx<'tcx> { .codegen_unimplemented( "ty::Generator", Type::code(vec![], Type::empty()), - Location::none(), + location.clone(), "https://github.com/model-checking/kani/issues/416", ) - .as_stmt(Location::none()); + .as_stmt(location); } _ => unreachable!(), }; let layout = self.layout_of(pt); match &layout.variants { - Variants::Single { .. } => Stmt::skip(Location::none()), + Variants::Single { .. } => Stmt::skip(location), Variants::Multiple { tag, tag_encoding, .. } => match tag_encoding { TagEncoding::Direct => { let discr = def.discriminant_for_variant(self.tcx, *variant_index); @@ -671,7 +689,7 @@ impl<'tcx> GotocCtx<'tcx> { ) .goto_expr .member("case", &self.symbol_table) - .assign(discr, Location::none()) + .assign(discr, location) } TagEncoding::Niche { dataful_variant, niche_variants, niche_start } => { if dataful_variant != variant_index { @@ -686,27 +704,28 @@ impl<'tcx> GotocCtx<'tcx> { let niche_value = variant_index.as_u32() - niche_variants.start().as_u32(); let niche_value = (niche_value as u128).wrapping_add(*niche_start); - let value = if niche_value == 0 && tag.value == Primitive::Pointer { - discr_ty.null() - } else { - Expr::int_constant(niche_value, discr_ty.clone()) - }; + let value = + if niche_value == 0 && tag.primitive() == Primitive::Pointer { + discr_ty.null() + } else { + Expr::int_constant(niche_value, discr_ty.clone()) + }; let place = unwrap_or_return_codegen_unimplemented_stmt!( self, self.codegen_place(place) ) .goto_expr; self.codegen_get_niche(place, offset, discr_ty) - .assign(value, Location::none()) + .assign(value, location) } else { - Stmt::skip(Location::none()) + Stmt::skip(location) } } }, } } - StatementKind::StorageLive(_) => Stmt::skip(Location::none()), // TODO: fix me - StatementKind::StorageDead(_) => Stmt::skip(Location::none()), // TODO: fix me + StatementKind::StorageLive(_) => Stmt::skip(location), // TODO: fix me + StatementKind::StorageDead(_) => Stmt::skip(location), // TODO: fix me StatementKind::CopyNonOverlapping(box mir::CopyNonOverlapping { ref src, ref dst, @@ -719,7 +738,7 @@ impl<'tcx> GotocCtx<'tcx> { let sz = Expr::int_constant(sz, Type::size_t()); let n = sz.mul(count); let dst = dst.cast_to(Type::void_pointer()); - let e = BuiltinFn::Memcpy.call(vec![dst, src, n.clone()], Location::none()); + let e = BuiltinFn::Memcpy.call(vec![dst, src, n.clone()], location.clone()); // The C implementation of memcpy does not allow an invalid pointer for // the src/dst, but the LLVM implementation specifies that a copy with @@ -727,18 +746,13 @@ impl<'tcx> GotocCtx<'tcx> { // the empty string; CBMC will fail on passing a reference to empty // string unless we codegen this zero check. // https://llvm.org/docs/LangRef.html#llvm-memcpy-intrinsic - Stmt::if_then_else( - n.is_zero().not(), - e.as_stmt(Location::none()), - None, - Location::none(), - ) + Stmt::if_then_else(n.is_zero().not(), e.as_stmt(location.clone()), None, location) } StatementKind::FakeRead(_) | StatementKind::Retag(_, _) | StatementKind::AscribeUserType(_, _) | StatementKind::Nop - | StatementKind::Coverage { .. } => Stmt::skip(Location::none()), + | StatementKind::Coverage { .. } => Stmt::skip(location), } .with_location(self.codegen_span(&stmt.source_info.span)) } diff --git a/kani-compiler/src/codegen_cprover_gotoc/codegen/typ.rs b/kani-compiler/src/codegen_cprover_gotoc/codegen/typ.rs index 42ff592e2b4c..92b9a6ccaeae 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/codegen/typ.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/codegen/typ.rs @@ -1206,7 +1206,7 @@ impl<'tcx> GotocCtx<'tcx> { pub fn codegen_enum_discr_typ(&self, ty: Ty<'tcx>) -> Ty<'tcx> { let layout = self.layout_of(ty); match &layout.variants { - Variants::Multiple { tag, .. } => self.codegen_prim_typ(tag.value), + Variants::Multiple { tag, .. } => self.codegen_prim_typ(tag.primitive()), _ => unreachable!("only enum has discriminant"), } } @@ -1263,7 +1263,7 @@ impl<'tcx> GotocCtx<'tcx> { _ => unreachable!(), }; - let rustc_target::abi::Scalar { value: prim_type, .. } = element; + let prim_type = element.primitive(); let rust_type = self.codegen_prim_typ(prim_type); let cbmc_type = self.codegen_ty(rust_type); diff --git a/kani-compiler/src/codegen_cprover_gotoc/utils/utils.rs b/kani-compiler/src/codegen_cprover_gotoc/utils/utils.rs index bbf91d827c3b..8157f2858f11 100644 --- a/kani-compiler/src/codegen_cprover_gotoc/utils/utils.rs +++ b/kani-compiler/src/codegen_cprover_gotoc/utils/utils.rs @@ -102,6 +102,9 @@ impl<'tcx> GotocCtx<'tcx> { } } +/// Members traverse path to get to the raw pointer of a box (b.0.pointer.pointer). +const RAW_PTR_FROM_BOX: [&str; 3] = ["0", "pointer", "pointer"]; + impl<'tcx> GotocCtx<'tcx> { /// Dereference a boxed type `std::boxed::Box` to get a `*T`. /// @@ -109,63 +112,45 @@ impl<'tcx> GotocCtx<'tcx> { /// a) implemented by the rust standard library /// b) codegenned by Kani. /// If either of those change, this will almost certainly stop working. - pub fn deref_box(&self, e: Expr) -> Expr { + pub fn deref_box(&self, box_expr: Expr) -> Expr { // Internally, a Boxed type is stored as a chain of structs. - // In particular: - // `Box` is an owning reference to an allocation of type T on the heap. - // It has a pointer of type `ptr::Unique` and an allocator of type `alloc::Global` - // Unique is an owning raw pointer to a location in memory. - // So given a Box, we can follow the chain to get the desired pointer. - // If either rustc or Kani changes how boxed types are represented, this will need to be - // updated. // - // The following C code is the result of running `kani --gen-c` on rust with boxed types: - // Given a boxed type (note that Rust can reorder fields to improve struct packing): - // ``` - // struct std::boxed::Box<[u8]> - // { - // struct std::alloc::Global 1; - // struct std::ptr::Unique<[u8]> 0; - // }; - // ``` - // We follow the Unique pointer: - // ``` - // struct std::ptr::Unique<[u8]> - // { - // struct std::marker::PhantomData<[u8]> _marker; - // struct &[u8] pointer; - // }; - // ``` - // And notice that its `.pointer` field is exactly what we want. - self.assert_is_rust_box_like(e.typ()); - e.member("0", &self.symbol_table).member("pointer", &self.symbol_table) + // This code has to match the exact structure from the std library version that is + // supported to access the raw pointer. If either rustc or Kani changes how boxed types are + // represented, this will need to be updated. + self.assert_is_rust_box_like(box_expr.typ()); + RAW_PTR_FROM_BOX.iter().fold(box_expr, |expr, name| expr.member(name, &self.symbol_table)) } /// Box initializer - /// `boxed_type the_box = >>> { .0=nondet(), .1={ ._marker=nondet(), .pointer=boxed_value } } <<<` + /// + /// Traverse over the Box representation and only initialize the raw_ptr field. All other + /// members are left uninitialized. /// `boxed_type` is the type of the resulting expression pub fn box_value(&self, boxed_value: Expr, boxed_type: Type) -> Expr { self.assert_is_rust_box_like(&boxed_type); - let unique_ptr_typ = boxed_type.lookup_field_type("0", &self.symbol_table).unwrap(); - self.assert_is_rust_unique_pointer_like(&unique_ptr_typ); - let unique_ptr_pointer_typ = - unique_ptr_typ.lookup_field_type("pointer", &self.symbol_table).unwrap(); - assert_eq!(&unique_ptr_pointer_typ, boxed_value.typ()); - let unique_ptr_val = Expr::struct_expr_with_nondet_fields( - unique_ptr_typ, - btree_string_map![("pointer", boxed_value),], - &self.symbol_table, - ); - let boxed_val = Expr::struct_expr_with_nondet_fields( - boxed_type, - btree_string_map![("0", unique_ptr_val),], - &self.symbol_table, - ); - boxed_val + tracing::debug!(?boxed_type, ?boxed_value, "box_value"); + let mut inner_type = boxed_type; + let type_members = RAW_PTR_FROM_BOX + .iter() + .map(|name| { + let outer_type = inner_type.clone(); + inner_type = outer_type.lookup_field_type(name, &self.symbol_table).unwrap(); + (*name, outer_type) + }) + .collect::>(); + + type_members.iter().rev().fold(boxed_value, |value, (name, typ)| { + Expr::struct_expr_with_nondet_fields( + typ.clone(), + btree_string_map![(*name, value),], + &self.symbol_table, + ) + }) } /// Best effort check if the struct represents a rust "std::alloc::Global". - pub fn assert_is_rust_global_alloc_like(&self, t: &Type) { + fn assert_is_rust_global_alloc_like(&self, t: &Type) { // TODO: A std::alloc::Global appears to be an empty struct, in the cases we've seen. // Is there something smarter we can do here? assert!(t.is_struct_like()); @@ -174,7 +159,7 @@ impl<'tcx> GotocCtx<'tcx> { } /// Best effort check if the struct represents a rust "std::marker::PhantomData". - pub fn assert_is_rust_phantom_data_like(&self, t: &Type) { + fn assert_is_rust_phantom_data_like(&self, t: &Type) { // TODO: A std::marker::PhantomData appears to be an empty struct, in the cases we've seen. // Is there something smarter we can do here? assert!(t.is_struct_like()); @@ -183,7 +168,7 @@ impl<'tcx> GotocCtx<'tcx> { } /// Best effort check if the struct represents a Rust "Box". May return false positives. - pub fn assert_is_rust_box_like(&self, t: &Type) { + fn assert_is_rust_box_like(&self, t: &Type) { // struct std::boxed::Box<[u8; 8]>::15334369982748499855 // { // // 1 @@ -204,13 +189,13 @@ impl<'tcx> GotocCtx<'tcx> { } /// Checks if the struct represents a Rust "std::ptr::Unique" - pub fn assert_is_rust_unique_pointer_like(&self, t: &Type) { + fn assert_is_rust_unique_pointer_like(&self, t: &Type) { // struct std::ptr::Unique<[u8; 8]>::14713681870393313245 // { // // _marker // struct std::marker::PhantomData<[u8; 8]>::18073278521438838603 _marker; // // pointer - // struct [u8::16712579856250238426; 8] *pointer; + // NonNull pointer; // }; assert!(t.is_struct_like()); let components = t.lookup_components(&self.symbol_table).unwrap(); @@ -218,11 +203,26 @@ impl<'tcx> GotocCtx<'tcx> { for c in components { match c.name().to_string().as_str() { "_marker" => self.assert_is_rust_phantom_data_like(&c.typ()), - "pointer" => { - assert!(c.typ().is_pointer() || c.typ().is_rust_fat_ptr(&self.symbol_table)) - } + "pointer" => self.assert_is_non_null_like(&c.typ()), _ => panic!("Unexpected component {} in {:?}", c.name(), t), } } } + + /// Best effort check if the struct represents a std::ptr::NonNull. + /// + /// This assumes the following structure. Any changes to this will break this code. + /// ``` + /// pub struct NonNull { + /// pointer: *const T, + /// } + /// ``` + fn assert_is_non_null_like(&self, t: &Type) { + assert!(t.is_struct_like()); + let components = t.lookup_components(&self.symbol_table).unwrap(); + assert_eq!(components.len(), 1); + let component = components.first().unwrap(); + assert_eq!(component.name().to_string().as_str(), "pointer"); + assert!(component.typ().is_pointer() || component.typ().is_rust_fat_ptr(&self.symbol_table)) + } } diff --git a/kani-driver/src/call_cbmc.rs b/kani-driver/src/call_cbmc.rs index 445e4e9b2b55..82929334b7e2 100644 --- a/kani-driver/src/call_cbmc.rs +++ b/kani-driver/src/call_cbmc.rs @@ -107,7 +107,6 @@ impl KaniSession { args.push("--pointer-check".into()); } if self.args.checks.overflow_on() { - args.push("--conversion-check".into()); args.push("--div-by-zero-check".into()); args.push("--float-overflow-check".into()); args.push("--nan-check".into()); @@ -116,7 +115,15 @@ impl KaniSession { // --unsigned-overflow-check // --signed-overflow-check // So these options are deliberately skipped to avoid erroneously re-checking operations. + + // TODO: Implement conversion checks as an optional check. + // They are a well defined operation in rust, but they may yield unexpected results to + // many users. https://github.com/model-checking/kani/issues/840 + // We might want to create a transformation pass instead of enabling CBMC since Kani + // compiler sometimes rely on the bitwise conversion of signed <-> unsigned. + // args.push("--conversion-check".into()); } + if self.args.checks.unwinding_on() { args.push("--unwinding-assertions".into()); } diff --git a/rust-toolchain.toml b/rust-toolchain.toml index 0f0548c8bc50..367b0c0ae687 100644 --- a/rust-toolchain.toml +++ b/rust-toolchain.toml @@ -2,5 +2,5 @@ # SPDX-License-Identifier: Apache-2.0 OR MIT [toolchain] -channel = "nightly-2022-03-23" +channel = "nightly-2022-05-03" components = ["llvm-tools-preview", "rustc-dev", "rust-src", "rustfmt"] diff --git a/tests/expected/dry-run/expected b/tests/expected/dry-run/expected index 70cd11bf1415..2a233e630536 100644 --- a/tests/expected/dry-run/expected +++ b/tests/expected/dry-run/expected @@ -3,4 +3,4 @@ symtab2gb goto-cc --function main goto-instrument -cbmc --bounds-check --pointer-check --conversion-check --div-by-zero-check --float-overflow-check --nan-check --undefined-shift-check --unwinding-assertions --object-bits 16 +cbmc --bounds-check --pointer-check --div-by-zero-check --float-overflow-check --nan-check --undefined-shift-check --unwinding-assertions --object-bits 16 diff --git a/tests/kani/Enum/discriminant_128bits.rs b/tests/kani/Enum/discriminant_128bits.rs new file mode 100644 index 000000000000..b846332f06ab --- /dev/null +++ b/tests/kani/Enum/discriminant_128bits.rs @@ -0,0 +1,18 @@ +// Copyright Kani Contributors +// SPDX-License-Identifier: Apache-2.0 OR MIT +// +// Check niche optimization for an object that is 128 bits long. + +use std::mem::{discriminant, size_of_val}; +use std::num::NonZeroU128; + +fn create() -> Option { + unsafe { Some(NonZeroU128::new_unchecked(120u128.into())) } +} + +#[kani::proof] +fn check_option_128bits() { + let opt = create(); + assert!(opt.is_some()); + assert_eq!(size_of_val(&opt), size_of_val(&opt.unwrap())); +} diff --git a/tests/ui/code-location/expected b/tests/ui/code-location/expected index 78d1e389708f..fb2832da1f27 100644 --- a/tests/ui/code-location/expected +++ b/tests/ui/code-location/expected @@ -1,7 +1,7 @@ module/mod.rs:10:5 in function module::not_empty main.rs:13:5 in function same_file /toolchains/ -alloc/src/vec/mod.rs:2821:81 in function as std::ops::Drop>::drop +alloc/src/vec/mod.rs:2881:81 in function as std::ops::Drop>::drop VERIFICATION:- SUCCESSFUL diff --git a/tools/bookrunner/librustdoc/clean/inline.rs b/tools/bookrunner/librustdoc/clean/inline.rs index c33e9458b512..84deb3a7c112 100644 --- a/tools/bookrunner/librustdoc/clean/inline.rs +++ b/tools/bookrunner/librustdoc/clean/inline.rs @@ -104,7 +104,7 @@ crate fn try_inline( record_extern_fqn(cx, did, ItemType::Module); clean::ModuleItem(build_module(cx, did, visited)) } - Res::Def(DefKind::Static, did) => { + Res::Def(DefKind::Static(..), did) => { record_extern_fqn(cx, did, ItemType::Static); clean::StaticItem(build_static(cx, did, cx.tcx.is_mutable_static(did))) } diff --git a/tools/bookrunner/librustdoc/clean/mod.rs b/tools/bookrunner/librustdoc/clean/mod.rs index 5b6f8a7eb3a4..4e3d0da2a81b 100644 --- a/tools/bookrunner/librustdoc/clean/mod.rs +++ b/tools/bookrunner/librustdoc/clean/mod.rs @@ -248,11 +248,7 @@ impl Clean for hir::WherePredicate<'_> { // Higher-ranked lifetimes can't have bounds. assert_matches!( param, - hir::GenericParam { - kind: hir::GenericParamKind::Lifetime { .. }, - bounds: [], - .. - } + hir::GenericParam { kind: hir::GenericParamKind::Lifetime { .. }, .. } ); Lifetime(param.name.ident().name) }) @@ -414,7 +410,7 @@ impl Clean for ty::GenericParamDef { // `self_def_id` set, we override it here. // See https://github.com/rust-lang/rust/issues/85454 if let QPath { ref mut self_def_id, .. } = default { - *self_def_id = cx.tcx.parent(self.def_id); + *self_def_id = Some(cx.tcx.parent(self.def_id)); } Some(default) @@ -452,27 +448,19 @@ impl Clean for hir::GenericParam<'_> { fn clean(&self, cx: &mut DocContext<'_>) -> GenericParamDef { let (name, kind) = match self.kind { hir::GenericParamKind::Lifetime { .. } => { - let outlives = self - .bounds - .iter() - .map(|bound| match bound { - hir::GenericBound::Outlives(lt) => lt.clean(cx), - _ => panic!(), - }) - .collect(); - (self.name.ident().name, GenericParamDefKind::Lifetime { outlives }) + (self.name, GenericParamDefKind::Lifetime { outlives: vec![] }) } - hir::GenericParamKind::Type { ref default, synthetic } => ( - self.name.ident().name, + hir::GenericParamKind::Type { ref default, synthetic, .. } => ( + self.name, GenericParamDefKind::Type { did: cx.tcx.hir().local_def_id(self.hir_id).to_def_id(), - bounds: self.bounds.iter().filter_map(|x| x.clean(cx)).collect(), + bounds: vec![], default: default.map(|t| t.clean(cx)).map(Box::new), synthetic, }, ), hir::GenericParamKind::Const { ref ty, default } => ( - self.name.ident().name, + self.name, GenericParamDefKind::Const { did: cx.tcx.hir().local_def_id(self.hir_id).to_def_id(), ty: Box::new(ty.clean(cx)), @@ -484,7 +472,7 @@ impl Clean for hir::GenericParam<'_> { ), }; - GenericParamDef { name, kind } + GenericParamDef { name: name.ident().name, kind } } } @@ -537,7 +525,7 @@ impl Clean for hir::Generics<'_> { let mut generics = Generics { params, - where_predicates: self.where_clause.predicates.iter().map(|x| x.clean(cx)).collect(), + where_predicates: self.predicates.iter().map(|x| x.clean(cx)).collect(), }; // Some duplicates are generated for ?Sized bounds between type params and where @@ -1666,9 +1654,7 @@ fn clean_field(def_id: DefId, name: Symbol, ty: Type, cx: &mut DocContext<'_>) - } fn is_field_vis_inherited(tcx: TyCtxt<'_>, def_id: DefId) -> bool { - let parent = tcx - .parent(def_id) - .expect("is_field_vis_inherited can only be called on struct or variant fields"); + let parent = tcx.parent(def_id); match tcx.def_kind(parent) { DefKind::Struct | DefKind::Union => false, DefKind::Variant => true, diff --git a/tools/bookrunner/librustdoc/clean/types.rs b/tools/bookrunner/librustdoc/clean/types.rs index 29ea5d95763a..2634d9b04501 100644 --- a/tools/bookrunner/librustdoc/clean/types.rs +++ b/tools/bookrunner/librustdoc/clean/types.rs @@ -4,6 +4,7 @@ // See GitHub history for details. use std::default::Default; use std::hash::Hash; +use std::iter; use std::lazy::SyncOnceCell as OnceCell; use std::sync::Arc; use std::{slice, vec}; @@ -21,6 +22,7 @@ use rustc_hir::def_id::{CrateNum, DefId, DefIndex, LOCAL_CRATE}; use rustc_hir::lang_items::LangItem; use rustc_hir::{BodyId, Mutability}; use rustc_index::vec::IndexVec; +use rustc_middle::ty::fast_reject::SimplifiedType; use rustc_middle::ty::{self, TyCtxt}; use rustc_span::hygiene::MacroKind; use rustc_span::source_map::DUMMY_SP; @@ -935,69 +937,70 @@ crate enum PrimitiveType { Never, } +type SimplifiedTypes = FxHashMap>; impl PrimitiveType { - crate fn impls(&self, tcx: TyCtxt<'_>) -> &'static ArrayVec { - Self::all_impls(tcx).get(self).expect("missing impl for primitive type") - } - - crate fn all_impls(tcx: TyCtxt<'_>) -> &'static FxHashMap> { - static CELL: OnceCell>> = OnceCell::new(); + crate fn simplified_types() -> &'static SimplifiedTypes { + use ty::fast_reject::SimplifiedTypeGen::*; + use ty::{FloatTy, IntTy, UintTy}; + use PrimitiveType::*; + static CELL: OnceCell = OnceCell::new(); + let single = |x| iter::once(x).collect(); CELL.get_or_init(move || { - use self::PrimitiveType::*; - - let single = |a: Option| a.into_iter().collect(); - let both = |a: Option, b: Option| -> ArrayVec<_, 4> { - a.into_iter().chain(b).collect() - }; - - let lang_items = tcx.lang_items(); map! { - Isize => single(lang_items.isize_impl()), - I8 => single(lang_items.i8_impl()), - I16 => single(lang_items.i16_impl()), - I32 => single(lang_items.i32_impl()), - I64 => single(lang_items.i64_impl()), - I128 => single(lang_items.i128_impl()), - Usize => single(lang_items.usize_impl()), - U8 => single(lang_items.u8_impl()), - U16 => single(lang_items.u16_impl()), - U32 => single(lang_items.u32_impl()), - U64 => single(lang_items.u64_impl()), - U128 => single(lang_items.u128_impl()), - F32 => both(lang_items.f32_impl(), lang_items.f32_runtime_impl()), - F64 => both(lang_items.f64_impl(), lang_items.f64_runtime_impl()), - Char => single(lang_items.char_impl()), - Bool => single(lang_items.bool_impl()), - Str => both(lang_items.str_impl(), lang_items.str_alloc_impl()), - Slice => { - lang_items - .slice_impl() - .into_iter() - .chain(lang_items.slice_u8_impl()) - .chain(lang_items.slice_alloc_impl()) - .chain(lang_items.slice_u8_alloc_impl()) - .collect() - }, - Array => single(lang_items.array_impl()), - Tuple => ArrayVec::new(), - Unit => ArrayVec::new(), - RawPointer => { - lang_items - .const_ptr_impl() - .into_iter() - .chain(lang_items.mut_ptr_impl()) - .chain(lang_items.const_slice_ptr_impl()) - .chain(lang_items.mut_slice_ptr_impl()) - .collect() - }, - Reference => ArrayVec::new(), + Isize => single(IntSimplifiedType(IntTy::Isize)), + I8 => single(IntSimplifiedType(IntTy::I8)), + I16 => single(IntSimplifiedType(IntTy::I16)), + I32 => single(IntSimplifiedType(IntTy::I32)), + I64 => single(IntSimplifiedType(IntTy::I64)), + I128 => single(IntSimplifiedType(IntTy::I128)), + Usize => single(UintSimplifiedType(UintTy::Usize)), + U8 => single(UintSimplifiedType(UintTy::U8)), + U16 => single(UintSimplifiedType(UintTy::U16)), + U32 => single(UintSimplifiedType(UintTy::U32)), + U64 => single(UintSimplifiedType(UintTy::U64)), + U128 => single(UintSimplifiedType(UintTy::U128)), + F32 => single(FloatSimplifiedType(FloatTy::F32)), + F64 => single(FloatSimplifiedType(FloatTy::F64)), + Str => single(StrSimplifiedType), + Bool => single(BoolSimplifiedType), + Char => single(CharSimplifiedType), + Array => single(ArraySimplifiedType), + Slice => single(SliceSimplifiedType), + // FIXME: If we ever add an inherent impl for tuples + // with different lengths, they won't show in rustdoc. + // + // Either manually update this arrayvec at this point + // or start with a more complex refactoring. + Tuple => [TupleSimplifiedType(2), TupleSimplifiedType(3)].into(), + Unit => single(TupleSimplifiedType(0)), + RawPointer => [PtrSimplifiedType(Mutability::Not), PtrSimplifiedType(Mutability::Mut)].into(), + Reference => [RefSimplifiedType(Mutability::Not), RefSimplifiedType(Mutability::Mut)].into(), + // FIXME: This will be wrong if we ever add inherent impls + // for function pointers. Fn => ArrayVec::new(), - Never => ArrayVec::new(), + Never => single(NeverSimplifiedType), } }) } + crate fn impls<'tcx>(&self, tcx: TyCtxt<'tcx>) -> impl Iterator + 'tcx { + Self::simplified_types() + .get(self) + .into_iter() + .flatten() + .flat_map(move |&simp| tcx.incoherent_impls(simp)) + .copied() + } + + crate fn all_impls(tcx: TyCtxt<'_>) -> impl Iterator + '_ { + Self::simplified_types() + .values() + .flatten() + .flat_map(move |&simp| tcx.incoherent_impls(simp)) + .copied() + } + crate fn as_sym(&self) -> Symbol { use PrimitiveType::*; match self { diff --git a/tools/bookrunner/librustdoc/clean/utils.rs b/tools/bookrunner/librustdoc/clean/utils.rs index dca05b32c8a0..75c2cbd2c76a 100644 --- a/tools/bookrunner/librustdoc/clean/utils.rs +++ b/tools/bookrunner/librustdoc/clean/utils.rs @@ -113,7 +113,7 @@ crate fn build_deref_target_impls(cx: &mut DocContext<'_>, items: &[Item], ret: if let Some(prim) = target.primitive_type() { let _prof_timer = cx.tcx.sess.prof.generic_activity("build_primitive_inherent_impls"); - for &did in prim.impls(tcx).iter().filter(|did| !did.is_local()) { + for did in prim.impls(tcx).filter(|did| !did.is_local()) { inline::build_impl(cx, None, did, None, ret); } } else if let Type::Path { path } = target { @@ -229,7 +229,7 @@ crate fn register_res(cx: &mut DocContext<'_>, res: Res) -> DefId { // These should be added to the cache using `record_extern_fqn`. Res::Def( kind @ (AssocTy | AssocFn | AssocConst | Variant | Fn | TyAlias | Enum | Trait | Struct - | Union | Mod | ForeignTy | Const | Static | Macro(..) | TraitAlias), + | Union | Mod | ForeignTy | Const | Static(..) | Macro(..) | TraitAlias), i, ) => (i, kind.into()), // This is part of a trait definition; document the trait. diff --git a/tools/bookrunner/librustdoc/doctest.rs b/tools/bookrunner/librustdoc/doctest.rs index ee5a6beb6947..630c183a3649 100644 --- a/tools/bookrunner/librustdoc/doctest.rs +++ b/tools/bookrunner/librustdoc/doctest.rs @@ -333,12 +333,32 @@ pub fn make_test( // Any errors in parsing should also appear when the doctest is compiled for real, so just // send all the errors that librustc_ast emits directly into a `Sink` instead of stderr. let sm = Lrc::new(SourceMap::new(FilePathMapping::empty())); - supports_color = - EmitterWriter::stderr(ColorConfig::Auto, None, false, false, Some(80), false) - .supports_color(); - let emitter = - EmitterWriter::new(box io::sink(), None, false, false, false, None, false); + let fallback_bundle = + rustc_errors::fallback_fluent_bundle(rustc_errors::DEFAULT_LOCALE_RESOURCES, false); + supports_color = EmitterWriter::stderr( + ColorConfig::Auto, + None, + None, + fallback_bundle.clone(), + false, + false, + Some(80), + false, + ) + .supports_color(); + + let emitter = EmitterWriter::new( + box io::sink(), + None, + None, + fallback_bundle, + false, + false, + false, + None, + false, + ); // FIXME(misdreavus): pass `-Z treat-err-as-bug` to the doctest parser let handler = Handler::with_emitter(false, None, box emitter); diff --git a/tools/bookrunner/librustdoc/formats/item_type.rs b/tools/bookrunner/librustdoc/formats/item_type.rs index da56334b3b4d..6a81df915db7 100644 --- a/tools/bookrunner/librustdoc/formats/item_type.rs +++ b/tools/bookrunner/librustdoc/formats/item_type.rs @@ -116,7 +116,7 @@ impl From for ItemType { DefKind::Fn => Self::Function, DefKind::Mod => Self::Module, DefKind::Const => Self::Constant, - DefKind::Static => Self::Static, + DefKind::Static(..) => Self::Static, DefKind::Struct => Self::Struct, DefKind::Union => Self::Union, DefKind::Trait => Self::Trait,