Skip to content

Rollup of 8 pull requests #108027

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 29 commits into from
Closed
Changes from all commits
Commits
Show all changes
29 commits
Select commit Hold shift + click to select a range
7824528
std: use id-based thread parking on SOLID
joboet Dec 31, 2022
837c1af
rework min_choice algorithm of member constraints
aliemjay Jan 15, 2023
8df27d0
Remove some superfluous type parameters from layout.rs.
mikebenfield Jan 21, 2023
e813132
--wip-- [skip ci]
spanishpear Oct 23, 2022
5287004
Apply automatic suggestions from code review
spanishpear Dec 1, 2022
655beb4
Attempt to address review comments via github web...
spanishpear Dec 1, 2022
4447949
revert to previous span
spanishpear Jan 22, 2023
8292d07
move tests to new rust-lang location
spanishpear Jan 22, 2023
70bfcc2
move to multipart spans
spanishpear Jan 31, 2023
a3d32bb
fix formatting + test syntax
spanishpear Feb 1, 2023
0d59b8c
Remove redundant test.
cjgillot Jan 21, 2023
cd3649b
Only exclude locals if the place is not indirect.
cjgillot Jan 21, 2023
9a6c04f
Handle discriminants in dataflow-const-prop.
cjgillot Jan 21, 2023
c48756c
Limit creation of tracked place directly.
cjgillot Jan 28, 2023
9af191f
Improve value_analysis API.
cjgillot Jan 29, 2023
67a8c16
Complete for_each_aliasing_place.
cjgillot Jan 30, 2023
df889c9
Rename assign_idx methods.
cjgillot Feb 3, 2023
5925400
Fix unintentional UB in ui tests
saethlin Feb 12, 2023
09797a4
Typo.
cjgillot Feb 13, 2023
3180f1c
Fix #107998, avoid ICE when the generic_span is empty
chenyukang Feb 13, 2023
826abcc
Shrink size of array benchmarks
JulianKnodt Feb 14, 2023
765f703
Rollup merge of #103478 - SpanishPear:spanishpear/issue_103366_fix, r…
Dylan-DPC Feb 14, 2023
86368ae
Rollup merge of #105300 - aliemjay:member-lower, r=oli-obk
Dylan-DPC Feb 14, 2023
6f88493
Rollup merge of #106372 - joboet:solid_id_parking, r=m-ou-se
Dylan-DPC Feb 14, 2023
6bfabbe
Rollup merge of #107163 - mikebenfield:parameters-pr, r=TaKO8Ki
Dylan-DPC Feb 14, 2023
1756653
Rollup merge of #107411 - cjgillot:dataflow-discriminant, r=oli-obk
Dylan-DPC Feb 14, 2023
81e6e3a
Rollup merge of #107972 - saethlin:fix-test-ub, r=michaelwoerister
Dylan-DPC Feb 14, 2023
d34957d
Rollup merge of #108003 - chenyukang:yukang/fix-107998, r=compiler-er…
Dylan-DPC Feb 14, 2023
b8d1547
Rollup merge of #108023 - JulianKnodt:smaller_benchmark, r=workingjub…
Dylan-DPC Feb 14, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
146 changes: 71 additions & 75 deletions compiler/rustc_abi/src/layout.rs

Large diffs are not rendered by default.

71 changes: 59 additions & 12 deletions compiler/rustc_abi/src/lib.rs
Original file line number Diff line number Diff line change
@@ -8,6 +8,7 @@ use std::ops::{Add, AddAssign, Mul, RangeInclusive, Sub};
use std::str::FromStr;

use bitflags::bitflags;
use rustc_data_structures::intern::Interned;
#[cfg(feature = "nightly")]
use rustc_data_structures::stable_hasher::StableOrd;
use rustc_index::vec::{Idx, IndexVec};
@@ -1250,9 +1251,9 @@ impl Abi {

#[derive(PartialEq, Eq, Hash, Clone, Debug)]
#[cfg_attr(feature = "nightly", derive(HashStable_Generic))]
pub enum Variants<V: Idx> {
pub enum Variants {
/// Single enum variants, structs/tuples, unions, and all non-ADTs.
Single { index: V },
Single { index: VariantIdx },

/// Enum-likes with more than one inhabited variant: each variant comes with
/// a *discriminant* (usually the same as the variant index but the user can
@@ -1262,15 +1263,15 @@ pub enum Variants<V: Idx> {
/// For enums, the tag is the sole field of the layout.
Multiple {
tag: Scalar,
tag_encoding: TagEncoding<V>,
tag_encoding: TagEncoding,
tag_field: usize,
variants: IndexVec<V, LayoutS<V>>,
variants: IndexVec<VariantIdx, LayoutS>,
},
}

#[derive(PartialEq, Eq, Hash, Clone, Debug)]
#[cfg_attr(feature = "nightly", derive(HashStable_Generic))]
pub enum TagEncoding<V: Idx> {
pub enum TagEncoding {
/// The tag directly stores the discriminant, but possibly with a smaller layout
/// (so converting the tag to the discriminant can require sign extension).
Direct,
@@ -1285,7 +1286,11 @@ pub enum TagEncoding<V: Idx> {
/// For example, `Option<(usize, &T)>` is represented such that
/// `None` has a null pointer for the second tuple field, and
/// `Some` is the identity function (with a non-null reference).
Niche { untagged_variant: V, niche_variants: RangeInclusive<V>, niche_start: u128 },
Niche {
untagged_variant: VariantIdx,
niche_variants: RangeInclusive<VariantIdx>,
niche_start: u128,
},
}

#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
@@ -1372,9 +1377,14 @@ impl Niche {
}
}

rustc_index::newtype_index! {
#[derive(HashStable_Generic)]
pub struct VariantIdx {}
}

#[derive(PartialEq, Eq, Hash, Clone)]
#[cfg_attr(feature = "nightly", derive(HashStable_Generic))]
pub struct LayoutS<V: Idx> {
pub struct LayoutS {
/// Says where the fields are located within the layout.
pub fields: FieldsShape,

@@ -1385,7 +1395,7 @@ pub struct LayoutS<V: Idx> {
///
/// To access all fields of this layout, both `fields` and the fields of the active variant
/// must be taken into account.
pub variants: Variants<V>,
pub variants: Variants,

/// The `abi` defines how this data is passed between functions, and it defines
/// value restrictions via `valid_range`.
@@ -1404,13 +1414,13 @@ pub struct LayoutS<V: Idx> {
pub size: Size,
}

impl<V: Idx> LayoutS<V> {
impl LayoutS {
pub fn scalar<C: HasDataLayout>(cx: &C, scalar: Scalar) -> Self {
let largest_niche = Niche::from_scalar(cx, Size::ZERO, scalar);
let size = scalar.size(cx);
let align = scalar.align(cx);
LayoutS {
variants: Variants::Single { index: V::new(0) },
variants: Variants::Single { index: VariantIdx::new(0) },
fields: FieldsShape::Primitive,
abi: Abi::Scalar(scalar),
largest_niche,
@@ -1420,7 +1430,7 @@ impl<V: Idx> LayoutS<V> {
}
}

impl<V: Idx> fmt::Debug for LayoutS<V> {
impl fmt::Debug for LayoutS {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// This is how `Layout` used to print before it become
// `Interned<LayoutS>`. We print it like this to avoid having to update
@@ -1437,6 +1447,43 @@ impl<V: Idx> fmt::Debug for LayoutS<V> {
}
}

#[derive(Copy, Clone, PartialEq, Eq, Hash, HashStable_Generic)]
#[rustc_pass_by_value]
pub struct Layout<'a>(pub Interned<'a, LayoutS>);

impl<'a> fmt::Debug for Layout<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// See comment on `<LayoutS as Debug>::fmt` above.
self.0.0.fmt(f)
}
}

impl<'a> Layout<'a> {
pub fn fields(self) -> &'a FieldsShape {
&self.0.0.fields
}

pub fn variants(self) -> &'a Variants {
&self.0.0.variants
}

pub fn abi(self) -> Abi {
self.0.0.abi
}

pub fn largest_niche(self) -> Option<Niche> {
self.0.0.largest_niche
}

pub fn align(self) -> AbiAndPrefAlign {
self.0.0.align
}

pub fn size(self) -> Size {
self.0.0.size
}
}

#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub enum PointerKind {
/// Shared reference. `frozen` indicates the absence of any `UnsafeCell`.
@@ -1464,7 +1511,7 @@ pub enum InitKind {
UninitMitigated0x01Fill,
}

impl<V: Idx> LayoutS<V> {
impl LayoutS {
/// Returns `true` if the layout corresponds to an unsized type.
pub fn is_unsized(&self) -> bool {
self.abi.is_unsized()
35 changes: 24 additions & 11 deletions compiler/rustc_borrowck/src/region_infer/mod.rs
Original file line number Diff line number Diff line change
@@ -746,20 +746,33 @@ impl<'tcx> RegionInferenceContext<'tcx> {
}
debug!(?choice_regions, "after ub");

// If we ruled everything out, we're done.
if choice_regions.is_empty() {
return false;
}

// Otherwise, we need to find the minimum remaining choice, if
// any, and take that.
debug!("choice_regions remaining are {:#?}", choice_regions);
let Some(&min_choice) = choice_regions.iter().find(|&r1| {
// At this point we can pick any member of `choice_regions`, but to avoid potential
// non-determinism we will pick the *unique minimum* choice.
//
// Because universal regions are only partially ordered (i.e, not every two regions are
// comparable), we will ignore any region that doesn't compare to all others when picking
// the minimum choice.
// For example, consider `choice_regions = ['static, 'a, 'b, 'c, 'd, 'e]`, where
// `'static: 'a, 'static: 'b, 'a: 'c, 'b: 'c, 'c: 'd, 'c: 'e`.
// `['d, 'e]` are ignored because they do not compare - the same goes for `['a, 'b]`.
let totally_ordered_subset = choice_regions.iter().copied().filter(|&r1| {
choice_regions.iter().all(|&r2| {
self.universal_region_relations.outlives(r2, *r1)
self.universal_region_relations.outlives(r1, r2)
|| self.universal_region_relations.outlives(r2, r1)
})
});
// Now we're left with `['static, 'c]`. Pick `'c` as the minimum!
let Some(min_choice) = totally_ordered_subset.reduce(|r1, r2| {
let r1_outlives_r2 = self.universal_region_relations.outlives(r1, r2);
let r2_outlives_r1 = self.universal_region_relations.outlives(r2, r1);
match (r1_outlives_r2, r2_outlives_r1) {
(true, true) => r1.min(r2),
(true, false) => r2,
(false, true) => r1,
(false, false) => bug!("incomparable regions in total order"),
}
}) else {
debug!("no choice region outlived by all others");
debug!("no unique minimum choice");
return false;
};

10 changes: 9 additions & 1 deletion compiler/rustc_lint/src/context.rs
Original file line number Diff line number Diff line change
@@ -837,9 +837,17 @@ pub trait LintContext: Sized {
(use_span, "'_".to_owned())
};
debug!(?deletion_span, ?use_span);

// issue 107998 for the case such as a wrong function pointer type
// `deletion_span` is empty and there is no need to report lifetime uses here
let suggestions = if deletion_span.is_empty() {
vec![(use_span, replace_lt)]
} else {
vec![(deletion_span, String::new()), (use_span, replace_lt)]
};
db.multipart_suggestion(
msg,
vec![(deletion_span, String::new()), (use_span, replace_lt)],
suggestions,
Applicability::MachineApplicable,
);
}
2 changes: 1 addition & 1 deletion compiler/rustc_middle/src/arena.rs
Original file line number Diff line number Diff line change
@@ -8,7 +8,7 @@
macro_rules! arena_types {
($macro:path) => (
$macro!([
[] layout: rustc_target::abi::LayoutS<rustc_target::abi::VariantIdx>,
[] layout: rustc_target::abi::LayoutS,
[] fn_abi: rustc_target::abi::call::FnAbi<'tcx, rustc_middle::ty::Ty<'tcx>>,
// AdtDef are interned and compared by address
[decode] adt_def: rustc_middle::ty::AdtDefData,
8 changes: 8 additions & 0 deletions compiler/rustc_middle/src/mir/mod.rs
Original file line number Diff line number Diff line change
@@ -1644,6 +1644,14 @@ impl<'tcx> PlaceRef<'tcx> {
}
}

/// Returns `true` if this `Place` contains a `Deref` projection.
///
/// If `Place::is_indirect` returns false, the caller knows that the `Place` refers to the
/// same region of memory as its base.
pub fn is_indirect(&self) -> bool {
self.projection.iter().any(|elem| elem.is_indirect())
}

/// If MirPhase >= Derefered and if projection contains Deref,
/// It's guaranteed to be in the first place
pub fn has_deref(&self) -> bool {
4 changes: 2 additions & 2 deletions compiler/rustc_middle/src/ty/context.rs
Original file line number Diff line number Diff line change
@@ -149,7 +149,7 @@ pub struct CtxtInterners<'tcx> {
const_: InternedSet<'tcx, ConstData<'tcx>>,
const_allocation: InternedSet<'tcx, Allocation>,
bound_variable_kinds: InternedSet<'tcx, List<ty::BoundVariableKind>>,
layout: InternedSet<'tcx, LayoutS<VariantIdx>>,
layout: InternedSet<'tcx, LayoutS>,
adt_def: InternedSet<'tcx, AdtDefData>,
external_constraints: InternedSet<'tcx, ExternalConstraintsData<'tcx>>,
}
@@ -1520,7 +1520,7 @@ direct_interners! {
region: mk_region(RegionKind<'tcx>): Region -> Region<'tcx>,
const_: mk_const_internal(ConstData<'tcx>): Const -> Const<'tcx>,
const_allocation: intern_const_alloc(Allocation): ConstAllocation -> ConstAllocation<'tcx>,
layout: intern_layout(LayoutS<VariantIdx>): Layout -> Layout<'tcx>,
layout: intern_layout(LayoutS): Layout -> Layout<'tcx>,
adt_def: intern_adt_def(AdtDefData): AdtDef -> AdtDef<'tcx>,
external_constraints: intern_external_constraints(ExternalConstraintsData<'tcx>): ExternalConstraints -> ExternalConstraints<'tcx>,
}
4 changes: 3 additions & 1 deletion compiler/rustc_mir_dataflow/src/impls/borrowed_locals.rs
Original file line number Diff line number Diff line change
@@ -121,7 +121,9 @@ where
// for now. See discussion on [#61069].
//
// [#61069]: https://github.com/rust-lang/rust/pull/61069
self.trans.gen(dropped_place.local);
if !dropped_place.is_indirect() {
self.trans.gen(dropped_place.local);
}
}

TerminatorKind::Abort
1 change: 1 addition & 0 deletions compiler/rustc_mir_dataflow/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
#![feature(associated_type_defaults)]
#![feature(box_patterns)]
#![feature(exact_size_is_empty)]
#![feature(let_chains)]
#![feature(min_specialization)]
#![feature(once_cell)]
#![feature(stmt_expr_attributes)]
275 changes: 214 additions & 61 deletions compiler/rustc_mir_dataflow/src/value_analysis.rs

Large diffs are not rendered by default.

127 changes: 95 additions & 32 deletions compiler/rustc_mir_transform/src/dataflow_const_prop.rs
Original file line number Diff line number Diff line change
@@ -13,6 +13,7 @@ use rustc_mir_dataflow::value_analysis::{Map, State, TrackElem, ValueAnalysis, V
use rustc_mir_dataflow::{lattice::FlatSet, Analysis, ResultsVisitor, SwitchIntEdgeEffects};
use rustc_span::DUMMY_SP;
use rustc_target::abi::Align;
use rustc_target::abi::VariantIdx;

use crate::MirPass;

@@ -30,14 +31,12 @@ impl<'tcx> MirPass<'tcx> for DataflowConstProp {

#[instrument(skip_all level = "debug")]
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
debug!(def_id = ?body.source.def_id());
if tcx.sess.mir_opt_level() < 4 && body.basic_blocks.len() > BLOCK_LIMIT {
debug!("aborted dataflow const prop due too many basic blocks");
return;
}

// Decide which places to track during the analysis.
let map = Map::from_filter(tcx, body, Ty::is_scalar);

// We want to have a somewhat linear runtime w.r.t. the number of statements/terminators.
// Let's call this number `n`. Dataflow analysis has `O(h*n)` transfer function
// applications, where `h` is the height of the lattice. Because the height of our lattice
@@ -46,10 +45,10 @@ impl<'tcx> MirPass<'tcx> for DataflowConstProp {
// `O(num_nodes * tracked_places * n)` in terms of time complexity. Since the number of
// map nodes is strongly correlated to the number of tracked places, this becomes more or
// less `O(n)` if we place a constant limit on the number of tracked places.
if tcx.sess.mir_opt_level() < 4 && map.tracked_places() > PLACE_LIMIT {
debug!("aborted dataflow const prop due to too many tracked places");
return;
}
let place_limit = if tcx.sess.mir_opt_level() < 4 { Some(PLACE_LIMIT) } else { None };

// Decide which places to track during the analysis.
let map = Map::from_filter(tcx, body, Ty::is_scalar, place_limit);

// Perform the actual dataflow analysis.
let analysis = ConstAnalysis::new(tcx, body, map);
@@ -63,14 +62,31 @@ impl<'tcx> MirPass<'tcx> for DataflowConstProp {
}
}

struct ConstAnalysis<'tcx> {
struct ConstAnalysis<'a, 'tcx> {
map: Map,
tcx: TyCtxt<'tcx>,
local_decls: &'a LocalDecls<'tcx>,
ecx: InterpCx<'tcx, 'tcx, DummyMachine>,
param_env: ty::ParamEnv<'tcx>,
}

impl<'tcx> ValueAnalysis<'tcx> for ConstAnalysis<'tcx> {
impl<'tcx> ConstAnalysis<'_, 'tcx> {
fn eval_discriminant(
&self,
enum_ty: Ty<'tcx>,
variant_index: VariantIdx,
) -> Option<ScalarTy<'tcx>> {
if !enum_ty.is_enum() {
return None;
}
let discr = enum_ty.discriminant_for_variant(self.tcx, variant_index)?;
let discr_layout = self.tcx.layout_of(self.param_env.and(discr.ty)).ok()?;
let discr_value = Scalar::try_from_uint(discr.val, discr_layout.size)?;
Some(ScalarTy(discr_value, discr.ty))
}
}

impl<'tcx> ValueAnalysis<'tcx> for ConstAnalysis<'_, 'tcx> {
type Value = FlatSet<ScalarTy<'tcx>>;

const NAME: &'static str = "ConstAnalysis";
@@ -79,6 +95,25 @@ impl<'tcx> ValueAnalysis<'tcx> for ConstAnalysis<'tcx> {
&self.map
}

fn handle_statement(&self, statement: &Statement<'tcx>, state: &mut State<Self::Value>) {
match statement.kind {
StatementKind::SetDiscriminant { box ref place, variant_index } => {
state.flood_discr(place.as_ref(), &self.map);
if self.map.find_discr(place.as_ref()).is_some() {
let enum_ty = place.ty(self.local_decls, self.tcx).ty;
if let Some(discr) = self.eval_discriminant(enum_ty, variant_index) {
state.assign_discr(
place.as_ref(),
ValueOrPlace::Value(FlatSet::Elem(discr)),
&self.map,
);
}
}
}
_ => self.super_statement(statement, state),
}
}

fn handle_assign(
&self,
target: Place<'tcx>,
@@ -87,36 +122,47 @@ impl<'tcx> ValueAnalysis<'tcx> for ConstAnalysis<'tcx> {
) {
match rvalue {
Rvalue::Aggregate(kind, operands) => {
let target = self.map().find(target.as_ref());
if let Some(target) = target {
state.flood_idx_with(target, self.map(), FlatSet::Bottom);
let field_based = match **kind {
AggregateKind::Tuple | AggregateKind::Closure(..) => true,
AggregateKind::Adt(def_id, ..) => {
matches!(self.tcx.def_kind(def_id), DefKind::Struct)
state.flood_with(target.as_ref(), self.map(), FlatSet::Bottom);
if let Some(target_idx) = self.map().find(target.as_ref()) {
let (variant_target, variant_index) = match **kind {
AggregateKind::Tuple | AggregateKind::Closure(..) => {
(Some(target_idx), None)
}
_ => false,
AggregateKind::Adt(def_id, variant_index, ..) => {
match self.tcx.def_kind(def_id) {
DefKind::Struct => (Some(target_idx), None),
DefKind::Enum => (Some(target_idx), Some(variant_index)),
_ => (None, None),
}
}
_ => (None, None),
};
if field_based {
if let Some(target) = variant_target {
for (field_index, operand) in operands.iter().enumerate() {
if let Some(field) = self
.map()
.apply(target, TrackElem::Field(Field::from_usize(field_index)))
{
let result = self.handle_operand(operand, state);
state.assign_idx(field, result, self.map());
state.insert_idx(field, result, self.map());
}
}
}
if let Some(variant_index) = variant_index
&& let Some(discr_idx) = self.map().apply(target_idx, TrackElem::Discriminant)
{
let enum_ty = target.ty(self.local_decls, self.tcx).ty;
if let Some(discr_val) = self.eval_discriminant(enum_ty, variant_index) {
state.insert_value_idx(discr_idx, FlatSet::Elem(discr_val), &self.map);
}
}
}
}
Rvalue::CheckedBinaryOp(op, box (left, right)) => {
// Flood everything now, so we can use `insert_value_idx` directly later.
state.flood(target.as_ref(), self.map());

let target = self.map().find(target.as_ref());
if let Some(target) = target {
// We should not track any projections other than
// what is overwritten below, but just in case...
state.flood_idx(target, self.map());
}

let value_target = target
.and_then(|target| self.map().apply(target, TrackElem::Field(0_u32.into())));
@@ -127,7 +173,8 @@ impl<'tcx> ValueAnalysis<'tcx> for ConstAnalysis<'tcx> {
let (val, overflow) = self.binary_op(state, *op, left, right);

if let Some(value_target) = value_target {
state.assign_idx(value_target, ValueOrPlace::Value(val), self.map());
// We have flooded `target` earlier.
state.insert_value_idx(value_target, val, self.map());
}
if let Some(overflow_target) = overflow_target {
let overflow = match overflow {
@@ -142,11 +189,8 @@ impl<'tcx> ValueAnalysis<'tcx> for ConstAnalysis<'tcx> {
}
FlatSet::Bottom => FlatSet::Bottom,
};
state.assign_idx(
overflow_target,
ValueOrPlace::Value(overflow),
self.map(),
);
// We have flooded `target` earlier.
state.insert_value_idx(overflow_target, overflow, self.map());
}
}
}
@@ -195,6 +239,9 @@ impl<'tcx> ValueAnalysis<'tcx> for ConstAnalysis<'tcx> {
FlatSet::Bottom => ValueOrPlace::Value(FlatSet::Bottom),
FlatSet::Top => ValueOrPlace::Value(FlatSet::Top),
},
Rvalue::Discriminant(place) => {
ValueOrPlace::Value(state.get_discr(place.as_ref(), self.map()))
}
_ => self.super_rvalue(rvalue, state),
}
}
@@ -268,12 +315,13 @@ impl<'tcx> std::fmt::Debug for ScalarTy<'tcx> {
}
}

impl<'tcx> ConstAnalysis<'tcx> {
pub fn new(tcx: TyCtxt<'tcx>, body: &Body<'tcx>, map: Map) -> Self {
impl<'a, 'tcx> ConstAnalysis<'a, 'tcx> {
pub fn new(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, map: Map) -> Self {
let param_env = tcx.param_env(body.source.def_id());
Self {
map,
tcx,
local_decls: &body.local_decls,
ecx: InterpCx::new(tcx, DUMMY_SP, param_env, DummyMachine),
param_env: param_env,
}
@@ -466,6 +514,21 @@ impl<'tcx, 'map, 'a> Visitor<'tcx> for OperandCollector<'tcx, 'map, 'a> {
_ => (),
}
}

fn visit_rvalue(&mut self, rvalue: &Rvalue<'tcx>, location: Location) {
match rvalue {
Rvalue::Discriminant(place) => {
match self.state.get_discr(place.as_ref(), self.visitor.map) {
FlatSet::Top => (),
FlatSet::Elem(value) => {
self.visitor.before_effect.insert((location, *place), value);
}
FlatSet::Bottom => (),
}
}
_ => self.super_rvalue(rvalue, location),
}
}
}

struct DummyMachine;
18 changes: 10 additions & 8 deletions compiler/rustc_mir_transform/src/sroa.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use crate::MirPass;
use rustc_index::bit_set::BitSet;
use rustc_index::bit_set::{BitSet, GrowableBitSet};
use rustc_index::vec::IndexVec;
use rustc_middle::mir::patch::MirPatch;
use rustc_middle::mir::visit::*;
@@ -26,10 +26,12 @@ impl<'tcx> MirPass<'tcx> for ScalarReplacementOfAggregates {
debug!(?replacements);
let all_dead_locals = replace_flattened_locals(tcx, body, replacements);
if !all_dead_locals.is_empty() {
for local in excluded.indices() {
excluded[local] |= all_dead_locals.contains(local);
}
excluded.raw.resize(body.local_decls.len(), false);
excluded.union(&all_dead_locals);
excluded = {
let mut growable = GrowableBitSet::from(excluded);
growable.ensure(body.local_decls.len());
growable.into()
};
} else {
break;
}
@@ -44,11 +46,11 @@ impl<'tcx> MirPass<'tcx> for ScalarReplacementOfAggregates {
/// - the locals is a union or an enum;
/// - the local's address is taken, and thus the relative addresses of the fields are observable to
/// client code.
fn escaping_locals(excluded: &IndexVec<Local, bool>, body: &Body<'_>) -> BitSet<Local> {
fn escaping_locals(excluded: &BitSet<Local>, body: &Body<'_>) -> BitSet<Local> {
let mut set = BitSet::new_empty(body.local_decls.len());
set.insert_range(RETURN_PLACE..=Local::from_usize(body.arg_count));
for (local, decl) in body.local_decls().iter_enumerated() {
if decl.ty.is_union() || decl.ty.is_enum() || excluded[local] {
if decl.ty.is_union() || decl.ty.is_enum() || excluded.contains(local) {
set.insert(local);
}
}
@@ -172,7 +174,7 @@ fn replace_flattened_locals<'tcx>(
body: &mut Body<'tcx>,
replacements: ReplacementMap<'tcx>,
) -> BitSet<Local> {
let mut all_dead_locals = BitSet::new_empty(body.local_decls.len());
let mut all_dead_locals = BitSet::new_empty(replacements.fragments.len());
for (local, replacements) in replacements.fragments.iter_enumerated() {
if replacements.is_some() {
all_dead_locals.insert(local);
58 changes: 56 additions & 2 deletions compiler/rustc_parse/src/parser/diagnostics.rs
Original file line number Diff line number Diff line change
@@ -284,7 +284,7 @@ impl<'a> Parser<'a> {
self.sess.source_map().span_to_snippet(span)
}

pub(super) fn expected_ident_found(&self) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
pub(super) fn expected_ident_found(&mut self) -> DiagnosticBuilder<'a, ErrorGuaranteed> {
let valid_follow = &[
TokenKind::Eq,
TokenKind::Colon,
@@ -324,7 +324,61 @@ impl<'a> Parser<'a> {
suggest_raw,
suggest_remove_comma,
};
err.into_diagnostic(&self.sess.span_diagnostic)
let mut err = err.into_diagnostic(&self.sess.span_diagnostic);

// if the token we have is a `<`
// it *might* be a misplaced generic
if self.token == token::Lt {
// all keywords that could have generic applied
let valid_prev_keywords =
[kw::Fn, kw::Type, kw::Struct, kw::Enum, kw::Union, kw::Trait];

// If we've expected an identifier,
// and the current token is a '<'
// if the previous token is a valid keyword
// that might use a generic, then suggest a correct
// generic placement (later on)
let maybe_keyword = self.prev_token.clone();
if valid_prev_keywords.into_iter().any(|x| maybe_keyword.is_keyword(x)) {
// if we have a valid keyword, attempt to parse generics
// also obtain the keywords symbol
match self.parse_generics() {
Ok(generic) => {
if let TokenKind::Ident(symbol, _) = maybe_keyword.kind {
let ident_name = symbol;
// at this point, we've found something like
// `fn <T>id`
// and current token should be Ident with the item name (i.e. the function name)
// if there is a `<` after the fn name, then don't show a suggestion, show help

if !self.look_ahead(1, |t| *t == token::Lt) &&
let Ok(snippet) = self.sess.source_map().span_to_snippet(generic.span) {
err.multipart_suggestion_verbose(
format!("place the generic parameter name after the {ident_name} name"),
vec![
(self.token.span.shrink_to_hi(), snippet),
(generic.span, String::new())
],
Applicability::MaybeIncorrect,
);
} else {
err.help(format!(
"place the generic parameter name after the {ident_name} name"
));
}
}
}
Err(err) => {
// if there's an error parsing the generics,
// then don't do a misplaced generics suggestion
// and emit the expected ident error instead;
err.cancel();
}
}
}
}

err
}

pub(super) fn expected_one_of_not_found(
48 changes: 2 additions & 46 deletions compiler/rustc_target/src/abi/mod.rs
Original file line number Diff line number Diff line change
@@ -3,10 +3,8 @@ pub use Primitive::*;

use crate::json::{Json, ToJson};

use std::fmt;
use std::ops::Deref;

use rustc_data_structures::intern::Interned;
use rustc_macros::HashStable_Generic;

pub mod call;
@@ -19,48 +17,6 @@ impl ToJson for Endian {
}
}

rustc_index::newtype_index! {
#[derive(HashStable_Generic)]
pub struct VariantIdx {}
}

#[derive(Copy, Clone, PartialEq, Eq, Hash, HashStable_Generic)]
#[rustc_pass_by_value]
pub struct Layout<'a>(pub Interned<'a, LayoutS<VariantIdx>>);

impl<'a> fmt::Debug for Layout<'a> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
// See comment on `<LayoutS as Debug>::fmt` above.
self.0.0.fmt(f)
}
}

impl<'a> Layout<'a> {
pub fn fields(self) -> &'a FieldsShape {
&self.0.0.fields
}

pub fn variants(self) -> &'a Variants<VariantIdx> {
&self.0.0.variants
}

pub fn abi(self) -> Abi {
self.0.0.abi
}

pub fn largest_niche(self) -> Option<Niche> {
self.0.0.largest_niche
}

pub fn align(self) -> AbiAndPrefAlign {
self.0.0.align
}

pub fn size(self) -> Size {
self.0.0.size
}
}

/// The layout of a type, alongside the type itself.
/// Provides various type traversal APIs (e.g., recursing into fields).
///
@@ -75,8 +31,8 @@ pub struct TyAndLayout<'a, Ty> {
}

impl<'a, Ty> Deref for TyAndLayout<'a, Ty> {
type Target = &'a LayoutS<VariantIdx>;
fn deref(&self) -> &&'a LayoutS<VariantIdx> {
type Target = &'a LayoutS;
fn deref(&self) -> &&'a LayoutS {
&self.layout.0.0
}
}
22 changes: 11 additions & 11 deletions compiler/rustc_ty_utils/src/layout.rs
Original file line number Diff line number Diff line change
@@ -78,10 +78,10 @@ fn invert_mapping(map: &[u32]) -> Vec<u32> {
fn univariant_uninterned<'tcx>(
cx: &LayoutCx<'tcx, TyCtxt<'tcx>>,
ty: Ty<'tcx>,
fields: &[TyAndLayout<'_>],
fields: &[Layout<'_>],
repr: &ReprOptions,
kind: StructKind,
) -> Result<LayoutS<VariantIdx>, LayoutError<'tcx>> {
) -> Result<LayoutS, LayoutError<'tcx>> {
let dl = cx.data_layout();
let pack = repr.pack;
if pack.is_some() && repr.align.is_some() {
@@ -106,7 +106,7 @@ fn layout_of_uncached<'tcx>(
};
let scalar = |value: Primitive| tcx.intern_layout(LayoutS::scalar(cx, scalar_unit(value)));

let univariant = |fields: &[TyAndLayout<'_>], repr: &ReprOptions, kind| {
let univariant = |fields: &[Layout<'_>], repr: &ReprOptions, kind| {
Ok(tcx.intern_layout(univariant_uninterned(cx, ty, fields, repr, kind)?))
};
debug_assert!(!ty.has_non_region_infer());
@@ -272,7 +272,7 @@ fn layout_of_uncached<'tcx>(
ty::Closure(_, ref substs) => {
let tys = substs.as_closure().upvar_tys();
univariant(
&tys.map(|ty| cx.layout_of(ty)).collect::<Result<Vec<_>, _>>()?,
&tys.map(|ty| Ok(cx.layout_of(ty)?.layout)).collect::<Result<Vec<_>, _>>()?,
&ReprOptions::default(),
StructKind::AlwaysSized,
)?
@@ -283,7 +283,7 @@ fn layout_of_uncached<'tcx>(
if tys.len() == 0 { StructKind::AlwaysSized } else { StructKind::MaybeUnsized };

univariant(
&tys.iter().map(|k| cx.layout_of(k)).collect::<Result<Vec<_>, _>>()?,
&tys.iter().map(|k| Ok(cx.layout_of(k)?.layout)).collect::<Result<Vec<_>, _>>()?,
&ReprOptions::default(),
kind,
)?
@@ -412,7 +412,7 @@ fn layout_of_uncached<'tcx>(
.map(|v| {
v.fields
.iter()
.map(|field| cx.layout_of(field.ty(tcx, substs)))
.map(|field| Ok(cx.layout_of(field.ty(tcx, substs))?.layout))
.collect::<Result<Vec<_>, _>>()
})
.collect::<Result<IndexVec<VariantIdx, _>, _>>()?;
@@ -630,23 +630,21 @@ fn generator_layout<'tcx>(
// `info.variant_fields` already accounts for the reserved variants, so no need to add them.
let max_discr = (info.variant_fields.len() - 1) as u128;
let discr_int = Integer::fit_unsigned(max_discr);
let discr_int_ty = discr_int.to_ty(tcx, false);
let tag = Scalar::Initialized {
value: Primitive::Int(discr_int, false),
valid_range: WrappingRange { start: 0, end: max_discr },
};
let tag_layout = cx.tcx.intern_layout(LayoutS::scalar(cx, tag));
let tag_layout = TyAndLayout { ty: discr_int_ty, layout: tag_layout };

let promoted_layouts = ineligible_locals
.iter()
.map(|local| subst_field(info.field_tys[local].ty))
.map(|ty| tcx.mk_maybe_uninit(ty))
.map(|ty| cx.layout_of(ty));
.map(|ty| Ok(cx.layout_of(ty)?.layout));
let prefix_layouts = substs
.as_generator()
.prefix_tys()
.map(|ty| cx.layout_of(ty))
.map(|ty| Ok(cx.layout_of(ty)?.layout))
.chain(iter::once(Ok(tag_layout)))
.chain(promoted_layouts)
.collect::<Result<Vec<_>, _>>()?;
@@ -715,7 +713,9 @@ fn generator_layout<'tcx>(
let mut variant = univariant_uninterned(
cx,
ty,
&variant_only_tys.map(|ty| cx.layout_of(ty)).collect::<Result<Vec<_>, _>>()?,
&variant_only_tys
.map(|ty| Ok(cx.layout_of(ty)?.layout))
.collect::<Result<Vec<_>, _>>()?,
&ReprOptions::default(),
StructKind::Prefixed(prefix_size, prefix_align.abi),
)?;
10 changes: 5 additions & 5 deletions library/core/benches/array.rs
Original file line number Diff line number Diff line change
@@ -11,9 +11,9 @@ macro_rules! map_array {
};
}

map_array!(map_8byte_8byte_8, 0u64, 1u64, 800);
map_array!(map_8byte_8byte_64, 0u64, 1u64, 6400);
map_array!(map_8byte_8byte_256, 0u64, 1u64, 25600);
map_array!(map_8byte_8byte_8, 0u64, 1u64, 80);
map_array!(map_8byte_8byte_64, 0u64, 1u64, 640);
map_array!(map_8byte_8byte_256, 0u64, 1u64, 2560);

map_array!(map_8byte_256byte_256, 0u64, [0u64; 4], 25600);
map_array!(map_256byte_8byte_256, [0u64; 4], 0u64, 25600);
map_array!(map_8byte_256byte_256, 0u64, [0u64; 4], 2560);
map_array!(map_256byte_8byte_256, [0u64; 4], 0u64, 2560);
37 changes: 37 additions & 0 deletions library/std/src/sys/itron/thread_parking.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
use super::abi;
use super::error::expect_success_aborting;
use super::time::with_tmos;
use crate::time::Duration;

pub type ThreadId = abi::ID;

pub use super::task::current_task_id_aborting as current;

pub fn park(_hint: usize) {
match unsafe { abi::slp_tsk() } {
abi::E_OK | abi::E_RLWAI => {}
err => {
expect_success_aborting(err, &"slp_tsk");
}
}
}

pub fn park_timeout(dur: Duration, _hint: usize) {
match with_tmos(dur, |tmo| unsafe { abi::tslp_tsk(tmo) }) {
abi::E_OK | abi::E_RLWAI | abi::E_TMOUT => {}
err => {
expect_success_aborting(err, &"tslp_tsk");
}
}
}

pub fn unpark(id: ThreadId, _hint: usize) {
match unsafe { abi::wup_tsk(id) } {
// It is allowed to try to wake up a destroyed or unrelated task, so we ignore all
// errors that could result from that situation.
abi::E_OK | abi::E_NOEXS | abi::E_OBJ | abi::E_QOVR => {}
err => {
expect_success_aborting(err, &"wup_tsk");
}
}
}
72 changes: 0 additions & 72 deletions library/std/src/sys/itron/wait_flag.rs

This file was deleted.

4 changes: 2 additions & 2 deletions library/std/src/sys/solid/mod.rs
Original file line number Diff line number Diff line change
@@ -13,9 +13,9 @@ mod itron {
pub(super) mod spin;
pub(super) mod task;
pub mod thread;
pub mod thread_parking;
pub(super) mod time;
use super::unsupported;
pub mod wait_flag;
}

pub mod alloc;
@@ -43,8 +43,8 @@ pub use self::itron::thread;
pub mod memchr;
pub mod thread_local_dtor;
pub mod thread_local_key;
pub use self::itron::thread_parking;
pub mod time;
pub use self::itron::wait_flag;

mod rwlock;

4 changes: 1 addition & 3 deletions library/std/src/sys_common/thread_parking/mod.rs
Original file line number Diff line number Diff line change
@@ -14,12 +14,10 @@ cfg_if::cfg_if! {
} else if #[cfg(any(
target_os = "netbsd",
all(target_vendor = "fortanix", target_env = "sgx"),
target_os = "solid_asp3",
))] {
mod id;
pub use id::Parker;
} else if #[cfg(target_os = "solid_asp3")] {
mod wait_flag;
pub use wait_flag::Parker;
} else if #[cfg(any(windows, target_family = "unix"))] {
pub use crate::sys::thread_parking::Parker;
} else {
102 changes: 0 additions & 102 deletions library/std/src/sys_common/thread_parking/wait_flag.rs

This file was deleted.

4 changes: 2 additions & 2 deletions src/librustdoc/html/render/print_item.rs
Original file line number Diff line number Diff line change
@@ -10,7 +10,7 @@ use rustc_middle::ty::layout::LayoutError;
use rustc_middle::ty::{self, Adt, TyCtxt};
use rustc_span::hygiene::MacroKind;
use rustc_span::symbol::{kw, sym, Symbol};
use rustc_target::abi::{LayoutS, Primitive, TagEncoding, VariantIdx, Variants};
use rustc_target::abi::{LayoutS, Primitive, TagEncoding, Variants};
use std::cmp::Ordering;
use std::fmt;
use std::rc::Rc;
@@ -1833,7 +1833,7 @@ fn document_non_exhaustive(w: &mut Buffer, item: &clean::Item) {
}

fn document_type_layout(w: &mut Buffer, cx: &Context<'_>, ty_def_id: DefId) {
fn write_size_of_layout(w: &mut Buffer, layout: &LayoutS<VariantIdx>, tag_size: u64) {
fn write_size_of_layout(w: &mut Buffer, layout: &LayoutS, tag_size: u64) {
if layout.abi.is_unsized() {
write!(w, "(unsized)");
} else {
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
- // MIR for `mutate_discriminant` before DataflowConstProp
+ // MIR for `mutate_discriminant` after DataflowConstProp

fn mutate_discriminant() -> u8 {
let mut _0: u8; // return place in scope 0 at $DIR/enum.rs:+0:29: +0:31
let mut _1: std::option::Option<NonZeroUsize>; // in scope 0 at $SRC_DIR/core/src/intrinsics/mir.rs:LL:COL
let mut _2: isize; // in scope 0 at $SRC_DIR/core/src/intrinsics/mir.rs:LL:COL

bb0: {
discriminant(_1) = 1; // scope 0 at $DIR/enum.rs:+4:13: +4:34
(((_1 as variant#1).0: NonZeroUsize).0: usize) = const 0_usize; // scope 0 at $DIR/enum.rs:+6:13: +6:64
_2 = discriminant(_1); // scope 0 at $SRC_DIR/core/src/intrinsics/mir.rs:LL:COL
switchInt(_2) -> [0: bb1, otherwise: bb2]; // scope 0 at $DIR/enum.rs:+9:13: +12:14
}

bb1: {
_0 = const 1_u8; // scope 0 at $DIR/enum.rs:+15:13: +15:20
return; // scope 0 at $DIR/enum.rs:+16:13: +16:21
}

bb2: {
_0 = const 2_u8; // scope 0 at $DIR/enum.rs:+19:13: +19:20
unreachable; // scope 0 at $DIR/enum.rs:+20:13: +20:26
}
}

45 changes: 42 additions & 3 deletions tests/mir-opt/dataflow-const-prop/enum.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,52 @@
// unit-test: DataflowConstProp

// Not trackable, because variants could be aliased.
#![feature(custom_mir, core_intrinsics, rustc_attrs)]

use std::intrinsics::mir::*;

enum E {
V1(i32),
V2(i32)
}

// EMIT_MIR enum.main.DataflowConstProp.diff
fn main() {
// EMIT_MIR enum.simple.DataflowConstProp.diff
fn simple() {
let e = E::V1(0);
let x = match e { E::V1(x) => x, E::V2(x) => x };
}

#[rustc_layout_scalar_valid_range_start(1)]
#[rustc_nonnull_optimization_guaranteed]
struct NonZeroUsize(usize);

// EMIT_MIR enum.mutate_discriminant.DataflowConstProp.diff
#[custom_mir(dialect = "runtime", phase = "post-cleanup")]
fn mutate_discriminant() -> u8 {
mir!(
let x: Option<NonZeroUsize>;
{
SetDiscriminant(x, 1);
// This assignment overwrites the niche in which the discriminant is stored.
place!(Field(Field(Variant(x, 1), 0), 0)) = 0_usize;
// So we cannot know the value of this discriminant.
let a = Discriminant(x);
match a {
0 => bb1,
_ => bad,
}
}
bb1 = {
RET = 1;
Return()
}
bad = {
RET = 2;
Unreachable()
}
)
}

fn main() {
simple();
mutate_discriminant();
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
- // MIR for `main` before DataflowConstProp
+ // MIR for `main` after DataflowConstProp
- // MIR for `simple` before DataflowConstProp
+ // MIR for `simple` after DataflowConstProp

fn main() -> () {
let mut _0: (); // return place in scope 0 at $DIR/enum.rs:+0:11: +0:11
fn simple() -> () {
let mut _0: (); // return place in scope 0 at $DIR/enum.rs:+0:13: +0:13
let _1: E; // in scope 0 at $DIR/enum.rs:+1:9: +1:10
let mut _3: isize; // in scope 0 at $DIR/enum.rs:+2:23: +2:31
scope 1 {
@@ -25,8 +25,10 @@
StorageLive(_1); // scope 0 at $DIR/enum.rs:+1:9: +1:10
_1 = E::V1(const 0_i32); // scope 0 at $DIR/enum.rs:+1:13: +1:21
StorageLive(_2); // scope 1 at $DIR/enum.rs:+2:9: +2:10
_3 = discriminant(_1); // scope 1 at $DIR/enum.rs:+2:19: +2:20
switchInt(move _3) -> [0: bb3, 1: bb1, otherwise: bb2]; // scope 1 at $DIR/enum.rs:+2:13: +2:20
- _3 = discriminant(_1); // scope 1 at $DIR/enum.rs:+2:19: +2:20
- switchInt(move _3) -> [0: bb3, 1: bb1, otherwise: bb2]; // scope 1 at $DIR/enum.rs:+2:13: +2:20
+ _3 = const 0_isize; // scope 1 at $DIR/enum.rs:+2:19: +2:20
+ switchInt(const 0_isize) -> [0: bb3, 1: bb1, otherwise: bb2]; // scope 1 at $DIR/enum.rs:+2:13: +2:20
}

bb1: {
@@ -50,7 +52,7 @@
}

bb4: {
_0 = const (); // scope 0 at $DIR/enum.rs:+0:11: +3:2
_0 = const (); // scope 0 at $DIR/enum.rs:+0:13: +3:2
StorageDead(_2); // scope 1 at $DIR/enum.rs:+3:1: +3:2
StorageDead(_1); // scope 0 at $DIR/enum.rs:+3:1: +3:2
return; // scope 0 at $DIR/enum.rs:+3:2: +3:2
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
// Regression test for #63033.

// check-pass
// edition: 2018

async fn test1(_: &'static u8, _: &'_ u8, _: &'_ u8) {}

async fn test2<'s>(_: &'s u8, _: &'_ &'s u8, _: &'_ &'s u8) {}

fn main() {}
Original file line number Diff line number Diff line change
@@ -26,7 +26,7 @@ fn test_send_trait() {
//~| HELP: add a dummy let to cause `fptr` to be fully captured
*fptr.0 = 20;
//~^ NOTE: in Rust 2018, this closure captures all of `fptr`, but in Rust 2021, it will only capture `fptr.0`
} });
} }).join().unwrap();
}

/* Test Sync Trait Migration */
@@ -47,7 +47,7 @@ fn test_sync_trait() {
//~| HELP: add a dummy let to cause `fptr` to be fully captured
*fptr.0.0 = 20;
//~^ NOTE: in Rust 2018, this closure captures all of `fptr`, but in Rust 2021, it will only capture `fptr.0.0`
} });
} }).join().unwrap();
}

/* Test Clone Trait Migration */
Original file line number Diff line number Diff line change
@@ -26,7 +26,7 @@ fn test_send_trait() {
//~| HELP: add a dummy let to cause `fptr` to be fully captured
*fptr.0 = 20;
//~^ NOTE: in Rust 2018, this closure captures all of `fptr`, but in Rust 2021, it will only capture `fptr.0`
});
}).join().unwrap();
}

/* Test Sync Trait Migration */
@@ -47,7 +47,7 @@ fn test_sync_trait() {
//~| HELP: add a dummy let to cause `fptr` to be fully captured
*fptr.0.0 = 20;
//~^ NOTE: in Rust 2018, this closure captures all of `fptr`, but in Rust 2021, it will only capture `fptr.0.0`
});
}).join().unwrap();
}

/* Test Clone Trait Migration */
Original file line number Diff line number Diff line change
@@ -19,7 +19,7 @@ LL ~ thread::spawn(move || { let _ = &fptr; unsafe {
LL |
...
LL |
LL ~ } });
LL ~ } }).join().unwrap();
|

error: changes to closure capture in Rust 2021 will affect which traits the closure implements
@@ -41,7 +41,7 @@ LL ~ thread::spawn(move || { let _ = &fptr; unsafe {
LL |
...
LL |
LL ~ } });
LL ~ } }).join().unwrap();
|

error: changes to closure capture in Rust 2021 will affect drop order and which traits the closure implements
Original file line number Diff line number Diff line change
@@ -145,7 +145,7 @@ fn test_multi_traits_issues() {
//~^ NOTE: in Rust 2018, this closure captures all of `fptr1`, but in Rust 2021, it will only capture `fptr1.0.0`
*fptr2.0 = 20;
//~^ NOTE: in Rust 2018, this closure captures all of `fptr2`, but in Rust 2021, it will only capture `fptr2.0`
} });
} }).join().unwrap();
}

fn main() {
Original file line number Diff line number Diff line change
@@ -141,7 +141,7 @@ fn test_multi_traits_issues() {
//~^ NOTE: in Rust 2018, this closure captures all of `fptr1`, but in Rust 2021, it will only capture `fptr1.0.0`
*fptr2.0 = 20;
//~^ NOTE: in Rust 2018, this closure captures all of `fptr2`, but in Rust 2021, it will only capture `fptr2.0`
});
}).join().unwrap();
}

fn main() {
Original file line number Diff line number Diff line change
@@ -111,7 +111,7 @@ LL ~ thread::spawn(move || { let _ = (&fptr1, &fptr2); unsafe {
LL |
...
LL |
LL ~ } });
LL ~ } }).join().unwrap();
|

error: aborting due to 5 previous errors
5 changes: 4 additions & 1 deletion tests/ui/consts/const-eval/issue-91827-extern-types.rs
Original file line number Diff line number Diff line change
@@ -28,7 +28,10 @@ pub struct ListImpl<T, const N: usize> {

impl<T> List<T> {
const fn as_slice(&self) -> &[T] {
unsafe { std::slice::from_raw_parts(self.data.as_ptr(), self.len) }
unsafe {
let ptr = (self as *const List<T>).cast::<usize>().add(1).cast::<T>();
std::slice::from_raw_parts(ptr, self.len)
}
}
}

43 changes: 43 additions & 0 deletions tests/ui/nll/member-constraints/min-choice-reject-ambiguous.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// ... continued from ./min-choice.rs

// check-fail

trait Cap<'a> {}
impl<T> Cap<'_> for T {}

fn type_test<'a, T: 'a>() -> &'a u8 { &0 }

// Make sure we don't pick `'b`.
fn test_b<'a, 'b, 'c, T>() -> impl Cap<'a> + Cap<'b> + Cap<'c>
where
'a: 'b,
'a: 'c,
T: 'b,
{
type_test::<'_, T>() // This should pass if we pick 'b.
//~^ ERROR the parameter type `T` may not live long enough
}

// Make sure we don't pick `'c`.
fn test_c<'a, 'b, 'c, T>() -> impl Cap<'a> + Cap<'b> + Cap<'c>
where
'a: 'b,
'a: 'c,
T: 'c,
{
type_test::<'_, T>() // This should pass if we pick 'c.
//~^ ERROR the parameter type `T` may not live long enough
}

// We need to pick min_choice from `['b, 'c]`, but it's ambiguous which one to pick because
// they're incomparable.
fn test_ambiguous<'a, 'b, 'c>(s: &'a u8) -> impl Cap<'b> + Cap<'c>
where
'a: 'b,
'a: 'c,
{
s
//~^ ERROR captures lifetime that does not appear in bounds
}

fn main() {}
40 changes: 40 additions & 0 deletions tests/ui/nll/member-constraints/min-choice-reject-ambiguous.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
error[E0309]: the parameter type `T` may not live long enough
--> $DIR/min-choice-reject-ambiguous.rs:17:5
|
LL | type_test::<'_, T>() // This should pass if we pick 'b.
| ^^^^^^^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds
|
help: consider adding an explicit lifetime bound...
|
LL | T: 'b + 'a,
| ++++

error[E0309]: the parameter type `T` may not live long enough
--> $DIR/min-choice-reject-ambiguous.rs:28:5
|
LL | type_test::<'_, T>() // This should pass if we pick 'c.
| ^^^^^^^^^^^^^^^^^^ ...so that the type `T` will meet its required lifetime bounds
|
help: consider adding an explicit lifetime bound...
|
LL | T: 'c + 'a,
| ++++

error[E0700]: hidden type for `impl Cap<'b> + Cap<'c>` captures lifetime that does not appear in bounds
--> $DIR/min-choice-reject-ambiguous.rs:39:5
|
LL | fn test_ambiguous<'a, 'b, 'c>(s: &'a u8) -> impl Cap<'b> + Cap<'c>
| -- hidden type `&'a u8` captures the lifetime `'a` as defined here
...
LL | s
| ^
|
help: to declare that `impl Cap<'b> + Cap<'c>` captures `'a`, you can add an explicit `'a` lifetime bound
|
LL | fn test_ambiguous<'a, 'b, 'c>(s: &'a u8) -> impl Cap<'b> + Cap<'c> + 'a
| ++++

