Auto merge of #78334 - jonas-schievink:rollup-z0gzbmm, r=jonas-schievink
Rollup of 12 pull requests Successful merges: - #75115 (`#[deny(unsafe_op_in_unsafe_fn)]` in sys/cloudabi) - #76614 (change the order of type arguments on ControlFlow) - #77610 (revise Hermit's mutex interface to support the behaviour of StaticMutex) - #77830 (Simplify query proc-macros) - #77930 (Do not ICE with TraitPredicates containing [type error]) - #78069 (Fix const core::panic!(non_literal_str).) - #78072 (Cleanup constant matching in exhaustiveness checking) - #78119 (Throw core::panic!("message") as &str instead of String.) - #78191 (Introduce a temporary for discriminant value in MatchBranchSimplification) - #78272 (const_evaluatable_checked: deal with unused nodes + div) - #78318 (TyCtxt: generate single impl block with `slice_interners` macro) - #78327 (resolve: Relax macro resolution consistency check to account for any errors) Failed merges: r? `@ghost`
This commit is contained in:
commit
f58ffc9381
66 changed files with 1421 additions and 823 deletions
|
@ -1,6 +1,7 @@
|
|||
use super::{DirectedGraph, WithNumNodes, WithStartNode, WithSuccessors};
|
||||
use rustc_index::bit_set::BitSet;
|
||||
use rustc_index::vec::IndexVec;
|
||||
use std::ops::ControlFlow;
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests;
|
||||
|
@ -86,10 +87,6 @@ where
|
|||
}
|
||||
}
|
||||
|
||||
/// Allows searches to terminate early with a value.
|
||||
// FIXME (#75744): remove the alias once the generics are in a better order and `C=()`.
|
||||
pub type ControlFlow<T> = std::ops::ControlFlow<(), T>;
|
||||
|
||||
/// The status of a node in the depth-first search.
|
||||
///
|
||||
/// See the documentation of `TriColorDepthFirstSearch` to see how a node's status is updated
|
||||
|
|
|
@ -111,17 +111,6 @@ cfg_if! {
|
|||
|
||||
type Profiler = measureme::Profiler<SerializationSink>;
|
||||
|
||||
#[derive(Clone, Copy, Debug, PartialEq, Eq, Ord, PartialOrd)]
|
||||
pub enum ProfileCategory {
|
||||
Parsing,
|
||||
Expansion,
|
||||
TypeChecking,
|
||||
BorrowChecking,
|
||||
Codegen,
|
||||
Linking,
|
||||
Other,
|
||||
}
|
||||
|
||||
bitflags::bitflags! {
|
||||
struct EventFilter: u32 {
|
||||
const GENERIC_ACTIVITIES = 1 << 0;
|
||||
|
|
|
@ -263,6 +263,7 @@ language_item_table! {
|
|||
// is required to define it somewhere. Additionally, there are restrictions on crates that use
|
||||
// a weak lang item, but do not have it defined.
|
||||
Panic, sym::panic, panic_fn, Target::Fn;
|
||||
PanicStr, sym::panic_str, panic_str, Target::Fn;
|
||||
PanicBoundsCheck, sym::panic_bounds_check, panic_bounds_check_fn, Target::Fn;
|
||||
PanicInfo, sym::panic_info, panic_info, Target::Struct;
|
||||
PanicLocation, sym::panic_location, panic_location, Target::Struct;
|
||||
|
|
|
@ -190,7 +190,11 @@ impl<T: Parse> Parse for List<T> {
|
|||
}
|
||||
|
||||
/// A named group containing queries.
|
||||
///
|
||||
/// For now, the name is not used any more, but the capability remains interesting for future
|
||||
/// developments of the query system.
|
||||
struct Group {
|
||||
#[allow(unused)]
|
||||
name: Ident,
|
||||
queries: List<Query>,
|
||||
}
|
||||
|
@ -417,12 +421,9 @@ pub fn rustc_queries(input: TokenStream) -> TokenStream {
|
|||
let mut query_stream = quote! {};
|
||||
let mut query_description_stream = quote! {};
|
||||
let mut dep_node_def_stream = quote! {};
|
||||
let mut dep_node_force_stream = quote! {};
|
||||
let mut try_load_from_on_disk_cache_stream = quote! {};
|
||||
let mut cached_queries = quote! {};
|
||||
|
||||
for group in groups.0 {
|
||||
let mut group_stream = quote! {};
|
||||
for mut query in group.queries.0 {
|
||||
let modifiers = process_modifiers(&mut query);
|
||||
let name = &query.name;
|
||||
|
@ -437,22 +438,6 @@ pub fn rustc_queries(input: TokenStream) -> TokenStream {
|
|||
cached_queries.extend(quote! {
|
||||
#name,
|
||||
});
|
||||
|
||||
try_load_from_on_disk_cache_stream.extend(quote! {
|
||||
::rustc_middle::dep_graph::DepKind::#name => {
|
||||
if <#arg as DepNodeParams<TyCtxt<'_>>>::can_reconstruct_query_key() {
|
||||
debug_assert!($tcx.dep_graph
|
||||
.node_color($dep_node)
|
||||
.map(|c| c.is_green())
|
||||
.unwrap_or(false));
|
||||
|
||||
let key = <#arg as DepNodeParams<TyCtxt<'_>>>::recover($tcx, $dep_node).unwrap();
|
||||
if queries::#name::cache_on_disk($tcx, &key, None) {
|
||||
let _ = $tcx.#name(key);
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
let mut attributes = Vec::new();
|
||||
|
@ -485,9 +470,9 @@ pub fn rustc_queries(input: TokenStream) -> TokenStream {
|
|||
let attribute_stream = quote! {#(#attributes),*};
|
||||
let doc_comments = query.doc_comments.iter();
|
||||
// Add the query to the group
|
||||
group_stream.extend(quote! {
|
||||
query_stream.extend(quote! {
|
||||
#(#doc_comments)*
|
||||
[#attribute_stream] fn #name: #name(#arg) #result,
|
||||
[#attribute_stream] fn #name(#arg) #result,
|
||||
});
|
||||
|
||||
// Create a dep node for the query
|
||||
|
@ -495,37 +480,10 @@ pub fn rustc_queries(input: TokenStream) -> TokenStream {
|
|||
[#attribute_stream] #name(#arg),
|
||||
});
|
||||
|
||||
// Add a match arm to force the query given the dep node
|
||||
dep_node_force_stream.extend(quote! {
|
||||
::rustc_middle::dep_graph::DepKind::#name => {
|
||||
if <#arg as DepNodeParams<TyCtxt<'_>>>::can_reconstruct_query_key() {
|
||||
if let Some(key) = <#arg as DepNodeParams<TyCtxt<'_>>>::recover($tcx, $dep_node) {
|
||||
force_query::<crate::ty::query::queries::#name<'_>, _>(
|
||||
$tcx,
|
||||
key,
|
||||
DUMMY_SP,
|
||||
*$dep_node
|
||||
);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
add_query_description_impl(&query, modifiers, &mut query_description_stream);
|
||||
}
|
||||
let name = &group.name;
|
||||
query_stream.extend(quote! {
|
||||
#name { #group_stream },
|
||||
});
|
||||
}
|
||||
|
||||
dep_node_force_stream.extend(quote! {
|
||||
::rustc_middle::dep_graph::DepKind::Null => {
|
||||
bug!("Cannot force dep node: {:?}", $dep_node)
|
||||
}
|
||||
});
|
||||
|
||||
TokenStream::from(quote! {
|
||||
macro_rules! rustc_query_append {
|
||||
([$($macro:tt)*][$($other:tt)*]) => {
|
||||
|
@ -546,15 +504,6 @@ pub fn rustc_queries(input: TokenStream) -> TokenStream {
|
|||
);
|
||||
}
|
||||
}
|
||||
macro_rules! rustc_dep_node_force {
|
||||
([$dep_node:expr, $tcx:expr] $($other:tt)*) => {
|
||||
match $dep_node.kind {
|
||||
$($other)*
|
||||
|
||||
#dep_node_force_stream
|
||||
}
|
||||
}
|
||||
}
|
||||
macro_rules! rustc_cached_queries {
|
||||
($($macro:tt)*) => {
|
||||
$($macro)*(#cached_queries);
|
||||
|
@ -562,14 +511,5 @@ pub fn rustc_queries(input: TokenStream) -> TokenStream {
|
|||
}
|
||||
|
||||
#query_description_stream
|
||||
|
||||
macro_rules! rustc_dep_node_try_load_from_on_disk_cache {
|
||||
($dep_node:expr, $tcx:expr) => {
|
||||
match $dep_node.kind {
|
||||
#try_load_from_on_disk_cache_stream
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
|
|
@ -2036,13 +2036,13 @@ direct_interners! {
|
|||
|
||||
macro_rules! slice_interners {
|
||||
($($field:ident: $method:ident($ty:ty)),+ $(,)?) => (
|
||||
$(impl<'tcx> TyCtxt<'tcx> {
|
||||
pub fn $method(self, v: &[$ty]) -> &'tcx List<$ty> {
|
||||
impl<'tcx> TyCtxt<'tcx> {
|
||||
$(pub fn $method(self, v: &[$ty]) -> &'tcx List<$ty> {
|
||||
self.interners.$field.intern_ref(v, || {
|
||||
Interned(List::from_arena(&*self.arena, v))
|
||||
}).0
|
||||
}
|
||||
})+
|
||||
})+
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -34,7 +34,6 @@ use crate::ty::util::AlwaysRequiresDrop;
|
|||
use crate::ty::{self, AdtSizedConstraint, CrateInherentImpls, ParamEnvAnd, Ty, TyCtxt};
|
||||
use rustc_data_structures::fingerprint::Fingerprint;
|
||||
use rustc_data_structures::fx::{FxHashMap, FxHashSet, FxIndexMap};
|
||||
use rustc_data_structures::profiling::ProfileCategory::*;
|
||||
use rustc_data_structures::stable_hasher::StableVec;
|
||||
use rustc_data_structures::svh::Svh;
|
||||
use rustc_data_structures::sync::Lrc;
|
||||
|
@ -169,26 +168,71 @@ pub fn force_from_dep_node<'tcx>(tcx: TyCtxt<'tcx>, dep_node: &DepNode) -> bool
|
|||
return false;
|
||||
}
|
||||
|
||||
rustc_dep_node_force!([dep_node, tcx]
|
||||
// These are inputs that are expected to be pre-allocated and that
|
||||
// should therefore always be red or green already.
|
||||
DepKind::CrateMetadata |
|
||||
macro_rules! force_from_dep_node {
|
||||
($($(#[$attr:meta])* [$($modifiers:tt)*] $name:ident($K:ty),)*) => {
|
||||
match dep_node.kind {
|
||||
// These are inputs that are expected to be pre-allocated and that
|
||||
// should therefore always be red or green already.
|
||||
DepKind::CrateMetadata |
|
||||
|
||||
// These are anonymous nodes.
|
||||
DepKind::TraitSelect |
|
||||
// These are anonymous nodes.
|
||||
DepKind::TraitSelect |
|
||||
|
||||
// We don't have enough information to reconstruct the query key of
|
||||
// these.
|
||||
DepKind::CompileCodegenUnit => {
|
||||
bug!("force_from_dep_node: encountered {:?}", dep_node)
|
||||
// We don't have enough information to reconstruct the query key of
|
||||
// these.
|
||||
DepKind::CompileCodegenUnit |
|
||||
|
||||
// Forcing this makes no sense.
|
||||
DepKind::Null => {
|
||||
bug!("force_from_dep_node: encountered {:?}", dep_node)
|
||||
}
|
||||
|
||||
$(DepKind::$name => {
|
||||
debug_assert!(<$K as DepNodeParams<TyCtxt<'_>>>::can_reconstruct_query_key());
|
||||
|
||||
if let Some(key) = <$K as DepNodeParams<TyCtxt<'_>>>::recover(tcx, dep_node) {
|
||||
force_query::<queries::$name<'_>, _>(
|
||||
tcx,
|
||||
key,
|
||||
DUMMY_SP,
|
||||
*dep_node
|
||||
);
|
||||
return true;
|
||||
}
|
||||
})*
|
||||
}
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
rustc_dep_node_append! { [force_from_dep_node!][] }
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
pub(crate) fn try_load_from_on_disk_cache<'tcx>(tcx: TyCtxt<'tcx>, dep_node: &DepNode) {
|
||||
rustc_dep_node_try_load_from_on_disk_cache!(dep_node, tcx)
|
||||
macro_rules! try_load_from_on_disk_cache {
|
||||
($($name:ident,)*) => {
|
||||
match dep_node.kind {
|
||||
$(DepKind::$name => {
|
||||
if <query_keys::$name<'tcx> as DepNodeParams<TyCtxt<'_>>>::can_reconstruct_query_key() {
|
||||
debug_assert!(tcx.dep_graph
|
||||
.node_color(dep_node)
|
||||
.map(|c| c.is_green())
|
||||
.unwrap_or(false));
|
||||
|
||||
let key = <query_keys::$name<'tcx> as DepNodeParams<TyCtxt<'_>>>::recover(tcx, dep_node).unwrap();
|
||||
if queries::$name::cache_on_disk(tcx, &key, None) {
|
||||
let _ = tcx.$name(key);
|
||||
}
|
||||
}
|
||||
})*
|
||||
|
||||
_ => (),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
rustc_cached_queries!(try_load_from_on_disk_cache!);
|
||||
}
|
||||
|
||||
mod sealed {
|
||||
|
|
|
@ -242,25 +242,15 @@ macro_rules! hash_result {
|
|||
};
|
||||
}
|
||||
|
||||
macro_rules! define_queries {
|
||||
(<$tcx:tt> $($category:tt {
|
||||
$($(#[$attr:meta])* [$($modifiers:tt)*] fn $name:ident: $node:ident($($K:tt)*) -> $V:ty,)*
|
||||
},)*) => {
|
||||
define_queries_inner! { <$tcx>
|
||||
$($( $(#[$attr])* category<$category> [$($modifiers)*] fn $name: $node($($K)*) -> $V,)*)*
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
macro_rules! query_helper_param_ty {
|
||||
(DefId) => { impl IntoQueryParam<DefId> };
|
||||
($K:ty) => { $K };
|
||||
}
|
||||
|
||||
macro_rules! define_queries_inner {
|
||||
macro_rules! define_queries {
|
||||
(<$tcx:tt>
|
||||
$($(#[$attr:meta])* category<$category:tt>
|
||||
[$($modifiers:tt)*] fn $name:ident: $node:ident($($K:tt)*) -> $V:ty,)*) => {
|
||||
$($(#[$attr:meta])*
|
||||
[$($modifiers:tt)*] fn $name:ident($($K:tt)*) -> $V:ty,)*) => {
|
||||
|
||||
use std::mem;
|
||||
use crate::{
|
||||
|
@ -268,7 +258,6 @@ macro_rules! define_queries_inner {
|
|||
rustc_data_structures::stable_hasher::StableHasher,
|
||||
ich::StableHashingContext
|
||||
};
|
||||
use rustc_data_structures::profiling::ProfileCategory;
|
||||
|
||||
define_queries_struct! {
|
||||
tcx: $tcx,
|
||||
|
@ -362,13 +351,12 @@ macro_rules! define_queries_inner {
|
|||
as QueryStorage
|
||||
>::Stored;
|
||||
const NAME: &'static str = stringify!($name);
|
||||
const CATEGORY: ProfileCategory = $category;
|
||||
}
|
||||
|
||||
impl<$tcx> QueryAccessors<TyCtxt<$tcx>> for queries::$name<$tcx> {
|
||||
const ANON: bool = is_anon!([$($modifiers)*]);
|
||||
const EVAL_ALWAYS: bool = is_eval_always!([$($modifiers)*]);
|
||||
const DEP_KIND: dep_graph::DepKind = dep_graph::DepKind::$node;
|
||||
const DEP_KIND: dep_graph::DepKind = dep_graph::DepKind::$name;
|
||||
|
||||
type Cache = query_storage!([$($modifiers)*][$($K)*, $V]);
|
||||
|
||||
|
|
|
@ -120,13 +120,13 @@ pub fn print_stats(tcx: TyCtxt<'_>) {
|
|||
}
|
||||
|
||||
macro_rules! print_stats {
|
||||
(<$tcx:tt> $($category:tt {
|
||||
$($(#[$attr:meta])* [$($modifiers:tt)*] fn $name:ident: $node:ident($K:ty) -> $V:ty,)*
|
||||
},)*) => {
|
||||
(<$tcx:tt>
|
||||
$($(#[$attr:meta])* [$($modifiers:tt)*] fn $name:ident($K:ty) -> $V:ty,)*
|
||||
) => {
|
||||
fn query_stats(tcx: TyCtxt<'_>) -> Vec<QueryStats> {
|
||||
let mut queries = Vec::new();
|
||||
|
||||
$($(
|
||||
$(
|
||||
queries.push(stats::<
|
||||
crate::dep_graph::DepKind,
|
||||
<TyCtxt<'_> as QueryContext>::Query,
|
||||
|
@ -135,7 +135,7 @@ macro_rules! print_stats {
|
|||
stringify!($name),
|
||||
&tcx.queries.$name,
|
||||
));
|
||||
)*)*
|
||||
)*
|
||||
|
||||
queries
|
||||
}
|
||||
|
|
|
@ -70,9 +70,10 @@ impl<'mir, 'tcx> InterpCx<'mir, 'tcx, CompileTimeInterpreter<'mir, 'tcx>> {
|
|||
) -> InterpResult<'tcx> {
|
||||
let def_id = instance.def_id();
|
||||
if Some(def_id) == self.tcx.lang_items().panic_fn()
|
||||
|| Some(def_id) == self.tcx.lang_items().panic_str()
|
||||
|| Some(def_id) == self.tcx.lang_items().begin_panic_fn()
|
||||
{
|
||||
// &'static str
|
||||
// &str
|
||||
assert!(args.len() == 1);
|
||||
|
||||
let msg_place = self.deref_operand(args[0])?;
|
||||
|
|
|
@ -74,7 +74,9 @@ impl ConstCx<'mir, 'tcx> {
|
|||
|
||||
/// Returns `true` if this `DefId` points to one of the official `panic` lang items.
|
||||
pub fn is_lang_panic_fn(tcx: TyCtxt<'tcx>, def_id: DefId) -> bool {
|
||||
Some(def_id) == tcx.lang_items().panic_fn() || Some(def_id) == tcx.lang_items().begin_panic_fn()
|
||||
Some(def_id) == tcx.lang_items().panic_fn()
|
||||
|| Some(def_id) == tcx.lang_items().panic_str()
|
||||
|| Some(def_id) == tcx.lang_items().begin_panic_fn()
|
||||
}
|
||||
|
||||
pub fn allow_internal_unstable(tcx: TyCtxt<'tcx>, def_id: DefId, feature_gate: Symbol) -> bool {
|
||||
|
|
|
@ -38,19 +38,16 @@ pub struct MatchBranchSimplification;
|
|||
|
||||
impl<'tcx> MirPass<'tcx> for MatchBranchSimplification {
|
||||
fn run_pass(&self, tcx: TyCtxt<'tcx>, body: &mut Body<'tcx>) {
|
||||
// FIXME: This optimization can result in unsoundness, because it introduces
|
||||
// additional uses of a place holding the discriminant value without ensuring that
|
||||
// it is valid to do so.
|
||||
if !tcx.sess.opts.debugging_opts.unsound_mir_opts {
|
||||
if tcx.sess.opts.debugging_opts.mir_opt_level <= 1 {
|
||||
return;
|
||||
}
|
||||
|
||||
let param_env = tcx.param_env(body.source.def_id());
|
||||
let bbs = body.basic_blocks_mut();
|
||||
let (bbs, local_decls) = body.basic_blocks_and_local_decls_mut();
|
||||
'outer: for bb_idx in bbs.indices() {
|
||||
let (discr, val, switch_ty, first, second) = match bbs[bb_idx].terminator().kind {
|
||||
TerminatorKind::SwitchInt {
|
||||
discr: Operand::Copy(ref place) | Operand::Move(ref place),
|
||||
discr: ref discr @ (Operand::Copy(_) | Operand::Move(_)),
|
||||
switch_ty,
|
||||
ref targets,
|
||||
..
|
||||
|
@ -59,7 +56,7 @@ impl<'tcx> MirPass<'tcx> for MatchBranchSimplification {
|
|||
if target == targets.otherwise() {
|
||||
continue;
|
||||
}
|
||||
(place, value, switch_ty, target, targets.otherwise())
|
||||
(discr, value, switch_ty, target, targets.otherwise())
|
||||
}
|
||||
// Only optimize switch int statements
|
||||
_ => continue,
|
||||
|
@ -99,6 +96,10 @@ impl<'tcx> MirPass<'tcx> for MatchBranchSimplification {
|
|||
// Take ownership of items now that we know we can optimize.
|
||||
let discr = discr.clone();
|
||||
|
||||
// Introduce a temporary for the discriminant value.
|
||||
let source_info = bbs[bb_idx].terminator().source_info;
|
||||
let discr_local = local_decls.push(LocalDecl::new(switch_ty, source_info.span));
|
||||
|
||||
// We already checked that first and second are different blocks,
|
||||
// and bb_idx has a different terminator from both of them.
|
||||
let (from, first, second) = bbs.pick3_mut(bb_idx, first, second);
|
||||
|
@ -127,7 +128,11 @@ impl<'tcx> MirPass<'tcx> for MatchBranchSimplification {
|
|||
rustc_span::DUMMY_SP,
|
||||
);
|
||||
let op = if f_b { BinOp::Eq } else { BinOp::Ne };
|
||||
let rhs = Rvalue::BinaryOp(op, Operand::Copy(discr.clone()), const_cmp);
|
||||
let rhs = Rvalue::BinaryOp(
|
||||
op,
|
||||
Operand::Copy(Place::from(discr_local)),
|
||||
const_cmp,
|
||||
);
|
||||
Statement {
|
||||
source_info: f.source_info,
|
||||
kind: StatementKind::Assign(box (*lhs, rhs)),
|
||||
|
@ -138,7 +143,16 @@ impl<'tcx> MirPass<'tcx> for MatchBranchSimplification {
|
|||
_ => unreachable!(),
|
||||
}
|
||||
});
|
||||
|
||||
from.statements
|
||||
.push(Statement { source_info, kind: StatementKind::StorageLive(discr_local) });
|
||||
from.statements.push(Statement {
|
||||
source_info,
|
||||
kind: StatementKind::Assign(box (Place::from(discr_local), Rvalue::Use(discr))),
|
||||
});
|
||||
from.statements.extend(new_stmts);
|
||||
from.statements
|
||||
.push(Statement { source_info, kind: StatementKind::StorageDead(discr_local) });
|
||||
from.terminator_mut().kind = first.terminator().kind.clone();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
use rustc_data_structures::graph::iterate::{
|
||||
ControlFlow, NodeStatus, TriColorDepthFirstSearch, TriColorVisitor,
|
||||
NodeStatus, TriColorDepthFirstSearch, TriColorVisitor,
|
||||
};
|
||||
use rustc_hir::intravisit::FnKind;
|
||||
use rustc_middle::hir::map::blocks::FnLikeNode;
|
||||
|
@ -8,6 +8,7 @@ use rustc_middle::ty::subst::{GenericArg, InternalSubsts};
|
|||
use rustc_middle::ty::{self, AssocItem, AssocItemContainer, Instance, TyCtxt};
|
||||
use rustc_session::lint::builtin::UNCONDITIONAL_RECURSION;
|
||||
use rustc_span::Span;
|
||||
use std::ops::ControlFlow;
|
||||
|
||||
crate fn check<'tcx>(tcx: TyCtxt<'tcx>, body: &Body<'tcx>) {
|
||||
let def_id = body.source.def_id().expect_local();
|
||||
|
|
|
@ -302,10 +302,9 @@ use super::{FieldPat, Pat, PatKind, PatRange};
|
|||
|
||||
use rustc_arena::TypedArena;
|
||||
use rustc_attr::{SignedInt, UnsignedInt};
|
||||
use rustc_errors::ErrorReported;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::{HirId, RangeEnd};
|
||||
use rustc_middle::mir::interpret::{truncate, AllocId, ConstValue, Pointer, Scalar};
|
||||
use rustc_middle::mir::interpret::{truncate, ConstValue};
|
||||
use rustc_middle::mir::Field;
|
||||
use rustc_middle::ty::layout::IntegerExt;
|
||||
use rustc_middle::ty::{self, Const, Ty, TyCtxt};
|
||||
|
@ -314,108 +313,21 @@ use rustc_span::{Span, DUMMY_SP};
|
|||
use rustc_target::abi::{Integer, Size, VariantIdx};
|
||||
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
use std::borrow::Cow;
|
||||
use std::cmp::{self, max, min, Ordering};
|
||||
use std::convert::TryInto;
|
||||
use std::fmt;
|
||||
use std::iter::{FromIterator, IntoIterator};
|
||||
use std::ops::RangeInclusive;
|
||||
|
||||
crate fn expand_pattern<'a, 'tcx>(cx: &MatchCheckCtxt<'a, 'tcx>, pat: Pat<'tcx>) -> Pat<'tcx> {
|
||||
LiteralExpander { tcx: cx.tcx, param_env: cx.param_env }.fold_pattern(&pat)
|
||||
crate fn expand_pattern<'tcx>(pat: Pat<'tcx>) -> Pat<'tcx> {
|
||||
LiteralExpander.fold_pattern(&pat)
|
||||
}
|
||||
|
||||
struct LiteralExpander<'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
}
|
||||
struct LiteralExpander;
|
||||
|
||||
impl<'tcx> LiteralExpander<'tcx> {
|
||||
/// Derefs `val` and potentially unsizes the value if `crty` is an array and `rty` a slice.
|
||||
///
|
||||
/// `crty` and `rty` can differ because you can use array constants in the presence of slice
|
||||
/// patterns. So the pattern may end up being a slice, but the constant is an array. We convert
|
||||
/// the array to a slice in that case.
|
||||
fn fold_const_value_deref(
|
||||
&mut self,
|
||||
val: ConstValue<'tcx>,
|
||||
// the pattern's pointee type
|
||||
rty: Ty<'tcx>,
|
||||
// the constant's pointee type
|
||||
crty: Ty<'tcx>,
|
||||
) -> ConstValue<'tcx> {
|
||||
debug!("fold_const_value_deref {:?} {:?} {:?}", val, rty, crty);
|
||||
match (val, &crty.kind(), &rty.kind()) {
|
||||
// the easy case, deref a reference
|
||||
(ConstValue::Scalar(p), x, y) if x == y => {
|
||||
match p {
|
||||
Scalar::Ptr(p) => {
|
||||
let alloc = self.tcx.global_alloc(p.alloc_id).unwrap_memory();
|
||||
ConstValue::ByRef { alloc, offset: p.offset }
|
||||
}
|
||||
Scalar::Raw { .. } => {
|
||||
let layout = self.tcx.layout_of(self.param_env.and(rty)).unwrap();
|
||||
if layout.is_zst() {
|
||||
// Deref of a reference to a ZST is a nop.
|
||||
ConstValue::Scalar(Scalar::zst())
|
||||
} else {
|
||||
// FIXME(oli-obk): this is reachable for `const FOO: &&&u32 = &&&42;`
|
||||
bug!("cannot deref {:#?}, {} -> {}", val, crty, rty);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// unsize array to slice if pattern is array but match value or other patterns are slice
|
||||
(ConstValue::Scalar(Scalar::Ptr(p)), ty::Array(t, n), ty::Slice(u)) => {
|
||||
assert_eq!(t, u);
|
||||
ConstValue::Slice {
|
||||
data: self.tcx.global_alloc(p.alloc_id).unwrap_memory(),
|
||||
start: p.offset.bytes().try_into().unwrap(),
|
||||
end: n.eval_usize(self.tcx, ty::ParamEnv::empty()).try_into().unwrap(),
|
||||
}
|
||||
}
|
||||
// fat pointers stay the same
|
||||
(ConstValue::Slice { .. }, _, _)
|
||||
| (_, ty::Slice(_), ty::Slice(_))
|
||||
| (_, ty::Str, ty::Str) => val,
|
||||
// FIXME(oli-obk): this is reachable for `const FOO: &&&u32 = &&&42;` being used
|
||||
_ => bug!("cannot deref {:#?}, {} -> {}", val, crty, rty),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> PatternFolder<'tcx> for LiteralExpander<'tcx> {
|
||||
impl<'tcx> PatternFolder<'tcx> for LiteralExpander {
|
||||
fn fold_pattern(&mut self, pat: &Pat<'tcx>) -> Pat<'tcx> {
|
||||
debug!("fold_pattern {:?} {:?} {:?}", pat, pat.ty.kind(), pat.kind);
|
||||
match (pat.ty.kind(), &*pat.kind) {
|
||||
(&ty::Ref(_, rty, _), &PatKind::Constant { value: Const { val, ty: const_ty } })
|
||||
if const_ty.is_ref() =>
|
||||
{
|
||||
let crty =
|
||||
if let ty::Ref(_, crty, _) = const_ty.kind() { crty } else { unreachable!() };
|
||||
if let ty::ConstKind::Value(val) = val {
|
||||
Pat {
|
||||
ty: pat.ty,
|
||||
span: pat.span,
|
||||
kind: box PatKind::Deref {
|
||||
subpattern: Pat {
|
||||
ty: rty,
|
||||
span: pat.span,
|
||||
kind: box PatKind::Constant {
|
||||
value: Const::from_value(
|
||||
self.tcx,
|
||||
self.fold_const_value_deref(*val, rty, crty),
|
||||
rty,
|
||||
),
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
} else {
|
||||
bug!("cannot deref {:#?}, {} -> {}", val, crty, rty)
|
||||
}
|
||||
}
|
||||
|
||||
(_, &PatKind::Binding { subpattern: Some(ref s), .. }) => s.fold_with(self),
|
||||
(_, &PatKind::AscribeUserType { subpattern: ref s, .. }) => s.fold_with(self),
|
||||
_ => pat.super_fold_with(self),
|
||||
|
@ -500,9 +412,15 @@ impl<'p, 'tcx> PatStack<'p, 'tcx> {
|
|||
cx: &mut MatchCheckCtxt<'p, 'tcx>,
|
||||
constructor: &Constructor<'tcx>,
|
||||
ctor_wild_subpatterns: &Fields<'p, 'tcx>,
|
||||
is_my_head_ctor: bool,
|
||||
) -> Option<PatStack<'p, 'tcx>> {
|
||||
let new_fields =
|
||||
specialize_one_pattern(cx, self.head(), constructor, ctor_wild_subpatterns)?;
|
||||
let new_fields = specialize_one_pattern(
|
||||
cx,
|
||||
self.head(),
|
||||
constructor,
|
||||
ctor_wild_subpatterns,
|
||||
is_my_head_ctor,
|
||||
)?;
|
||||
Some(new_fields.push_on_patstack(&self.0[1..]))
|
||||
}
|
||||
}
|
||||
|
@ -680,6 +598,7 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> {
|
|||
cx,
|
||||
constructor,
|
||||
ctor_wild_subpatterns,
|
||||
false,
|
||||
)
|
||||
})
|
||||
.collect()
|
||||
|
@ -705,7 +624,9 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> {
|
|||
SpecializationCache::Incompatible => self
|
||||
.patterns
|
||||
.iter()
|
||||
.filter_map(|r| r.specialize_constructor(cx, constructor, ctor_wild_subpatterns))
|
||||
.filter_map(|r| {
|
||||
r.specialize_constructor(cx, constructor, ctor_wild_subpatterns, false)
|
||||
})
|
||||
.collect(),
|
||||
}
|
||||
}
|
||||
|
@ -725,6 +646,7 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> {
|
|||
/// +++++++++++++++++++++++++++++
|
||||
/// + _ + [_, _, tail @ ..] +
|
||||
/// +++++++++++++++++++++++++++++
|
||||
/// ```
|
||||
impl<'p, 'tcx> fmt::Debug for Matrix<'p, 'tcx> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "\n")?;
|
||||
|
@ -926,42 +848,30 @@ enum Constructor<'tcx> {
|
|||
Single,
|
||||
/// Enum variants.
|
||||
Variant(DefId),
|
||||
/// Literal values.
|
||||
ConstantValue(&'tcx ty::Const<'tcx>),
|
||||
/// Ranges of integer literal values (`2`, `2..=5` or `2..5`).
|
||||
IntRange(IntRange<'tcx>),
|
||||
/// Ranges of floating-point literal values (`2.0..=5.2`).
|
||||
FloatRange(&'tcx ty::Const<'tcx>, &'tcx ty::Const<'tcx>, RangeEnd),
|
||||
/// String literals. Strings are not quite the same as `&[u8]` so we treat them separately.
|
||||
Str(&'tcx ty::Const<'tcx>),
|
||||
/// Array and slice patterns.
|
||||
Slice(Slice),
|
||||
/// Constants that must not be matched structurally. They are treated as black
|
||||
/// boxes for the purposes of exhaustiveness: we must not inspect them, and they
|
||||
/// don't count towards making a match exhaustive.
|
||||
Opaque,
|
||||
/// Fake extra constructor for enums that aren't allowed to be matched exhaustively.
|
||||
NonExhaustive,
|
||||
}
|
||||
|
||||
impl<'tcx> Constructor<'tcx> {
|
||||
fn is_slice(&self) -> bool {
|
||||
match self {
|
||||
Slice(_) => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
||||
fn variant_index_for_adt<'a>(
|
||||
&self,
|
||||
cx: &MatchCheckCtxt<'a, 'tcx>,
|
||||
adt: &'tcx ty::AdtDef,
|
||||
) -> VariantIdx {
|
||||
fn variant_index_for_adt(&self, adt: &'tcx ty::AdtDef) -> VariantIdx {
|
||||
match *self {
|
||||
Variant(id) => adt.variant_index_with_id(id),
|
||||
Single => {
|
||||
assert!(!adt.is_enum());
|
||||
VariantIdx::new(0)
|
||||
}
|
||||
ConstantValue(c) => cx
|
||||
.tcx
|
||||
.destructure_const(cx.param_env.and(c))
|
||||
.variant
|
||||
.expect("destructed const of adt without variant id"),
|
||||
_ => bug!("bad constructor {:?} for adt {:?}", self, adt),
|
||||
}
|
||||
}
|
||||
|
@ -975,7 +885,7 @@ impl<'tcx> Constructor<'tcx> {
|
|||
|
||||
match self {
|
||||
// Those constructors can only match themselves.
|
||||
Single | Variant(_) | ConstantValue(..) | FloatRange(..) => {
|
||||
Single | Variant(_) | Str(..) | FloatRange(..) => {
|
||||
if other_ctors.iter().any(|c| c == self) { vec![] } else { vec![self.clone()] }
|
||||
}
|
||||
&Slice(slice) => {
|
||||
|
@ -983,8 +893,6 @@ impl<'tcx> Constructor<'tcx> {
|
|||
.iter()
|
||||
.filter_map(|c: &Constructor<'_>| match c {
|
||||
Slice(slice) => Some(*slice),
|
||||
// FIXME(oli-obk): implement `deref` for `ConstValue`
|
||||
ConstantValue(..) => None,
|
||||
_ => bug!("bad slice pattern constructor {:?}", c),
|
||||
})
|
||||
.map(Slice::value_kind);
|
||||
|
@ -1048,6 +956,7 @@ impl<'tcx> Constructor<'tcx> {
|
|||
}
|
||||
// This constructor is never covered by anything else
|
||||
NonExhaustive => vec![NonExhaustive],
|
||||
Opaque => bug!("unexpected opaque ctor {:?} found in all_ctors", self),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1087,7 +996,7 @@ impl<'tcx> Constructor<'tcx> {
|
|||
PatKind::Variant {
|
||||
adt_def: adt,
|
||||
substs,
|
||||
variant_index: self.variant_index_for_adt(cx, adt),
|
||||
variant_index: self.variant_index_for_adt(adt),
|
||||
subpatterns,
|
||||
}
|
||||
} else {
|
||||
|
@ -1126,10 +1035,11 @@ impl<'tcx> Constructor<'tcx> {
|
|||
PatKind::Slice { prefix, slice: Some(wild), suffix }
|
||||
}
|
||||
},
|
||||
&ConstantValue(value) => PatKind::Constant { value },
|
||||
&Str(value) => PatKind::Constant { value },
|
||||
&FloatRange(lo, hi, end) => PatKind::Range(PatRange { lo, hi, end }),
|
||||
IntRange(range) => return range.to_pat(cx.tcx),
|
||||
NonExhaustive => PatKind::Wild,
|
||||
Opaque => bug!("we should not try to apply an opaque constructor {:?}", self),
|
||||
};
|
||||
|
||||
Pat { ty, span: DUMMY_SP, kind: Box::new(pat) }
|
||||
|
@ -1204,12 +1114,6 @@ impl<'p, 'tcx> Fields<'p, 'tcx> {
|
|||
Fields::Slice(std::slice::from_ref(pat))
|
||||
}
|
||||
|
||||
/// Construct a new `Fields` from the given patterns. You must be sure those patterns can't
|
||||
/// contain fields that need to be filtered out. When in doubt, prefer `replace_fields`.
|
||||
fn from_slice_unfiltered(pats: &'p [Pat<'tcx>]) -> Self {
|
||||
Fields::Slice(pats)
|
||||
}
|
||||
|
||||
/// Convenience; internal use.
|
||||
fn wildcards_from_tys(
|
||||
cx: &MatchCheckCtxt<'p, 'tcx>,
|
||||
|
@ -1239,7 +1143,7 @@ impl<'p, 'tcx> Fields<'p, 'tcx> {
|
|||
// Use T as the sub pattern type of Box<T>.
|
||||
Fields::from_single_pattern(wildcard_from_ty(substs.type_at(0)))
|
||||
} else {
|
||||
let variant = &adt.variants[constructor.variant_index_for_adt(cx, adt)];
|
||||
let variant = &adt.variants[constructor.variant_index_for_adt(adt)];
|
||||
// Whether we must not match the fields of this variant exhaustively.
|
||||
let is_non_exhaustive =
|
||||
variant.is_field_list_non_exhaustive() && !adt.did.is_local();
|
||||
|
@ -1287,7 +1191,7 @@ impl<'p, 'tcx> Fields<'p, 'tcx> {
|
|||
}
|
||||
_ => bug!("bad slice pattern {:?} {:?}", constructor, ty),
|
||||
},
|
||||
ConstantValue(..) | FloatRange(..) | IntRange(..) | NonExhaustive => Fields::empty(),
|
||||
Str(..) | FloatRange(..) | IntRange(..) | NonExhaustive | Opaque => Fields::empty(),
|
||||
};
|
||||
debug!("Fields::wildcards({:?}, {:?}) = {:#?}", constructor, ty, ret);
|
||||
ret
|
||||
|
@ -1600,9 +1504,7 @@ fn all_constructors<'a, 'tcx>(
|
|||
)
|
||||
};
|
||||
match *pcx.ty.kind() {
|
||||
ty::Bool => {
|
||||
[true, false].iter().map(|&b| ConstantValue(ty::Const::from_bool(cx.tcx, b))).collect()
|
||||
}
|
||||
ty::Bool => vec![make_range(0, 1)],
|
||||
ty::Array(ref sub_ty, len) if len.try_eval_usize(cx.tcx, cx.param_env).is_some() => {
|
||||
let len = len.eval_usize(cx.tcx, cx.param_env);
|
||||
if len != 0 && cx.is_uninhabited(sub_ty) {
|
||||
|
@ -1717,7 +1619,7 @@ impl<'tcx> IntRange<'tcx> {
|
|||
#[inline]
|
||||
fn is_integral(ty: Ty<'_>) -> bool {
|
||||
match ty.kind() {
|
||||
ty::Char | ty::Int(_) | ty::Uint(_) => true,
|
||||
ty::Char | ty::Int(_) | ty::Uint(_) | ty::Bool => true,
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
|
@ -1739,6 +1641,7 @@ impl<'tcx> IntRange<'tcx> {
|
|||
#[inline]
|
||||
fn integral_size_and_signed_bias(tcx: TyCtxt<'tcx>, ty: Ty<'_>) -> Option<(Size, u128)> {
|
||||
match *ty.kind() {
|
||||
ty::Bool => Some((Size::from_bytes(1), 0)),
|
||||
ty::Char => Some((Size::from_bytes(4), 0)),
|
||||
ty::Int(ity) => {
|
||||
let size = Integer::from_attr(&tcx, SignedInt(ity)).size();
|
||||
|
@ -2230,7 +2133,7 @@ fn is_useful_specialized<'p, 'tcx>(
|
|||
// We cache the result of `Fields::wildcards` because it is used a lot.
|
||||
let ctor_wild_subpatterns = Fields::wildcards(cx, &ctor, ty);
|
||||
let matrix = matrix.specialize_constructor(cx, &ctor, &ctor_wild_subpatterns);
|
||||
v.specialize_constructor(cx, &ctor, &ctor_wild_subpatterns)
|
||||
v.specialize_constructor(cx, &ctor, &ctor_wild_subpatterns, true)
|
||||
.map(|v| is_useful(cx, &matrix, &v, witness_preference, hir_id, is_under_guard, false))
|
||||
.map(|u| u.apply_constructor(cx, &ctor, ty, &ctor_wild_subpatterns))
|
||||
.unwrap_or(NotUseful)
|
||||
|
@ -2255,18 +2158,13 @@ fn pat_constructor<'tcx>(
|
|||
if let Some(int_range) = IntRange::from_const(tcx, param_env, value, pat.span) {
|
||||
Some(IntRange(int_range))
|
||||
} else {
|
||||
match (value.val, &value.ty.kind()) {
|
||||
(_, ty::Array(_, n)) => {
|
||||
let len = n.eval_usize(tcx, param_env);
|
||||
Some(Slice(Slice { array_len: Some(len), kind: FixedLen(len) }))
|
||||
}
|
||||
(ty::ConstKind::Value(ConstValue::Slice { start, end, .. }), ty::Slice(_)) => {
|
||||
let len = (end - start) as u64;
|
||||
Some(Slice(Slice { array_len: None, kind: FixedLen(len) }))
|
||||
}
|
||||
// FIXME(oli-obk): implement `deref` for `ConstValue`
|
||||
// (ty::ConstKind::Value(ConstValue::ByRef { .. }), ty::Slice(_)) => { ... }
|
||||
_ => Some(ConstantValue(value)),
|
||||
match value.ty.kind() {
|
||||
ty::Float(_) => Some(FloatRange(value, value, RangeEnd::Included)),
|
||||
ty::Ref(_, t, _) if t.is_str() => Some(Str(value)),
|
||||
// All constants that can be structurally matched have already been expanded
|
||||
// into the corresponding `Pat`s by `const_to_pat`. Constants that remain are
|
||||
// opaque.
|
||||
_ => Some(Opaque),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2302,75 +2200,6 @@ fn pat_constructor<'tcx>(
|
|||
}
|
||||
}
|
||||
|
||||
// checks whether a constant is equal to a user-written slice pattern. Only supports byte slices,
|
||||
// meaning all other types will compare unequal and thus equal patterns often do not cause the
|
||||
// second pattern to lint about unreachable match arms.
|
||||
fn slice_pat_covered_by_const<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
_span: Span,
|
||||
const_val: &'tcx ty::Const<'tcx>,
|
||||
prefix: &[Pat<'tcx>],
|
||||
slice: &Option<Pat<'tcx>>,
|
||||
suffix: &[Pat<'tcx>],
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
) -> Result<bool, ErrorReported> {
|
||||
let const_val_val = if let ty::ConstKind::Value(val) = const_val.val {
|
||||
val
|
||||
} else {
|
||||
bug!(
|
||||
"slice_pat_covered_by_const: {:#?}, {:#?}, {:#?}, {:#?}",
|
||||
const_val,
|
||||
prefix,
|
||||
slice,
|
||||
suffix,
|
||||
)
|
||||
};
|
||||
|
||||
let data: &[u8] = match (const_val_val, &const_val.ty.kind()) {
|
||||
(ConstValue::ByRef { offset, alloc, .. }, ty::Array(t, n)) => {
|
||||
assert_eq!(*t, tcx.types.u8);
|
||||
let n = n.eval_usize(tcx, param_env);
|
||||
let ptr = Pointer::new(AllocId(0), offset);
|
||||
alloc.get_bytes(&tcx, ptr, Size::from_bytes(n)).unwrap()
|
||||
}
|
||||
(ConstValue::Slice { data, start, end }, ty::Slice(t)) => {
|
||||
assert_eq!(*t, tcx.types.u8);
|
||||
let ptr = Pointer::new(AllocId(0), Size::from_bytes(start));
|
||||
data.get_bytes(&tcx, ptr, Size::from_bytes(end - start)).unwrap()
|
||||
}
|
||||
// FIXME(oli-obk): create a way to extract fat pointers from ByRef
|
||||
(_, ty::Slice(_)) => return Ok(false),
|
||||
_ => bug!(
|
||||
"slice_pat_covered_by_const: {:#?}, {:#?}, {:#?}, {:#?}",
|
||||
const_val,
|
||||
prefix,
|
||||
slice,
|
||||
suffix,
|
||||
),
|
||||
};
|
||||
|
||||
let pat_len = prefix.len() + suffix.len();
|
||||
if data.len() < pat_len || (slice.is_none() && data.len() > pat_len) {
|
||||
return Ok(false);
|
||||
}
|
||||
|
||||
for (ch, pat) in data[..prefix.len()]
|
||||
.iter()
|
||||
.zip(prefix)
|
||||
.chain(data[data.len() - suffix.len()..].iter().zip(suffix))
|
||||
{
|
||||
if let box PatKind::Constant { value } = pat.kind {
|
||||
let b = value.eval_bits(tcx, param_env, pat.ty);
|
||||
assert_eq!(b as u8 as u128, b);
|
||||
if b as u8 != *ch {
|
||||
return Ok(false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Ok(true)
|
||||
}
|
||||
|
||||
/// For exhaustive integer matching, some constructors are grouped within other constructors
|
||||
/// (namely integer typed values are grouped within ranges). However, when specialising these
|
||||
/// constructors, we want to be specialising for the underlying constructors (the integers), not
|
||||
|
@ -2668,35 +2497,6 @@ fn lint_overlapping_patterns<'tcx>(
|
|||
}
|
||||
}
|
||||
|
||||
fn constructor_covered_by_range<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
ctor: &Constructor<'tcx>,
|
||||
pat: &Pat<'tcx>,
|
||||
) -> Option<()> {
|
||||
if let Single = ctor {
|
||||
return Some(());
|
||||
}
|
||||
|
||||
let (pat_from, pat_to, pat_end, ty) = match *pat.kind {
|
||||
PatKind::Constant { value } => (value, value, RangeEnd::Included, value.ty),
|
||||
PatKind::Range(PatRange { lo, hi, end }) => (lo, hi, end, lo.ty),
|
||||
_ => bug!("`constructor_covered_by_range` called with {:?}", pat),
|
||||
};
|
||||
let (ctor_from, ctor_to, ctor_end) = match *ctor {
|
||||
ConstantValue(value) => (value, value, RangeEnd::Included),
|
||||
FloatRange(from, to, ctor_end) => (from, to, ctor_end),
|
||||
_ => bug!("`constructor_covered_by_range` called with {:?}", ctor),
|
||||
};
|
||||
trace!("constructor_covered_by_range {:#?}, {:#?}, {:#?}, {}", ctor, pat_from, pat_to, ty);
|
||||
|
||||
let to = compare_const_vals(tcx, ctor_to, pat_to, param_env, ty)?;
|
||||
let from = compare_const_vals(tcx, ctor_from, pat_from, param_env, ty)?;
|
||||
let intersects = (from == Ordering::Greater || from == Ordering::Equal)
|
||||
&& (to == Ordering::Less || (pat_end == ctor_end && to == Ordering::Equal));
|
||||
if intersects { Some(()) } else { None }
|
||||
}
|
||||
|
||||
/// This is the main specialization step. It expands the pattern
|
||||
/// into `arity` patterns based on the constructor. For most patterns, the step is trivial,
|
||||
/// for instance tuple patterns are flattened and box patterns expand into their inner pattern.
|
||||
|
@ -2713,15 +2513,43 @@ fn specialize_one_pattern<'p, 'tcx>(
|
|||
pat: &'p Pat<'tcx>,
|
||||
constructor: &Constructor<'tcx>,
|
||||
ctor_wild_subpatterns: &Fields<'p, 'tcx>,
|
||||
is_its_own_ctor: bool, // Whether `ctor` is known to be derived from `pat`
|
||||
) -> Option<Fields<'p, 'tcx>> {
|
||||
if let NonExhaustive = constructor {
|
||||
// Only a wildcard pattern can match the special extra constructor
|
||||
// Only a wildcard pattern can match the special extra constructor.
|
||||
if !pat.is_wildcard() {
|
||||
return None;
|
||||
}
|
||||
return Some(Fields::empty());
|
||||
}
|
||||
|
||||
if let Opaque = constructor {
|
||||
// Only a wildcard pattern can match an opaque constant, unless we're specializing the
|
||||
// value against its own constructor. That happens when we call
|
||||
// `v.specialize_constructor(ctor)` with `ctor` obtained from `pat_constructor(v.head())`.
|
||||
// For example, in the following match, when we are dealing with the third branch, we will
|
||||
// specialize with an `Opaque` ctor. We want to ignore the second branch because opaque
|
||||
// constants should not be inspected, but we don't want to ignore the current (third)
|
||||
// branch, as that would cause us to always conclude that such a branch is unreachable.
|
||||
// ```rust
|
||||
// #[derive(PartialEq)]
|
||||
// struct Foo(i32);
|
||||
// impl Eq for Foo {}
|
||||
// const FOO: Foo = Foo(42);
|
||||
//
|
||||
// match (Foo(0), true) {
|
||||
// (_, true) => {}
|
||||
// (FOO, true) => {}
|
||||
// (FOO, false) => {}
|
||||
// }
|
||||
// ```
|
||||
if is_its_own_ctor || pat.is_wildcard() {
|
||||
return Some(Fields::empty());
|
||||
} else {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
|
||||
let result = match *pat.kind {
|
||||
PatKind::AscribeUserType { .. } => bug!(), // Handled by `expand_pattern`
|
||||
|
||||
|
@ -2741,93 +2569,52 @@ fn specialize_one_pattern<'p, 'tcx>(
|
|||
|
||||
PatKind::Deref { ref subpattern } => Some(Fields::from_single_pattern(subpattern)),
|
||||
|
||||
PatKind::Constant { value } if constructor.is_slice() => {
|
||||
// We extract an `Option` for the pointer because slices of zero
|
||||
// elements don't necessarily point to memory, they are usually
|
||||
// just integers. The only time they should be pointing to memory
|
||||
// is when they are subslices of nonzero slices.
|
||||
let (alloc, offset, n, ty) = match value.ty.kind() {
|
||||
ty::Array(t, n) => {
|
||||
let n = n.eval_usize(cx.tcx, cx.param_env);
|
||||
// Shortcut for `n == 0` where no matter what `alloc` and `offset` we produce,
|
||||
// the result would be exactly what we early return here.
|
||||
if n == 0 {
|
||||
if ctor_wild_subpatterns.len() as u64 != n {
|
||||
return None;
|
||||
}
|
||||
return Some(Fields::empty());
|
||||
}
|
||||
match value.val {
|
||||
ty::ConstKind::Value(ConstValue::ByRef { offset, alloc, .. }) => {
|
||||
(Cow::Borrowed(alloc), offset, n, t)
|
||||
}
|
||||
_ => span_bug!(pat.span, "array pattern is {:?}", value,),
|
||||
PatKind::Constant { .. } | PatKind::Range { .. } => {
|
||||
match constructor {
|
||||
IntRange(ctor) => {
|
||||
let pat = IntRange::from_pat(cx.tcx, cx.param_env, pat)?;
|
||||
ctor.intersection(cx.tcx, &pat)?;
|
||||
// Constructor splitting should ensure that all intersections we encounter
|
||||
// are actually inclusions.
|
||||
assert!(ctor.is_subrange(&pat));
|
||||
}
|
||||
FloatRange(ctor_from, ctor_to, ctor_end) => {
|
||||
let (pat_from, pat_to, pat_end, ty) = match *pat.kind {
|
||||
PatKind::Constant { value } => (value, value, RangeEnd::Included, value.ty),
|
||||
PatKind::Range(PatRange { lo, hi, end }) => (lo, hi, end, lo.ty),
|
||||
_ => unreachable!(), // This is ensured by the branch we're in
|
||||
};
|
||||
let to = compare_const_vals(cx.tcx, ctor_to, pat_to, cx.param_env, ty)?;
|
||||
let from = compare_const_vals(cx.tcx, ctor_from, pat_from, cx.param_env, ty)?;
|
||||
let intersects = (from == Ordering::Greater || from == Ordering::Equal)
|
||||
&& (to == Ordering::Less
|
||||
|| (pat_end == *ctor_end && to == Ordering::Equal));
|
||||
if !intersects {
|
||||
return None;
|
||||
}
|
||||
}
|
||||
ty::Slice(t) => {
|
||||
match value.val {
|
||||
ty::ConstKind::Value(ConstValue::Slice { data, start, end }) => {
|
||||
let offset = Size::from_bytes(start);
|
||||
let n = (end - start) as u64;
|
||||
(Cow::Borrowed(data), offset, n, t)
|
||||
}
|
||||
ty::ConstKind::Value(ConstValue::ByRef { .. }) => {
|
||||
// FIXME(oli-obk): implement `deref` for `ConstValue`
|
||||
return None;
|
||||
}
|
||||
Str(ctor_value) => {
|
||||
let pat_value = match *pat.kind {
|
||||
PatKind::Constant { value } => value,
|
||||
_ => span_bug!(
|
||||
pat.span,
|
||||
"slice pattern constant must be scalar pair but is {:?}",
|
||||
value,
|
||||
"unexpected range pattern {:?} for constant value ctor",
|
||||
pat
|
||||
),
|
||||
};
|
||||
|
||||
// FIXME: there's probably a more direct way of comparing for equality
|
||||
if compare_const_vals(cx.tcx, ctor_value, pat_value, cx.param_env, pat.ty)?
|
||||
!= Ordering::Equal
|
||||
{
|
||||
return None;
|
||||
}
|
||||
}
|
||||
_ => span_bug!(
|
||||
pat.span,
|
||||
"unexpected const-val {:?} with ctor {:?}",
|
||||
value,
|
||||
constructor,
|
||||
),
|
||||
};
|
||||
if ctor_wild_subpatterns.len() as u64 != n {
|
||||
return None;
|
||||
}
|
||||
|
||||
// Convert a constant slice/array pattern to a list of patterns.
|
||||
let layout = cx.tcx.layout_of(cx.param_env.and(ty)).ok()?;
|
||||
let ptr = Pointer::new(AllocId(0), offset);
|
||||
let pats = cx.pattern_arena.alloc_from_iter((0..n).filter_map(|i| {
|
||||
let ptr = ptr.offset(layout.size * i, &cx.tcx).ok()?;
|
||||
let scalar = alloc.read_scalar(&cx.tcx, ptr, layout.size).ok()?;
|
||||
let scalar = scalar.check_init().ok()?;
|
||||
let value = ty::Const::from_scalar(cx.tcx, scalar, ty);
|
||||
let pattern = Pat { ty, span: pat.span, kind: box PatKind::Constant { value } };
|
||||
Some(pattern)
|
||||
}));
|
||||
// Ensure none of the dereferences failed.
|
||||
if pats.len() as u64 != n {
|
||||
return None;
|
||||
}
|
||||
Some(Fields::from_slice_unfiltered(pats))
|
||||
}
|
||||
|
||||
PatKind::Constant { .. } | PatKind::Range { .. } => {
|
||||
// If the constructor is a:
|
||||
// - Single value: add a row if the pattern contains the constructor.
|
||||
// - Range: add a row if the constructor intersects the pattern.
|
||||
if let IntRange(ctor) = constructor {
|
||||
let pat = IntRange::from_pat(cx.tcx, cx.param_env, pat)?;
|
||||
ctor.intersection(cx.tcx, &pat)?;
|
||||
// Constructor splitting should ensure that all intersections we encounter
|
||||
// are actually inclusions.
|
||||
assert!(ctor.is_subrange(&pat));
|
||||
} else {
|
||||
// Fallback for non-ranges and ranges that involve
|
||||
// floating-point numbers, which are not conveniently handled
|
||||
// by `IntRange`. For these cases, the constructor may not be a
|
||||
// range so intersection actually devolves into being covered
|
||||
// by the pattern.
|
||||
constructor_covered_by_range(cx.tcx, cx.param_env, constructor, pat)?;
|
||||
_ => {
|
||||
// If we reach here, we must be trying to inspect an opaque constant. Thus we skip
|
||||
// the row.
|
||||
return None;
|
||||
}
|
||||
}
|
||||
Some(Fields::empty())
|
||||
}
|
||||
|
@ -2850,21 +2637,6 @@ fn specialize_one_pattern<'p, 'tcx>(
|
|||
let suffix = suffix.iter().enumerate().map(|(i, p)| (arity - suffix.len() + i, p));
|
||||
Some(ctor_wild_subpatterns.replace_fields_indexed(prefix.chain(suffix)))
|
||||
}
|
||||
ConstantValue(cv) => {
|
||||
match slice_pat_covered_by_const(
|
||||
cx.tcx,
|
||||
pat.span,
|
||||
cv,
|
||||
prefix,
|
||||
slice,
|
||||
suffix,
|
||||
cx.param_env,
|
||||
) {
|
||||
Ok(true) => Some(Fields::empty()),
|
||||
Ok(false) => None,
|
||||
Err(ErrorReported) => None,
|
||||
}
|
||||
}
|
||||
_ => span_bug!(pat.span, "unexpected ctor {:?} for slice pat", constructor),
|
||||
},
|
||||
|
||||
|
|
|
@ -137,7 +137,7 @@ impl<'tcx> MatchVisitor<'_, 'tcx> {
|
|||
patcx.include_lint_checks();
|
||||
let pattern = patcx.lower_pattern(pat);
|
||||
let pattern_ty = pattern.ty;
|
||||
let pattern: &_ = cx.pattern_arena.alloc(expand_pattern(cx, pattern));
|
||||
let pattern: &_ = cx.pattern_arena.alloc(expand_pattern(pattern));
|
||||
if !patcx.errors.is_empty() {
|
||||
*have_errors = true;
|
||||
patcx.report_inlining_errors(pat.span);
|
||||
|
|
|
@ -387,14 +387,16 @@ impl<'a, 'tcx> ConstToPat<'a, 'tcx> {
|
|||
// `&str` and `&[u8]` are represented as `ConstValue::Slice`, let's keep using this
|
||||
// optimization for now.
|
||||
ty::Str => PatKind::Constant { value: cv },
|
||||
ty::Slice(elem_ty) if elem_ty == tcx.types.u8 => PatKind::Constant { value: cv },
|
||||
// `b"foo"` produces a `&[u8; 3]`, but you can't use constants of array type when
|
||||
// matching against references, you can only use byte string literals.
|
||||
// FIXME: clean this up, likely by permitting array patterns when matching on slices
|
||||
ty::Array(elem_ty, _) if elem_ty == tcx.types.u8 => PatKind::Constant { value: cv },
|
||||
// The typechecker has a special case for byte string literals, by treating them
|
||||
// as slices. This means we turn `&[T; N]` constants into slice patterns, which
|
||||
// has no negative effects on pattern matching, even if we're actually matching on
|
||||
// arrays.
|
||||
ty::Array(..) |
|
||||
// Cannot merge this with the catch all branch below, because the `const_deref`
|
||||
// changes the type from slice to array, and slice patterns behave differently from
|
||||
// array patterns.
|
||||
// changes the type from slice to array, we need to keep the original type in the
|
||||
// pattern.
|
||||
ty::Slice(..) => {
|
||||
let old = self.behind_reference.replace(true);
|
||||
let array = tcx.deref_const(self.param_env.and(cv));
|
||||
|
|
|
@ -158,6 +158,13 @@ crate enum PatKind<'tcx> {
|
|||
subpattern: Pat<'tcx>,
|
||||
},
|
||||
|
||||
/// One of the following:
|
||||
/// * `&str`, which will be handled as a string pattern and thus exhaustiveness
|
||||
/// checking will detect if you use the same string twice in different patterns.
|
||||
/// * integer, bool, char or float, which will be handled by exhaustivenes to cover exactly
|
||||
/// its own value, similar to `&str`, but these values are much simpler.
|
||||
/// * Opaque constants, that must not be matched structurally. So anything that does not derive
|
||||
/// `PartialEq` and `Eq`.
|
||||
Constant {
|
||||
value: &'tcx ty::Const<'tcx>,
|
||||
},
|
||||
|
|
|
@ -5,7 +5,6 @@ use crate::dep_graph::SerializedDepNodeIndex;
|
|||
use crate::query::caches::QueryCache;
|
||||
use crate::query::plumbing::CycleError;
|
||||
use crate::query::{QueryContext, QueryState};
|
||||
use rustc_data_structures::profiling::ProfileCategory;
|
||||
|
||||
use rustc_data_structures::fingerprint::Fingerprint;
|
||||
use std::borrow::Cow;
|
||||
|
@ -14,7 +13,6 @@ use std::hash::Hash;
|
|||
|
||||
pub trait QueryConfig {
|
||||
const NAME: &'static str;
|
||||
const CATEGORY: ProfileCategory;
|
||||
|
||||
type Key: Eq + Hash + Clone + Debug;
|
||||
type Value;
|
||||
|
|
|
@ -19,7 +19,7 @@ use rustc_feature::is_builtin_attr_name;
|
|||
use rustc_hir::def::{self, DefKind, NonMacroAttrKind};
|
||||
use rustc_hir::def_id;
|
||||
use rustc_middle::middle::stability;
|
||||
use rustc_middle::{span_bug, ty};
|
||||
use rustc_middle::ty;
|
||||
use rustc_session::lint::builtin::UNUSED_MACROS;
|
||||
use rustc_session::Session;
|
||||
use rustc_span::edition::Edition;
|
||||
|
@ -885,11 +885,11 @@ impl<'a> Resolver<'a> {
|
|||
initial_res: Option<Res>,
|
||||
res: Res| {
|
||||
if let Some(initial_res) = initial_res {
|
||||
if res != initial_res && res != Res::Err && this.ambiguity_errors.is_empty() {
|
||||
if res != initial_res {
|
||||
// Make sure compilation does not succeed if preferred macro resolution
|
||||
// has changed after the macro had been expanded. In theory all such
|
||||
// situations should be reported as ambiguity errors, so this is a bug.
|
||||
span_bug!(span, "inconsistent resolution for a macro");
|
||||
// situations should be reported as errors, so this is a bug.
|
||||
this.session.delay_span_bug(span, "inconsistent resolution for a macro");
|
||||
}
|
||||
} else {
|
||||
// It's possible that the macro was unresolved (indeterminate) and silently
|
||||
|
|
|
@ -777,6 +777,7 @@ symbols! {
|
|||
panic_info,
|
||||
panic_location,
|
||||
panic_runtime,
|
||||
panic_str,
|
||||
panic_unwind,
|
||||
param_attrs,
|
||||
parent_trait,
|
||||
|
|
|
@ -121,7 +121,10 @@ where
|
|||
// contains unbound type parameters. It could be a slight
|
||||
// optimization to stop iterating early.
|
||||
if let Err(errors) = fulfill_cx.select_all_or_error(infcx) {
|
||||
bug!("Encountered errors `{:?}` resolving bounds after type-checking", errors);
|
||||
infcx.tcx.sess.delay_span_bug(
|
||||
rustc_span::DUMMY_SP,
|
||||
&format!("Encountered errors `{:?}` resolving bounds after type-checking", errors),
|
||||
);
|
||||
}
|
||||
|
||||
let result = infcx.resolve_vars_if_possible(result);
|
||||
|
|
|
@ -223,11 +223,23 @@ impl AbstractConst<'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
|
||||
struct WorkNode<'tcx> {
|
||||
node: Node<'tcx>,
|
||||
span: Span,
|
||||
used: bool,
|
||||
}
|
||||
|
||||
struct AbstractConstBuilder<'a, 'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
body: &'a mir::Body<'tcx>,
|
||||
/// The current WIP node tree.
|
||||
nodes: IndexVec<NodeId, Node<'tcx>>,
|
||||
///
|
||||
/// We require all nodes to be used in the final abstract const,
|
||||
/// so we store this here. Note that we also consider nodes as used
|
||||
/// if they are mentioned in an assert, so some used nodes are never
|
||||
/// actually reachable by walking the [`AbstractConst`].
|
||||
nodes: IndexVec<NodeId, WorkNode<'tcx>>,
|
||||
locals: IndexVec<mir::Local, NodeId>,
|
||||
/// We only allow field accesses if they access
|
||||
/// the result of a checked operation.
|
||||
|
@ -274,6 +286,27 @@ impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> {
|
|||
Ok(Some(builder))
|
||||
}
|
||||
|
||||
fn add_node(&mut self, node: Node<'tcx>, span: Span) -> NodeId {
|
||||
// Mark used nodes.
|
||||
match node {
|
||||
Node::Leaf(_) => (),
|
||||
Node::Binop(_, lhs, rhs) => {
|
||||
self.nodes[lhs].used = true;
|
||||
self.nodes[rhs].used = true;
|
||||
}
|
||||
Node::UnaryOp(_, input) => {
|
||||
self.nodes[input].used = true;
|
||||
}
|
||||
Node::FunctionCall(func, nodes) => {
|
||||
self.nodes[func].used = true;
|
||||
nodes.iter().for_each(|&n| self.nodes[n].used = true);
|
||||
}
|
||||
}
|
||||
|
||||
// Nodes start as unused.
|
||||
self.nodes.push(WorkNode { node, span, used: false })
|
||||
}
|
||||
|
||||
fn place_to_local(
|
||||
&mut self,
|
||||
span: Span,
|
||||
|
@ -311,7 +344,7 @@ impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> {
|
|||
let local = self.place_to_local(span, p)?;
|
||||
Ok(self.locals[local])
|
||||
}
|
||||
mir::Operand::Constant(ct) => Ok(self.nodes.push(Node::Leaf(ct.literal))),
|
||||
mir::Operand::Constant(ct) => Ok(self.add_node(Node::Leaf(ct.literal), span)),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -336,19 +369,19 @@ impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> {
|
|||
|
||||
fn build_statement(&mut self, stmt: &mir::Statement<'tcx>) -> Result<(), ErrorReported> {
|
||||
debug!("AbstractConstBuilder: stmt={:?}", stmt);
|
||||
let span = stmt.source_info.span;
|
||||
match stmt.kind {
|
||||
StatementKind::Assign(box (ref place, ref rvalue)) => {
|
||||
let local = self.place_to_local(stmt.source_info.span, place)?;
|
||||
let local = self.place_to_local(span, place)?;
|
||||
match *rvalue {
|
||||
Rvalue::Use(ref operand) => {
|
||||
self.locals[local] =
|
||||
self.operand_to_node(stmt.source_info.span, operand)?;
|
||||
self.locals[local] = self.operand_to_node(span, operand)?;
|
||||
Ok(())
|
||||
}
|
||||
Rvalue::BinaryOp(op, ref lhs, ref rhs) if Self::check_binop(op) => {
|
||||
let lhs = self.operand_to_node(stmt.source_info.span, lhs)?;
|
||||
let rhs = self.operand_to_node(stmt.source_info.span, rhs)?;
|
||||
self.locals[local] = self.nodes.push(Node::Binop(op, lhs, rhs));
|
||||
let lhs = self.operand_to_node(span, lhs)?;
|
||||
let rhs = self.operand_to_node(span, rhs)?;
|
||||
self.locals[local] = self.add_node(Node::Binop(op, lhs, rhs), span);
|
||||
if op.is_checkable() {
|
||||
bug!("unexpected unchecked checkable binary operation");
|
||||
} else {
|
||||
|
@ -356,18 +389,18 @@ impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> {
|
|||
}
|
||||
}
|
||||
Rvalue::CheckedBinaryOp(op, ref lhs, ref rhs) if Self::check_binop(op) => {
|
||||
let lhs = self.operand_to_node(stmt.source_info.span, lhs)?;
|
||||
let rhs = self.operand_to_node(stmt.source_info.span, rhs)?;
|
||||
self.locals[local] = self.nodes.push(Node::Binop(op, lhs, rhs));
|
||||
let lhs = self.operand_to_node(span, lhs)?;
|
||||
let rhs = self.operand_to_node(span, rhs)?;
|
||||
self.locals[local] = self.add_node(Node::Binop(op, lhs, rhs), span);
|
||||
self.checked_op_locals.insert(local);
|
||||
Ok(())
|
||||
}
|
||||
Rvalue::UnaryOp(op, ref operand) if Self::check_unop(op) => {
|
||||
let operand = self.operand_to_node(stmt.source_info.span, operand)?;
|
||||
self.locals[local] = self.nodes.push(Node::UnaryOp(op, operand));
|
||||
let operand = self.operand_to_node(span, operand)?;
|
||||
self.locals[local] = self.add_node(Node::UnaryOp(op, operand), span);
|
||||
Ok(())
|
||||
}
|
||||
_ => self.error(Some(stmt.source_info.span), "unsupported rvalue")?,
|
||||
_ => self.error(Some(span), "unsupported rvalue")?,
|
||||
}
|
||||
}
|
||||
// These are not actually relevant for us here, so we can ignore them.
|
||||
|
@ -415,13 +448,9 @@ impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> {
|
|||
.map(|arg| self.operand_to_node(terminator.source_info.span, arg))
|
||||
.collect::<Result<Vec<NodeId>, _>>()?,
|
||||
);
|
||||
self.locals[local] = self.nodes.push(Node::FunctionCall(func, args));
|
||||
self.locals[local] = self.add_node(Node::FunctionCall(func, args), fn_span);
|
||||
Ok(Some(target))
|
||||
}
|
||||
// We only allow asserts for checked operations.
|
||||
//
|
||||
// These asserts seem to all have the form `!_local.0` so
|
||||
// we only allow exactly that.
|
||||
TerminatorKind::Assert { ref cond, expected: false, target, .. } => {
|
||||
let p = match cond {
|
||||
mir::Operand::Copy(p) | mir::Operand::Move(p) => p,
|
||||
|
@ -430,7 +459,15 @@ impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> {
|
|||
|
||||
const ONE_FIELD: mir::Field = mir::Field::from_usize(1);
|
||||
debug!("proj: {:?}", p.projection);
|
||||
if let &[mir::ProjectionElem::Field(ONE_FIELD, _)] = p.projection.as_ref() {
|
||||
if let Some(p) = p.as_local() {
|
||||
debug_assert!(!self.checked_op_locals.contains(p));
|
||||
// Mark locals directly used in asserts as used.
|
||||
//
|
||||
// This is needed because division does not use `CheckedBinop` but instead
|
||||
// adds an explicit assert for `divisor != 0`.
|
||||
self.nodes[self.locals[p]].used = true;
|
||||
return Ok(Some(target));
|
||||
} else if let &[mir::ProjectionElem::Field(ONE_FIELD, _)] = p.projection.as_ref() {
|
||||
// Only allow asserts checking the result of a checked operation.
|
||||
if self.checked_op_locals.contains(p.local) {
|
||||
return Ok(Some(target));
|
||||
|
@ -457,7 +494,13 @@ impl<'a, 'tcx> AbstractConstBuilder<'a, 'tcx> {
|
|||
if let Some(next) = self.build_terminator(block.terminator())? {
|
||||
block = &self.body.basic_blocks()[next];
|
||||
} else {
|
||||
return Ok(self.tcx.arena.alloc_from_iter(self.nodes));
|
||||
assert_eq!(self.locals[mir::RETURN_PLACE], self.nodes.last().unwrap());
|
||||
self.nodes[self.locals[mir::RETURN_PLACE]].used = true;
|
||||
if let Some(&unused) = self.nodes.iter().find(|n| !n.used) {
|
||||
self.error(Some(unused.span), "dead code")?;
|
||||
}
|
||||
|
||||
return Ok(self.tcx.arena.alloc_from_iter(self.nodes.into_iter().map(|n| n.node)));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue