mirror of
https://github.com/rust-lang/rust.git
synced 2025-04-28 11:07:42 +00:00
Auto merge of #65009 - Centril:rollup-06g05xj, r=Centril
Rollup of 13 pull requests Successful merges: - #64581 (Fix unreachable_code warnings for try{} block ok-wrapped expressions) - #64850 (Remove inlines from DepNode code) - #64914 (regression test for 64453 borrow check error.) - #64922 (Use PlaceBuilder to avoid a lot of slice -> vec -> slice convertions) - #64948 (Improve sidebar styling to make its integration easier) - #64961 (Make comment about dummy type a bit more clear) - #64967 (Don't mark borrows of zero-sized arrays as indirectly mutable) - #64973 (Fix typo while setting `compile-flags` in test) - #64980 (Enable support for `IndirectlyMutableLocals` in `rustc_peek` ) - #64989 (Fix ICE #64964) - #64991 ([const-prop] Correctly handle locals that can't be propagated) - #64995 (Remove rustdoc warning) - #64997 (rustc book: nitpick SLP vectorization) Failed merges: r? @ghost
This commit is contained in:
commit
2daa404e9a
@ -105,7 +105,7 @@ flag will turn that behavior off.
|
|||||||
|
|
||||||
## no-vectorize-slp
|
## no-vectorize-slp
|
||||||
|
|
||||||
By default, `rustc` will attempt to vectorize loops using [superword-level
|
By default, `rustc` will attempt to vectorize code using [superword-level
|
||||||
parallelism](https://llvm.org/docs/Vectorizers.html#the-slp-vectorizer). This
|
parallelism](https://llvm.org/docs/Vectorizers.html#the-slp-vectorizer). This
|
||||||
flag will turn that behavior off.
|
flag will turn that behavior off.
|
||||||
|
|
||||||
|
@ -114,7 +114,6 @@ macro_rules! define_dep_nodes {
|
|||||||
|
|
||||||
impl DepKind {
|
impl DepKind {
|
||||||
#[allow(unreachable_code)]
|
#[allow(unreachable_code)]
|
||||||
#[inline]
|
|
||||||
pub fn can_reconstruct_query_key<$tcx>(&self) -> bool {
|
pub fn can_reconstruct_query_key<$tcx>(&self) -> bool {
|
||||||
match *self {
|
match *self {
|
||||||
$(
|
$(
|
||||||
@ -150,7 +149,6 @@ macro_rules! define_dep_nodes {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[inline(always)]
|
|
||||||
pub fn is_eval_always(&self) -> bool {
|
pub fn is_eval_always(&self) -> bool {
|
||||||
match *self {
|
match *self {
|
||||||
$(
|
$(
|
||||||
@ -199,7 +197,6 @@ macro_rules! define_dep_nodes {
|
|||||||
|
|
||||||
impl DepNode {
|
impl DepNode {
|
||||||
#[allow(unreachable_code, non_snake_case)]
|
#[allow(unreachable_code, non_snake_case)]
|
||||||
#[inline(always)]
|
|
||||||
pub fn new<'tcx>(tcx: TyCtxt<'tcx>,
|
pub fn new<'tcx>(tcx: TyCtxt<'tcx>,
|
||||||
dep: DepConstructor<'tcx>)
|
dep: DepConstructor<'tcx>)
|
||||||
-> DepNode
|
-> DepNode
|
||||||
@ -219,14 +216,16 @@ macro_rules! define_dep_nodes {
|
|||||||
hash
|
hash
|
||||||
};
|
};
|
||||||
|
|
||||||
if cfg!(debug_assertions) &&
|
#[cfg(debug_assertions)]
|
||||||
!dep_node.kind.can_reconstruct_query_key() &&
|
|
||||||
(tcx.sess.opts.debugging_opts.incremental_info ||
|
|
||||||
tcx.sess.opts.debugging_opts.query_dep_graph)
|
|
||||||
{
|
{
|
||||||
tcx.dep_graph.register_dep_node_debug_str(dep_node, || {
|
if !dep_node.kind.can_reconstruct_query_key() &&
|
||||||
arg.to_debug_str(tcx)
|
(tcx.sess.opts.debugging_opts.incremental_info ||
|
||||||
});
|
tcx.sess.opts.debugging_opts.query_dep_graph)
|
||||||
|
{
|
||||||
|
tcx.dep_graph.register_dep_node_debug_str(dep_node, || {
|
||||||
|
arg.to_debug_str(tcx)
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return dep_node;
|
return dep_node;
|
||||||
@ -242,14 +241,16 @@ macro_rules! define_dep_nodes {
|
|||||||
hash
|
hash
|
||||||
};
|
};
|
||||||
|
|
||||||
if cfg!(debug_assertions) &&
|
#[cfg(debug_assertions)]
|
||||||
!dep_node.kind.can_reconstruct_query_key() &&
|
|
||||||
(tcx.sess.opts.debugging_opts.incremental_info ||
|
|
||||||
tcx.sess.opts.debugging_opts.query_dep_graph)
|
|
||||||
{
|
{
|
||||||
tcx.dep_graph.register_dep_node_debug_str(dep_node, || {
|
if !dep_node.kind.can_reconstruct_query_key() &&
|
||||||
tupled_args.to_debug_str(tcx)
|
(tcx.sess.opts.debugging_opts.incremental_info ||
|
||||||
});
|
tcx.sess.opts.debugging_opts.query_dep_graph)
|
||||||
|
{
|
||||||
|
tcx.dep_graph.register_dep_node_debug_str(dep_node, || {
|
||||||
|
tupled_args.to_debug_str(tcx)
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
return dep_node;
|
return dep_node;
|
||||||
@ -267,7 +268,6 @@ macro_rules! define_dep_nodes {
|
|||||||
/// Construct a DepNode from the given DepKind and DefPathHash. This
|
/// Construct a DepNode from the given DepKind and DefPathHash. This
|
||||||
/// method will assert that the given DepKind actually requires a
|
/// method will assert that the given DepKind actually requires a
|
||||||
/// single DefId/DefPathHash parameter.
|
/// single DefId/DefPathHash parameter.
|
||||||
#[inline(always)]
|
|
||||||
pub fn from_def_path_hash(kind: DepKind,
|
pub fn from_def_path_hash(kind: DepKind,
|
||||||
def_path_hash: DefPathHash)
|
def_path_hash: DefPathHash)
|
||||||
-> DepNode {
|
-> DepNode {
|
||||||
@ -281,7 +281,6 @@ macro_rules! define_dep_nodes {
|
|||||||
/// Creates a new, parameterless DepNode. This method will assert
|
/// Creates a new, parameterless DepNode. This method will assert
|
||||||
/// that the DepNode corresponding to the given DepKind actually
|
/// that the DepNode corresponding to the given DepKind actually
|
||||||
/// does not require any parameters.
|
/// does not require any parameters.
|
||||||
#[inline(always)]
|
|
||||||
pub fn new_no_params(kind: DepKind) -> DepNode {
|
pub fn new_no_params(kind: DepKind) -> DepNode {
|
||||||
debug_assert!(!kind.has_params());
|
debug_assert!(!kind.has_params());
|
||||||
DepNode {
|
DepNode {
|
||||||
@ -300,7 +299,6 @@ macro_rules! define_dep_nodes {
|
|||||||
/// DepNode. Condition (2) might not be fulfilled if a DepNode
|
/// DepNode. Condition (2) might not be fulfilled if a DepNode
|
||||||
/// refers to something from the previous compilation session that
|
/// refers to something from the previous compilation session that
|
||||||
/// has been removed.
|
/// has been removed.
|
||||||
#[inline]
|
|
||||||
pub fn extract_def_id(&self, tcx: TyCtxt<'_>) -> Option<DefId> {
|
pub fn extract_def_id(&self, tcx: TyCtxt<'_>) -> Option<DefId> {
|
||||||
if self.kind.can_reconstruct_query_key() {
|
if self.kind.can_reconstruct_query_key() {
|
||||||
let def_path_hash = DefPathHash(self.hash);
|
let def_path_hash = DefPathHash(self.hash);
|
||||||
@ -386,14 +384,12 @@ impl fmt::Debug for DepNode {
|
|||||||
|
|
||||||
|
|
||||||
impl DefPathHash {
|
impl DefPathHash {
|
||||||
#[inline(always)]
|
|
||||||
pub fn to_dep_node(self, kind: DepKind) -> DepNode {
|
pub fn to_dep_node(self, kind: DepKind) -> DepNode {
|
||||||
DepNode::from_def_path_hash(kind, self)
|
DepNode::from_def_path_hash(kind, self)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DefId {
|
impl DefId {
|
||||||
#[inline(always)]
|
|
||||||
pub fn to_dep_node(self, tcx: TyCtxt<'_>, kind: DepKind) -> DepNode {
|
pub fn to_dep_node(self, tcx: TyCtxt<'_>, kind: DepKind) -> DepNode {
|
||||||
DepNode::from_def_path_hash(kind, tcx.def_path_hash(self))
|
DepNode::from_def_path_hash(kind, tcx.def_path_hash(self))
|
||||||
}
|
}
|
||||||
|
@ -392,19 +392,35 @@ impl LoweringContext<'_> {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Desugar `try { <stmts>; <expr> }` into `{ <stmts>; ::std::ops::Try::from_ok(<expr>) }`,
|
||||||
|
/// `try { <stmts>; }` into `{ <stmts>; ::std::ops::Try::from_ok(()) }`
|
||||||
|
/// and save the block id to use it as a break target for desugaring of the `?` operator.
|
||||||
fn lower_expr_try_block(&mut self, body: &Block) -> hir::ExprKind {
|
fn lower_expr_try_block(&mut self, body: &Block) -> hir::ExprKind {
|
||||||
self.with_catch_scope(body.id, |this| {
|
self.with_catch_scope(body.id, |this| {
|
||||||
let unstable_span = this.mark_span_with_reason(
|
let mut block = this.lower_block(body, true).into_inner();
|
||||||
|
|
||||||
|
let try_span = this.mark_span_with_reason(
|
||||||
DesugaringKind::TryBlock,
|
DesugaringKind::TryBlock,
|
||||||
body.span,
|
body.span,
|
||||||
this.allow_try_trait.clone(),
|
this.allow_try_trait.clone(),
|
||||||
);
|
);
|
||||||
let mut block = this.lower_block(body, true).into_inner();
|
|
||||||
let tail = block.expr.take().map_or_else(
|
// Final expression of the block (if present) or `()` with span at the end of block
|
||||||
|| this.expr_unit(this.sess.source_map().end_point(unstable_span)),
|
let tail_expr = block.expr.take().map_or_else(
|
||||||
|
|| this.expr_unit(this.sess.source_map().end_point(try_span)),
|
||||||
|x: P<hir::Expr>| x.into_inner(),
|
|x: P<hir::Expr>| x.into_inner(),
|
||||||
);
|
);
|
||||||
block.expr = Some(this.wrap_in_try_constructor(sym::from_ok, tail, unstable_span));
|
|
||||||
|
let ok_wrapped_span = this.mark_span_with_reason(
|
||||||
|
DesugaringKind::TryBlock,
|
||||||
|
tail_expr.span,
|
||||||
|
None
|
||||||
|
);
|
||||||
|
|
||||||
|
// `::std::ops::Try::from_ok($tail_expr)`
|
||||||
|
block.expr = Some(this.wrap_in_try_constructor(
|
||||||
|
sym::from_ok, try_span, tail_expr, ok_wrapped_span));
|
||||||
|
|
||||||
hir::ExprKind::Block(P(block), None)
|
hir::ExprKind::Block(P(block), None)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -412,12 +428,13 @@ impl LoweringContext<'_> {
|
|||||||
fn wrap_in_try_constructor(
|
fn wrap_in_try_constructor(
|
||||||
&mut self,
|
&mut self,
|
||||||
method: Symbol,
|
method: Symbol,
|
||||||
e: hir::Expr,
|
method_span: Span,
|
||||||
unstable_span: Span,
|
expr: hir::Expr,
|
||||||
|
overall_span: Span,
|
||||||
) -> P<hir::Expr> {
|
) -> P<hir::Expr> {
|
||||||
let path = &[sym::ops, sym::Try, method];
|
let path = &[sym::ops, sym::Try, method];
|
||||||
let from_err = P(self.expr_std_path(unstable_span, path, None, ThinVec::new()));
|
let constructor = P(self.expr_std_path(method_span, path, None, ThinVec::new()));
|
||||||
P(self.expr_call(e.span, from_err, hir_vec![e]))
|
P(self.expr_call(overall_span, constructor, hir_vec![expr]))
|
||||||
}
|
}
|
||||||
|
|
||||||
fn lower_arm(&mut self, arm: &Arm) -> hir::Arm {
|
fn lower_arm(&mut self, arm: &Arm) -> hir::Arm {
|
||||||
@ -1244,7 +1261,7 @@ impl LoweringContext<'_> {
|
|||||||
self.expr_call_std_path(try_span, from_path, hir_vec![err_expr])
|
self.expr_call_std_path(try_span, from_path, hir_vec![err_expr])
|
||||||
};
|
};
|
||||||
let from_err_expr =
|
let from_err_expr =
|
||||||
self.wrap_in_try_constructor(sym::from_error, from_expr, unstable_span);
|
self.wrap_in_try_constructor(sym::from_error, unstable_span, from_expr, try_span);
|
||||||
let thin_attrs = ThinVec::from(attrs);
|
let thin_attrs = ThinVec::from(attrs);
|
||||||
let catch_scope = self.catch_scopes.last().map(|x| *x);
|
let catch_scope = self.catch_scopes.last().map(|x| *x);
|
||||||
let ret_expr = if let Some(catch_node) = catch_scope {
|
let ret_expr = if let Some(catch_node) = catch_scope {
|
||||||
|
@ -861,7 +861,7 @@ pub struct Block {
|
|||||||
pub span: Span,
|
pub span: Span,
|
||||||
/// If true, then there may exist `break 'a` values that aim to
|
/// If true, then there may exist `break 'a` values that aim to
|
||||||
/// break out of this block early.
|
/// break out of this block early.
|
||||||
/// Used by `'label: {}` blocks and by `catch` statements.
|
/// Used by `'label: {}` blocks and by `try {}` blocks.
|
||||||
pub targeted_by_break: bool,
|
pub targeted_by_break: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -317,6 +317,12 @@ pub struct GeneratorInteriorTypeCause<'tcx> {
|
|||||||
pub scope_span: Option<Span>,
|
pub scope_span: Option<Span>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BraceStructTypeFoldableImpl! {
|
||||||
|
impl<'tcx> TypeFoldable<'tcx> for GeneratorInteriorTypeCause<'tcx> {
|
||||||
|
ty, span, scope_span
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(RustcEncodable, RustcDecodable, Debug)]
|
#[derive(RustcEncodable, RustcDecodable, Debug)]
|
||||||
pub struct TypeckTables<'tcx> {
|
pub struct TypeckTables<'tcx> {
|
||||||
/// The HirId::owner all ItemLocalIds in this table are relative to.
|
/// The HirId::owner all ItemLocalIds in this table are relative to.
|
||||||
|
@ -600,7 +600,8 @@ impl<'tcx> rustc_serialize::UseSpecializedDecodable for Ty<'tcx> {}
|
|||||||
pub type CanonicalTy<'tcx> = Canonical<'tcx, Ty<'tcx>>;
|
pub type CanonicalTy<'tcx> = Canonical<'tcx, Ty<'tcx>>;
|
||||||
|
|
||||||
extern {
|
extern {
|
||||||
/// A dummy type used to force `List` to by unsized without requiring fat pointers.
|
/// A dummy type used to force `List` to be unsized while not requiring references to it be wide
|
||||||
|
/// pointers.
|
||||||
type OpaqueListContents;
|
type OpaqueListContents;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,13 +6,79 @@ use crate::build::{BlockAnd, BlockAndExtension, Builder};
|
|||||||
use crate::hair::*;
|
use crate::hair::*;
|
||||||
use rustc::mir::interpret::{PanicInfo::BoundsCheck};
|
use rustc::mir::interpret::{PanicInfo::BoundsCheck};
|
||||||
use rustc::mir::*;
|
use rustc::mir::*;
|
||||||
use rustc::ty::{CanonicalUserTypeAnnotation, Variance};
|
use rustc::ty::{CanonicalUserTypeAnnotation, Ty, Variance};
|
||||||
|
|
||||||
use rustc_index::vec::Idx;
|
use rustc_index::vec::Idx;
|
||||||
|
|
||||||
|
/// `PlaceBuilder` is used to create places during MIR construction. It allows you to "build up" a
|
||||||
|
/// place by pushing more and more projections onto the end, and then convert the final set into a
|
||||||
|
/// place using the `into_place` method.
|
||||||
|
///
|
||||||
|
/// This is used internally when building a place for an expression like `a.b.c`. The fields `b`
|
||||||
|
/// and `c` can be progressively pushed onto the place builder that is created when converting `a`.
|
||||||
|
#[derive(Clone)]
|
||||||
|
struct PlaceBuilder<'tcx> {
|
||||||
|
base: PlaceBase<'tcx>,
|
||||||
|
projection: Vec<PlaceElem<'tcx>>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PlaceBuilder<'tcx> {
|
||||||
|
fn into_place(self) -> Place<'tcx> {
|
||||||
|
Place {
|
||||||
|
base: self.base,
|
||||||
|
projection: self.projection.into_boxed_slice(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn field(self, f: Field, ty: Ty<'tcx>) -> Self {
|
||||||
|
self.project(PlaceElem::Field(f, ty))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn deref(self) -> Self {
|
||||||
|
self.project(PlaceElem::Deref)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn index(self, index: Local) -> Self {
|
||||||
|
self.project(PlaceElem::Index(index))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn project(mut self, elem: PlaceElem<'tcx>) -> Self {
|
||||||
|
self.projection.push(elem);
|
||||||
|
self
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<Local> for PlaceBuilder<'tcx> {
|
||||||
|
fn from(local: Local) -> Self {
|
||||||
|
Self {
|
||||||
|
base: local.into(),
|
||||||
|
projection: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl From<PlaceBase<'tcx>> for PlaceBuilder<'tcx> {
|
||||||
|
fn from(base: PlaceBase<'tcx>) -> Self {
|
||||||
|
Self {
|
||||||
|
base,
|
||||||
|
projection: Vec::new(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'a, 'tcx> Builder<'a, 'tcx> {
|
impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||||
/// Compile `expr`, yielding a place that we can move from etc.
|
/// Compile `expr`, yielding a place that we can move from etc.
|
||||||
pub fn as_place<M>(&mut self, block: BasicBlock, expr: M) -> BlockAnd<Place<'tcx>>
|
pub fn as_place<M>(&mut self, mut block: BasicBlock, expr: M) -> BlockAnd<Place<'tcx>>
|
||||||
|
where
|
||||||
|
M: Mirror<'tcx, Output = Expr<'tcx>>,
|
||||||
|
{
|
||||||
|
let place_builder = unpack!(block = self.as_place_builder(block, expr));
|
||||||
|
block.and(place_builder.into_place())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This is used when constructing a compound `Place`, so that we can avoid creating
|
||||||
|
/// intermediate `Place` values until we know the full set of projections.
|
||||||
|
fn as_place_builder<M>(&mut self, block: BasicBlock, expr: M) -> BlockAnd<PlaceBuilder<'tcx>>
|
||||||
where
|
where
|
||||||
M: Mirror<'tcx, Output = Expr<'tcx>>,
|
M: Mirror<'tcx, Output = Expr<'tcx>>,
|
||||||
{
|
{
|
||||||
@ -25,7 +91,25 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||||||
/// place. The place itself may or may not be mutable:
|
/// place. The place itself may or may not be mutable:
|
||||||
/// * If this expr is a place expr like a.b, then we will return that place.
|
/// * If this expr is a place expr like a.b, then we will return that place.
|
||||||
/// * Otherwise, a temporary is created: in that event, it will be an immutable temporary.
|
/// * Otherwise, a temporary is created: in that event, it will be an immutable temporary.
|
||||||
pub fn as_read_only_place<M>(&mut self, block: BasicBlock, expr: M) -> BlockAnd<Place<'tcx>>
|
pub fn as_read_only_place<M>(&mut self, mut block: BasicBlock, expr: M) -> BlockAnd<Place<'tcx>>
|
||||||
|
where
|
||||||
|
M: Mirror<'tcx, Output = Expr<'tcx>>,
|
||||||
|
{
|
||||||
|
let place_builder = unpack!(block = self.as_read_only_place_builder(block, expr));
|
||||||
|
block.and(place_builder.into_place())
|
||||||
|
}
|
||||||
|
|
||||||
|
/// This is used when constructing a compound `Place`, so that we can avoid creating
|
||||||
|
/// intermediate `Place` values until we know the full set of projections.
|
||||||
|
/// Mutability note: The caller of this method promises only to read from the resulting
|
||||||
|
/// place. The place itself may or may not be mutable:
|
||||||
|
/// * If this expr is a place expr like a.b, then we will return that place.
|
||||||
|
/// * Otherwise, a temporary is created: in that event, it will be an immutable temporary.
|
||||||
|
fn as_read_only_place_builder<M>(
|
||||||
|
&mut self,
|
||||||
|
block: BasicBlock,
|
||||||
|
expr: M,
|
||||||
|
) -> BlockAnd<PlaceBuilder<'tcx>>
|
||||||
where
|
where
|
||||||
M: Mirror<'tcx, Output = Expr<'tcx>>,
|
M: Mirror<'tcx, Output = Expr<'tcx>>,
|
||||||
{
|
{
|
||||||
@ -38,7 +122,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||||||
mut block: BasicBlock,
|
mut block: BasicBlock,
|
||||||
expr: Expr<'tcx>,
|
expr: Expr<'tcx>,
|
||||||
mutability: Mutability,
|
mutability: Mutability,
|
||||||
) -> BlockAnd<Place<'tcx>> {
|
) -> BlockAnd<PlaceBuilder<'tcx>> {
|
||||||
debug!(
|
debug!(
|
||||||
"expr_as_place(block={:?}, expr={:?}, mutability={:?})",
|
"expr_as_place(block={:?}, expr={:?}, mutability={:?})",
|
||||||
block, expr, mutability
|
block, expr, mutability
|
||||||
@ -54,25 +138,23 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||||||
value,
|
value,
|
||||||
} => this.in_scope((region_scope, source_info), lint_level, |this| {
|
} => this.in_scope((region_scope, source_info), lint_level, |this| {
|
||||||
if mutability == Mutability::Not {
|
if mutability == Mutability::Not {
|
||||||
this.as_read_only_place(block, value)
|
this.as_read_only_place_builder(block, value)
|
||||||
} else {
|
} else {
|
||||||
this.as_place(block, value)
|
this.as_place_builder(block, value)
|
||||||
}
|
}
|
||||||
}),
|
}),
|
||||||
ExprKind::Field { lhs, name } => {
|
ExprKind::Field { lhs, name } => {
|
||||||
let place = unpack!(block = this.as_place(block, lhs));
|
let place_builder = unpack!(block = this.as_place_builder(block, lhs));
|
||||||
let place = place.field(name, expr.ty);
|
block.and(place_builder.field(name, expr.ty))
|
||||||
block.and(place)
|
|
||||||
}
|
}
|
||||||
ExprKind::Deref { arg } => {
|
ExprKind::Deref { arg } => {
|
||||||
let place = unpack!(block = this.as_place(block, arg));
|
let place_builder = unpack!(block = this.as_place_builder(block, arg));
|
||||||
let place = place.deref();
|
block.and(place_builder.deref())
|
||||||
block.and(place)
|
|
||||||
}
|
}
|
||||||
ExprKind::Index { lhs, index } => {
|
ExprKind::Index { lhs, index } => {
|
||||||
let (usize_ty, bool_ty) = (this.hir.usize_ty(), this.hir.bool_ty());
|
let (usize_ty, bool_ty) = (this.hir.usize_ty(), this.hir.bool_ty());
|
||||||
|
|
||||||
let slice = unpack!(block = this.as_place(block, lhs));
|
let place_builder = unpack!(block = this.as_place_builder(block, lhs));
|
||||||
// Making this a *fresh* temporary also means we do not have to worry about
|
// Making this a *fresh* temporary also means we do not have to worry about
|
||||||
// the index changing later: Nothing will ever change this temporary.
|
// the index changing later: Nothing will ever change this temporary.
|
||||||
// The "retagging" transformation (for Stacked Borrows) relies on this.
|
// The "retagging" transformation (for Stacked Borrows) relies on this.
|
||||||
@ -83,6 +165,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||||||
Mutability::Not,
|
Mutability::Not,
|
||||||
));
|
));
|
||||||
|
|
||||||
|
let slice = place_builder.clone().into_place();
|
||||||
// bounds check:
|
// bounds check:
|
||||||
let (len, lt) = (
|
let (len, lt) = (
|
||||||
this.temp(usize_ty.clone(), expr_span),
|
this.temp(usize_ty.clone(), expr_span),
|
||||||
@ -92,7 +175,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||||||
block,
|
block,
|
||||||
source_info, // len = len(slice)
|
source_info, // len = len(slice)
|
||||||
&len,
|
&len,
|
||||||
Rvalue::Len(slice.clone()),
|
Rvalue::Len(slice),
|
||||||
);
|
);
|
||||||
this.cfg.push_assign(
|
this.cfg.push_assign(
|
||||||
block,
|
block,
|
||||||
@ -110,30 +193,29 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||||||
index: Operand::Copy(Place::from(idx)),
|
index: Operand::Copy(Place::from(idx)),
|
||||||
};
|
};
|
||||||
let success = this.assert(block, Operand::Move(lt), true, msg, expr_span);
|
let success = this.assert(block, Operand::Move(lt), true, msg, expr_span);
|
||||||
success.and(slice.index(idx))
|
success.and(place_builder.index(idx))
|
||||||
}
|
}
|
||||||
ExprKind::SelfRef => block.and(Place::from(Local::new(1))),
|
ExprKind::SelfRef => block.and(PlaceBuilder::from(Local::new(1))),
|
||||||
ExprKind::VarRef { id } => {
|
ExprKind::VarRef { id } => {
|
||||||
let place = if this.is_bound_var_in_guard(id) {
|
let place_builder = if this.is_bound_var_in_guard(id) {
|
||||||
let index = this.var_local_id(id, RefWithinGuard);
|
let index = this.var_local_id(id, RefWithinGuard);
|
||||||
Place::from(index).deref()
|
PlaceBuilder::from(index).deref()
|
||||||
} else {
|
} else {
|
||||||
let index = this.var_local_id(id, OutsideGuard);
|
let index = this.var_local_id(id, OutsideGuard);
|
||||||
Place::from(index)
|
PlaceBuilder::from(index)
|
||||||
};
|
};
|
||||||
block.and(place)
|
block.and(place_builder)
|
||||||
}
|
}
|
||||||
ExprKind::StaticRef { id } => block.and(Place {
|
ExprKind::StaticRef { id } => block.and(PlaceBuilder::from(
|
||||||
base: PlaceBase::Static(Box::new(Static {
|
PlaceBase::Static(Box::new(Static {
|
||||||
ty: expr.ty,
|
ty: expr.ty,
|
||||||
kind: StaticKind::Static,
|
kind: StaticKind::Static,
|
||||||
def_id: id,
|
def_id: id,
|
||||||
})),
|
}))
|
||||||
projection: box [],
|
)),
|
||||||
}),
|
|
||||||
|
|
||||||
ExprKind::PlaceTypeAscription { source, user_ty } => {
|
ExprKind::PlaceTypeAscription { source, user_ty } => {
|
||||||
let place = unpack!(block = this.as_place(block, source));
|
let place_builder = unpack!(block = this.as_place_builder(block, source));
|
||||||
if let Some(user_ty) = user_ty {
|
if let Some(user_ty) = user_ty {
|
||||||
let annotation_index = this.canonical_user_type_annotations.push(
|
let annotation_index = this.canonical_user_type_annotations.push(
|
||||||
CanonicalUserTypeAnnotation {
|
CanonicalUserTypeAnnotation {
|
||||||
@ -142,13 +224,15 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||||||
inferred_ty: expr.ty,
|
inferred_ty: expr.ty,
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
|
|
||||||
|
let place = place_builder.clone().into_place();
|
||||||
this.cfg.push(
|
this.cfg.push(
|
||||||
block,
|
block,
|
||||||
Statement {
|
Statement {
|
||||||
source_info,
|
source_info,
|
||||||
kind: StatementKind::AscribeUserType(
|
kind: StatementKind::AscribeUserType(
|
||||||
box(
|
box(
|
||||||
place.clone(),
|
place,
|
||||||
UserTypeProjection { base: annotation_index, projs: vec![], }
|
UserTypeProjection { base: annotation_index, projs: vec![], }
|
||||||
),
|
),
|
||||||
Variance::Invariant,
|
Variance::Invariant,
|
||||||
@ -156,7 +240,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
block.and(place)
|
block.and(place_builder)
|
||||||
}
|
}
|
||||||
ExprKind::ValueTypeAscription { source, user_ty } => {
|
ExprKind::ValueTypeAscription { source, user_ty } => {
|
||||||
let source = this.hir.mirror(source);
|
let source = this.hir.mirror(source);
|
||||||
@ -185,7 +269,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||||||
},
|
},
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
block.and(Place::from(temp))
|
block.and(PlaceBuilder::from(temp))
|
||||||
}
|
}
|
||||||
|
|
||||||
ExprKind::Array { .. }
|
ExprKind::Array { .. }
|
||||||
@ -221,7 +305,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
|||||||
});
|
});
|
||||||
let temp =
|
let temp =
|
||||||
unpack!(block = this.as_temp(block, expr.temp_lifetime, expr, mutability));
|
unpack!(block = this.as_temp(block, expr.temp_lifetime, expr, mutability));
|
||||||
block.and(Place::from(temp))
|
block.and(PlaceBuilder::from(temp))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -97,6 +97,36 @@ struct TransferFunction<'a, 'mir, 'tcx> {
|
|||||||
param_env: ty::ParamEnv<'tcx>,
|
param_env: ty::ParamEnv<'tcx>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl<'tcx> TransferFunction<'_, '_, 'tcx> {
|
||||||
|
/// Returns `true` if this borrow would allow mutation of the `borrowed_place`.
|
||||||
|
fn borrow_allows_mutation(
|
||||||
|
&self,
|
||||||
|
kind: mir::BorrowKind,
|
||||||
|
borrowed_place: &mir::Place<'tcx>,
|
||||||
|
) -> bool {
|
||||||
|
let borrowed_ty = borrowed_place.ty(self.body, self.tcx).ty;
|
||||||
|
|
||||||
|
// Zero-sized types cannot be mutated, since there is nothing inside to mutate.
|
||||||
|
//
|
||||||
|
// FIXME: For now, we only exempt arrays of length zero. We need to carefully
|
||||||
|
// consider the effects before extending this to all ZSTs.
|
||||||
|
if let ty::Array(_, len) = borrowed_ty.kind {
|
||||||
|
if len.try_eval_usize(self.tcx, self.param_env) == Some(0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
match kind {
|
||||||
|
mir::BorrowKind::Mut { .. } => true,
|
||||||
|
|
||||||
|
| mir::BorrowKind::Shared
|
||||||
|
| mir::BorrowKind::Shallow
|
||||||
|
| mir::BorrowKind::Unique
|
||||||
|
=> !borrowed_ty.is_freeze(self.tcx, self.param_env, DUMMY_SP),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
impl<'tcx> Visitor<'tcx> for TransferFunction<'_, '_, 'tcx> {
|
impl<'tcx> Visitor<'tcx> for TransferFunction<'_, '_, 'tcx> {
|
||||||
fn visit_rvalue(
|
fn visit_rvalue(
|
||||||
&mut self,
|
&mut self,
|
||||||
@ -104,21 +134,7 @@ impl<'tcx> Visitor<'tcx> for TransferFunction<'_, '_, 'tcx> {
|
|||||||
location: Location,
|
location: Location,
|
||||||
) {
|
) {
|
||||||
if let mir::Rvalue::Ref(_, kind, ref borrowed_place) = *rvalue {
|
if let mir::Rvalue::Ref(_, kind, ref borrowed_place) = *rvalue {
|
||||||
let is_mut = match kind {
|
if self.borrow_allows_mutation(kind, borrowed_place) {
|
||||||
mir::BorrowKind::Mut { .. } => true,
|
|
||||||
|
|
||||||
| mir::BorrowKind::Shared
|
|
||||||
| mir::BorrowKind::Shallow
|
|
||||||
| mir::BorrowKind::Unique
|
|
||||||
=> {
|
|
||||||
!borrowed_place
|
|
||||||
.ty(self.body, self.tcx)
|
|
||||||
.ty
|
|
||||||
.is_freeze(self.tcx, self.param_env, DUMMY_SP)
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if is_mut {
|
|
||||||
match borrowed_place.base {
|
match borrowed_place.base {
|
||||||
mir::PlaceBase::Local(borrowed_local) if !borrowed_place.is_indirect()
|
mir::PlaceBase::Local(borrowed_local) if !borrowed_place.is_indirect()
|
||||||
=> self.trans.gen(borrowed_local),
|
=> self.trans.gen(borrowed_local),
|
||||||
|
@ -137,7 +137,7 @@ pub fn compute_indirectly_mutable_locals<'mir, 'tcx>(
|
|||||||
item.tcx,
|
item.tcx,
|
||||||
item.body,
|
item.body,
|
||||||
item.def_id,
|
item.def_id,
|
||||||
&[],
|
&item.tcx.get_attrs(item.def_id),
|
||||||
&dead_unwinds,
|
&dead_unwinds,
|
||||||
old_dataflow::IndirectlyMutableLocals::new(item.tcx, item.body, item.param_env),
|
old_dataflow::IndirectlyMutableLocals::new(item.tcx, item.body, item.param_env),
|
||||||
|_, local| old_dataflow::DebugFormatted::new(&local),
|
|_, local| old_dataflow::DebugFormatted::new(&local),
|
||||||
|
@ -335,34 +335,9 @@ impl<'mir, 'tcx> ConstPropagator<'mir, 'tcx> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fn get_const(&self, local: Local) -> Option<Const<'tcx>> {
|
fn get_const(&self, local: Local) -> Option<Const<'tcx>> {
|
||||||
let l = &self.ecx.frame().locals[local];
|
|
||||||
|
|
||||||
// If the local is `Unitialized` or `Dead` then we haven't propagated a value into it.
|
|
||||||
//
|
|
||||||
// `InterpCx::access_local()` mostly takes care of this for us however, for ZSTs,
|
|
||||||
// it will synthesize a value for us. In doing so, that will cause the
|
|
||||||
// `get_const(l).is_empty()` assert right before we call `set_const()` in `visit_statement`
|
|
||||||
// to fail.
|
|
||||||
if let LocalValue::Uninitialized | LocalValue::Dead = l.value {
|
|
||||||
return None;
|
|
||||||
}
|
|
||||||
|
|
||||||
self.ecx.access_local(self.ecx.frame(), local, None).ok()
|
self.ecx.access_local(self.ecx.frame(), local, None).ok()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn set_const(&mut self, local: Local, c: Const<'tcx>) {
|
|
||||||
let frame = self.ecx.frame_mut();
|
|
||||||
|
|
||||||
if let Some(layout) = frame.locals[local].layout.get() {
|
|
||||||
debug_assert_eq!(c.layout, layout);
|
|
||||||
}
|
|
||||||
|
|
||||||
frame.locals[local] = LocalState {
|
|
||||||
value: LocalValue::Live(*c),
|
|
||||||
layout: Cell::new(Some(c.layout)),
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
fn remove_const(&mut self, local: Local) {
|
fn remove_const(&mut self, local: Local) {
|
||||||
self.ecx.frame_mut().locals[local] = LocalState {
|
self.ecx.frame_mut().locals[local] = LocalState {
|
||||||
value: LocalValue::Uninitialized,
|
value: LocalValue::Uninitialized,
|
||||||
@ -735,10 +710,8 @@ impl<'mir, 'tcx> MutVisitor<'tcx> for ConstPropagator<'mir, 'tcx> {
|
|||||||
place) {
|
place) {
|
||||||
trace!("checking whether {:?} can be stored to {:?}", value, local);
|
trace!("checking whether {:?} can be stored to {:?}", value, local);
|
||||||
if self.can_const_prop[local] {
|
if self.can_const_prop[local] {
|
||||||
trace!("storing {:?} to {:?}", value, local);
|
trace!("stored {:?} to {:?}", value, local);
|
||||||
assert!(self.get_const(local).is_none() ||
|
assert_eq!(self.get_const(local), Some(value));
|
||||||
self.get_const(local) == Some(value));
|
|
||||||
self.set_const(local, value);
|
|
||||||
|
|
||||||
if self.should_const_prop() {
|
if self.should_const_prop() {
|
||||||
self.replace_with_const(
|
self.replace_with_const(
|
||||||
@ -747,6 +720,9 @@ impl<'mir, 'tcx> MutVisitor<'tcx> for ConstPropagator<'mir, 'tcx> {
|
|||||||
statement.source_info,
|
statement.source_info,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
trace!("can't propagate {:?} to {:?}", value, local);
|
||||||
|
self.remove_const(local);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,9 +3,9 @@ use syntax::ast;
|
|||||||
use syntax::symbol::sym;
|
use syntax::symbol::sym;
|
||||||
use syntax_pos::Span;
|
use syntax_pos::Span;
|
||||||
|
|
||||||
use rustc::ty::{self, TyCtxt};
|
use rustc::ty::{self, TyCtxt, Ty};
|
||||||
use rustc::hir::def_id::DefId;
|
use rustc::hir::def_id::DefId;
|
||||||
use rustc::mir::{self, Body, Location};
|
use rustc::mir::{self, Body, Location, Local};
|
||||||
use rustc_index::bit_set::BitSet;
|
use rustc_index::bit_set::BitSet;
|
||||||
use crate::transform::{MirPass, MirSource};
|
use crate::transform::{MirPass, MirSource};
|
||||||
|
|
||||||
@ -13,9 +13,11 @@ use crate::dataflow::{do_dataflow, DebugFormatted};
|
|||||||
use crate::dataflow::MoveDataParamEnv;
|
use crate::dataflow::MoveDataParamEnv;
|
||||||
use crate::dataflow::BitDenotation;
|
use crate::dataflow::BitDenotation;
|
||||||
use crate::dataflow::DataflowResults;
|
use crate::dataflow::DataflowResults;
|
||||||
|
use crate::dataflow::DataflowResultsCursor;
|
||||||
use crate::dataflow::{
|
use crate::dataflow::{
|
||||||
DefinitelyInitializedPlaces, MaybeInitializedPlaces, MaybeUninitializedPlaces
|
DefinitelyInitializedPlaces, MaybeInitializedPlaces, MaybeUninitializedPlaces
|
||||||
};
|
};
|
||||||
|
use crate::dataflow::IndirectlyMutableLocals;
|
||||||
use crate::dataflow::move_paths::{MovePathIndex, LookupResult};
|
use crate::dataflow::move_paths::{MovePathIndex, LookupResult};
|
||||||
use crate::dataflow::move_paths::{HasMoveData, MoveData};
|
use crate::dataflow::move_paths::{HasMoveData, MoveData};
|
||||||
|
|
||||||
@ -50,6 +52,10 @@ impl<'tcx> MirPass<'tcx> for SanityCheck {
|
|||||||
do_dataflow(tcx, body, def_id, &attributes, &dead_unwinds,
|
do_dataflow(tcx, body, def_id, &attributes, &dead_unwinds,
|
||||||
DefinitelyInitializedPlaces::new(tcx, body, &mdpe),
|
DefinitelyInitializedPlaces::new(tcx, body, &mdpe),
|
||||||
|bd, i| DebugFormatted::new(&bd.move_data().move_paths[i]));
|
|bd, i| DebugFormatted::new(&bd.move_data().move_paths[i]));
|
||||||
|
let flow_indirectly_mut =
|
||||||
|
do_dataflow(tcx, body, def_id, &attributes, &dead_unwinds,
|
||||||
|
IndirectlyMutableLocals::new(tcx, body, param_env),
|
||||||
|
|_, i| DebugFormatted::new(&i));
|
||||||
|
|
||||||
if has_rustc_mir_with(&attributes, sym::rustc_peek_maybe_init).is_some() {
|
if has_rustc_mir_with(&attributes, sym::rustc_peek_maybe_init).is_some() {
|
||||||
sanity_check_via_rustc_peek(tcx, body, def_id, &attributes, &flow_inits);
|
sanity_check_via_rustc_peek(tcx, body, def_id, &attributes, &flow_inits);
|
||||||
@ -60,6 +66,9 @@ impl<'tcx> MirPass<'tcx> for SanityCheck {
|
|||||||
if has_rustc_mir_with(&attributes, sym::rustc_peek_definite_init).is_some() {
|
if has_rustc_mir_with(&attributes, sym::rustc_peek_definite_init).is_some() {
|
||||||
sanity_check_via_rustc_peek(tcx, body, def_id, &attributes, &flow_def_inits);
|
sanity_check_via_rustc_peek(tcx, body, def_id, &attributes, &flow_def_inits);
|
||||||
}
|
}
|
||||||
|
if has_rustc_mir_with(&attributes, sym::rustc_peek_indirectly_mutable).is_some() {
|
||||||
|
sanity_check_via_rustc_peek(tcx, body, def_id, &attributes, &flow_indirectly_mut);
|
||||||
|
}
|
||||||
if has_rustc_mir_with(&attributes, sym::stop_after_dataflow).is_some() {
|
if has_rustc_mir_with(&attributes, sym::stop_after_dataflow).is_some() {
|
||||||
tcx.sess.fatal("stop_after_dataflow ended compilation");
|
tcx.sess.fatal("stop_after_dataflow ended compilation");
|
||||||
}
|
}
|
||||||
@ -88,151 +97,196 @@ pub fn sanity_check_via_rustc_peek<'tcx, O>(
|
|||||||
def_id: DefId,
|
def_id: DefId,
|
||||||
_attributes: &[ast::Attribute],
|
_attributes: &[ast::Attribute],
|
||||||
results: &DataflowResults<'tcx, O>,
|
results: &DataflowResults<'tcx, O>,
|
||||||
) where
|
) where O: RustcPeekAt<'tcx> {
|
||||||
O: BitDenotation<'tcx, Idx = MovePathIndex> + HasMoveData<'tcx>,
|
|
||||||
{
|
|
||||||
debug!("sanity_check_via_rustc_peek def_id: {:?}", def_id);
|
debug!("sanity_check_via_rustc_peek def_id: {:?}", def_id);
|
||||||
// FIXME: this is not DRY. Figure out way to abstract this and
|
|
||||||
// `dataflow::build_sets`. (But note it is doing non-standard
|
|
||||||
// stuff, so such generalization may not be realistic.)
|
|
||||||
|
|
||||||
for bb in body.basic_blocks().indices() {
|
let mut cursor = DataflowResultsCursor::new(results, body);
|
||||||
each_block(tcx, body, results, bb);
|
|
||||||
|
let peek_calls = body
|
||||||
|
.basic_blocks()
|
||||||
|
.iter_enumerated()
|
||||||
|
.filter_map(|(bb, block_data)| {
|
||||||
|
PeekCall::from_terminator(tcx, block_data.terminator())
|
||||||
|
.map(|call| (bb, block_data, call))
|
||||||
|
});
|
||||||
|
|
||||||
|
for (bb, block_data, call) in peek_calls {
|
||||||
|
// Look for a sequence like the following to indicate that we should be peeking at `_1`:
|
||||||
|
// _2 = &_1;
|
||||||
|
// rustc_peek(_2);
|
||||||
|
//
|
||||||
|
// /* or */
|
||||||
|
//
|
||||||
|
// _2 = _1;
|
||||||
|
// rustc_peek(_2);
|
||||||
|
let (statement_index, peek_rval) = block_data
|
||||||
|
.statements
|
||||||
|
.iter()
|
||||||
|
.enumerate()
|
||||||
|
.filter_map(|(i, stmt)| value_assigned_to_local(stmt, call.arg).map(|rval| (i, rval)))
|
||||||
|
.next()
|
||||||
|
.expect("call to rustc_peek should be preceded by \
|
||||||
|
assignment to temporary holding its argument");
|
||||||
|
|
||||||
|
match (call.kind, peek_rval) {
|
||||||
|
| (PeekCallKind::ByRef, mir::Rvalue::Ref(_, _, place))
|
||||||
|
| (PeekCallKind::ByVal, mir::Rvalue::Use(mir::Operand::Move(place)))
|
||||||
|
| (PeekCallKind::ByVal, mir::Rvalue::Use(mir::Operand::Copy(place)))
|
||||||
|
=> {
|
||||||
|
let loc = Location { block: bb, statement_index };
|
||||||
|
cursor.seek(loc);
|
||||||
|
let state = cursor.get();
|
||||||
|
results.operator().peek_at(tcx, place, state, call);
|
||||||
|
}
|
||||||
|
|
||||||
|
_ => {
|
||||||
|
let msg = "rustc_peek: argument expression \
|
||||||
|
must be either `place` or `&place`";
|
||||||
|
tcx.sess.span_err(call.span, msg);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn each_block<'tcx, O>(
|
/// If `stmt` is an assignment where the LHS is the given local (with no projections), returns the
|
||||||
tcx: TyCtxt<'tcx>,
|
/// RHS of the assignment.
|
||||||
body: &Body<'tcx>,
|
fn value_assigned_to_local<'a, 'tcx>(
|
||||||
results: &DataflowResults<'tcx, O>,
|
stmt: &'a mir::Statement<'tcx>,
|
||||||
bb: mir::BasicBlock,
|
local: Local,
|
||||||
) where
|
) -> Option<&'a mir::Rvalue<'tcx>> {
|
||||||
O: BitDenotation<'tcx, Idx = MovePathIndex> + HasMoveData<'tcx>,
|
if let mir::StatementKind::Assign(box (place, rvalue)) = &stmt.kind {
|
||||||
{
|
if let mir::Place { base: mir::PlaceBase::Local(l), projection: box [] } = place {
|
||||||
let move_data = results.0.operator.move_data();
|
if local == *l {
|
||||||
let mir::BasicBlockData { ref statements, ref terminator, is_cleanup: _ } = body[bb];
|
return Some(&*rvalue);
|
||||||
|
}
|
||||||
let (args, span) = match is_rustc_peek(tcx, terminator) {
|
}
|
||||||
Some(args_and_span) => args_and_span,
|
}
|
||||||
None => return,
|
|
||||||
};
|
None
|
||||||
assert!(args.len() == 1);
|
}
|
||||||
let peek_arg_place = match args[0] {
|
|
||||||
mir::Operand::Copy(ref place @ mir::Place {
|
#[derive(Clone, Copy, Debug)]
|
||||||
base: mir::PlaceBase::Local(_),
|
enum PeekCallKind {
|
||||||
projection: box [],
|
ByVal,
|
||||||
}) |
|
ByRef,
|
||||||
mir::Operand::Move(ref place @ mir::Place {
|
}
|
||||||
base: mir::PlaceBase::Local(_),
|
|
||||||
projection: box [],
|
impl PeekCallKind {
|
||||||
}) => Some(place),
|
fn from_arg_ty(arg: Ty<'_>) -> Self {
|
||||||
_ => None,
|
match arg.kind {
|
||||||
};
|
ty::Ref(_, _, _) => PeekCallKind::ByRef,
|
||||||
|
_ => PeekCallKind::ByVal,
|
||||||
let peek_arg_place = match peek_arg_place {
|
}
|
||||||
Some(arg) => arg,
|
}
|
||||||
None => {
|
}
|
||||||
tcx.sess.diagnostic().span_err(
|
|
||||||
span, "dataflow::sanity_check cannot feed a non-temp to rustc_peek.");
|
#[derive(Clone, Copy, Debug)]
|
||||||
return;
|
pub struct PeekCall {
|
||||||
}
|
arg: Local,
|
||||||
};
|
kind: PeekCallKind,
|
||||||
|
span: Span,
|
||||||
let mut on_entry = results.0.sets.entry_set_for(bb.index()).to_owned();
|
}
|
||||||
let mut trans = results.0.sets.trans_for(bb.index()).clone();
|
|
||||||
|
impl PeekCall {
|
||||||
// Emulate effect of all statements in the block up to (but not
|
fn from_terminator<'tcx>(
|
||||||
// including) the borrow within `peek_arg_place`. Do *not* include
|
tcx: TyCtxt<'tcx>,
|
||||||
// call to `peek_arg_place` itself (since we are peeking the state
|
terminator: &mir::Terminator<'tcx>,
|
||||||
// of the argument at time immediate preceding Call to
|
) -> Option<Self> {
|
||||||
// `rustc_peek`).
|
use mir::{Operand, Place, PlaceBase};
|
||||||
|
|
||||||
for (j, stmt) in statements.iter().enumerate() {
|
let span = terminator.source_info.span;
|
||||||
debug!("rustc_peek: ({:?},{}) {:?}", bb, j, stmt);
|
if let mir::TerminatorKind::Call { func: Operand::Constant(func), args, .. } =
|
||||||
let (place, rvalue) = match stmt.kind {
|
&terminator.kind
|
||||||
mir::StatementKind::Assign(box(ref place, ref rvalue)) => {
|
{
|
||||||
(place, rvalue)
|
if let ty::FnDef(def_id, substs) = func.literal.ty.kind {
|
||||||
|
let sig = tcx.fn_sig(def_id);
|
||||||
|
let name = tcx.item_name(def_id);
|
||||||
|
if sig.abi() != Abi::RustIntrinsic || name != sym::rustc_peek {
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
|
||||||
|
assert_eq!(args.len(), 1);
|
||||||
|
let kind = PeekCallKind::from_arg_ty(substs.type_at(0));
|
||||||
|
let arg = match args[0] {
|
||||||
|
| Operand::Copy(Place { base: PlaceBase::Local(local), projection: box [] })
|
||||||
|
| Operand::Move(Place { base: PlaceBase::Local(local), projection: box [] })
|
||||||
|
=> local,
|
||||||
|
|
||||||
|
_ => {
|
||||||
|
tcx.sess.diagnostic().span_err(
|
||||||
|
span, "dataflow::sanity_check cannot feed a non-temp to rustc_peek.");
|
||||||
|
return None;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return Some(PeekCall {
|
||||||
|
arg,
|
||||||
|
kind,
|
||||||
|
span,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub trait RustcPeekAt<'tcx>: BitDenotation<'tcx> {
|
||||||
|
fn peek_at(
|
||||||
|
&self,
|
||||||
|
tcx: TyCtxt<'tcx>,
|
||||||
|
place: &mir::Place<'tcx>,
|
||||||
|
flow_state: &BitSet<Self::Idx>,
|
||||||
|
call: PeekCall,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'tcx, O> RustcPeekAt<'tcx> for O
|
||||||
|
where O: BitDenotation<'tcx, Idx = MovePathIndex> + HasMoveData<'tcx>,
|
||||||
|
{
|
||||||
|
fn peek_at(
|
||||||
|
&self,
|
||||||
|
tcx: TyCtxt<'tcx>,
|
||||||
|
place: &mir::Place<'tcx>,
|
||||||
|
flow_state: &BitSet<Self::Idx>,
|
||||||
|
call: PeekCall,
|
||||||
|
) {
|
||||||
|
match self.move_data().rev_lookup.find(place.as_ref()) {
|
||||||
|
LookupResult::Exact(peek_mpi) => {
|
||||||
|
let bit_state = flow_state.contains(peek_mpi);
|
||||||
|
debug!("rustc_peek({:?} = &{:?}) bit_state: {}",
|
||||||
|
call.arg, place, bit_state);
|
||||||
|
if !bit_state {
|
||||||
|
tcx.sess.span_err(call.span, "rustc_peek: bit not set");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
LookupResult::Parent(..) => {
|
||||||
|
tcx.sess.span_err(call.span, "rustc_peek: argument untracked");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl<'tcx> RustcPeekAt<'tcx> for IndirectlyMutableLocals<'_, 'tcx> {
|
||||||
|
fn peek_at(
|
||||||
|
&self,
|
||||||
|
tcx: TyCtxt<'tcx>,
|
||||||
|
place: &mir::Place<'tcx>,
|
||||||
|
flow_state: &BitSet<Local>,
|
||||||
|
call: PeekCall,
|
||||||
|
) {
|
||||||
|
warn!("peek_at: place={:?}", place);
|
||||||
|
let local = match place {
|
||||||
|
mir::Place { base: mir::PlaceBase::Local(l), projection: box [] } => *l,
|
||||||
|
_ => {
|
||||||
|
tcx.sess.span_err(call.span, "rustc_peek: argument was not a local");
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
mir::StatementKind::FakeRead(..) |
|
|
||||||
mir::StatementKind::StorageLive(_) |
|
|
||||||
mir::StatementKind::StorageDead(_) |
|
|
||||||
mir::StatementKind::InlineAsm { .. } |
|
|
||||||
mir::StatementKind::Retag { .. } |
|
|
||||||
mir::StatementKind::AscribeUserType(..) |
|
|
||||||
mir::StatementKind::Nop => continue,
|
|
||||||
mir::StatementKind::SetDiscriminant{ .. } =>
|
|
||||||
span_bug!(stmt.source_info.span,
|
|
||||||
"sanity_check should run before Deaggregator inserts SetDiscriminant"),
|
|
||||||
};
|
};
|
||||||
|
|
||||||
if place == peek_arg_place {
|
if !flow_state.contains(local) {
|
||||||
if let mir::Rvalue::Ref(_, mir::BorrowKind::Shared, ref peeking_at_place) = *rvalue {
|
tcx.sess.span_err(call.span, "rustc_peek: bit not set");
|
||||||
// Okay, our search is over.
|
|
||||||
match move_data.rev_lookup.find(peeking_at_place.as_ref()) {
|
|
||||||
LookupResult::Exact(peek_mpi) => {
|
|
||||||
let bit_state = on_entry.contains(peek_mpi);
|
|
||||||
debug!("rustc_peek({:?} = &{:?}) bit_state: {}",
|
|
||||||
place, peeking_at_place, bit_state);
|
|
||||||
if !bit_state {
|
|
||||||
tcx.sess.span_err(span, "rustc_peek: bit not set");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
LookupResult::Parent(..) => {
|
|
||||||
tcx.sess.span_err(span, "rustc_peek: argument untracked");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
} else {
|
|
||||||
// Our search should have been over, but the input
|
|
||||||
// does not match expectations of `rustc_peek` for
|
|
||||||
// this sanity_check.
|
|
||||||
let msg = "rustc_peek: argument expression \
|
|
||||||
must be immediate borrow of form `&expr`";
|
|
||||||
tcx.sess.span_err(span, msg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
let lhs_mpi = move_data.rev_lookup.find(place.as_ref());
|
|
||||||
|
|
||||||
debug!("rustc_peek: computing effect on place: {:?} ({:?}) in stmt: {:?}",
|
|
||||||
place, lhs_mpi, stmt);
|
|
||||||
// reset GEN and KILL sets before emulating their effect.
|
|
||||||
trans.clear();
|
|
||||||
results.0.operator.before_statement_effect(
|
|
||||||
&mut trans,
|
|
||||||
Location { block: bb, statement_index: j });
|
|
||||||
results.0.operator.statement_effect(
|
|
||||||
&mut trans,
|
|
||||||
Location { block: bb, statement_index: j });
|
|
||||||
trans.apply(&mut on_entry);
|
|
||||||
}
|
|
||||||
|
|
||||||
results.0.operator.before_terminator_effect(
|
|
||||||
&mut trans,
|
|
||||||
Location { block: bb, statement_index: statements.len() });
|
|
||||||
|
|
||||||
tcx.sess.span_err(span, &format!("rustc_peek: MIR did not match \
|
|
||||||
anticipated pattern; note that \
|
|
||||||
rustc_peek expects input of \
|
|
||||||
form `&expr`"));
|
|
||||||
}
|
|
||||||
|
|
||||||
fn is_rustc_peek<'a, 'tcx>(
|
|
||||||
tcx: TyCtxt<'tcx>,
|
|
||||||
terminator: &'a Option<mir::Terminator<'tcx>>,
|
|
||||||
) -> Option<(&'a [mir::Operand<'tcx>], Span)> {
|
|
||||||
if let Some(mir::Terminator { ref kind, source_info, .. }) = *terminator {
|
|
||||||
if let mir::TerminatorKind::Call { func: ref oper, ref args, .. } = *kind {
|
|
||||||
if let mir::Operand::Constant(ref func) = *oper {
|
|
||||||
if let ty::FnDef(def_id, _) = func.literal.ty.kind {
|
|
||||||
let abi = tcx.fn_sig(def_id).abi();
|
|
||||||
let name = tcx.item_name(def_id);
|
|
||||||
if abi == Abi::RustIntrinsic && name == sym::rustc_peek {
|
|
||||||
return Some((args, source_info.span));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return None;
|
|
||||||
}
|
}
|
||||||
|
@ -18,6 +18,7 @@ use crate::util::nodemap::FxHashMap;
|
|||||||
use crate::astconv::AstConv as _;
|
use crate::astconv::AstConv as _;
|
||||||
|
|
||||||
use errors::{Applicability, DiagnosticBuilder, pluralise};
|
use errors::{Applicability, DiagnosticBuilder, pluralise};
|
||||||
|
use syntax_pos::hygiene::DesugaringKind;
|
||||||
use syntax::ast;
|
use syntax::ast;
|
||||||
use syntax::symbol::{Symbol, kw, sym};
|
use syntax::symbol::{Symbol, kw, sym};
|
||||||
use syntax::source_map::Span;
|
use syntax::source_map::Span;
|
||||||
@ -150,8 +151,20 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||||||
debug!(">> type-checking: expr={:?} expected={:?}",
|
debug!(">> type-checking: expr={:?} expected={:?}",
|
||||||
expr, expected);
|
expr, expected);
|
||||||
|
|
||||||
|
// True if `expr` is a `Try::from_ok(())` that is a result of desugaring a try block
|
||||||
|
// without the final expr (e.g. `try { return; }`). We don't want to generate an
|
||||||
|
// unreachable_code lint for it since warnings for autogenerated code are confusing.
|
||||||
|
let is_try_block_generated_unit_expr = match expr.kind {
|
||||||
|
ExprKind::Call(_, ref args) if expr.span.is_desugaring(DesugaringKind::TryBlock) =>
|
||||||
|
args.len() == 1 && args[0].span.is_desugaring(DesugaringKind::TryBlock),
|
||||||
|
|
||||||
|
_ => false,
|
||||||
|
};
|
||||||
|
|
||||||
// Warn for expressions after diverging siblings.
|
// Warn for expressions after diverging siblings.
|
||||||
self.warn_if_unreachable(expr.hir_id, expr.span, "expression");
|
if !is_try_block_generated_unit_expr {
|
||||||
|
self.warn_if_unreachable(expr.hir_id, expr.span, "expression");
|
||||||
|
}
|
||||||
|
|
||||||
// Hide the outer diverging and has_errors flags.
|
// Hide the outer diverging and has_errors flags.
|
||||||
let old_diverges = self.diverges.get();
|
let old_diverges = self.diverges.get();
|
||||||
@ -164,6 +177,10 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
|||||||
// Warn for non-block expressions with diverging children.
|
// Warn for non-block expressions with diverging children.
|
||||||
match expr.kind {
|
match expr.kind {
|
||||||
ExprKind::Block(..) | ExprKind::Loop(..) | ExprKind::Match(..) => {},
|
ExprKind::Block(..) | ExprKind::Loop(..) | ExprKind::Match(..) => {},
|
||||||
|
// If `expr` is a result of desugaring the try block and is an ok-wrapped
|
||||||
|
// diverging expression (e.g. it arose from desugaring of `try { return }`),
|
||||||
|
// we skip issuing a warning because it is autogenerated code.
|
||||||
|
ExprKind::Call(..) if expr.span.is_desugaring(DesugaringKind::TryBlock) => {},
|
||||||
ExprKind::Call(ref callee, _) =>
|
ExprKind::Call(ref callee, _) =>
|
||||||
self.warn_if_unreachable(expr.hir_id, callee.span, "call"),
|
self.warn_if_unreachable(expr.hir_id, callee.span, "call"),
|
||||||
ExprKind::MethodCall(_, ref span, _) =>
|
ExprKind::MethodCall(_, ref span, _) =>
|
||||||
|
@ -123,13 +123,6 @@ pub fn resolve_interior<'a, 'tcx>(
|
|||||||
// Sort types by insertion order
|
// Sort types by insertion order
|
||||||
types.sort_by_key(|t| t.1);
|
types.sort_by_key(|t| t.1);
|
||||||
|
|
||||||
// Store the generator types and spans into the tables for this generator.
|
|
||||||
let interior_types = types.iter().cloned().map(|t| t.0).collect::<Vec<_>>();
|
|
||||||
visitor.fcx.inh.tables.borrow_mut().generator_interior_types = interior_types;
|
|
||||||
|
|
||||||
// Extract type components
|
|
||||||
let type_list = fcx.tcx.mk_type_list(types.into_iter().map(|t| (t.0).ty));
|
|
||||||
|
|
||||||
// The types in the generator interior contain lifetimes local to the generator itself,
|
// The types in the generator interior contain lifetimes local to the generator itself,
|
||||||
// which should not be exposed outside of the generator. Therefore, we replace these
|
// which should not be exposed outside of the generator. Therefore, we replace these
|
||||||
// lifetimes with existentially-bound lifetimes, which reflect the exact value of the
|
// lifetimes with existentially-bound lifetimes, which reflect the exact value of the
|
||||||
@ -139,18 +132,25 @@ pub fn resolve_interior<'a, 'tcx>(
|
|||||||
// if a Sync generator contains an &'α T, we need to check whether &'α T: Sync),
|
// if a Sync generator contains an &'α T, we need to check whether &'α T: Sync),
|
||||||
// so knowledge of the exact relationships between them isn't particularly important.
|
// so knowledge of the exact relationships between them isn't particularly important.
|
||||||
|
|
||||||
debug!("types in generator {:?}, span = {:?}", type_list, body.value.span);
|
debug!("types in generator {:?}, span = {:?}", types, body.value.span);
|
||||||
|
|
||||||
// Replace all regions inside the generator interior with late bound regions
|
// Replace all regions inside the generator interior with late bound regions
|
||||||
// Note that each region slot in the types gets a new fresh late bound region,
|
// Note that each region slot in the types gets a new fresh late bound region,
|
||||||
// which means that none of the regions inside relate to any other, even if
|
// which means that none of the regions inside relate to any other, even if
|
||||||
// typeck had previously found constraints that would cause them to be related.
|
// typeck had previously found constraints that would cause them to be related.
|
||||||
let mut counter = 0;
|
let mut counter = 0;
|
||||||
let type_list = fcx.tcx.fold_regions(&type_list, &mut false, |_, current_depth| {
|
let types = fcx.tcx.fold_regions(&types, &mut false, |_, current_depth| {
|
||||||
counter += 1;
|
counter += 1;
|
||||||
fcx.tcx.mk_region(ty::ReLateBound(current_depth, ty::BrAnon(counter)))
|
fcx.tcx.mk_region(ty::ReLateBound(current_depth, ty::BrAnon(counter)))
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Store the generator types and spans into the tables for this generator.
|
||||||
|
let interior_types = types.iter().map(|t| t.0.clone()).collect::<Vec<_>>();
|
||||||
|
visitor.fcx.inh.tables.borrow_mut().generator_interior_types = interior_types;
|
||||||
|
|
||||||
|
// Extract type components
|
||||||
|
let type_list = fcx.tcx.mk_type_list(types.into_iter().map(|t| (t.0).ty));
|
||||||
|
|
||||||
let witness = fcx.tcx.mk_generator_witness(ty::Binder::bind(type_list));
|
let witness = fcx.tcx.mk_generator_witness(ty::Binder::bind(type_list));
|
||||||
|
|
||||||
debug!("types in generator after region replacement {:?}, span = {:?}",
|
debug!("types in generator after region replacement {:?}, span = {:?}",
|
||||||
|
@ -473,7 +473,7 @@ pub enum Diverges {
|
|||||||
WarnedAlways
|
WarnedAlways
|
||||||
}
|
}
|
||||||
|
|
||||||
// Convenience impls for combinig `Diverges`.
|
// Convenience impls for combining `Diverges`.
|
||||||
|
|
||||||
impl ops::BitAnd for Diverges {
|
impl ops::BitAnd for Diverges {
|
||||||
type Output = Self;
|
type Output = Self;
|
||||||
|
@ -183,7 +183,7 @@ nav.sub {
|
|||||||
position: fixed;
|
position: fixed;
|
||||||
left: 0;
|
left: 0;
|
||||||
top: 0;
|
top: 0;
|
||||||
height: 100vh;
|
bottom: 0;
|
||||||
overflow: auto;
|
overflow: auto;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -573,7 +573,7 @@ h4 > code, h3 > code, .invisible > code {
|
|||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
nav {
|
nav:not(.sidebar) {
|
||||||
border-bottom: 1px solid;
|
border-bottom: 1px solid;
|
||||||
padding-bottom: 10px;
|
padding-bottom: 10px;
|
||||||
margin-bottom: 10px;
|
margin-bottom: 10px;
|
||||||
|
@ -129,7 +129,7 @@ pre {
|
|||||||
pre.rust .comment { color: #8d8d8b; }
|
pre.rust .comment { color: #8d8d8b; }
|
||||||
pre.rust .doccomment { color: #8ca375; }
|
pre.rust .doccomment { color: #8ca375; }
|
||||||
|
|
||||||
nav {
|
nav:not(.sidebar) {
|
||||||
border-bottom-color: #4e4e4e;
|
border-bottom-color: #4e4e4e;
|
||||||
}
|
}
|
||||||
nav.main .current {
|
nav.main .current {
|
||||||
|
@ -129,7 +129,7 @@ pre {
|
|||||||
pre.rust .comment { color: #8E908C; }
|
pre.rust .comment { color: #8E908C; }
|
||||||
pre.rust .doccomment { color: #4D4D4C; }
|
pre.rust .doccomment { color: #4D4D4C; }
|
||||||
|
|
||||||
nav {
|
nav:not(.sidebar) {
|
||||||
border-bottom-color: #e0e0e0;
|
border-bottom-color: #e0e0e0;
|
||||||
}
|
}
|
||||||
nav.main .current {
|
nav.main .current {
|
||||||
|
@ -597,6 +597,7 @@ symbols! {
|
|||||||
rustc_peek_definite_init,
|
rustc_peek_definite_init,
|
||||||
rustc_peek_maybe_init,
|
rustc_peek_maybe_init,
|
||||||
rustc_peek_maybe_uninit,
|
rustc_peek_maybe_uninit,
|
||||||
|
rustc_peek_indirectly_mutable,
|
||||||
rustc_private,
|
rustc_private,
|
||||||
rustc_proc_macro_decls,
|
rustc_proc_macro_decls,
|
||||||
rustc_promotable,
|
rustc_promotable,
|
||||||
|
@ -1109,7 +1109,7 @@ pub enum RunStrategy {
|
|||||||
InProcess,
|
InProcess,
|
||||||
|
|
||||||
/// Spawns a subprocess to run the test, and sends the result back over the
|
/// Spawns a subprocess to run the test, and sends the result back over the
|
||||||
/// supplied channel. Requires argv[0] to exist and point to the binary
|
/// supplied channel. Requires `argv[0]` to exist and point to the binary
|
||||||
/// that's currently running.
|
/// that's currently running.
|
||||||
SpawnPrimary,
|
SpawnPrimary,
|
||||||
}
|
}
|
||||||
|
22
src/test/ui/async-await/issues/issue-64964.rs
Normal file
22
src/test/ui/async-await/issues/issue-64964.rs
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
// check-pass
|
||||||
|
// compile-flags: -Z query-dep-graph
|
||||||
|
// edition:2018
|
||||||
|
|
||||||
|
// Regression test for ICE related to `await`ing in a method + incr. comp. (#64964)
|
||||||
|
|
||||||
|
struct Body;
|
||||||
|
impl Body {
|
||||||
|
async fn next(&mut self) {
|
||||||
|
async {}.await
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Another reproduction: `await`ing with a variable from for-loop.
|
||||||
|
|
||||||
|
async fn bar() {
|
||||||
|
for x in 0..10 {
|
||||||
|
async { Some(x) }.await.unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
24
src/test/ui/borrowck/issue-64453.rs
Normal file
24
src/test/ui/borrowck/issue-64453.rs
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
struct Project;
|
||||||
|
struct Value;
|
||||||
|
|
||||||
|
static settings_dir: String = format!("");
|
||||||
|
//~^ ERROR [E0019]
|
||||||
|
//~| ERROR [E0015]
|
||||||
|
//~| ERROR [E0015]
|
||||||
|
|
||||||
|
fn from_string(_: String) -> Value {
|
||||||
|
Value
|
||||||
|
}
|
||||||
|
fn set_editor(_: Value) {}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
let settings_data = from_string(settings_dir);
|
||||||
|
//~^ ERROR cannot move out of static item `settings_dir` [E0507]
|
||||||
|
let args: i32 = 0;
|
||||||
|
|
||||||
|
match args {
|
||||||
|
ref x if x == &0 => set_editor(settings_data),
|
||||||
|
ref x if x == &1 => set_editor(settings_data),
|
||||||
|
_ => unimplemented!(),
|
||||||
|
}
|
||||||
|
}
|
34
src/test/ui/borrowck/issue-64453.stderr
Normal file
34
src/test/ui/borrowck/issue-64453.stderr
Normal file
@ -0,0 +1,34 @@
|
|||||||
|
error[E0507]: cannot move out of static item `settings_dir`
|
||||||
|
--> $DIR/issue-64453.rs:15:37
|
||||||
|
|
|
||||||
|
LL | let settings_data = from_string(settings_dir);
|
||||||
|
| ^^^^^^^^^^^^ move occurs because `settings_dir` has type `std::string::String`, which does not implement the `Copy` trait
|
||||||
|
|
||||||
|
error[E0019]: static contains unimplemented expression type
|
||||||
|
--> $DIR/issue-64453.rs:4:31
|
||||||
|
|
|
||||||
|
LL | static settings_dir: String = format!("");
|
||||||
|
| ^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
|
||||||
|
|
||||||
|
error[E0015]: calls in statics are limited to constant functions, tuple structs and tuple variants
|
||||||
|
--> $DIR/issue-64453.rs:4:31
|
||||||
|
|
|
||||||
|
LL | static settings_dir: String = format!("");
|
||||||
|
| ^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
|
||||||
|
|
||||||
|
error[E0015]: calls in statics are limited to constant functions, tuple structs and tuple variants
|
||||||
|
--> $DIR/issue-64453.rs:4:31
|
||||||
|
|
|
||||||
|
LL | static settings_dir: String = format!("");
|
||||||
|
| ^^^^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: this error originates in a macro outside of the current crate (in Nightly builds, run with -Z external-macro-backtrace for more info)
|
||||||
|
|
||||||
|
error: aborting due to 4 previous errors
|
||||||
|
|
||||||
|
Some errors have detailed explanations: E0015, E0019, E0507.
|
||||||
|
For more information about an error, try `rustc --explain E0015`.
|
31
src/test/ui/consts/const-eval/generic-slice.rs
Normal file
31
src/test/ui/consts/const-eval/generic-slice.rs
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
// Several variants of #64945.
|
||||||
|
|
||||||
|
// This struct is not important, we just use it to put `T` and `'a` in scope for our associated
|
||||||
|
// consts.
|
||||||
|
struct Generic<'a, T>(std::marker::PhantomData<&'a T>);
|
||||||
|
|
||||||
|
impl<'a, T: 'static> Generic<'a, T> {
|
||||||
|
const EMPTY_SLICE: &'a [T] = {
|
||||||
|
let x: &'a [T] = &[];
|
||||||
|
x
|
||||||
|
};
|
||||||
|
|
||||||
|
const EMPTY_SLICE_REF: &'a &'static [T] = {
|
||||||
|
let x: &'static [T] = &[];
|
||||||
|
&x
|
||||||
|
//~^ ERROR `x` does not live long enough
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
static mut INTERIOR_MUT_AND_DROP: &'static [std::cell::RefCell<Vec<i32>>] = {
|
||||||
|
let x: &[_] = &[];
|
||||||
|
x
|
||||||
|
};
|
||||||
|
|
||||||
|
static mut INTERIOR_MUT_AND_DROP_REF: &'static &'static [std::cell::RefCell<Vec<i32>>] = {
|
||||||
|
let x: &[_] = &[];
|
||||||
|
&x
|
||||||
|
//~^ ERROR `x` does not live long enough
|
||||||
|
};
|
||||||
|
|
||||||
|
fn main() {}
|
30
src/test/ui/consts/const-eval/generic-slice.stderr
Normal file
30
src/test/ui/consts/const-eval/generic-slice.stderr
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
error[E0597]: `x` does not live long enough
|
||||||
|
--> $DIR/generic-slice.rs:15:9
|
||||||
|
|
|
||||||
|
LL | impl<'a, T: 'static> Generic<'a, T> {
|
||||||
|
| -- lifetime `'a` defined here
|
||||||
|
...
|
||||||
|
LL | &x
|
||||||
|
| ^^
|
||||||
|
| |
|
||||||
|
| borrowed value does not live long enough
|
||||||
|
| using this value as a constant requires that `x` is borrowed for `'a`
|
||||||
|
LL |
|
||||||
|
LL | };
|
||||||
|
| - `x` dropped here while still borrowed
|
||||||
|
|
||||||
|
error[E0597]: `x` does not live long enough
|
||||||
|
--> $DIR/generic-slice.rs:27:5
|
||||||
|
|
|
||||||
|
LL | &x
|
||||||
|
| ^^
|
||||||
|
| |
|
||||||
|
| borrowed value does not live long enough
|
||||||
|
| using this value as a static requires that `x` is borrowed for `'static`
|
||||||
|
LL |
|
||||||
|
LL | };
|
||||||
|
| - `x` dropped here while still borrowed
|
||||||
|
|
||||||
|
error: aborting due to 2 previous errors
|
||||||
|
|
||||||
|
For more information about this error, try `rustc --explain E0597`.
|
15
src/test/ui/consts/const-eval/issue-64970.rs
Normal file
15
src/test/ui/consts/const-eval/issue-64970.rs
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
// run-pass
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
foo(10);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn foo(mut n: i32) {
|
||||||
|
if false {
|
||||||
|
n = 0i32;
|
||||||
|
}
|
||||||
|
|
||||||
|
if n > 0i32 {
|
||||||
|
1i32 / n;
|
||||||
|
}
|
||||||
|
}
|
8
src/test/ui/consts/const-eval/issue-64970.stderr
Normal file
8
src/test/ui/consts/const-eval/issue-64970.stderr
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
warning: unused arithmetic operation that must be used
|
||||||
|
--> $DIR/issue-64970.rs:13:9
|
||||||
|
|
|
||||||
|
LL | 1i32 / n;
|
||||||
|
| ^^^^^^^^
|
||||||
|
|
|
||||||
|
= note: `#[warn(unused_must_use)]` on by default
|
||||||
|
|
@ -1,5 +1,5 @@
|
|||||||
// build-pass (FIXME(62277): could be check-pass?)
|
// build-pass
|
||||||
// compiler-flags: -g
|
// compile-flags: -g
|
||||||
|
|
||||||
pub struct Dst {
|
pub struct Dst {
|
||||||
pub a: (),
|
pub a: (),
|
||||||
|
42
src/test/ui/mir-dataflow/indirect-mutation-offset.rs
Normal file
42
src/test/ui/mir-dataflow/indirect-mutation-offset.rs
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
// compile-flags: -Zunleash-the-miri-inside-of-you
|
||||||
|
|
||||||
|
#![feature(core_intrinsics, rustc_attrs, const_raw_ptr_deref)]
|
||||||
|
|
||||||
|
use std::cell::UnsafeCell;
|
||||||
|
use std::intrinsics::rustc_peek;
|
||||||
|
|
||||||
|
#[repr(C)]
|
||||||
|
struct PartialInteriorMut {
|
||||||
|
zst: [i32; 0],
|
||||||
|
cell: UnsafeCell<i32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[rustc_mir(rustc_peek_indirectly_mutable,stop_after_dataflow)]
|
||||||
|
#[rustc_mir(borrowck_graphviz_postflow="indirect.dot")]
|
||||||
|
const BOO: i32 = {
|
||||||
|
let x = PartialInteriorMut {
|
||||||
|
zst: [],
|
||||||
|
cell: UnsafeCell::new(0),
|
||||||
|
};
|
||||||
|
|
||||||
|
let p_zst: *const _ = &x.zst ; // Doesn't cause `x` to get marked as indirectly mutable.
|
||||||
|
|
||||||
|
let rmut_cell = unsafe {
|
||||||
|
// Take advantage of the fact that `zst` and `cell` are at the same location in memory.
|
||||||
|
// This trick would work with any size type if miri implemented `ptr::offset`.
|
||||||
|
let p_cell = p_zst as *const UnsafeCell<i32>;
|
||||||
|
|
||||||
|
let pmut_cell = (*p_cell).get();
|
||||||
|
&mut *pmut_cell
|
||||||
|
};
|
||||||
|
|
||||||
|
*rmut_cell = 42; // Mutates `x` indirectly even though `x` is not marked indirectly mutable!!!
|
||||||
|
let val = *rmut_cell;
|
||||||
|
unsafe { rustc_peek(x) }; //~ ERROR rustc_peek: bit not set
|
||||||
|
|
||||||
|
val
|
||||||
|
};
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
println!("{}", BOO);
|
||||||
|
}
|
10
src/test/ui/mir-dataflow/indirect-mutation-offset.stderr
Normal file
10
src/test/ui/mir-dataflow/indirect-mutation-offset.stderr
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
error: rustc_peek: bit not set
|
||||||
|
--> $DIR/indirect-mutation-offset.rs:35:14
|
||||||
|
|
|
||||||
|
LL | unsafe { rustc_peek(x) };
|
||||||
|
| ^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
error: stop_after_dataflow ended compilation
|
||||||
|
|
||||||
|
error: aborting due to 2 previous errors
|
||||||
|
|
76
src/test/ui/try-block/try-block-unreachable-code-lint.rs
Normal file
76
src/test/ui/try-block/try-block-unreachable-code-lint.rs
Normal file
@ -0,0 +1,76 @@
|
|||||||
|
// Test unreachable_code lint for `try {}` block ok-wrapping. See issues #54165, #63324.
|
||||||
|
|
||||||
|
// compile-flags: --edition 2018
|
||||||
|
// check-pass
|
||||||
|
#![feature(try_blocks)]
|
||||||
|
#![warn(unreachable_code)]
|
||||||
|
|
||||||
|
fn err() -> Result<u32, ()> {
|
||||||
|
Err(())
|
||||||
|
}
|
||||||
|
|
||||||
|
// In the following cases unreachable code is autogenerated and should not be reported.
|
||||||
|
|
||||||
|
fn test_ok_wrapped_divergent_expr_1() {
|
||||||
|
let res: Result<u32, ()> = try {
|
||||||
|
loop {
|
||||||
|
err()?;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
println!("res: {:?}", res);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_ok_wrapped_divergent_expr_2() {
|
||||||
|
let _: Result<u32, ()> = try {
|
||||||
|
return
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_autogenerated_unit_after_divergent_expr() {
|
||||||
|
let _: Result<(), ()> = try {
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
// In the following cases unreachable code should be reported.
|
||||||
|
|
||||||
|
fn test_try_block_after_divergent_stmt() {
|
||||||
|
let _: Result<u32, ()> = {
|
||||||
|
return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
loop {
|
||||||
|
err()?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// ~^^^^^ WARNING unreachable expression
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_wrapped_divergent_expr() {
|
||||||
|
let _: Result<u32, ()> = {
|
||||||
|
Err(return)
|
||||||
|
// ~^ WARNING unreachable call
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
fn test_expr_after_divergent_stmt_in_try_block() {
|
||||||
|
let res: Result<u32, ()> = try {
|
||||||
|
loop {
|
||||||
|
err()?;
|
||||||
|
}
|
||||||
|
|
||||||
|
42
|
||||||
|
// ~^ WARNING unreachable expression
|
||||||
|
};
|
||||||
|
println!("res: {:?}", res);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
test_ok_wrapped_divergent_expr_1();
|
||||||
|
test_ok_wrapped_divergent_expr_2();
|
||||||
|
test_autogenerated_unit_after_divergent_expr();
|
||||||
|
test_try_block_after_divergent_stmt();
|
||||||
|
test_wrapped_divergent_expr();
|
||||||
|
test_expr_after_divergent_stmt_in_try_block();
|
||||||
|
}
|
38
src/test/ui/try-block/try-block-unreachable-code-lint.stderr
Normal file
38
src/test/ui/try-block/try-block-unreachable-code-lint.stderr
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
warning: unreachable expression
|
||||||
|
--> $DIR/try-block-unreachable-code-lint.rs:41:9
|
||||||
|
|
|
||||||
|
LL | return;
|
||||||
|
| ------ any code following this expression is unreachable
|
||||||
|
LL |
|
||||||
|
LL | / try {
|
||||||
|
LL | | loop {
|
||||||
|
LL | | err()?;
|
||||||
|
LL | | }
|
||||||
|
LL | | }
|
||||||
|
| |_________^ unreachable expression
|
||||||
|
|
|
||||||
|
note: lint level defined here
|
||||||
|
--> $DIR/try-block-unreachable-code-lint.rs:6:9
|
||||||
|
|
|
||||||
|
LL | #![warn(unreachable_code)]
|
||||||
|
| ^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
|
warning: unreachable call
|
||||||
|
--> $DIR/try-block-unreachable-code-lint.rs:52:9
|
||||||
|
|
|
||||||
|
LL | Err(return)
|
||||||
|
| ^^^ ------ any code following this expression is unreachable
|
||||||
|
| |
|
||||||
|
| unreachable call
|
||||||
|
|
||||||
|
warning: unreachable expression
|
||||||
|
--> $DIR/try-block-unreachable-code-lint.rs:63:9
|
||||||
|
|
|
||||||
|
LL | / loop {
|
||||||
|
LL | | err()?;
|
||||||
|
LL | | }
|
||||||
|
| |_________- any code following this expression is unreachable
|
||||||
|
LL |
|
||||||
|
LL | 42
|
||||||
|
| ^^ unreachable expression
|
||||||
|
|
Loading…
Reference in New Issue
Block a user