error: aborting due to 3 previous errors

Some errors have detailed explanations: E0309, E0700.
For more information about an error, try `rustc --explain E0309`.
34 changes: 34 additions & 0 deletions tests/ui/nll/member-constraints/min-choice.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
// Assuming that the hidden type in these tests is `&'_#15r u8`,
// we have a member constraint: `'_#15r member ['static, 'a, 'b, 'c]`.
//
// Make sure we pick up the minimum non-ambiguous region among them.
// We will have to exclude `['b, 'c]` because they're incomparable,
// and then we should pick `'a` because we know `'static: 'a`.

// check-pass

trait Cap<'a> {}
impl<T> Cap<'_> for T {}

fn type_test<'a, T: 'a>() -> &'a u8 { &0 }

// Basic test: make sure we don't bail out because 'b and 'c are incomparable.
fn basic<'a, 'b, 'c>() -> impl Cap<'a> + Cap<'b> + Cap<'c>
where
'a: 'b,
'a: 'c,
{
&0
}

// Make sure we don't pick `'static`.
fn test_static<'a, 'b, 'c, T>() -> impl Cap<'a> + Cap<'b> + Cap<'c>
where
'a: 'b,
'a: 'c,
T: 'a,
{
type_test::<'_, T>() // This will fail if we pick 'static
}

fn main() {}
33 changes: 33 additions & 0 deletions tests/ui/nll/member-constraints/nested-impl-trait-fail.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// Nested impl-traits can impose different member constraints on the same region variable.

// check-fail

trait Cap<'a> {}
impl<T> Cap<'_> for T {}

// Assuming the hidden type is `[&'_#15r u8; 1]`, we have two distinct member constraints:
// - '_#15r member ['static, 'a, 'b] // from outer impl-trait
// - '_#15r member ['static, 'a, 'b] // from inner impl-trait
// To satisfy both we can choose 'a or 'b, so it's a failure due to ambiguity.
fn fail_early_bound<'s, 'a, 'b>(a: &'s u8) -> impl IntoIterator<Item = impl Cap<'a> + Cap<'b>>
where
's: 'a,
's: 'b,
{
[a]
//~^ E0700
//~| E0700
}

// Same as the above but with late-bound regions.
fn fail_late_bound<'s, 'a, 'b>(
a: &'s u8,
_: &'a &'s u8,
_: &'b &'s u8,
) -> impl IntoIterator<Item = impl Cap<'a> + Cap<'b>> {
[a]
//~^ E0700
//~| E0700
}

fn main() {}
75 changes: 75 additions & 0 deletions tests/ui/nll/member-constraints/nested-impl-trait-fail.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
error[E0700]: hidden type for `impl IntoIterator<Item = impl Cap<'a> + Cap<'b>>` captures lifetime that does not appear in bounds
--> $DIR/nested-impl-trait-fail.rs:17:5
|
LL | fn fail_early_bound<'s, 'a, 'b>(a: &'s u8) -> impl IntoIterator<Item = impl Cap<'a> + Cap<'b>>
| -- hidden type `[&'s u8; 1]` captures the lifetime `'s` as defined here
...
LL | [a]
| ^^^
|
help: to declare that `impl IntoIterator<Item = impl Cap<'a> + Cap<'b>>` captures `'s`, you can add an explicit `'s` lifetime bound
|
LL | fn fail_early_bound<'s, 'a, 'b>(a: &'s u8) -> impl IntoIterator<Item = impl Cap<'a> + Cap<'b>> + 's
| ++++
help: to declare that `impl Cap<'a> + Cap<'b>` captures `'s`, you can add an explicit `'s` lifetime bound
|
LL | fn fail_early_bound<'s, 'a, 'b>(a: &'s u8) -> impl IntoIterator<Item = impl Cap<'a> + Cap<'b> + 's>
| ++++

error[E0700]: hidden type for `impl Cap<'a> + Cap<'b>` captures lifetime that does not appear in bounds
--> $DIR/nested-impl-trait-fail.rs:17:5
|
LL | fn fail_early_bound<'s, 'a, 'b>(a: &'s u8) -> impl IntoIterator<Item = impl Cap<'a> + Cap<'b>>
| -- hidden type `&'s u8` captures the lifetime `'s` as defined here
...
LL | [a]
| ^^^
|
help: to declare that `impl IntoIterator<Item = impl Cap<'a> + Cap<'b>>` captures `'s`, you can add an explicit `'s` lifetime bound
|
LL | fn fail_early_bound<'s, 'a, 'b>(a: &'s u8) -> impl IntoIterator<Item = impl Cap<'a> + Cap<'b>> + 's
| ++++
help: to declare that `impl Cap<'a> + Cap<'b>` captures `'s`, you can add an explicit `'s` lifetime bound
|
LL | fn fail_early_bound<'s, 'a, 'b>(a: &'s u8) -> impl IntoIterator<Item = impl Cap<'a> + Cap<'b> + 's>
| ++++

error[E0700]: hidden type for `impl IntoIterator<Item = impl Cap<'a> + Cap<'b>>` captures lifetime that does not appear in bounds
--> $DIR/nested-impl-trait-fail.rs:28:5
|
LL | fn fail_late_bound<'s, 'a, 'b>(
| -- hidden type `[&'s u8; 1]` captures the lifetime `'s` as defined here
...
LL | [a]
| ^^^
|
help: to declare that `impl IntoIterator<Item = impl Cap<'a> + Cap<'b>>` captures `'s`, you can add an explicit `'s` lifetime bound
|
LL | ) -> impl IntoIterator<Item = impl Cap<'a> + Cap<'b>> + 's {
| ++++
help: to declare that `impl Cap<'a> + Cap<'b>` captures `'s`, you can add an explicit `'s` lifetime bound
|
LL | ) -> impl IntoIterator<Item = impl Cap<'a> + Cap<'b> + 's> {
| ++++

error[E0700]: hidden type for `impl Cap<'a> + Cap<'b>` captures lifetime that does not appear in bounds
--> $DIR/nested-impl-trait-fail.rs:28:5
|
LL | fn fail_late_bound<'s, 'a, 'b>(
| -- hidden type `&'s u8` captures the lifetime `'s` as defined here
...
LL | [a]
| ^^^
|
help: to declare that `impl IntoIterator<Item = impl Cap<'a> + Cap<'b>>` captures `'s`, you can add an explicit `'s` lifetime bound
|
LL | ) -> impl IntoIterator<Item = impl Cap<'a> + Cap<'b>> + 's {
| ++++
help: to declare that `impl Cap<'a> + Cap<'b>` captures `'s`, you can add an explicit `'s` lifetime bound
|
LL | ) -> impl IntoIterator<Item = impl Cap<'a> + Cap<'b> + 's> {
| ++++

error: aborting due to 4 previous errors

For more information about this error, try `rustc --explain E0700`.
29 changes: 29 additions & 0 deletions tests/ui/nll/member-constraints/nested-impl-trait-pass.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// Nested impl-traits can impose different member constraints on the same region variable.

// check-pass

trait Cap<'a> {}
impl<T> Cap<'_> for T {}

// Assuming the hidden type is `[&'_#15r u8; 1]`, we have two distinct member constraints:
// - '_#15r member ['static, 'a, 'b] // from outer impl-trait
// - '_#15r member ['static, 'a] // from inner impl-trait
// To satisfy both we can only choose 'a.
fn pass_early_bound<'s, 'a, 'b>(a: &'s u8) -> impl IntoIterator<Item = impl Cap<'a>> + Cap<'b>
where
's: 'a,
's: 'b,
{
[a]
}

// Same as the above but with late-bound regions.
fn pass_late_bound<'s, 'a, 'b>(
a: &'s u8,
_: &'a &'s u8,
_: &'b &'s u8,
) -> impl IntoIterator<Item = impl Cap<'a>> + Cap<'b> {
[a]
}

fn main() {}
9 changes: 9 additions & 0 deletions tests/ui/parser/suggest_misplaced_generics/enum.fixed
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// Issue: 103366 , Suggest fix for misplaced generic params
// run-rustfix

#[allow(unused)]
enum Foo<T> { Variant(T) }
//~^ ERROR expected identifier, found `<`
//~| HELP place the generic parameter name after the enum name

fn main() {}
9 changes: 9 additions & 0 deletions tests/ui/parser/suggest_misplaced_generics/enum.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// Issue: 103366 , Suggest fix for misplaced generic params
// run-rustfix

#[allow(unused)]
enum<T> Foo { Variant(T) }
//~^ ERROR expected identifier, found `<`
//~| HELP place the generic parameter name after the enum name

fn main() {}
14 changes: 14 additions & 0 deletions tests/ui/parser/suggest_misplaced_generics/enum.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
error: expected identifier, found `<`
--> $DIR/enum.rs:5:5
|
LL | enum<T> Foo { Variant(T) }
| ^ expected identifier
|
help: place the generic parameter name after the enum name
|
LL - enum<T> Foo { Variant(T) }
LL + enum Foo<T> { Variant(T) }
|

error: aborting due to previous error

Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// Issue: 103366
// there is already an existing generic on f, so don't show a suggestion

#[allow(unused)]
fn<'a, B: 'a + std::ops::Add<Output = u32>> f<T>(_x: B) { }
//~^ ERROR expected identifier, found `<`
//~| HELP place the generic parameter name after the fn name

fn main() {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
error: expected identifier, found `<`
--> $DIR/existing_generics.rs:5:3
|
LL | fn<'a, B: 'a + std::ops::Add<Output = u32>> f<T>(_x: B) { }
| ^ expected identifier
|
= help: place the generic parameter name after the fn name

error: aborting due to previous error

Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// Issue: 103366 , Suggest fix for misplaced generic params
// run-rustfix

#[allow(unused)]
fn f<'a, B: 'a + std::ops::Add<Output = u32>>(_x: B) { }
//~^ ERROR expected identifier, found `<`
//~| HELP place the generic parameter name after the fn name

fn main() {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// Issue: 103366 , Suggest fix for misplaced generic params
// run-rustfix

#[allow(unused)]
fn<'a, B: 'a + std::ops::Add<Output = u32>> f(_x: B) { }
//~^ ERROR expected identifier, found `<`
//~| HELP place the generic parameter name after the fn name

fn main() {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
error: expected identifier, found `<`
--> $DIR/fn-complex-generics.rs:5:3
|
LL | fn<'a, B: 'a + std::ops::Add<Output = u32>> f(_x: B) { }
| ^ expected identifier
|
help: place the generic parameter name after the fn name
|
LL - fn<'a, B: 'a + std::ops::Add<Output = u32>> f(_x: B) { }
LL + fn f<'a, B: 'a + std::ops::Add<Output = u32>>(_x: B) { }
|

error: aborting due to previous error

Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
// Issue: 103366 , Suggest fix for misplaced generic params
// The generics fail to parse here, so don't make any suggestions/help

#[allow(unused)]
fn<~>()> id(x: T) -> T { x }
//~^ ERROR expected identifier, found `<`

fn main() {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
error: expected identifier, found `<`
--> $DIR/fn-invalid-generics.rs:5:3
|
LL | fn<~>()> id(x: T) -> T { x }
| ^ expected identifier

error: aborting due to previous error

9 changes: 9 additions & 0 deletions tests/ui/parser/suggest_misplaced_generics/fn-simple.fixed
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// Issue: 103366 , Suggest fix for misplaced generic params
// run-rustfix

#[allow(unused)]
fn id<T>(x: T) -> T { x }
//~^ ERROR expected identifier, found `<`
//~| HELP place the generic parameter name after the fn name

fn main() {}
9 changes: 9 additions & 0 deletions tests/ui/parser/suggest_misplaced_generics/fn-simple.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// Issue: 103366 , Suggest fix for misplaced generic params
// run-rustfix

#[allow(unused)]
fn<T> id(x: T) -> T { x }
//~^ ERROR expected identifier, found `<`
//~| HELP place the generic parameter name after the fn name

fn main() {}
14 changes: 14 additions & 0 deletions tests/ui/parser/suggest_misplaced_generics/fn-simple.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
error: expected identifier, found `<`
--> $DIR/fn-simple.rs:5:3
|
LL | fn<T> id(x: T) -> T { x }
| ^ expected identifier
|
help: place the generic parameter name after the fn name
|
LL - fn<T> id(x: T) -> T { x }
LL + fn id<T>(x: T) -> T { x }
|

error: aborting due to previous error

9 changes: 9 additions & 0 deletions tests/ui/parser/suggest_misplaced_generics/struct.fixed
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// Issue: 103366 , Suggest fix for misplaced generic params
// run-rustfix

#[allow(unused)]
struct Foo<T> { x: T }
//~^ ERROR expected identifier, found `<`
//~| HELP place the generic parameter name after the struct name

fn main() {}
9 changes: 9 additions & 0 deletions tests/ui/parser/suggest_misplaced_generics/struct.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// Issue: 103366 , Suggest fix for misplaced generic params
// run-rustfix

#[allow(unused)]
struct<T> Foo { x: T }
//~^ ERROR expected identifier, found `<`
//~| HELP place the generic parameter name after the struct name

fn main() {}
14 changes: 14 additions & 0 deletions tests/ui/parser/suggest_misplaced_generics/struct.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
error: expected identifier, found `<`
--> $DIR/struct.rs:5:7
|
LL | struct<T> Foo { x: T }
| ^ expected identifier
|
help: place the generic parameter name after the struct name
|
LL - struct<T> Foo { x: T }
LL + struct Foo<T> { x: T }
|

error: aborting due to previous error

11 changes: 11 additions & 0 deletions tests/ui/parser/suggest_misplaced_generics/trait.fixed
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Issue: 103366 , Suggest fix for misplaced generic params
// run-rustfix

#[allow(unused)]
trait Foo<T> {
//~^ ERROR expected identifier, found `<`
//~| HELP place the generic parameter name after the trait name
}


fn main() {}
11 changes: 11 additions & 0 deletions tests/ui/parser/suggest_misplaced_generics/trait.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
// Issue: 103366 , Suggest fix for misplaced generic params
// run-rustfix

#[allow(unused)]
trait<T> Foo {
//~^ ERROR expected identifier, found `<`
//~| HELP place the generic parameter name after the trait name
}


fn main() {}
14 changes: 14 additions & 0 deletions tests/ui/parser/suggest_misplaced_generics/trait.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
error: expected identifier, found `<`
--> $DIR/trait.rs:5:6
|
LL | trait<T> Foo {
| ^ expected identifier
|
help: place the generic parameter name after the trait name
|
LL - trait<T> Foo {
LL + trait Foo<T> {
|

error: aborting due to previous error

9 changes: 9 additions & 0 deletions tests/ui/parser/suggest_misplaced_generics/type.fixed
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// Issue: 103366 , Suggest fix for misplaced generic params
// run-rustfix

#[allow(unused)]
type Foo<T> = T;
//~^ ERROR expected identifier, found `<`
//~| HELP place the generic parameter name after the type name

fn main() {}
9 changes: 9 additions & 0 deletions tests/ui/parser/suggest_misplaced_generics/type.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
// Issue: 103366 , Suggest fix for misplaced generic params
// run-rustfix

#[allow(unused)]
type<T> Foo = T;
//~^ ERROR expected identifier, found `<`
//~| HELP place the generic parameter name after the type name

fn main() {}
14 changes: 14 additions & 0 deletions tests/ui/parser/suggest_misplaced_generics/type.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
error: expected identifier, found `<`
--> $DIR/type.rs:5:5
|
LL | type<T> Foo = T;
| ^ expected identifier
|
help: place the generic parameter name after the type name
|
LL - type<T> Foo = T;
LL + type Foo<T> = T;
|

error: aborting due to previous error

9 changes: 9 additions & 0 deletions tests/ui/single-use-lifetime/issue-107998.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#![deny(single_use_lifetimes)]

fn with<R>(f: &fn<'a>(x: &'a i32) -> R) -> R {
//~^ ERROR function pointer types may not have generic parameters
//~| ERROR lifetime parameter `'a` only used once
f(&3)
}

fn main() {}
30 changes: 30 additions & 0 deletions tests/ui/single-use-lifetime/issue-107998.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
error: function pointer types may not have generic parameters
--> $DIR/issue-107998.rs:3:18
|
LL | fn with<R>(f: &fn<'a>(x: &'a i32) -> R) -> R {
| ^^^^
|
help: consider moving the lifetime parameter to a `for` parameter list
|
LL - fn with<R>(f: &fn<'a>(x: &'a i32) -> R) -> R {
LL + fn with<R>(f: &for<'a> fn(x: &'a i32) -> R) -> R {
|

error: lifetime parameter `'a` only used once
--> $DIR/issue-107998.rs:3:19
|
LL | fn with<R>(f: &fn<'a>(x: &'a i32) -> R) -> R {
| ^^ ---
| | |
| | ...is used only here
| | help: elide the single-use lifetime
| this lifetime...
|
note: the lint level is defined here
--> $DIR/issue-107998.rs:1:9
|
LL | #![deny(single_use_lifetimes)]
| ^^^^^^^^^^^^^^^^^^^^

error: aborting due to 2 previous errors

4 changes: 2 additions & 2 deletions tests/ui/unsized/unsized3-rpass.rs
Original file line number Diff line number Diff line change
@@ -59,7 +59,7 @@ pub fn main() {
}

let data: Box<Foo_<i32>> = Box::new(Foo_ { f: [1, 2, 3] });
let x: &Foo<i32> = mem::transmute(slice::from_raw_parts(&*data, 3));
let x: &Foo<i32> = mem::transmute(ptr::slice_from_raw_parts(&*data, 3));
assert_eq!(x.f.len(), 3);
assert_eq!(x.f[0], 1);

@@ -70,7 +70,7 @@ pub fn main() {

let data: Box<_> =
Box::new(Baz_ { f1: 42, f2: ['a' as u8, 'b' as u8, 'c' as u8, 'd' as u8, 'e' as u8] });
let x: &Baz = mem::transmute(slice::from_raw_parts(&*data, 5));
let x: &Baz = mem::transmute(ptr::slice_from_raw_parts(&*data, 5));
assert_eq!(x.f1, 42);
let chs: Vec<char> = x.f2.chars().collect();
assert_eq!(chs.len(), 5);