commit
23efae075a
@ -4354,6 +4354,7 @@ dependencies = [
|
||||
"rustc_target",
|
||||
"smallvec",
|
||||
"tracing",
|
||||
"typed-arena",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
@ -5689,6 +5690,12 @@ dependencies = [
|
||||
"rustc-hash",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "typed-arena"
|
||||
version = "2.0.2"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6af6ae20167a9ece4bcb41af5b80f8a1f1df981f6391189ce00fd257af04126a"
|
||||
|
||||
[[package]]
|
||||
name = "typenum"
|
||||
version = "1.16.0"
|
||||
|
125
RELEASES.md
125
RELEASES.md
@ -1,3 +1,128 @@
|
||||
Version 1.75.0 (2023-12-28)
|
||||
==========================
|
||||
|
||||
<a id="1.75.0-Language"></a>
|
||||
|
||||
Language
|
||||
--------
|
||||
|
||||
- [Stabilize `async fn` and return-position `impl Trait` in traits.](https://github.com/rust-lang/rust/pull/115822/)
|
||||
- [Allow function pointer signatures containing `&mut T` in `const` contexts.](https://github.com/rust-lang/rust/pull/116015/)
|
||||
- [Match `usize`/`isize` exhaustively with half-open ranges.](https://github.com/rust-lang/rust/pull/116692/)
|
||||
- [Guarantee that `char` has the same size and alignment as `u32`.](https://github.com/rust-lang/rust/pull/116894/)
|
||||
- [Document that the null pointer has the 0 address.](https://github.com/rust-lang/rust/pull/116988/)
|
||||
- [Allow partially moved values in `match`.](https://github.com/rust-lang/rust/pull/103208/)
|
||||
- [Add notes about non-compliant FP behavior on 32bit x86 targets.](https://github.com/rust-lang/rust/pull/113053/)
|
||||
- [Stabilize ratified RISC-V target features.](https://github.com/rust-lang/rust/pull/116485/)
|
||||
|
||||
<a id="1.75.0-Compiler"></a>
|
||||
|
||||
Compiler
|
||||
--------
|
||||
|
||||
- [Rework negative coherence to properly consider impls that only partly overlap.](https://github.com/rust-lang/rust/pull/112875/)
|
||||
- [Bump `COINDUCTIVE_OVERLAP_IN_COHERENCE` to deny, and warn in dependencies.](https://github.com/rust-lang/rust/pull/116493/)
|
||||
- [Consider alias bounds when computing liveness in NLL.](https://github.com/rust-lang/rust/pull/116733/)
|
||||
- [Add the V (vector) extension to the `riscv64-linux-android` target spec.](https://github.com/rust-lang/rust/pull/116618/)
|
||||
- [Automatically enable cross-crate inlining for small functions](https://github.com/rust-lang/rust/pull/116505)
|
||||
- Add several new tier 3 targets:
|
||||
- [`csky-unknown-linux-gnuabiv2hf`](https://github.com/rust-lang/rust/pull/117049/)
|
||||
- [`i586-unknown-netbsd`](https://github.com/rust-lang/rust/pull/117170/)
|
||||
- [`mipsel-unknown-netbsd`](https://github.com/rust-lang/rust/pull/117356/)
|
||||
|
||||
Refer to Rust's [platform support page][platform-support-doc]
|
||||
for more information on Rust's tiered platform support.
|
||||
|
||||
<a id="1.75.0-Libraries"></a>
|
||||
|
||||
Libraries
|
||||
---------
|
||||
|
||||
- [Override `Waker::clone_from` to avoid cloning `Waker`s unnecessarily.](https://github.com/rust-lang/rust/pull/96979/)
|
||||
- [Implement `BufRead` for `VecDeque<u8>`.](https://github.com/rust-lang/rust/pull/110604/)
|
||||
- [Implement `FusedIterator` for `DecodeUtf16` when the inner iterator does.](https://github.com/rust-lang/rust/pull/110729/)
|
||||
- [Implement `Not, Bit{And,Or}{,Assign}` for IP addresses.](https://github.com/rust-lang/rust/pull/113747/)
|
||||
- [Implement `Default` for `ExitCode`.](https://github.com/rust-lang/rust/pull/114589/)
|
||||
- [Guarantee representation of None in NPO](https://github.com/rust-lang/rust/pull/115333/)
|
||||
- [Document when atomic loads are guaranteed read-only.](https://github.com/rust-lang/rust/pull/115577/)
|
||||
- [Broaden the consequences of recursive TLS initialization.](https://github.com/rust-lang/rust/pull/116172/)
|
||||
- [Windows: Support sub-millisecond sleep.](https://github.com/rust-lang/rust/pull/116461/)
|
||||
- [Fix generic bound of `str::SplitInclusive`'s `DoubleEndedIterator` impl](https://github.com/rust-lang/rust/pull/100806/)
|
||||
- [Fix exit status / wait status on non-Unix `cfg(unix)` platforms.](https://github.com/rust-lang/rust/pull/115108/)
|
||||
|
||||
<a id="1.75.0-Stabilized-APIs"></a>
|
||||
|
||||
Stabilized APIs
|
||||
---------------
|
||||
|
||||
- [`Atomic*::from_ptr`](https://doc.rust-lang.org/stable/core/sync/atomic/struct.AtomicUsize.html#method.from_ptr)
|
||||
- [`FileTimes`](https://doc.rust-lang.org/stable/std/fs/struct.FileTimes.html)
|
||||
- [`FileTimesExt`](https://doc.rust-lang.org/stable/std/os/windows/fs/trait.FileTimesExt.html)
|
||||
- [`File::set_modified`](https://doc.rust-lang.org/stable/std/fs/struct.File.html#method.set_modified)
|
||||
- [`File::set_times`](https://doc.rust-lang.org/stable/std/fs/struct.File.html#method.set_times)
|
||||
- [`IpAddr::to_canonical`](https://doc.rust-lang.org/stable/core/net/enum.IpAddr.html#method.to_canonical)
|
||||
- [`Ipv6Addr::to_canonical`](https://doc.rust-lang.org/stable/core/net/struct.Ipv6Addr.html#method.to_canonical)
|
||||
- [`Option::as_slice`](https://doc.rust-lang.org/stable/core/option/enum.Option.html#method.as_slice)
|
||||
- [`Option::as_mut_slice`](https://doc.rust-lang.org/stable/core/option/enum.Option.html#method.as_mut_slice)
|
||||
- [`pointer::byte_add`](https://doc.rust-lang.org/stable/core/primitive.pointer.html#method.byte_add)
|
||||
- [`pointer::byte_offset`](https://doc.rust-lang.org/stable/core/primitive.pointer.html#method.byte_offset)
|
||||
- [`pointer::byte_offset_from`](https://doc.rust-lang.org/stable/core/primitive.pointer.html#method.byte_offset_from)
|
||||
- [`pointer::byte_sub`](https://doc.rust-lang.org/stable/core/primitive.pointer.html#method.byte_sub)
|
||||
- [`pointer::wrapping_byte_add`](https://doc.rust-lang.org/stable/core/primitive.pointer.html#method.wrapping_byte_add)
|
||||
- [`pointer::wrapping_byte_offset`](https://doc.rust-lang.org/stable/core/primitive.pointer.html#method.wrapping_byte_offset)
|
||||
- [`pointer::wrapping_byte_sub`](https://doc.rust-lang.org/stable/core/primitive.pointer.html#method.wrapping_byte_sub)
|
||||
|
||||
These APIs are now stable in const contexts:
|
||||
|
||||
- [`Ipv6Addr::to_ipv4_mapped`](https://doc.rust-lang.org/stable/core/net/struct.Ipv6Addr.html#method.to_ipv4_mapped)
|
||||
- [`MaybeUninit::assume_init_read`](https://doc.rust-lang.org/stable/core/mem/union.MaybeUninit.html#method.assume_init_read)
|
||||
- [`MaybeUninit::zeroed`](https://doc.rust-lang.org/stable/core/mem/union.MaybeUninit.html#method.zeroed)
|
||||
- [`mem::discriminant`](https://doc.rust-lang.org/stable/core/mem/fn.discriminant.html)
|
||||
- [`mem::zeroed`](https://doc.rust-lang.org/stable/core/mem/fn.zeroed.html)
|
||||
|
||||
<a id="1.75.0-Cargo"></a>
|
||||
|
||||
Cargo
|
||||
-----
|
||||
|
||||
- [Add new packages to `[workspace.members]` automatically.](https://github.com/rust-lang/cargo/pull/12779/)
|
||||
- [Allow version-less `Cargo.toml` manifests.](https://github.com/rust-lang/cargo/pull/12786/)
|
||||
- [Make browser links out of HTML file paths.](https://github.com/rust-lang/cargo/pull/12889)
|
||||
|
||||
<a id="1.75.0-Rustdoc"></a>
|
||||
|
||||
Rustdoc
|
||||
-------
|
||||
|
||||
- [Accept less invalid Rust in rustdoc.](https://github.com/rust-lang/rust/pull/117450/)
|
||||
- [Document lack of object safety on affected traits.](https://github.com/rust-lang/rust/pull/113241/)
|
||||
- [Hide `#[repr(transparent)]` if it isn't part of the public ABI.](https://github.com/rust-lang/rust/pull/115439/)
|
||||
- [Show enum discriminant if it is a C-like variant.](https://github.com/rust-lang/rust/pull/116142/)
|
||||
|
||||
<a id="1.75.0-Compatibility-Notes"></a>
|
||||
|
||||
Compatibility Notes
|
||||
-------------------
|
||||
|
||||
- [FreeBSD targets now require at least version 12.](https://github.com/rust-lang/rust/pull/114521/)
|
||||
- [Formally demote tier 2 MIPS targets to tier 3.](https://github.com/rust-lang/rust/pull/115238/)
|
||||
- [Make misalignment a hard error in `const` contexts.](https://github.com/rust-lang/rust/pull/115524/)
|
||||
- [Fix detecting references to packed unsized fields.](https://github.com/rust-lang/rust/pull/115583/)
|
||||
- [Remove support for compiler plugins.](https://github.com/rust-lang/rust/pull/116412/)
|
||||
|
||||
<a id="1.75.0-Internal-Changes"></a>
|
||||
|
||||
Internal Changes
|
||||
----------------
|
||||
|
||||
These changes do not affect any public interfaces of Rust, but they represent
|
||||
significant improvements to the performance or internals of rustc and related
|
||||
tools.
|
||||
|
||||
- [Optimize `librustc_driver.so` with BOLT.](https://github.com/rust-lang/rust/pull/116352/)
|
||||
- [Enable parallel rustc front end in dev and nightly builds.](https://github.com/rust-lang/rust/pull/117435/)
|
||||
- [Distribute `rustc-codegen-cranelift` as rustup component on the nightly channel.](https://github.com/rust-lang/rust/pull/81746/)
|
||||
|
||||
Version 1.74.1 (2023-12-07)
|
||||
===========================
|
||||
|
||||
|
@ -2788,7 +2788,11 @@ pub enum VariantData {
|
||||
/// Struct variant.
|
||||
///
|
||||
/// E.g., `Bar { .. }` as in `enum Foo { Bar { .. } }`.
|
||||
Struct(ThinVec<FieldDef>, bool),
|
||||
Struct {
|
||||
fields: ThinVec<FieldDef>,
|
||||
// FIXME: investigate making this a `Option<ErrorGuaranteed>`
|
||||
recovered: bool,
|
||||
},
|
||||
/// Tuple variant.
|
||||
///
|
||||
/// E.g., `Bar(..)` as in `enum Foo { Bar(..) }`.
|
||||
@ -2803,7 +2807,7 @@ impl VariantData {
|
||||
/// Return the fields of this variant.
|
||||
pub fn fields(&self) -> &[FieldDef] {
|
||||
match self {
|
||||
VariantData::Struct(fields, ..) | VariantData::Tuple(fields, _) => fields,
|
||||
VariantData::Struct { fields, .. } | VariantData::Tuple(fields, _) => fields,
|
||||
_ => &[],
|
||||
}
|
||||
}
|
||||
@ -2811,7 +2815,7 @@ impl VariantData {
|
||||
/// Return the `NodeId` of this variant's constructor, if it has one.
|
||||
pub fn ctor_node_id(&self) -> Option<NodeId> {
|
||||
match *self {
|
||||
VariantData::Struct(..) => None,
|
||||
VariantData::Struct { .. } => None,
|
||||
VariantData::Tuple(_, id) | VariantData::Unit(id) => Some(id),
|
||||
}
|
||||
}
|
||||
|
@ -976,7 +976,7 @@ pub fn noop_visit_where_predicate<T: MutVisitor>(pred: &mut WherePredicate, vis:
|
||||
|
||||
pub fn noop_visit_variant_data<T: MutVisitor>(vdata: &mut VariantData, vis: &mut T) {
|
||||
match vdata {
|
||||
VariantData::Struct(fields, ..) => {
|
||||
VariantData::Struct { fields, .. } => {
|
||||
fields.flat_map_in_place(|field| vis.flat_map_field_def(field));
|
||||
}
|
||||
VariantData::Tuple(fields, id) => {
|
||||
|
@ -1,7 +1,6 @@
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_data_structures::sorted_map::SortedMap;
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def_id::LocalDefId;
|
||||
use rustc_hir::def_id::{LocalDefId, LocalDefIdMap};
|
||||
use rustc_hir::intravisit::Visitor;
|
||||
use rustc_hir::*;
|
||||
use rustc_index::{Idx, IndexVec};
|
||||
@ -17,7 +16,7 @@ struct NodeCollector<'a, 'hir> {
|
||||
|
||||
/// Outputs
|
||||
nodes: IndexVec<ItemLocalId, Option<ParentedNode<'hir>>>,
|
||||
parenting: FxHashMap<LocalDefId, ItemLocalId>,
|
||||
parenting: LocalDefIdMap<ItemLocalId>,
|
||||
|
||||
/// The parent of this node
|
||||
parent_node: hir::ItemLocalId,
|
||||
@ -30,7 +29,7 @@ pub(super) fn index_hir<'hir>(
|
||||
tcx: TyCtxt<'hir>,
|
||||
item: hir::OwnerNode<'hir>,
|
||||
bodies: &SortedMap<ItemLocalId, &'hir Body<'hir>>,
|
||||
) -> (IndexVec<ItemLocalId, Option<ParentedNode<'hir>>>, FxHashMap<LocalDefId, ItemLocalId>) {
|
||||
) -> (IndexVec<ItemLocalId, Option<ParentedNode<'hir>>>, LocalDefIdMap<ItemLocalId>) {
|
||||
let mut nodes = IndexVec::new();
|
||||
// This node's parent should never be accessed: the owner's parent is computed by the
|
||||
// hir_owner_parent query. Make it invalid (= ItemLocalId::MAX) to force an ICE whenever it is
|
||||
@ -42,7 +41,7 @@ pub(super) fn index_hir<'hir>(
|
||||
parent_node: ItemLocalId::new(0),
|
||||
nodes,
|
||||
bodies,
|
||||
parenting: FxHashMap::default(),
|
||||
parenting: Default::default(),
|
||||
};
|
||||
|
||||
match item {
|
||||
|
@ -661,11 +661,12 @@ impl<'hir> LoweringContext<'_, 'hir> {
|
||||
vdata: &VariantData,
|
||||
) -> hir::VariantData<'hir> {
|
||||
match vdata {
|
||||
VariantData::Struct(fields, recovered) => hir::VariantData::Struct(
|
||||
self.arena
|
||||
VariantData::Struct { fields, recovered } => hir::VariantData::Struct {
|
||||
fields: self
|
||||
.arena
|
||||
.alloc_from_iter(fields.iter().enumerate().map(|f| self.lower_field_def(f))),
|
||||
*recovered,
|
||||
),
|
||||
recovered: *recovered,
|
||||
},
|
||||
VariantData::Tuple(fields, id) => {
|
||||
let ctor_id = self.lower_node_id(*id);
|
||||
self.alias_attrs(ctor_id, parent_id);
|
||||
|
@ -44,23 +44,23 @@ extern crate tracing;
|
||||
|
||||
use crate::errors::{AssocTyParentheses, AssocTyParenthesesSub, MisplacedImplTrait};
|
||||
|
||||
use rustc_ast::node_id::NodeMap;
|
||||
use rustc_ast::ptr::P;
|
||||
use rustc_ast::{self as ast, *};
|
||||
use rustc_ast_pretty::pprust;
|
||||
use rustc_data_structures::captures::Captures;
|
||||
use rustc_data_structures::fingerprint::Fingerprint;
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_data_structures::sorted_map::SortedMap;
|
||||
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
|
||||
use rustc_data_structures::sync::Lrc;
|
||||
use rustc_errors::{DiagnosticArgFromDisplay, StashKey};
|
||||
use rustc_hir as hir;
|
||||
use rustc_hir::def::{DefKind, LifetimeRes, Namespace, PartialRes, PerNS, Res};
|
||||
use rustc_hir::def_id::{LocalDefId, CRATE_DEF_ID, LOCAL_CRATE};
|
||||
use rustc_hir::{ConstArg, GenericArg, ItemLocalId, ParamName, TraitCandidate};
|
||||
use rustc_hir::def_id::{LocalDefId, LocalDefIdMap, CRATE_DEF_ID, LOCAL_CRATE};
|
||||
use rustc_hir::{ConstArg, GenericArg, ItemLocalMap, ParamName, TraitCandidate};
|
||||
use rustc_index::{Idx, IndexSlice, IndexVec};
|
||||
use rustc_middle::span_bug;
|
||||
use rustc_middle::ty::{ResolverAstLowering, TyCtxt, Visibility};
|
||||
use rustc_middle::ty::{ResolverAstLowering, TyCtxt};
|
||||
use rustc_session::parse::{add_feature_diagnostics, feature_err};
|
||||
use rustc_span::symbol::{kw, sym, Ident, Symbol};
|
||||
use rustc_span::{DesugaringKind, Span, DUMMY_SP};
|
||||
@ -119,13 +119,13 @@ struct LoweringContext<'a, 'hir> {
|
||||
|
||||
current_hir_id_owner: hir::OwnerId,
|
||||
item_local_id_counter: hir::ItemLocalId,
|
||||
trait_map: FxHashMap<ItemLocalId, Box<[TraitCandidate]>>,
|
||||
trait_map: ItemLocalMap<Box<[TraitCandidate]>>,
|
||||
|
||||
impl_trait_defs: Vec<hir::GenericParam<'hir>>,
|
||||
impl_trait_bounds: Vec<hir::WherePredicate<'hir>>,
|
||||
|
||||
/// NodeIds that are lowered inside the current HIR owner.
|
||||
node_id_to_local_id: FxHashMap<NodeId, hir::ItemLocalId>,
|
||||
node_id_to_local_id: NodeMap<hir::ItemLocalId>,
|
||||
|
||||
allow_try_trait: Lrc<[Symbol]>,
|
||||
allow_gen_future: Lrc<[Symbol]>,
|
||||
@ -135,7 +135,7 @@ struct LoweringContext<'a, 'hir> {
|
||||
/// For each captured lifetime (e.g., 'a), we create a new lifetime parameter that is a generic
|
||||
/// defined on the TAIT, so we have type Foo<'a1> = ... and we establish a mapping in this
|
||||
/// field from the original parameter 'a to the new parameter 'a1.
|
||||
generics_def_id_map: Vec<FxHashMap<LocalDefId, LocalDefId>>,
|
||||
generics_def_id_map: Vec<LocalDefIdMap<LocalDefId>>,
|
||||
|
||||
host_param_id: Option<LocalDefId>,
|
||||
}
|
||||
@ -380,7 +380,7 @@ enum AstOwner<'a> {
|
||||
}
|
||||
|
||||
fn index_crate<'a>(
|
||||
node_id_to_def_id: &FxHashMap<NodeId, LocalDefId>,
|
||||
node_id_to_def_id: &NodeMap<LocalDefId>,
|
||||
krate: &'a Crate,
|
||||
) -> IndexVec<LocalDefId, AstOwner<'a>> {
|
||||
let mut indexer = Indexer { node_id_to_def_id, index: IndexVec::new() };
|
||||
@ -390,7 +390,7 @@ fn index_crate<'a>(
|
||||
return indexer.index;
|
||||
|
||||
struct Indexer<'s, 'a> {
|
||||
node_id_to_def_id: &'s FxHashMap<NodeId, LocalDefId>,
|
||||
node_id_to_def_id: &'s NodeMap<LocalDefId>,
|
||||
index: IndexVec<LocalDefId, AstOwner<'a>>,
|
||||
}
|
||||
|
||||
@ -642,7 +642,7 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
/// `'a` declared on the TAIT, instead of the function.
|
||||
fn with_remapping<R>(
|
||||
&mut self,
|
||||
remap: FxHashMap<LocalDefId, LocalDefId>,
|
||||
remap: LocalDefIdMap<LocalDefId>,
|
||||
f: impl FnOnce(&mut Self) -> R,
|
||||
) -> R {
|
||||
self.generics_def_id_map.push(remap);
|
||||
@ -1651,13 +1651,9 @@ impl<'a, 'hir> LoweringContext<'a, 'hir> {
|
||||
);
|
||||
debug!(?opaque_ty_def_id);
|
||||
|
||||
// Meaningless, but provided so that all items have visibilities.
|
||||
let parent_mod = self.tcx.parent_module_from_def_id(opaque_ty_def_id).to_def_id();
|
||||
self.tcx.feed_local_def_id(opaque_ty_def_id).visibility(Visibility::Restricted(parent_mod));
|
||||
|
||||
// Map from captured (old) lifetime to synthetic (new) lifetime.
|
||||
// Used to resolve lifetimes in the bounds of the opaque.
|
||||
let mut captured_to_synthesized_mapping = FxHashMap::default();
|
||||
let mut captured_to_synthesized_mapping = LocalDefIdMap::default();
|
||||
// List of (early-bound) synthetic lifetimes that are owned by the opaque.
|
||||
// This is used to create the `hir::Generics` owned by the opaque.
|
||||
let mut synthesized_lifetime_definitions = vec![];
|
||||
|
@ -117,13 +117,13 @@ ast_passes_fn_without_body =
|
||||
free function without a body
|
||||
.suggestion = provide a definition for the function
|
||||
|
||||
ast_passes_forbidden_bound =
|
||||
bounds cannot be used in this context
|
||||
|
||||
ast_passes_forbidden_default =
|
||||
`default` is only allowed on items in trait impls
|
||||
.label = `default` because of this
|
||||
|
||||
ast_passes_forbidden_lifetime_bound =
|
||||
lifetime bounds cannot be used in this context
|
||||
|
||||
ast_passes_forbidden_non_lifetime_param =
|
||||
only lifetime parameters can be used in this context
|
||||
|
||||
|
@ -1000,7 +1000,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
|
||||
}
|
||||
}
|
||||
ItemKind::Struct(vdata, generics) => match vdata {
|
||||
VariantData::Struct(fields, ..) => {
|
||||
VariantData::Struct { fields, .. } => {
|
||||
self.visit_vis(&item.vis);
|
||||
self.visit_ident(item.ident);
|
||||
self.visit_generics(generics);
|
||||
@ -1016,7 +1016,7 @@ impl<'a> Visitor<'a> for AstValidator<'a> {
|
||||
self.dcx().emit_err(errors::FieldlessUnion { span: item.span });
|
||||
}
|
||||
match vdata {
|
||||
VariantData::Struct(fields, ..) => {
|
||||
VariantData::Struct { fields, .. } => {
|
||||
self.visit_vis(&item.vis);
|
||||
self.visit_ident(item.ident);
|
||||
self.visit_generics(generics);
|
||||
|
@ -52,8 +52,8 @@ pub struct TraitFnConst {
|
||||
}
|
||||
|
||||
#[derive(Diagnostic)]
|
||||
#[diag(ast_passes_forbidden_lifetime_bound)]
|
||||
pub struct ForbiddenLifetimeBound {
|
||||
#[diag(ast_passes_forbidden_bound)]
|
||||
pub struct ForbiddenBound {
|
||||
#[primary_span]
|
||||
pub spans: Vec<Span>,
|
||||
}
|
||||
|
@ -152,8 +152,8 @@ impl<'a> PostExpansionVisitor<'a> {
|
||||
}
|
||||
|
||||
fn check_late_bound_lifetime_defs(&self, params: &[ast::GenericParam]) {
|
||||
// Check only lifetime parameters are present and that the lifetime
|
||||
// parameters that are present have no bounds.
|
||||
// Check only lifetime parameters are present and that the
|
||||
// generic parameters that are present have no bounds.
|
||||
let non_lt_param_spans = params.iter().filter_map(|param| match param.kind {
|
||||
ast::GenericParamKind::Lifetime { .. } => None,
|
||||
_ => Some(param.ident.span),
|
||||
@ -164,10 +164,11 @@ impl<'a> PostExpansionVisitor<'a> {
|
||||
non_lt_param_spans,
|
||||
crate::fluent_generated::ast_passes_forbidden_non_lifetime_param
|
||||
);
|
||||
|
||||
for param in params {
|
||||
if !param.bounds.is_empty() {
|
||||
let spans: Vec<_> = param.bounds.iter().map(|b| b.span()).collect();
|
||||
self.sess.emit_err(errors::ForbiddenLifetimeBound { spans });
|
||||
self.sess.emit_err(errors::ForbiddenBound { spans });
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -500,7 +500,7 @@ impl<'a> State<'a> {
|
||||
self.end();
|
||||
self.end(); // Close the outer-box.
|
||||
}
|
||||
ast::VariantData::Struct(fields, ..) => {
|
||||
ast::VariantData::Struct { fields, .. } => {
|
||||
self.print_where_clause(&generics.where_clause);
|
||||
self.print_record_struct_body(fields, span);
|
||||
}
|
||||
|
@ -674,13 +674,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
// eagerly.
|
||||
let mut outlives_requirements = infcx.tcx.is_typeck_child(mir_def_id).then(Vec::new);
|
||||
|
||||
self.check_type_tests(
|
||||
infcx,
|
||||
param_env,
|
||||
body,
|
||||
outlives_requirements.as_mut(),
|
||||
&mut errors_buffer,
|
||||
);
|
||||
self.check_type_tests(infcx, body, outlives_requirements.as_mut(), &mut errors_buffer);
|
||||
|
||||
debug!(?errors_buffer);
|
||||
debug!(?outlives_requirements);
|
||||
@ -938,7 +932,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
fn check_type_tests(
|
||||
&self,
|
||||
infcx: &InferCtxt<'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
body: &Body<'tcx>,
|
||||
mut propagated_outlives_requirements: Option<&mut Vec<ClosureOutlivesRequirement<'tcx>>>,
|
||||
errors_buffer: &mut RegionErrors<'tcx>,
|
||||
@ -956,7 +949,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
let generic_ty = type_test.generic_kind.to_ty(tcx);
|
||||
if self.eval_verify_bound(
|
||||
infcx,
|
||||
param_env,
|
||||
generic_ty,
|
||||
type_test.lower_bound,
|
||||
&type_test.verify_bound,
|
||||
@ -967,7 +959,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
if let Some(propagated_outlives_requirements) = &mut propagated_outlives_requirements {
|
||||
if self.try_promote_type_test(
|
||||
infcx,
|
||||
param_env,
|
||||
body,
|
||||
type_test,
|
||||
propagated_outlives_requirements,
|
||||
@ -1025,7 +1016,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
fn try_promote_type_test(
|
||||
&self,
|
||||
infcx: &InferCtxt<'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
body: &Body<'tcx>,
|
||||
type_test: &TypeTest<'tcx>,
|
||||
propagated_outlives_requirements: &mut Vec<ClosureOutlivesRequirement<'tcx>>,
|
||||
@ -1087,7 +1077,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
// where `ur` is a local bound -- we are sometimes in a
|
||||
// position to prove things that our caller cannot. See
|
||||
// #53570 for an example.
|
||||
if self.eval_verify_bound(infcx, param_env, generic_ty, ur, &type_test.verify_bound) {
|
||||
if self.eval_verify_bound(infcx, generic_ty, ur, &type_test.verify_bound) {
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -1270,7 +1260,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
fn eval_verify_bound(
|
||||
&self,
|
||||
infcx: &InferCtxt<'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
generic_ty: Ty<'tcx>,
|
||||
lower_bound: RegionVid,
|
||||
verify_bound: &VerifyBound<'tcx>,
|
||||
@ -1279,7 +1268,7 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
|
||||
match verify_bound {
|
||||
VerifyBound::IfEq(verify_if_eq_b) => {
|
||||
self.eval_if_eq(infcx, param_env, generic_ty, lower_bound, *verify_if_eq_b)
|
||||
self.eval_if_eq(infcx, generic_ty, lower_bound, *verify_if_eq_b)
|
||||
}
|
||||
|
||||
VerifyBound::IsEmpty => {
|
||||
@ -1293,11 +1282,11 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
}
|
||||
|
||||
VerifyBound::AnyBound(verify_bounds) => verify_bounds.iter().any(|verify_bound| {
|
||||
self.eval_verify_bound(infcx, param_env, generic_ty, lower_bound, verify_bound)
|
||||
self.eval_verify_bound(infcx, generic_ty, lower_bound, verify_bound)
|
||||
}),
|
||||
|
||||
VerifyBound::AllBounds(verify_bounds) => verify_bounds.iter().all(|verify_bound| {
|
||||
self.eval_verify_bound(infcx, param_env, generic_ty, lower_bound, verify_bound)
|
||||
self.eval_verify_bound(infcx, generic_ty, lower_bound, verify_bound)
|
||||
}),
|
||||
}
|
||||
}
|
||||
@ -1305,19 +1294,13 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
fn eval_if_eq(
|
||||
&self,
|
||||
infcx: &InferCtxt<'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
generic_ty: Ty<'tcx>,
|
||||
lower_bound: RegionVid,
|
||||
verify_if_eq_b: ty::Binder<'tcx, VerifyIfEq<'tcx>>,
|
||||
) -> bool {
|
||||
let generic_ty = self.normalize_to_scc_representatives(infcx.tcx, generic_ty);
|
||||
let verify_if_eq_b = self.normalize_to_scc_representatives(infcx.tcx, verify_if_eq_b);
|
||||
match test_type_match::extract_verify_if_eq(
|
||||
infcx.tcx,
|
||||
param_env,
|
||||
&verify_if_eq_b,
|
||||
generic_ty,
|
||||
) {
|
||||
match test_type_match::extract_verify_if_eq(infcx.tcx, &verify_if_eq_b, generic_ty) {
|
||||
Some(r) => {
|
||||
let r_vid = self.to_region_vid(r);
|
||||
self.eval_outlives(r_vid, lower_bound)
|
||||
|
@ -188,7 +188,7 @@ fn cs_clone(
|
||||
}
|
||||
|
||||
let expr = match *vdata {
|
||||
VariantData::Struct(..) => {
|
||||
VariantData::Struct { .. } => {
|
||||
let fields = all_fields
|
||||
.iter()
|
||||
.map(|field| {
|
||||
|
@ -71,7 +71,7 @@ fn show_substructure(cx: &mut ExtCtxt<'_>, span: Span, substr: &Substructure<'_>
|
||||
(false, 0)
|
||||
}
|
||||
ast::VariantData::Tuple(..) => (false, 1),
|
||||
ast::VariantData::Struct(..) => (true, 2),
|
||||
ast::VariantData::Struct { .. } => (true, 2),
|
||||
};
|
||||
|
||||
// The number of fields that can be handled without an array.
|
||||
@ -226,7 +226,7 @@ fn show_fieldless_enum(
|
||||
debug_assert!(fields.is_empty());
|
||||
cx.pat_tuple_struct(span, variant_path, ThinVec::new())
|
||||
}
|
||||
ast::VariantData::Struct(fields, _) => {
|
||||
ast::VariantData::Struct { fields, .. } => {
|
||||
debug_assert!(fields.is_empty());
|
||||
cx.pat_struct(span, variant_path, ThinVec::new())
|
||||
}
|
||||
|
@ -1485,7 +1485,7 @@ impl<'a> TraitDef<'a> {
|
||||
|
||||
let struct_path = struct_path.clone();
|
||||
match *struct_def {
|
||||
VariantData::Struct(..) => {
|
||||
VariantData::Struct { .. } => {
|
||||
let field_pats = pieces_iter
|
||||
.map(|(sp, ident, pat)| {
|
||||
if ident.is_none() {
|
||||
|
@ -232,6 +232,13 @@ const EXTENDED_SYSROOT_SUITE: &[TestCase] = &[
|
||||
if runner.is_native {
|
||||
let mut test_cmd = PORTABLE_SIMD.test(&runner.target_compiler, &runner.dirs);
|
||||
test_cmd.arg("-q");
|
||||
// FIXME remove after portable-simd update
|
||||
test_cmd
|
||||
.arg("--")
|
||||
.arg("--skip")
|
||||
.arg("core_simd::swizzle::simd_swizzle")
|
||||
.arg("--skip")
|
||||
.arg("core_simd::vector::Simd<T,N>::lanes");
|
||||
spawn_and_wait(test_cmd);
|
||||
}
|
||||
}),
|
||||
|
@ -337,17 +337,6 @@ fn main() {
|
||||
static REF2: &u8 = REF1;
|
||||
assert_eq!(*REF1, *REF2);
|
||||
|
||||
extern "C" {
|
||||
type A;
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let x: &A = unsafe { &*(1usize as *const A) };
|
||||
|
||||
assert_eq!(unsafe { intrinsics::size_of_val(x) }, 0);
|
||||
assert_eq!(unsafe { intrinsics::min_align_of_val(x) }, 1);
|
||||
}
|
||||
|
||||
#[repr(simd)]
|
||||
struct V([f64; 2]);
|
||||
|
||||
|
@ -0,0 +1,22 @@
|
||||
From a101a43b795431ce617e7782afb451f4853afc00 Mon Sep 17 00:00:00 2001
|
||||
From: bjorn3 <17426603+bjorn3@users.noreply.github.com>
|
||||
Date: Thu, 7 Dec 2023 14:51:35 +0000
|
||||
Subject: [PATCH] Enable the exposed_provenance feature
|
||||
|
||||
---
|
||||
crates/core_simd/tests/pointers.rs | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/crates/core_simd/tests/pointers.rs b/crates/core_simd/tests/pointers.rs
|
||||
index 0ae8f83..06620d6 100644
|
||||
--- a/crates/core_simd/tests/pointers.rs
|
||||
+++ b/crates/core_simd/tests/pointers.rs
|
||||
@@ -1,4 +1,4 @@
|
||||
-#![feature(portable_simd, strict_provenance)]
|
||||
+#![feature(exposed_provenance, portable_simd, strict_provenance)]
|
||||
|
||||
use core_simd::simd::{Simd, SimdConstPtr, SimdMutPtr};
|
||||
|
||||
--
|
||||
2.34.1
|
||||
|
@ -36,15 +36,18 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "allocator-api2"
|
||||
version = "0.2.15"
|
||||
version = "0.2.16"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "56fc6cf8dc8c4158eed8649f9b8b0ea1518eb62b544fe9490d66fa0b349eafe9"
|
||||
checksum = "0942ffc6dcaadf03badf6e6a2d0228460359d5e34b57ccdc720b7382dfbd5ec5"
|
||||
|
||||
[[package]]
|
||||
name = "cc"
|
||||
version = "1.0.79"
|
||||
version = "1.0.83"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "50d30906286121d95be3d479533b458f87493b30a4b5f79a607db8f5d11aa91f"
|
||||
checksum = "f1174fb0b6ec23863f8b971027804a42614e347eafb0a95bf0b12cdae21fc4d0"
|
||||
dependencies = [
|
||||
"libc",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "cfg-if"
|
||||
@ -58,9 +61,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "compiler_builtins"
|
||||
version = "0.1.103"
|
||||
version = "0.1.104"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "a3b73c3443a5fd2438d7ba4853c64e4c8efc2404a9e28a9234cc2d5eebc6c242"
|
||||
checksum = "99c3f9035afc33f4358773239573f7d121099856753e1bbd2a6a5207098fc741"
|
||||
dependencies = [
|
||||
"cc",
|
||||
"rustc-std-workspace-core",
|
||||
@ -124,9 +127,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "gimli"
|
||||
version = "0.28.0"
|
||||
version = "0.28.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "6fb8d784f27acf97159b40fc4db5ecd8aa23b9ad5ef69cdd136d3bc80665f0c0"
|
||||
checksum = "4271d37baee1b8c7e4b708028c57d816cf9d2434acb33a549475f78c181f6253"
|
||||
dependencies = [
|
||||
"compiler_builtins",
|
||||
"rustc-std-workspace-alloc",
|
||||
@ -135,9 +138,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "hashbrown"
|
||||
version = "0.14.0"
|
||||
version = "0.14.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2c6201b9ff9fd90a5a3bac2e56a830d0caa509576f0e503818ee82c181b3437a"
|
||||
checksum = "290f1a1d9242c78d09ce40a5e87e7554ee637af1351968159f4952f028f75604"
|
||||
dependencies = [
|
||||
"allocator-api2",
|
||||
"compiler_builtins",
|
||||
@ -147,9 +150,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "hermit-abi"
|
||||
version = "0.3.2"
|
||||
version = "0.3.3"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "443144c8cdadd93ebf52ddb4056d257f5b52c04d3c804e657d19eb73fc33668b"
|
||||
checksum = "d77f7ec81a6d05a3abb01ab6eb7590f6083d08449fe5a1c8b1e620283546ccb7"
|
||||
dependencies = [
|
||||
"compiler_builtins",
|
||||
"rustc-std-workspace-alloc",
|
||||
@ -167,9 +170,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "memchr"
|
||||
version = "2.5.0"
|
||||
version = "2.6.4"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "2dffe52ecf27772e601905b7522cb4ef790d2cc203488bbd0e2fe85fcb74566d"
|
||||
checksum = "f665ee40bc4a3c5590afb1e9677db74a508659dfd71e126420da8274909a0167"
|
||||
dependencies = [
|
||||
"compiler_builtins",
|
||||
"rustc-std-workspace-core",
|
||||
@ -189,9 +192,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "object"
|
||||
version = "0.32.0"
|
||||
version = "0.32.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "77ac5bbd07aea88c60a577a1ce218075ffd59208b2d7ca97adf9bfc5aeb21ebe"
|
||||
checksum = "9cf5f9dd3933bd50a9e1f149ec995f39ae2c496d31fd772c1fd45ebc27e902b0"
|
||||
dependencies = [
|
||||
"compiler_builtins",
|
||||
"memchr",
|
||||
@ -241,9 +244,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "r-efi"
|
||||
version = "4.2.0"
|
||||
version = "4.3.0"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "575fc2d9b3da54adbdfaddf6eca48fec256d977c8630a1750b8991347d1ac911"
|
||||
checksum = "0e244f96e03a3067f9e521d3167bd42657594cb8588c8d3a2db01545dc1af2e0"
|
||||
dependencies = [
|
||||
"compiler_builtins",
|
||||
"rustc-std-workspace-core",
|
||||
@ -402,9 +405,9 @@ dependencies = [
|
||||
|
||||
[[package]]
|
||||
name = "unicode-width"
|
||||
version = "0.1.10"
|
||||
version = "0.1.11"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "c0edd1e5b14653f783770bce4a4dabb4a5108a5370a5f5d8cfe8710c361f6c8b"
|
||||
checksum = "e51733f11c9c4f72aa0c160008246859e340b00807569a0da0e7a1079b27ba85"
|
||||
dependencies = [
|
||||
"compiler_builtins",
|
||||
"rustc-std-workspace-core",
|
||||
@ -419,6 +422,18 @@ dependencies = [
|
||||
"compiler_builtins",
|
||||
"core",
|
||||
"libc",
|
||||
"unwinding",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
name = "unwinding"
|
||||
version = "0.2.1"
|
||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||
checksum = "37a19a21a537f635c16c7576f22d0f2f7d63353c1337ad4ce0d8001c7952a25b"
|
||||
dependencies = [
|
||||
"compiler_builtins",
|
||||
"gimli",
|
||||
"rustc-std-workspace-core",
|
||||
]
|
||||
|
||||
[[package]]
|
||||
|
@ -1,3 +1,3 @@
|
||||
[toolchain]
|
||||
channel = "nightly-2023-11-25"
|
||||
channel = "nightly-2023-12-19"
|
||||
components = ["rust-src", "rustc-dev", "llvm-tools"]
|
||||
|
@ -44,6 +44,7 @@ rm tests/ui/proc-macro/no-mangle-in-proc-macro-issue-111888.rs
|
||||
# vendor intrinsics
|
||||
rm tests/ui/sse2.rs # CodegenBackend::target_features not yet implemented
|
||||
rm tests/ui/simd/array-type.rs # "Index argument for `simd_insert` is not a constant"
|
||||
rm tests/ui/simd/masked-load-store.rs
|
||||
|
||||
# exotic linkages
|
||||
rm tests/ui/issues/issue-33992.rs # unsupported linkages
|
||||
|
@ -353,7 +353,7 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) {
|
||||
fx,
|
||||
rustc_hir::LangItem::PanicBoundsCheck,
|
||||
&[index, len, location],
|
||||
source_info.span,
|
||||
Some(source_info.span),
|
||||
);
|
||||
}
|
||||
AssertKind::MisalignedPointerDereference { ref required, ref found } => {
|
||||
@ -365,7 +365,7 @@ fn codegen_fn_body(fx: &mut FunctionCx<'_, '_, '_>, start_block: Block) {
|
||||
fx,
|
||||
rustc_hir::LangItem::PanicMisalignedPointerDereference,
|
||||
&[required, found, location],
|
||||
source_info.span,
|
||||
Some(source_info.span),
|
||||
);
|
||||
}
|
||||
_ => {
|
||||
@ -945,19 +945,19 @@ pub(crate) fn codegen_panic<'tcx>(
|
||||
let msg_len = fx.bcx.ins().iconst(fx.pointer_type, i64::try_from(msg_str.len()).unwrap());
|
||||
let args = [msg_ptr, msg_len, location];
|
||||
|
||||
codegen_panic_inner(fx, rustc_hir::LangItem::Panic, &args, source_info.span);
|
||||
codegen_panic_inner(fx, rustc_hir::LangItem::Panic, &args, Some(source_info.span));
|
||||
}
|
||||
|
||||
pub(crate) fn codegen_panic_nounwind<'tcx>(
|
||||
fx: &mut FunctionCx<'_, '_, 'tcx>,
|
||||
msg_str: &str,
|
||||
source_info: mir::SourceInfo,
|
||||
span: Option<Span>,
|
||||
) {
|
||||
let msg_ptr = fx.anonymous_str(msg_str);
|
||||
let msg_len = fx.bcx.ins().iconst(fx.pointer_type, i64::try_from(msg_str.len()).unwrap());
|
||||
let args = [msg_ptr, msg_len];
|
||||
|
||||
codegen_panic_inner(fx, rustc_hir::LangItem::PanicNounwind, &args, source_info.span);
|
||||
codegen_panic_inner(fx, rustc_hir::LangItem::PanicNounwind, &args, span);
|
||||
}
|
||||
|
||||
pub(crate) fn codegen_unwind_terminate<'tcx>(
|
||||
@ -967,16 +967,16 @@ pub(crate) fn codegen_unwind_terminate<'tcx>(
|
||||
) {
|
||||
let args = [];
|
||||
|
||||
codegen_panic_inner(fx, reason.lang_item(), &args, source_info.span);
|
||||
codegen_panic_inner(fx, reason.lang_item(), &args, Some(source_info.span));
|
||||
}
|
||||
|
||||
fn codegen_panic_inner<'tcx>(
|
||||
fx: &mut FunctionCx<'_, '_, 'tcx>,
|
||||
lang_item: rustc_hir::LangItem,
|
||||
args: &[Value],
|
||||
span: Span,
|
||||
span: Option<Span>,
|
||||
) {
|
||||
let def_id = fx.tcx.require_lang_item(lang_item, Some(span));
|
||||
let def_id = fx.tcx.require_lang_item(lang_item, span);
|
||||
|
||||
let instance = Instance::mono(fx.tcx, def_id).polymorphize(fx.tcx);
|
||||
let symbol_name = fx.tcx.symbol_name(instance).name;
|
||||
|
@ -98,11 +98,15 @@ fn clif_pair_type_from_ty<'tcx>(
|
||||
|
||||
/// Is a pointer to this type a fat ptr?
|
||||
pub(crate) fn has_ptr_meta<'tcx>(tcx: TyCtxt<'tcx>, ty: Ty<'tcx>) -> bool {
|
||||
let ptr_ty = Ty::new_ptr(tcx, TypeAndMut { ty, mutbl: rustc_hir::Mutability::Not });
|
||||
match &tcx.layout_of(ParamEnv::reveal_all().and(ptr_ty)).unwrap().abi {
|
||||
Abi::Scalar(_) => false,
|
||||
Abi::ScalarPair(_, _) => true,
|
||||
abi => unreachable!("Abi of ptr to {:?} is {:?}???", ty, abi),
|
||||
if ty.is_sized(tcx, ParamEnv::reveal_all()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
let tail = tcx.struct_tail_erasing_lifetimes(ty, ParamEnv::reveal_all());
|
||||
match tail.kind() {
|
||||
ty::Foreign(..) => false,
|
||||
ty::Str | ty::Slice(..) | ty::Dynamic(..) => true,
|
||||
_ => bug!("unexpected unsized tail: {:?}", tail),
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -487,13 +487,12 @@ fn codegen_regular_intrinsic_call<'tcx>(
|
||||
let layout = fx.layout_of(generic_args.type_at(0));
|
||||
// Note: Can't use is_unsized here as truly unsized types need to take the fixed size
|
||||
// branch
|
||||
let size = if let Abi::ScalarPair(_, _) = ptr.layout().abi {
|
||||
let (_ptr, info) = ptr.load_scalar_pair(fx);
|
||||
let (size, _align) = crate::unsize::size_and_align_of_dst(fx, layout, info);
|
||||
size
|
||||
let meta = if let Abi::ScalarPair(_, _) = ptr.layout().abi {
|
||||
Some(ptr.load_scalar_pair(fx).1)
|
||||
} else {
|
||||
fx.bcx.ins().iconst(fx.pointer_type, layout.size.bytes() as i64)
|
||||
None
|
||||
};
|
||||
let (size, _align) = crate::unsize::size_and_align_of(fx, layout, meta);
|
||||
ret.write_cvalue(fx, CValue::by_val(size, usize_layout));
|
||||
}
|
||||
sym::min_align_of_val => {
|
||||
@ -502,13 +501,12 @@ fn codegen_regular_intrinsic_call<'tcx>(
|
||||
let layout = fx.layout_of(generic_args.type_at(0));
|
||||
// Note: Can't use is_unsized here as truly unsized types need to take the fixed size
|
||||
// branch
|
||||
let align = if let Abi::ScalarPair(_, _) = ptr.layout().abi {
|
||||
let (_ptr, info) = ptr.load_scalar_pair(fx);
|
||||
let (_size, align) = crate::unsize::size_and_align_of_dst(fx, layout, info);
|
||||
align
|
||||
let meta = if let Abi::ScalarPair(_, _) = ptr.layout().abi {
|
||||
Some(ptr.load_scalar_pair(fx).1)
|
||||
} else {
|
||||
fx.bcx.ins().iconst(fx.pointer_type, layout.align.abi.bytes() as i64)
|
||||
None
|
||||
};
|
||||
let (_size, align) = crate::unsize::size_and_align_of(fx, layout, meta);
|
||||
ret.write_cvalue(fx, CValue::by_val(align, usize_layout));
|
||||
}
|
||||
|
||||
@ -688,7 +686,7 @@ fn codegen_regular_intrinsic_call<'tcx>(
|
||||
}
|
||||
})
|
||||
});
|
||||
crate::base::codegen_panic_nounwind(fx, &msg_str, source_info);
|
||||
crate::base::codegen_panic_nounwind(fx, &msg_str, Some(source_info.span));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -2,6 +2,9 @@
|
||||
//!
|
||||
//! [`PointerCoercion::Unsize`]: `rustc_middle::ty::adjustment::PointerCoercion::Unsize`
|
||||
|
||||
use rustc_middle::ty::print::{with_no_trimmed_paths, with_no_visible_paths};
|
||||
|
||||
use crate::base::codegen_panic_nounwind;
|
||||
use crate::prelude::*;
|
||||
|
||||
// Adapted from https://github.com/rust-lang/rust/blob/2a663555ddf36f6b041445894a8c175cd1bc718c/src/librustc_codegen_ssa/base.rs#L159-L307
|
||||
@ -187,63 +190,113 @@ pub(crate) fn coerce_dyn_star<'tcx>(
|
||||
|
||||
// Adapted from https://github.com/rust-lang/rust/blob/2a663555ddf36f6b041445894a8c175cd1bc718c/src/librustc_codegen_ssa/glue.rs
|
||||
|
||||
pub(crate) fn size_and_align_of_dst<'tcx>(
|
||||
pub(crate) fn size_and_align_of<'tcx>(
|
||||
fx: &mut FunctionCx<'_, '_, 'tcx>,
|
||||
layout: TyAndLayout<'tcx>,
|
||||
info: Value,
|
||||
info: Option<Value>,
|
||||
) -> (Value, Value) {
|
||||
assert!(layout.is_unsized() || layout.abi == Abi::Uninhabited);
|
||||
match layout.ty.kind() {
|
||||
if layout.is_sized() {
|
||||
return (
|
||||
fx.bcx.ins().iconst(fx.pointer_type, layout.size.bytes() as i64),
|
||||
fx.bcx.ins().iconst(fx.pointer_type, layout.align.abi.bytes() as i64),
|
||||
);
|
||||
}
|
||||
|
||||
let ty = layout.ty;
|
||||
match ty.kind() {
|
||||
ty::Dynamic(..) => {
|
||||
// load size/align from vtable
|
||||
(crate::vtable::size_of_obj(fx, info), crate::vtable::min_align_of_obj(fx, info))
|
||||
(
|
||||
crate::vtable::size_of_obj(fx, info.unwrap()),
|
||||
crate::vtable::min_align_of_obj(fx, info.unwrap()),
|
||||
)
|
||||
}
|
||||
ty::Slice(_) | ty::Str => {
|
||||
let unit = layout.field(fx, 0);
|
||||
// The info in this case is the length of the str, so the size is that
|
||||
// times the unit size.
|
||||
(
|
||||
fx.bcx.ins().imul_imm(info, unit.size.bytes() as i64),
|
||||
fx.bcx.ins().imul_imm(info.unwrap(), unit.size.bytes() as i64),
|
||||
fx.bcx.ins().iconst(fx.pointer_type, unit.align.abi.bytes() as i64),
|
||||
)
|
||||
}
|
||||
_ => {
|
||||
ty::Foreign(_) => {
|
||||
let trap_block = fx.bcx.create_block();
|
||||
let true_ = fx.bcx.ins().iconst(types::I8, 1);
|
||||
let next_block = fx.bcx.create_block();
|
||||
fx.bcx.ins().brif(true_, trap_block, &[], next_block, &[]);
|
||||
fx.bcx.seal_block(trap_block);
|
||||
fx.bcx.seal_block(next_block);
|
||||
fx.bcx.switch_to_block(trap_block);
|
||||
|
||||
// `extern` type. We cannot compute the size, so panic.
|
||||
let msg_str = with_no_visible_paths!({
|
||||
with_no_trimmed_paths!({
|
||||
format!("attempted to compute the size or alignment of extern type `{ty}`")
|
||||
})
|
||||
});
|
||||
|
||||
codegen_panic_nounwind(fx, &msg_str, None);
|
||||
|
||||
fx.bcx.switch_to_block(next_block);
|
||||
|
||||
// This function does not return so we can now return whatever we want.
|
||||
let size = fx.bcx.ins().iconst(fx.pointer_type, 42);
|
||||
let align = fx.bcx.ins().iconst(fx.pointer_type, 42);
|
||||
(size, align)
|
||||
}
|
||||
ty::Adt(..) | ty::Tuple(..) => {
|
||||
// First get the size of all statically known fields.
|
||||
// Don't use size_of because it also rounds up to alignment, which we
|
||||
// want to avoid, as the unsized field's alignment could be smaller.
|
||||
assert!(!layout.ty.is_simd());
|
||||
|
||||
let i = layout.fields.count() - 1;
|
||||
let sized_size = layout.fields.offset(i).bytes();
|
||||
let unsized_offset_unadjusted = layout.fields.offset(i).bytes();
|
||||
let unsized_offset_unadjusted =
|
||||
fx.bcx.ins().iconst(fx.pointer_type, unsized_offset_unadjusted as i64);
|
||||
let sized_align = layout.align.abi.bytes();
|
||||
let sized_align = fx.bcx.ins().iconst(fx.pointer_type, sized_align as i64);
|
||||
|
||||
// Recurse to get the size of the dynamically sized field (must be
|
||||
// the last field).
|
||||
let field_layout = layout.field(fx, i);
|
||||
let (unsized_size, mut unsized_align) = size_and_align_of_dst(fx, field_layout, info);
|
||||
let (unsized_size, mut unsized_align) = size_and_align_of(fx, field_layout, info);
|
||||
|
||||
// FIXME (#26403, #27023): We should be adding padding
|
||||
// to `sized_size` (to accommodate the `unsized_align`
|
||||
// required of the unsized field that follows) before
|
||||
// summing it with `sized_size`. (Note that since #26403
|
||||
// is unfixed, we do not yet add the necessary padding
|
||||
// here. But this is where the add would go.)
|
||||
// # First compute the dynamic alignment
|
||||
|
||||
// Return the sum of sizes and max of aligns.
|
||||
let size = fx.bcx.ins().iadd_imm(unsized_size, sized_size as i64);
|
||||
|
||||
// Packed types ignore the alignment of their fields.
|
||||
if let ty::Adt(def, _) = layout.ty.kind() {
|
||||
if def.repr().packed() {
|
||||
unsized_align = sized_align;
|
||||
// For packed types, we need to cap the alignment.
|
||||
if let ty::Adt(def, _) = ty.kind() {
|
||||
if let Some(packed) = def.repr().pack {
|
||||
if packed.bytes() == 1 {
|
||||
// We know this will be capped to 1.
|
||||
unsized_align = fx.bcx.ins().iconst(fx.pointer_type, 1);
|
||||
} else {
|
||||
// We have to dynamically compute `min(unsized_align, packed)`.
|
||||
let packed = fx.bcx.ins().iconst(fx.pointer_type, packed.bytes() as i64);
|
||||
let cmp = fx.bcx.ins().icmp(IntCC::UnsignedLessThan, unsized_align, packed);
|
||||
unsized_align = fx.bcx.ins().select(cmp, unsized_align, packed);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Choose max of two known alignments (combined value must
|
||||
// be aligned according to more restrictive of the two).
|
||||
let cmp = fx.bcx.ins().icmp(IntCC::UnsignedGreaterThan, sized_align, unsized_align);
|
||||
let align = fx.bcx.ins().select(cmp, sized_align, unsized_align);
|
||||
let full_align = fx.bcx.ins().select(cmp, sized_align, unsized_align);
|
||||
|
||||
// # Then compute the dynamic size
|
||||
|
||||
// The full formula for the size would be:
|
||||
// let unsized_offset_adjusted = unsized_offset_unadjusted.align_to(unsized_align);
|
||||
// let full_size = (unsized_offset_adjusted + unsized_size).align_to(full_align);
|
||||
// However, `unsized_size` is a multiple of `unsized_align`.
|
||||
// Therefore, we can equivalently do the `align_to(unsized_align)` *after* adding `unsized_size`:
|
||||
// let full_size = (unsized_offset_unadjusted + unsized_size).align_to(unsized_align).align_to(full_align);
|
||||
// Furthermore, `align >= unsized_align`, and therefore we only need to do:
|
||||
// let full_size = (unsized_offset_unadjusted + unsized_size).align_to(full_align);
|
||||
|
||||
let full_size = fx.bcx.ins().iadd(unsized_offset_unadjusted, unsized_size);
|
||||
|
||||
// Issue #27023: must add any necessary padding to `size`
|
||||
// (to make it a multiple of `align`) before returning it.
|
||||
@ -255,12 +308,13 @@ pub(crate) fn size_and_align_of_dst<'tcx>(
|
||||
// emulated via the semi-standard fast bit trick:
|
||||
//
|
||||
// `(size + (align-1)) & -align`
|
||||
let addend = fx.bcx.ins().iadd_imm(align, -1);
|
||||
let add = fx.bcx.ins().iadd(size, addend);
|
||||
let neg = fx.bcx.ins().ineg(align);
|
||||
let size = fx.bcx.ins().band(add, neg);
|
||||
let addend = fx.bcx.ins().iadd_imm(full_align, -1);
|
||||
let add = fx.bcx.ins().iadd(full_size, addend);
|
||||
let neg = fx.bcx.ins().ineg(full_align);
|
||||
let full_size = fx.bcx.ins().band(add, neg);
|
||||
|
||||
(size, align)
|
||||
(full_size, full_align)
|
||||
}
|
||||
_ => bug!("size_and_align_of_dst: {ty} not supported"),
|
||||
}
|
||||
}
|
||||
|
@ -20,34 +20,36 @@ fn codegen_field<'tcx>(
|
||||
(base.offset_i64(fx, i64::try_from(field_offset.bytes()).unwrap()), field_layout)
|
||||
};
|
||||
|
||||
if let Some(extra) = extra {
|
||||
if field_layout.is_sized() {
|
||||
return simple(fx);
|
||||
}
|
||||
match field_layout.ty.kind() {
|
||||
ty::Slice(..) | ty::Str | ty::Foreign(..) => simple(fx),
|
||||
ty::Adt(def, _) if def.repr().packed() => {
|
||||
assert_eq!(layout.align.abi.bytes(), 1);
|
||||
simple(fx)
|
||||
}
|
||||
_ => {
|
||||
// We have to align the offset for DST's
|
||||
let unaligned_offset = field_offset.bytes();
|
||||
let (_, unsized_align) =
|
||||
crate::unsize::size_and_align_of_dst(fx, field_layout, extra);
|
||||
if field_layout.is_sized() {
|
||||
return simple(fx);
|
||||
}
|
||||
match field_layout.ty.kind() {
|
||||
ty::Slice(..) | ty::Str => simple(fx),
|
||||
_ => {
|
||||
let unaligned_offset = field_offset.bytes();
|
||||
|
||||
let one = fx.bcx.ins().iconst(fx.pointer_type, 1);
|
||||
let align_sub_1 = fx.bcx.ins().isub(unsized_align, one);
|
||||
let and_lhs = fx.bcx.ins().iadd_imm(align_sub_1, unaligned_offset as i64);
|
||||
let zero = fx.bcx.ins().iconst(fx.pointer_type, 0);
|
||||
let and_rhs = fx.bcx.ins().isub(zero, unsized_align);
|
||||
let offset = fx.bcx.ins().band(and_lhs, and_rhs);
|
||||
// Get the alignment of the field
|
||||
let (_, mut unsized_align) = crate::unsize::size_and_align_of(fx, field_layout, extra);
|
||||
|
||||
(base.offset_value(fx, offset), field_layout)
|
||||
// For packed types, we need to cap alignment.
|
||||
if let ty::Adt(def, _) = layout.ty.kind() {
|
||||
if let Some(packed) = def.repr().pack {
|
||||
let packed = fx.bcx.ins().iconst(fx.pointer_type, packed.bytes() as i64);
|
||||
let cmp = fx.bcx.ins().icmp(IntCC::UnsignedLessThan, unsized_align, packed);
|
||||
unsized_align = fx.bcx.ins().select(cmp, unsized_align, packed);
|
||||
}
|
||||
}
|
||||
|
||||
// Bump the unaligned offset up to the appropriate alignment
|
||||
let one = fx.bcx.ins().iconst(fx.pointer_type, 1);
|
||||
let align_sub_1 = fx.bcx.ins().isub(unsized_align, one);
|
||||
let and_lhs = fx.bcx.ins().iadd_imm(align_sub_1, unaligned_offset as i64);
|
||||
let zero = fx.bcx.ins().iconst(fx.pointer_type, 0);
|
||||
let and_rhs = fx.bcx.ins().isub(zero, unsized_align);
|
||||
let offset = fx.bcx.ins().band(and_lhs, and_rhs);
|
||||
|
||||
(base.offset_value(fx, offset), field_layout)
|
||||
}
|
||||
} else {
|
||||
simple(fx)
|
||||
}
|
||||
}
|
||||
|
||||
@ -731,13 +733,8 @@ impl<'tcx> CPlace<'tcx> {
|
||||
};
|
||||
|
||||
let (field_ptr, field_layout) = codegen_field(fx, base, extra, layout, field);
|
||||
if field_layout.is_unsized() {
|
||||
if let ty::Foreign(_) = field_layout.ty.kind() {
|
||||
assert!(extra.is_none());
|
||||
CPlace::for_ptr(field_ptr, field_layout)
|
||||
} else {
|
||||
CPlace::for_ptr_with_extra(field_ptr, extra.unwrap(), field_layout)
|
||||
}
|
||||
if has_ptr_meta(fx.tcx, field_layout.ty) {
|
||||
CPlace::for_ptr_with_extra(field_ptr, extra.unwrap(), field_layout)
|
||||
} else {
|
||||
CPlace::for_ptr(field_ptr, field_layout)
|
||||
}
|
||||
|
9
compiler/rustc_codegen_cranelift/y.cmd
Normal file
9
compiler/rustc_codegen_cranelift/y.cmd
Normal file
@ -0,0 +1,9 @@
|
||||
@echo off
|
||||
echo [BUILD] build system >&2
|
||||
mkdir build 2>nul
|
||||
rustc build_system/main.rs -o build\y.exe -Cdebuginfo=1 --edition 2021 || goto :error
|
||||
build\y.exe %* || goto :error
|
||||
goto :EOF
|
||||
|
||||
:error
|
||||
exit /b
|
12
compiler/rustc_codegen_cranelift/y.ps1
Normal file
12
compiler/rustc_codegen_cranelift/y.ps1
Normal file
@ -0,0 +1,12 @@
|
||||
$ErrorActionPreference = "Stop"
|
||||
|
||||
$host.ui.WriteErrorLine("[BUILD] build system")
|
||||
New-Item -ItemType Directory -Force -Path build | Out-Null
|
||||
& rustc build_system/main.rs -o build\y.exe -Cdebuginfo=1 --edition 2021
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
exit $LASTEXITCODE
|
||||
}
|
||||
& build\y.exe $args
|
||||
if ($LASTEXITCODE -ne 0) {
|
||||
exit $LASTEXITCODE
|
||||
}
|
@ -278,13 +278,13 @@ impl CguReuseTracker {
|
||||
|
||||
if error {
|
||||
let at_least = if at_least { 1 } else { 0 };
|
||||
errors::IncorrectCguReuseType {
|
||||
sess.emit_err(errors::IncorrectCguReuseType {
|
||||
span: *error_span,
|
||||
cgu_user_name,
|
||||
actual_reuse,
|
||||
expected_reuse,
|
||||
at_least,
|
||||
};
|
||||
});
|
||||
}
|
||||
} else {
|
||||
sess.emit_fatal(errors::CguNotRecorded { cgu_user_name, cgu_name });
|
||||
|
@ -132,7 +132,7 @@ impl<'a, 'tcx, V: CodegenObject> OperandRef<'tcx, V> {
|
||||
offset: Size,
|
||||
) -> Self {
|
||||
let alloc_align = alloc.inner().align;
|
||||
assert_eq!(alloc_align, layout.align.abi);
|
||||
assert!(alloc_align >= layout.align.abi);
|
||||
|
||||
let read_scalar = |start, size, s: abi::Scalar, ty| {
|
||||
match alloc.0.read_scalar(
|
||||
|
@ -15,7 +15,7 @@ fn foo() {}
|
||||
|
||||
mod ambiguous_module; // error: file for module `ambiguous_module`
|
||||
// found at both ambiguous_module.rs and
|
||||
// ambiguous_module.rs/mod.rs
|
||||
// ambiguous_module/mod.rs
|
||||
```
|
||||
|
||||
Please remove this ambiguity by deleting/renaming one of the candidate files.
|
||||
|
@ -174,7 +174,7 @@ pub fn placeholder(
|
||||
}]),
|
||||
AstFragmentKind::Variants => AstFragment::Variants(smallvec![ast::Variant {
|
||||
attrs: Default::default(),
|
||||
data: ast::VariantData::Struct(Default::default(), false),
|
||||
data: ast::VariantData::Struct { fields: Default::default(), recovered: false },
|
||||
disr_expr: None,
|
||||
id,
|
||||
ident,
|
||||
|
@ -3,8 +3,8 @@ use crate::hir;
|
||||
|
||||
use rustc_ast as ast;
|
||||
use rustc_ast::NodeId;
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_data_structures::stable_hasher::ToStableHashKey;
|
||||
use rustc_data_structures::unord::UnordMap;
|
||||
use rustc_macros::HashStable_Generic;
|
||||
use rustc_span::def_id::{DefId, LocalDefId};
|
||||
use rustc_span::hygiene::MacroKind;
|
||||
@ -603,7 +603,7 @@ impl CtorKind {
|
||||
match *vdata {
|
||||
ast::VariantData::Tuple(_, node_id) => Some((CtorKind::Fn, node_id)),
|
||||
ast::VariantData::Unit(node_id) => Some((CtorKind::Const, node_id)),
|
||||
ast::VariantData::Struct(..) => None,
|
||||
ast::VariantData::Struct { .. } => None,
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -806,4 +806,4 @@ pub enum LifetimeRes {
|
||||
ElidedAnchor { start: NodeId, end: NodeId },
|
||||
}
|
||||
|
||||
pub type DocLinkResMap = FxHashMap<(Symbol, Namespace), Option<Res<NodeId>>>;
|
||||
pub type DocLinkResMap = UnordMap<(Symbol, Namespace), Option<Res<NodeId>>>;
|
||||
|
@ -8,8 +8,8 @@ pub use crate::def_id::DefPathHash;
|
||||
use crate::def_id::{CrateNum, DefIndex, LocalDefId, StableCrateId, CRATE_DEF_INDEX, LOCAL_CRATE};
|
||||
use crate::def_path_hash_map::DefPathHashMap;
|
||||
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_data_structures::stable_hasher::{Hash64, StableHasher};
|
||||
use rustc_data_structures::unord::UnordMap;
|
||||
use rustc_index::IndexVec;
|
||||
use rustc_span::symbol::{kw, sym, Symbol};
|
||||
|
||||
@ -95,7 +95,7 @@ impl DefPathTable {
|
||||
#[derive(Debug)]
|
||||
pub struct Definitions {
|
||||
table: DefPathTable,
|
||||
next_disambiguator: FxHashMap<(LocalDefId, DefPathData), u32>,
|
||||
next_disambiguator: UnordMap<(LocalDefId, DefPathData), u32>,
|
||||
|
||||
/// The [StableCrateId] of the local crate.
|
||||
stable_crate_id: StableCrateId,
|
||||
|
@ -1,12 +1,13 @@
|
||||
use crate::def_id::DefId;
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_data_structures::fx::FxIndexMap;
|
||||
use rustc_data_structures::stable_hasher::{HashStable, StableHasher};
|
||||
use rustc_span::def_id::DefIdMap;
|
||||
use rustc_span::Symbol;
|
||||
|
||||
#[derive(Debug, Default)]
|
||||
pub struct DiagnosticItems {
|
||||
pub id_to_name: FxHashMap<DefId, Symbol>,
|
||||
pub name_to_id: FxHashMap<Symbol, DefId>,
|
||||
pub id_to_name: DefIdMap<Symbol>,
|
||||
pub name_to_id: FxIndexMap<Symbol, DefId>,
|
||||
}
|
||||
|
||||
impl<CTX: crate::HashStableContext> HashStable<CTX> for DiagnosticItems {
|
||||
|
@ -1,6 +1,6 @@
|
||||
use crate::def::{CtorKind, DefKind, Res};
|
||||
use crate::def_id::DefId;
|
||||
pub(crate) use crate::hir_id::{HirId, ItemLocalId, OwnerId};
|
||||
use crate::def_id::{DefId, LocalDefIdMap};
|
||||
pub(crate) use crate::hir_id::{HirId, ItemLocalId, ItemLocalMap, OwnerId};
|
||||
use crate::intravisit::FnKind;
|
||||
use crate::LangItem;
|
||||
|
||||
@ -11,7 +11,6 @@ pub use rustc_ast::{BinOp, BinOpKind, BindingAnnotation, BorrowKind, ByRef, Capt
|
||||
pub use rustc_ast::{ImplPolarity, IsAuto, Movability, Mutability, UnOp};
|
||||
use rustc_ast::{InlineAsmOptions, InlineAsmTemplatePiece};
|
||||
use rustc_data_structures::fingerprint::Fingerprint;
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_data_structures::sorted_map::SortedMap;
|
||||
use rustc_index::IndexVec;
|
||||
use rustc_macros::HashStable_Generic;
|
||||
@ -874,12 +873,12 @@ pub struct OwnerInfo<'hir> {
|
||||
/// Contents of the HIR.
|
||||
pub nodes: OwnerNodes<'hir>,
|
||||
/// Map from each nested owner to its parent's local id.
|
||||
pub parenting: FxHashMap<LocalDefId, ItemLocalId>,
|
||||
pub parenting: LocalDefIdMap<ItemLocalId>,
|
||||
/// Collected attributes of the HIR nodes.
|
||||
pub attrs: AttributeMap<'hir>,
|
||||
/// Map indicating what traits are in scope for places where this
|
||||
/// is relevant; generated by resolve.
|
||||
pub trait_map: FxHashMap<ItemLocalId, Box<[TraitCandidate]>>,
|
||||
pub trait_map: ItemLocalMap<Box<[TraitCandidate]>>,
|
||||
}
|
||||
|
||||
impl<'tcx> OwnerInfo<'tcx> {
|
||||
@ -2849,7 +2848,11 @@ pub enum VariantData<'hir> {
|
||||
/// A struct variant.
|
||||
///
|
||||
/// E.g., `Bar { .. }` as in `enum Foo { Bar { .. } }`.
|
||||
Struct(&'hir [FieldDef<'hir>], /* recovered */ bool),
|
||||
Struct {
|
||||
fields: &'hir [FieldDef<'hir>],
|
||||
// FIXME: investigate making this a `Option<ErrorGuaranteed>`
|
||||
recovered: bool,
|
||||
},
|
||||
/// A tuple variant.
|
||||
///
|
||||
/// E.g., `Bar(..)` as in `enum Foo { Bar(..) }`.
|
||||
@ -2864,7 +2867,7 @@ impl<'hir> VariantData<'hir> {
|
||||
/// Return the fields of this variant.
|
||||
pub fn fields(&self) -> &'hir [FieldDef<'hir>] {
|
||||
match *self {
|
||||
VariantData::Struct(ref fields, ..) | VariantData::Tuple(ref fields, ..) => fields,
|
||||
VariantData::Struct { fields, .. } | VariantData::Tuple(fields, ..) => fields,
|
||||
_ => &[],
|
||||
}
|
||||
}
|
||||
@ -2873,7 +2876,7 @@ impl<'hir> VariantData<'hir> {
|
||||
match *self {
|
||||
VariantData::Tuple(_, hir_id, def_id) => Some((CtorKind::Fn, hir_id, def_id)),
|
||||
VariantData::Unit(hir_id, def_id) => Some((CtorKind::Const, hir_id, def_id)),
|
||||
VariantData::Struct(..) => None,
|
||||
VariantData::Struct { .. } => None,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1,7 +1,6 @@
|
||||
use crate::def::{CtorOf, DefKind, Res};
|
||||
use crate::def_id::DefId;
|
||||
use crate::def_id::{DefId, DefIdSet};
|
||||
use crate::hir::{self, BindingAnnotation, ByRef, HirId, PatKind};
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_span::symbol::Ident;
|
||||
use rustc_span::Span;
|
||||
|
||||
@ -114,9 +113,9 @@ impl hir::Pat<'_> {
|
||||
}
|
||||
_ => true,
|
||||
});
|
||||
// We remove duplicates by inserting into a `FxHashSet` to avoid re-ordering
|
||||
// We remove duplicates by inserting into a hash set to avoid re-ordering
|
||||
// the bounds
|
||||
let mut duplicates = FxHashSet::default();
|
||||
let mut duplicates = DefIdSet::default();
|
||||
variants.retain(|def_id| duplicates.insert(*def_id));
|
||||
variants
|
||||
}
|
||||
|
@ -262,7 +262,7 @@ pub fn create_args_for_parent_generic_args<'tcx, 'a>(
|
||||
// impl const PartialEq for () {}
|
||||
// ```
|
||||
//
|
||||
// Since this is a const impl, we need to insert `<false>` at the end of
|
||||
// Since this is a const impl, we need to insert a host arg at the end of
|
||||
// `PartialEq`'s generics, but this errors since `Rhs` isn't specified.
|
||||
// To work around this, we infer all arguments until we reach the host param.
|
||||
args.push(ctx.inferred_kind(Some(&args), param, infer_args));
|
||||
|
@ -814,7 +814,7 @@ fn convert_variant(
|
||||
})
|
||||
.collect();
|
||||
let recovered = match def {
|
||||
hir::VariantData::Struct(_, r) => *r,
|
||||
hir::VariantData::Struct { recovered, .. } => *recovered,
|
||||
_ => false,
|
||||
};
|
||||
ty::VariantDef::new(
|
||||
|
@ -481,7 +481,7 @@ pub(super) fn type_of(tcx: TyCtxt<'_>, def_id: LocalDefId) -> ty::EarlyBinder<Ty
|
||||
},
|
||||
|
||||
Node::Ctor(def) | Node::Variant(Variant { data: def, .. }) => match def {
|
||||
VariantData::Unit(..) | VariantData::Struct(..) => {
|
||||
VariantData::Unit(..) | VariantData::Struct { .. } => {
|
||||
tcx.type_of(tcx.hir().get_parent_item(hir_id)).instantiate_identity()
|
||||
}
|
||||
VariantData::Tuple(..) => {
|
||||
|
@ -741,7 +741,7 @@ impl<'a> State<'a> {
|
||||
self.end();
|
||||
self.end() // close the outer-box
|
||||
}
|
||||
hir::VariantData::Struct(..) => {
|
||||
hir::VariantData::Struct { .. } => {
|
||||
self.print_where_clause(generics);
|
||||
self.nbsp();
|
||||
self.bopen();
|
||||
|
@ -1498,7 +1498,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
|
||||
let ty = self.resolve_vars_with_obligations(ty);
|
||||
|
||||
if self.next_trait_solver()
|
||||
&& let ty::Alias(ty::Projection | ty::Inherent | ty::Weak, _) = ty.kind()
|
||||
&& let ty::Alias(..) = ty.kind()
|
||||
{
|
||||
match self
|
||||
.at(&self.misc(sp), self.param_env)
|
||||
|
@ -9,6 +9,7 @@ use std::slice;
|
||||
use arrayvec::ArrayVec;
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
|
||||
#[cfg(feature = "nightly")]
|
||||
use rustc_macros::{Decodable, Encodable};
|
||||
|
||||
use crate::{Idx, IndexVec};
|
||||
@ -111,7 +112,8 @@ macro_rules! bit_relations_inherent_impls {
|
||||
/// to or greater than the domain size. All operations that involve two bitsets
|
||||
/// will panic if the bitsets have differing domain sizes.
|
||||
///
|
||||
#[derive(Eq, PartialEq, Hash, Decodable, Encodable)]
|
||||
#[cfg_attr(feature = "nightly", derive(Decodable, Encodable))]
|
||||
#[derive(Eq, PartialEq, Hash)]
|
||||
pub struct BitSet<T> {
|
||||
domain_size: usize,
|
||||
words: SmallVec<[Word; 2]>,
|
||||
@ -491,10 +493,21 @@ impl<T: Idx> ChunkedBitSet<T> {
|
||||
match *chunk {
|
||||
Zeros(chunk_domain_size) => {
|
||||
if chunk_domain_size > 1 {
|
||||
// We take some effort to avoid copying the words.
|
||||
let words = Rc::<[Word; CHUNK_WORDS]>::new_zeroed();
|
||||
// SAFETY: `words` can safely be all zeroes.
|
||||
let mut words = unsafe { words.assume_init() };
|
||||
#[cfg(feature = "nightly")]
|
||||
let mut words = {
|
||||
// We take some effort to avoid copying the words.
|
||||
let words = Rc::<[Word; CHUNK_WORDS]>::new_zeroed();
|
||||
// SAFETY: `words` can safely be all zeroes.
|
||||
unsafe { words.assume_init() }
|
||||
};
|
||||
#[cfg(not(feature = "nightly"))]
|
||||
let mut words = {
|
||||
let words = mem::MaybeUninit::<[Word; CHUNK_WORDS]>::zeroed();
|
||||
// SAFETY: `words` can safely be all zeroes.
|
||||
let words = unsafe { words.assume_init() };
|
||||
// Unfortunate possibly-large copy
|
||||
Rc::new(words)
|
||||
};
|
||||
let words_ref = Rc::get_mut(&mut words).unwrap();
|
||||
|
||||
let (word_index, mask) = chunk_word_index_and_mask(elem);
|
||||
@ -545,10 +558,21 @@ impl<T: Idx> ChunkedBitSet<T> {
|
||||
Zeros(_) => false,
|
||||
Ones(chunk_domain_size) => {
|
||||
if chunk_domain_size > 1 {
|
||||
// We take some effort to avoid copying the words.
|
||||
let words = Rc::<[Word; CHUNK_WORDS]>::new_zeroed();
|
||||
// SAFETY: `words` can safely be all zeroes.
|
||||
let mut words = unsafe { words.assume_init() };
|
||||
#[cfg(feature = "nightly")]
|
||||
let mut words = {
|
||||
// We take some effort to avoid copying the words.
|
||||
let words = Rc::<[Word; CHUNK_WORDS]>::new_zeroed();
|
||||
// SAFETY: `words` can safely be all zeroes.
|
||||
unsafe { words.assume_init() }
|
||||
};
|
||||
#[cfg(not(feature = "nightly"))]
|
||||
let mut words = {
|
||||
let words = mem::MaybeUninit::<[Word; CHUNK_WORDS]>::zeroed();
|
||||
// SAFETY: `words` can safely be all zeroes.
|
||||
let words = unsafe { words.assume_init() };
|
||||
// Unfortunate possibly-large copy
|
||||
Rc::new(words)
|
||||
};
|
||||
let words_ref = Rc::get_mut(&mut words).unwrap();
|
||||
|
||||
// Set only the bits in use.
|
||||
@ -1564,7 +1588,8 @@ impl<T: Idx> From<BitSet<T>> for GrowableBitSet<T> {
|
||||
///
|
||||
/// All operations that involve a row and/or column index will panic if the
|
||||
/// index exceeds the relevant bound.
|
||||
#[derive(Clone, Eq, PartialEq, Hash, Decodable, Encodable)]
|
||||
#[cfg_attr(feature = "nightly", derive(Decodable, Encodable))]
|
||||
#[derive(Clone, Eq, PartialEq, Hash)]
|
||||
pub struct BitMatrix<R: Idx, C: Idx> {
|
||||
num_rows: usize,
|
||||
num_columns: usize,
|
||||
@ -1993,7 +2018,8 @@ impl std::fmt::Debug for FiniteBitSet<u32> {
|
||||
|
||||
/// A fixed-sized bitset type represented by an integer type. Indices outwith than the range
|
||||
/// representable by `T` are considered set.
|
||||
#[derive(Copy, Clone, Eq, PartialEq, Decodable, Encodable)]
|
||||
#[cfg_attr(feature = "nightly", derive(Decodable, Encodable))]
|
||||
#[derive(Copy, Clone, Eq, PartialEq)]
|
||||
pub struct FiniteBitSet<T: FiniteBitSetTy>(pub T);
|
||||
|
||||
impl<T: FiniteBitSetTy> FiniteBitSet<T> {
|
||||
|
@ -14,7 +14,6 @@
|
||||
)]
|
||||
#![cfg_attr(feature = "nightly", allow(internal_features))]
|
||||
|
||||
#[cfg(feature = "nightly")]
|
||||
pub mod bit_set;
|
||||
#[cfg(feature = "nightly")]
|
||||
pub mod interval;
|
||||
|
@ -2653,11 +2653,6 @@ impl<'tcx> TypeRelation<'tcx> for SameTypeModuloInfer<'_, 'tcx> {
|
||||
self.0.tcx
|
||||
}
|
||||
|
||||
fn param_env(&self) -> ty::ParamEnv<'tcx> {
|
||||
// Unused, only for consts which we treat as always equal
|
||||
ty::ParamEnv::empty()
|
||||
}
|
||||
|
||||
fn tag(&self) -> &'static str {
|
||||
"SameTypeModuloInfer"
|
||||
}
|
||||
|
@ -31,13 +31,12 @@ use super::outlives::test_type_match;
|
||||
/// all the variables as well as a set of errors that must be reported.
|
||||
#[instrument(level = "debug", skip(region_rels, var_infos, data))]
|
||||
pub(crate) fn resolve<'tcx>(
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
region_rels: &RegionRelations<'_, 'tcx>,
|
||||
var_infos: VarInfos,
|
||||
data: RegionConstraintData<'tcx>,
|
||||
) -> (LexicalRegionResolutions<'tcx>, Vec<RegionResolutionError<'tcx>>) {
|
||||
let mut errors = vec![];
|
||||
let mut resolver = LexicalResolver { param_env, region_rels, var_infos, data };
|
||||
let mut resolver = LexicalResolver { region_rels, var_infos, data };
|
||||
let values = resolver.infer_variable_values(&mut errors);
|
||||
(values, errors)
|
||||
}
|
||||
@ -120,7 +119,6 @@ struct RegionAndOrigin<'tcx> {
|
||||
type RegionGraph<'tcx> = Graph<(), Constraint<'tcx>>;
|
||||
|
||||
struct LexicalResolver<'cx, 'tcx> {
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
region_rels: &'cx RegionRelations<'cx, 'tcx>,
|
||||
var_infos: VarInfos,
|
||||
data: RegionConstraintData<'tcx>,
|
||||
@ -914,12 +912,8 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
|
||||
match bound {
|
||||
VerifyBound::IfEq(verify_if_eq_b) => {
|
||||
let verify_if_eq_b = var_values.normalize(self.region_rels.tcx, *verify_if_eq_b);
|
||||
match test_type_match::extract_verify_if_eq(
|
||||
self.tcx(),
|
||||
self.param_env,
|
||||
&verify_if_eq_b,
|
||||
generic_ty,
|
||||
) {
|
||||
match test_type_match::extract_verify_if_eq(self.tcx(), &verify_if_eq_b, generic_ty)
|
||||
{
|
||||
Some(r) => {
|
||||
self.bound_is_met(&VerifyBound::OutlivedBy(r), var_values, generic_ty, min)
|
||||
}
|
||||
|
@ -84,7 +84,6 @@ where
|
||||
} else {
|
||||
test_type_match::extract_verify_if_eq(
|
||||
tcx,
|
||||
param_env,
|
||||
&outlives.map_bound(|ty::OutlivesPredicate(ty, bound)| {
|
||||
VerifyIfEq { ty, bound }
|
||||
}),
|
||||
|
@ -67,7 +67,7 @@ impl<'tcx> InferCtxt<'tcx> {
|
||||
let region_rels = &RegionRelations::new(self.tcx, outlives_env.free_region_map());
|
||||
|
||||
let (lexical_region_resolutions, errors) =
|
||||
lexical_region_resolve::resolve(outlives_env.param_env, region_rels, var_infos, data);
|
||||
lexical_region_resolve::resolve(region_rels, var_infos, data);
|
||||
|
||||
let old_value = self.lexical_region_resolutions.replace(Some(lexical_region_resolutions));
|
||||
assert!(old_value.is_none());
|
||||
|
@ -36,15 +36,14 @@ use crate::infer::region_constraints::VerifyIfEq;
|
||||
/// like are used. This is a particular challenge since this function is invoked
|
||||
/// very late in inference and hence cannot make use of the normal inference
|
||||
/// machinery.
|
||||
#[instrument(level = "debug", skip(tcx, param_env))]
|
||||
#[instrument(level = "debug", skip(tcx))]
|
||||
pub fn extract_verify_if_eq<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
verify_if_eq_b: &ty::Binder<'tcx, VerifyIfEq<'tcx>>,
|
||||
test_ty: Ty<'tcx>,
|
||||
) -> Option<ty::Region<'tcx>> {
|
||||
assert!(!verify_if_eq_b.has_escaping_bound_vars());
|
||||
let mut m = MatchAgainstHigherRankedOutlives::new(tcx, param_env);
|
||||
let mut m = MatchAgainstHigherRankedOutlives::new(tcx);
|
||||
let verify_if_eq = verify_if_eq_b.skip_binder();
|
||||
m.relate(verify_if_eq.ty, test_ty).ok()?;
|
||||
|
||||
@ -73,10 +72,9 @@ pub fn extract_verify_if_eq<'tcx>(
|
||||
}
|
||||
|
||||
/// True if a (potentially higher-ranked) outlives
|
||||
#[instrument(level = "debug", skip(tcx, param_env))]
|
||||
#[instrument(level = "debug", skip(tcx))]
|
||||
pub(super) fn can_match_erased_ty<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
outlives_predicate: ty::Binder<'tcx, ty::TypeOutlivesPredicate<'tcx>>,
|
||||
erased_ty: Ty<'tcx>,
|
||||
) -> bool {
|
||||
@ -87,25 +85,20 @@ pub(super) fn can_match_erased_ty<'tcx>(
|
||||
// pointless micro-optimization
|
||||
true
|
||||
} else {
|
||||
MatchAgainstHigherRankedOutlives::new(tcx, param_env).relate(outlives_ty, erased_ty).is_ok()
|
||||
MatchAgainstHigherRankedOutlives::new(tcx).relate(outlives_ty, erased_ty).is_ok()
|
||||
}
|
||||
}
|
||||
|
||||
struct MatchAgainstHigherRankedOutlives<'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
pattern_depth: ty::DebruijnIndex,
|
||||
map: FxHashMap<ty::BoundRegion, ty::Region<'tcx>>,
|
||||
}
|
||||
|
||||
impl<'tcx> MatchAgainstHigherRankedOutlives<'tcx> {
|
||||
fn new(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
) -> MatchAgainstHigherRankedOutlives<'tcx> {
|
||||
fn new(tcx: TyCtxt<'tcx>) -> MatchAgainstHigherRankedOutlives<'tcx> {
|
||||
MatchAgainstHigherRankedOutlives {
|
||||
tcx,
|
||||
param_env,
|
||||
pattern_depth: ty::INNERMOST,
|
||||
map: FxHashMap::default(),
|
||||
}
|
||||
@ -144,15 +137,13 @@ impl<'tcx> MatchAgainstHigherRankedOutlives<'tcx> {
|
||||
|
||||
impl<'tcx> TypeRelation<'tcx> for MatchAgainstHigherRankedOutlives<'tcx> {
|
||||
fn tag(&self) -> &'static str {
|
||||
"Match"
|
||||
"MatchAgainstHigherRankedOutlives"
|
||||
}
|
||||
|
||||
fn tcx(&self) -> TyCtxt<'tcx> {
|
||||
self.tcx
|
||||
}
|
||||
fn param_env(&self) -> ty::ParamEnv<'tcx> {
|
||||
self.param_env
|
||||
}
|
||||
|
||||
fn a_is_expected(&self) -> bool {
|
||||
true
|
||||
} // irrelevant
|
||||
|
@ -1,7 +1,7 @@
|
||||
use crate::infer::outlives::components::{compute_alias_components_recursive, Component};
|
||||
use crate::infer::outlives::env::RegionBoundPairs;
|
||||
use crate::infer::region_constraints::VerifyIfEq;
|
||||
use crate::infer::VerifyBound;
|
||||
use crate::infer::{GenericKind, VerifyBound};
|
||||
use rustc_data_structures::sso::SsoHashSet;
|
||||
use rustc_middle::ty::GenericArg;
|
||||
use rustc_middle::ty::{self, OutlivesPredicate, Ty, TyCtxt};
|
||||
@ -240,10 +240,20 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
|
||||
"declared_generic_bounds_from_env_for_erased_ty: region_bound_pair = {:?}",
|
||||
(r, p)
|
||||
);
|
||||
// Fast path for the common case.
|
||||
match (&p, erased_ty.kind()) {
|
||||
// In outlive routines, all types are expected to be fully normalized.
|
||||
// And therefore we can safely use structural equality for alias types.
|
||||
(GenericKind::Param(p1), ty::Param(p2)) if p1 == p2 => {}
|
||||
(GenericKind::Placeholder(p1), ty::Placeholder(p2)) if p1 == p2 => {}
|
||||
(GenericKind::Alias(a1), ty::Alias(_, a2)) if a1.def_id == a2.def_id => {}
|
||||
_ => return None,
|
||||
}
|
||||
|
||||
let p_ty = p.to_ty(tcx);
|
||||
let erased_p_ty = self.tcx.erase_regions(p_ty);
|
||||
(erased_p_ty == erased_ty)
|
||||
.then_some(ty::Binder::dummy(ty::OutlivesPredicate(p.to_ty(tcx), r)))
|
||||
.then_some(ty::Binder::dummy(ty::OutlivesPredicate(p_ty, r)))
|
||||
});
|
||||
|
||||
param_bounds
|
||||
@ -312,14 +322,8 @@ impl<'cx, 'tcx> VerifyBoundCx<'cx, 'tcx> {
|
||||
) -> impl Iterator<Item = ty::Binder<'tcx, ty::OutlivesPredicate<Ty<'tcx>, ty::Region<'tcx>>>>
|
||||
{
|
||||
let tcx = self.tcx;
|
||||
let param_env = self.param_env;
|
||||
clauses.filter_map(|p| p.as_type_outlives_clause()).filter(move |outlives_predicate| {
|
||||
super::test_type_match::can_match_erased_ty(
|
||||
tcx,
|
||||
param_env,
|
||||
*outlives_predicate,
|
||||
erased_ty,
|
||||
)
|
||||
super::test_type_match::can_match_erased_ty(tcx, *outlives_predicate, erased_ty)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
@ -563,6 +563,8 @@ impl<'infcx, 'tcx> CombineFields<'infcx, 'tcx> {
|
||||
}
|
||||
|
||||
pub trait ObligationEmittingRelation<'tcx>: TypeRelation<'tcx> {
|
||||
fn param_env(&self) -> ty::ParamEnv<'tcx>;
|
||||
|
||||
/// Register obligations that must hold in order for this relation to hold
|
||||
fn register_obligations(&mut self, obligations: PredicateObligations<'tcx>);
|
||||
|
||||
|
@ -33,10 +33,6 @@ impl<'tcx> TypeRelation<'tcx> for Equate<'_, '_, 'tcx> {
|
||||
self.fields.tcx()
|
||||
}
|
||||
|
||||
fn param_env(&self) -> ty::ParamEnv<'tcx> {
|
||||
self.fields.param_env
|
||||
}
|
||||
|
||||
fn a_is_expected(&self) -> bool {
|
||||
self.a_is_expected
|
||||
}
|
||||
@ -174,6 +170,10 @@ impl<'tcx> TypeRelation<'tcx> for Equate<'_, '_, 'tcx> {
|
||||
}
|
||||
|
||||
impl<'tcx> ObligationEmittingRelation<'tcx> for Equate<'_, '_, 'tcx> {
|
||||
fn param_env(&self) -> ty::ParamEnv<'tcx> {
|
||||
self.fields.param_env
|
||||
}
|
||||
|
||||
fn register_predicates(&mut self, obligations: impl IntoIterator<Item: ty::ToPredicate<'tcx>>) {
|
||||
self.fields.register_predicates(obligations);
|
||||
}
|
||||
|
@ -182,10 +182,6 @@ where
|
||||
self.infcx.tcx
|
||||
}
|
||||
|
||||
fn param_env(&self) -> ty::ParamEnv<'tcx> {
|
||||
self.delegate.param_env()
|
||||
}
|
||||
|
||||
fn tag(&self) -> &'static str {
|
||||
"Generalizer"
|
||||
}
|
||||
|
@ -32,10 +32,6 @@ impl<'tcx> TypeRelation<'tcx> for Glb<'_, '_, 'tcx> {
|
||||
self.fields.tcx()
|
||||
}
|
||||
|
||||
fn param_env(&self) -> ty::ParamEnv<'tcx> {
|
||||
self.fields.param_env
|
||||
}
|
||||
|
||||
fn a_is_expected(&self) -> bool {
|
||||
self.a_is_expected
|
||||
}
|
||||
@ -138,6 +134,10 @@ impl<'combine, 'infcx, 'tcx> LatticeDir<'infcx, 'tcx> for Glb<'combine, 'infcx,
|
||||
}
|
||||
|
||||
impl<'tcx> ObligationEmittingRelation<'tcx> for Glb<'_, '_, 'tcx> {
|
||||
fn param_env(&self) -> ty::ParamEnv<'tcx> {
|
||||
self.fields.param_env
|
||||
}
|
||||
|
||||
fn register_predicates(&mut self, obligations: impl IntoIterator<Item: ty::ToPredicate<'tcx>>) {
|
||||
self.fields.register_predicates(obligations);
|
||||
}
|
||||
|
@ -32,10 +32,6 @@ impl<'tcx> TypeRelation<'tcx> for Lub<'_, '_, 'tcx> {
|
||||
self.fields.tcx()
|
||||
}
|
||||
|
||||
fn param_env(&self) -> ty::ParamEnv<'tcx> {
|
||||
self.fields.param_env
|
||||
}
|
||||
|
||||
fn a_is_expected(&self) -> bool {
|
||||
self.a_is_expected
|
||||
}
|
||||
@ -138,6 +134,10 @@ impl<'combine, 'infcx, 'tcx> LatticeDir<'infcx, 'tcx> for Lub<'combine, 'infcx,
|
||||
}
|
||||
|
||||
impl<'tcx> ObligationEmittingRelation<'tcx> for Lub<'_, '_, 'tcx> {
|
||||
fn param_env(&self) -> ty::ParamEnv<'tcx> {
|
||||
self.fields.param_env
|
||||
}
|
||||
|
||||
fn register_predicates(&mut self, obligations: impl IntoIterator<Item: ty::ToPredicate<'tcx>>) {
|
||||
self.fields.register_predicates(obligations);
|
||||
}
|
||||
|
@ -431,10 +431,6 @@ where
|
||||
self.infcx.tcx
|
||||
}
|
||||
|
||||
fn param_env(&self) -> ty::ParamEnv<'tcx> {
|
||||
self.delegate.param_env()
|
||||
}
|
||||
|
||||
fn tag(&self) -> &'static str {
|
||||
"nll::subtype"
|
||||
}
|
||||
@ -670,6 +666,10 @@ impl<'tcx, D> ObligationEmittingRelation<'tcx> for TypeRelating<'_, 'tcx, D>
|
||||
where
|
||||
D: TypeRelatingDelegate<'tcx>,
|
||||
{
|
||||
fn param_env(&self) -> ty::ParamEnv<'tcx> {
|
||||
self.delegate.param_env()
|
||||
}
|
||||
|
||||
fn register_predicates(&mut self, obligations: impl IntoIterator<Item: ty::ToPredicate<'tcx>>) {
|
||||
self.delegate.register_obligations(
|
||||
obligations
|
||||
|
@ -39,10 +39,6 @@ impl<'tcx> TypeRelation<'tcx> for Sub<'_, '_, 'tcx> {
|
||||
self.fields.infcx.tcx
|
||||
}
|
||||
|
||||
fn param_env(&self) -> ty::ParamEnv<'tcx> {
|
||||
self.fields.param_env
|
||||
}
|
||||
|
||||
fn a_is_expected(&self) -> bool {
|
||||
self.a_is_expected
|
||||
}
|
||||
@ -203,6 +199,10 @@ impl<'tcx> TypeRelation<'tcx> for Sub<'_, '_, 'tcx> {
|
||||
}
|
||||
|
||||
impl<'tcx> ObligationEmittingRelation<'tcx> for Sub<'_, '_, 'tcx> {
|
||||
fn param_env(&self) -> ty::ParamEnv<'tcx> {
|
||||
self.fields.param_env
|
||||
}
|
||||
|
||||
fn register_predicates(&mut self, obligations: impl IntoIterator<Item: ty::ToPredicate<'tcx>>) {
|
||||
self.fields.register_predicates(obligations);
|
||||
}
|
||||
|
@ -12,7 +12,7 @@ use rustc_data_structures::unhash::UnhashMap;
|
||||
use rustc_expand::base::{SyntaxExtension, SyntaxExtensionKind};
|
||||
use rustc_expand::proc_macro::{AttrProcMacro, BangProcMacro, DeriveProcMacro};
|
||||
use rustc_hir::def::Res;
|
||||
use rustc_hir::def_id::{CRATE_DEF_INDEX, LOCAL_CRATE};
|
||||
use rustc_hir::def_id::{DefIdMap, CRATE_DEF_INDEX, LOCAL_CRATE};
|
||||
use rustc_hir::definitions::{DefPath, DefPathData};
|
||||
use rustc_hir::diagnostic_items::DiagnosticItems;
|
||||
use rustc_index::Idx;
|
||||
@ -1200,7 +1200,7 @@ impl<'a, 'tcx> CrateMetadataRef<'a> {
|
||||
|
||||
/// Iterates over the diagnostic items in the given crate.
|
||||
fn get_diagnostic_items(self) -> DiagnosticItems {
|
||||
let mut id_to_name = FxHashMap::default();
|
||||
let mut id_to_name = DefIdMap::default();
|
||||
let name_to_id = self
|
||||
.root
|
||||
.diagnostic_items
|
||||
|
@ -69,7 +69,7 @@ use rustc_hir::def_id::{
|
||||
CrateNum, DefId, DefIdMap, DefIdSet, LocalDefId, LocalDefIdMap, LocalDefIdSet, LocalModDefId,
|
||||
};
|
||||
use rustc_hir::lang_items::{LangItem, LanguageItems};
|
||||
use rustc_hir::{Crate, ItemLocalId, TraitCandidate};
|
||||
use rustc_hir::{Crate, ItemLocalId, ItemLocalMap, TraitCandidate};
|
||||
use rustc_index::IndexVec;
|
||||
use rustc_query_system::ich::StableHashingContext;
|
||||
use rustc_query_system::query::{try_get_cached, CacheSelector, QueryCache, QueryMode, QueryState};
|
||||
@ -1490,7 +1490,7 @@ rustc_queries! {
|
||||
desc { "computing whether impls specialize one another" }
|
||||
}
|
||||
query in_scope_traits_map(_: hir::OwnerId)
|
||||
-> Option<&'tcx FxHashMap<ItemLocalId, Box<[TraitCandidate]>>> {
|
||||
-> Option<&'tcx ItemLocalMap<Box<[TraitCandidate]>>> {
|
||||
desc { "getting traits in scope at a block" }
|
||||
}
|
||||
|
||||
|
@ -233,6 +233,27 @@ impl<'tcx> TypeVisitable<TyCtxt<'tcx>> for PredefinedOpaques<'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Why a specific goal has to be proven.
|
||||
///
|
||||
/// This is necessary as we treat nested goals different depending on
|
||||
/// their source.
|
||||
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
|
||||
pub enum GoalSource {
|
||||
Misc,
|
||||
/// We're proving a where-bound of an impl.
|
||||
///
|
||||
/// FIXME(-Znext-solver=coinductive): Explain how and why this
|
||||
/// changes whether cycles are coinductive.
|
||||
///
|
||||
/// This also impacts whether we erase constraints on overflow.
|
||||
/// Erasing constraints is generally very useful for perf and also
|
||||
/// results in better error messages by avoiding spurious errors.
|
||||
/// We do not erase overflow constraints in `normalizes-to` goals unless
|
||||
/// they are from an impl where-clause. This is necessary due to
|
||||
/// backwards compatability, cc trait-system-refactor-initiatitive#70.
|
||||
ImplWhereBound,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash, HashStable)]
|
||||
pub enum IsNormalizesToHack {
|
||||
Yes,
|
||||
|
@ -19,8 +19,8 @@
|
||||
//! [canonicalized]: https://rustc-dev-guide.rust-lang.org/solve/canonicalization.html
|
||||
|
||||
use super::{
|
||||
CandidateSource, Canonical, CanonicalInput, Certainty, Goal, IsNormalizesToHack, NoSolution,
|
||||
QueryInput, QueryResult,
|
||||
CandidateSource, Canonical, CanonicalInput, Certainty, Goal, GoalSource, IsNormalizesToHack,
|
||||
NoSolution, QueryInput, QueryResult,
|
||||
};
|
||||
use crate::{infer::canonical::CanonicalVarValues, ty};
|
||||
use format::ProofTreeFormatter;
|
||||
@ -115,7 +115,7 @@ impl Debug for Probe<'_> {
|
||||
pub enum ProbeStep<'tcx> {
|
||||
/// We added a goal to the `EvalCtxt` which will get proven
|
||||
/// the next time `EvalCtxt::try_evaluate_added_goals` is called.
|
||||
AddGoal(CanonicalState<'tcx, Goal<'tcx, ty::Predicate<'tcx>>>),
|
||||
AddGoal(GoalSource, CanonicalState<'tcx, Goal<'tcx, ty::Predicate<'tcx>>>),
|
||||
/// The inside of a `EvalCtxt::try_evaluate_added_goals` call.
|
||||
EvaluateGoals(AddedGoalsEvaluation<'tcx>),
|
||||
/// A call to `probe` while proving the current goal. This is
|
||||
|
@ -123,7 +123,13 @@ impl<'a, 'b> ProofTreeFormatter<'a, 'b> {
|
||||
self.nested(|this| {
|
||||
for step in &probe.steps {
|
||||
match step {
|
||||
ProbeStep::AddGoal(goal) => writeln!(this.f, "ADDED GOAL: {goal:?}")?,
|
||||
ProbeStep::AddGoal(source, goal) => {
|
||||
let source = match source {
|
||||
GoalSource::Misc => "misc",
|
||||
GoalSource::ImplWhereBound => "impl where-bound",
|
||||
};
|
||||
writeln!(this.f, "ADDED GOAL ({source}): {goal:?}")?
|
||||
}
|
||||
ProbeStep::EvaluateGoals(eval) => this.format_added_goals_evaluation(eval)?,
|
||||
ProbeStep::NestedProbe(probe) => this.format_probe(probe)?,
|
||||
ProbeStep::CommitIfOkStart => writeln!(this.f, "COMMIT_IF_OK START")?,
|
||||
|
@ -20,12 +20,11 @@ use crate::ty::{self, InferConst, Ty, TyCtxt};
|
||||
/// affects any type variables or unification state.
|
||||
pub struct MatchAgainstFreshVars<'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
param_env: ty::ParamEnv<'tcx>,
|
||||
}
|
||||
|
||||
impl<'tcx> MatchAgainstFreshVars<'tcx> {
|
||||
pub fn new(tcx: TyCtxt<'tcx>, param_env: ty::ParamEnv<'tcx>) -> MatchAgainstFreshVars<'tcx> {
|
||||
MatchAgainstFreshVars { tcx, param_env }
|
||||
pub fn new(tcx: TyCtxt<'tcx>) -> MatchAgainstFreshVars<'tcx> {
|
||||
MatchAgainstFreshVars { tcx }
|
||||
}
|
||||
}
|
||||
|
||||
@ -33,13 +32,11 @@ impl<'tcx> TypeRelation<'tcx> for MatchAgainstFreshVars<'tcx> {
|
||||
fn tag(&self) -> &'static str {
|
||||
"MatchAgainstFreshVars"
|
||||
}
|
||||
|
||||
fn tcx(&self) -> TyCtxt<'tcx> {
|
||||
self.tcx
|
||||
}
|
||||
|
||||
fn param_env(&self) -> ty::ParamEnv<'tcx> {
|
||||
self.param_env
|
||||
}
|
||||
fn a_is_expected(&self) -> bool {
|
||||
true
|
||||
} // irrelevant
|
||||
|
@ -1039,17 +1039,34 @@ impl<'tcx> TyCtxtAt<'tcx> {
|
||||
// This is fine because:
|
||||
// - those queries are `eval_always` so we won't miss their result changing;
|
||||
// - this write will have happened before these queries are called.
|
||||
let data = def_kind.def_path_data(name);
|
||||
let key = self.untracked.definitions.write().create_def(parent, data);
|
||||
let def_id = self.tcx.create_def(parent, name, def_kind);
|
||||
|
||||
let feed = TyCtxtFeed { tcx: self.tcx, key };
|
||||
feed.def_kind(def_kind);
|
||||
let feed = self.tcx.feed_local_def_id(def_id);
|
||||
feed.def_span(self.span);
|
||||
feed
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> TyCtxt<'tcx> {
|
||||
/// `tcx`-dependent operations performed for every created definition.
|
||||
pub fn create_def(self, parent: LocalDefId, name: Symbol, def_kind: DefKind) -> LocalDefId {
|
||||
let data = def_kind.def_path_data(name);
|
||||
let def_id = self.untracked.definitions.write().create_def(parent, data);
|
||||
|
||||
let feed = self.feed_local_def_id(def_id);
|
||||
feed.def_kind(def_kind);
|
||||
// Unique types created for closures participate in type privacy checking.
|
||||
// They have visibilities inherited from the module they are defined in.
|
||||
// Visibilities for opaque types are meaningless, but still provided
|
||||
// so that all items have visibilities.
|
||||
if matches!(def_kind, DefKind::Closure | DefKind::OpaqueTy) {
|
||||
let parent_mod = self.parent_module_from_def_id(def_id).to_def_id();
|
||||
feed.visibility(ty::Visibility::Restricted(parent_mod));
|
||||
}
|
||||
|
||||
def_id
|
||||
}
|
||||
|
||||
pub fn iter_local_def_id(self) -> impl Iterator<Item = LocalDefId> + 'tcx {
|
||||
// Create a dependency to the red node to be sure we re-execute this when the amount of
|
||||
// definitions change.
|
||||
|
@ -192,7 +192,7 @@ pub struct ResolverAstLowering {
|
||||
|
||||
pub next_node_id: ast::NodeId,
|
||||
|
||||
pub node_id_to_def_id: FxHashMap<ast::NodeId, LocalDefId>,
|
||||
pub node_id_to_def_id: NodeMap<LocalDefId>,
|
||||
pub def_id_to_node_id: IndexVec<LocalDefId, ast::NodeId>,
|
||||
|
||||
pub trait_map: NodeMap<Vec<hir::TraitCandidate>>,
|
||||
|
@ -23,8 +23,6 @@ pub enum Cause {
|
||||
pub trait TypeRelation<'tcx>: Sized {
|
||||
fn tcx(&self) -> TyCtxt<'tcx>;
|
||||
|
||||
fn param_env(&self) -> ty::ParamEnv<'tcx>;
|
||||
|
||||
/// Returns a static string we can use for printouts.
|
||||
fn tag(&self) -> &'static str;
|
||||
|
||||
@ -505,13 +503,9 @@ pub fn structurally_relate_tys<'tcx, R: TypeRelation<'tcx>>(
|
||||
Err(err) => {
|
||||
// Check whether the lengths are both concrete/known values,
|
||||
// but are unequal, for better diagnostics.
|
||||
//
|
||||
// It might seem dubious to eagerly evaluate these constants here,
|
||||
// we however cannot end up with errors in `Relate` during both
|
||||
// `type_of` and `predicates_of`. This means that evaluating the
|
||||
// constants should not cause cycle errors here.
|
||||
let sz_a = sz_a.try_eval_target_usize(tcx, relation.param_env());
|
||||
let sz_b = sz_b.try_eval_target_usize(tcx, relation.param_env());
|
||||
let sz_a = sz_a.try_to_target_usize(tcx);
|
||||
let sz_b = sz_b.try_to_target_usize(tcx);
|
||||
|
||||
match (sz_a, sz_b) {
|
||||
(Some(sz_a_val), Some(sz_b_val)) if sz_a_val != sz_b_val => Err(
|
||||
TypeError::FixedArraySize(expected_found(relation, sz_a_val, sz_b_val)),
|
||||
|
@ -1699,59 +1699,51 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
debug!("tested_candidates: {}", total_candidate_count - candidates.len());
|
||||
debug!("untested_candidates: {}", candidates.len());
|
||||
|
||||
// HACK(matthewjasper) This is a closure so that we can let the test
|
||||
// create its blocks before the rest of the match. This currently
|
||||
// improves the speed of llvm when optimizing long string literal
|
||||
// matches
|
||||
let make_target_blocks = move |this: &mut Self| -> Vec<BasicBlock> {
|
||||
// The block that we should branch to if none of the
|
||||
// `target_candidates` match. This is either the block where we
|
||||
// start matching the untested candidates if there are any,
|
||||
// otherwise it's the `otherwise_block`.
|
||||
let remainder_start = &mut None;
|
||||
let remainder_start =
|
||||
if candidates.is_empty() { &mut *otherwise_block } else { remainder_start };
|
||||
// The block that we should branch to if none of the
|
||||
// `target_candidates` match. This is either the block where we
|
||||
// start matching the untested candidates if there are any,
|
||||
// otherwise it's the `otherwise_block`.
|
||||
let remainder_start = &mut None;
|
||||
let remainder_start =
|
||||
if candidates.is_empty() { &mut *otherwise_block } else { remainder_start };
|
||||
|
||||
// For each outcome of test, process the candidates that still
|
||||
// apply. Collect a list of blocks where control flow will
|
||||
// branch if one of the `target_candidate` sets is not
|
||||
// exhaustive.
|
||||
let target_blocks: Vec<_> = target_candidates
|
||||
.into_iter()
|
||||
.map(|mut candidates| {
|
||||
if !candidates.is_empty() {
|
||||
let candidate_start = this.cfg.start_new_block();
|
||||
this.match_candidates(
|
||||
span,
|
||||
scrutinee_span,
|
||||
candidate_start,
|
||||
remainder_start,
|
||||
&mut *candidates,
|
||||
fake_borrows,
|
||||
);
|
||||
candidate_start
|
||||
} else {
|
||||
*remainder_start.get_or_insert_with(|| this.cfg.start_new_block())
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
// For each outcome of test, process the candidates that still
|
||||
// apply. Collect a list of blocks where control flow will
|
||||
// branch if one of the `target_candidate` sets is not
|
||||
// exhaustive.
|
||||
let target_blocks: Vec<_> = target_candidates
|
||||
.into_iter()
|
||||
.map(|mut candidates| {
|
||||
if !candidates.is_empty() {
|
||||
let candidate_start = self.cfg.start_new_block();
|
||||
self.match_candidates(
|
||||
span,
|
||||
scrutinee_span,
|
||||
candidate_start,
|
||||
remainder_start,
|
||||
&mut *candidates,
|
||||
fake_borrows,
|
||||
);
|
||||
candidate_start
|
||||
} else {
|
||||
*remainder_start.get_or_insert_with(|| self.cfg.start_new_block())
|
||||
}
|
||||
})
|
||||
.collect();
|
||||
|
||||
if !candidates.is_empty() {
|
||||
let remainder_start = remainder_start.unwrap_or_else(|| this.cfg.start_new_block());
|
||||
this.match_candidates(
|
||||
span,
|
||||
scrutinee_span,
|
||||
remainder_start,
|
||||
otherwise_block,
|
||||
candidates,
|
||||
fake_borrows,
|
||||
);
|
||||
};
|
||||
if !candidates.is_empty() {
|
||||
let remainder_start = remainder_start.unwrap_or_else(|| self.cfg.start_new_block());
|
||||
self.match_candidates(
|
||||
span,
|
||||
scrutinee_span,
|
||||
remainder_start,
|
||||
otherwise_block,
|
||||
candidates,
|
||||
fake_borrows,
|
||||
);
|
||||
}
|
||||
|
||||
target_blocks
|
||||
};
|
||||
|
||||
self.perform_test(span, scrutinee_span, block, &match_place, &test, make_target_blocks);
|
||||
self.perform_test(span, scrutinee_span, block, &match_place, &test, target_blocks);
|
||||
}
|
||||
|
||||
/// Determine the fake borrows that are needed from a set of places that
|
||||
|
@ -147,7 +147,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
#[instrument(skip(self, make_target_blocks, place_builder), level = "debug")]
|
||||
#[instrument(skip(self, target_blocks, place_builder), level = "debug")]
|
||||
pub(super) fn perform_test(
|
||||
&mut self,
|
||||
match_start_span: Span,
|
||||
@ -155,7 +155,7 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
block: BasicBlock,
|
||||
place_builder: &PlaceBuilder<'tcx>,
|
||||
test: &Test<'tcx>,
|
||||
make_target_blocks: impl FnOnce(&mut Self) -> Vec<BasicBlock>,
|
||||
target_blocks: Vec<BasicBlock>,
|
||||
) {
|
||||
let place = place_builder.to_place(self);
|
||||
let place_ty = place.ty(&self.local_decls, self.tcx);
|
||||
@ -164,7 +164,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
let source_info = self.source_info(test.span);
|
||||
match test.kind {
|
||||
TestKind::Switch { adt_def, ref variants } => {
|
||||
let target_blocks = make_target_blocks(self);
|
||||
// Variants is a BitVec of indexes into adt_def.variants.
|
||||
let num_enum_variants = adt_def.variants().len();
|
||||
debug_assert_eq!(target_blocks.len(), num_enum_variants + 1);
|
||||
@ -210,7 +209,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
}
|
||||
|
||||
TestKind::SwitchInt { switch_ty, ref options } => {
|
||||
let target_blocks = make_target_blocks(self);
|
||||
let terminator = if *switch_ty.kind() == ty::Bool {
|
||||
assert!(!options.is_empty() && options.len() <= 2);
|
||||
let [first_bb, second_bb] = *target_blocks else {
|
||||
@ -240,6 +238,9 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
|
||||
TestKind::Eq { value, ty } => {
|
||||
let tcx = self.tcx;
|
||||
let [success_block, fail_block] = *target_blocks else {
|
||||
bug!("`TestKind::Eq` should have two target blocks")
|
||||
};
|
||||
if let ty::Adt(def, _) = ty.kind()
|
||||
&& Some(def.did()) == tcx.lang_items().string()
|
||||
{
|
||||
@ -280,38 +281,43 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
);
|
||||
self.non_scalar_compare(
|
||||
eq_block,
|
||||
make_target_blocks,
|
||||
success_block,
|
||||
fail_block,
|
||||
source_info,
|
||||
value,
|
||||
ref_str,
|
||||
ref_str_ty,
|
||||
);
|
||||
return;
|
||||
}
|
||||
if !ty.is_scalar() {
|
||||
} else if !ty.is_scalar() {
|
||||
// Use `PartialEq::eq` instead of `BinOp::Eq`
|
||||
// (the binop can only handle primitives)
|
||||
self.non_scalar_compare(
|
||||
block,
|
||||
make_target_blocks,
|
||||
success_block,
|
||||
fail_block,
|
||||
source_info,
|
||||
value,
|
||||
place,
|
||||
ty,
|
||||
);
|
||||
} else if let [success, fail] = *make_target_blocks(self) {
|
||||
} else {
|
||||
assert_eq!(value.ty(), ty);
|
||||
let expect = self.literal_operand(test.span, value);
|
||||
let val = Operand::Copy(place);
|
||||
self.compare(block, success, fail, source_info, BinOp::Eq, expect, val);
|
||||
} else {
|
||||
bug!("`TestKind::Eq` should have two target blocks");
|
||||
self.compare(
|
||||
block,
|
||||
success_block,
|
||||
fail_block,
|
||||
source_info,
|
||||
BinOp::Eq,
|
||||
expect,
|
||||
val,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
TestKind::Range(ref range) => {
|
||||
let lower_bound_success = self.cfg.start_new_block();
|
||||
let target_blocks = make_target_blocks(self);
|
||||
|
||||
// Test `val` by computing `lo <= val && val <= hi`, using primitive comparisons.
|
||||
// FIXME: skip useless comparison when the range is half-open.
|
||||
@ -341,8 +347,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
}
|
||||
|
||||
TestKind::Len { len, op } => {
|
||||
let target_blocks = make_target_blocks(self);
|
||||
|
||||
let usize_ty = self.tcx.types.usize;
|
||||
let actual = self.temp(usize_ty, test.span);
|
||||
|
||||
@ -406,7 +410,8 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
fn non_scalar_compare(
|
||||
&mut self,
|
||||
block: BasicBlock,
|
||||
make_target_blocks: impl FnOnce(&mut Self) -> Vec<BasicBlock>,
|
||||
success_block: BasicBlock,
|
||||
fail_block: BasicBlock,
|
||||
source_info: SourceInfo,
|
||||
value: Const<'tcx>,
|
||||
mut val: Place<'tcx>,
|
||||
@ -531,9 +536,6 @@ impl<'a, 'tcx> Builder<'a, 'tcx> {
|
||||
);
|
||||
self.diverge_from(block);
|
||||
|
||||
let [success_block, fail_block] = *make_target_blocks(self) else {
|
||||
bug!("`TestKind::Eq` should have two target blocks")
|
||||
};
|
||||
// check the result
|
||||
self.cfg.terminate(
|
||||
eq_block,
|
||||
|
@ -6,7 +6,7 @@ use rustc_errors::{
|
||||
};
|
||||
use rustc_macros::{Diagnostic, LintDiagnostic, Subdiagnostic};
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_pattern_analysis::{cx::MatchCheckCtxt, errors::Uncovered};
|
||||
use rustc_pattern_analysis::{errors::Uncovered, rustc::RustcMatchCheckCtxt};
|
||||
use rustc_span::symbol::Symbol;
|
||||
use rustc_span::Span;
|
||||
|
||||
@ -454,7 +454,7 @@ pub enum UnusedUnsafeEnclosing {
|
||||
}
|
||||
|
||||
pub(crate) struct NonExhaustivePatternsTypeNotEmpty<'p, 'tcx, 'm> {
|
||||
pub cx: &'m MatchCheckCtxt<'p, 'tcx>,
|
||||
pub cx: &'m RustcMatchCheckCtxt<'p, 'tcx>,
|
||||
pub expr_span: Span,
|
||||
pub span: Span,
|
||||
pub ty: Ty<'tcx>,
|
||||
|
@ -1,13 +1,13 @@
|
||||
use rustc_pattern_analysis::constructor::Constructor;
|
||||
use rustc_pattern_analysis::cx::MatchCheckCtxt;
|
||||
use rustc_pattern_analysis::errors::Uncovered;
|
||||
use rustc_pattern_analysis::pat::{DeconstructedPat, WitnessPat};
|
||||
use rustc_pattern_analysis::usefulness::{Usefulness, UsefulnessReport};
|
||||
use rustc_pattern_analysis::rustc::{
|
||||
Constructor, DeconstructedPat, RustcMatchCheckCtxt as MatchCheckCtxt, Usefulness,
|
||||
UsefulnessReport, WitnessPat,
|
||||
};
|
||||
use rustc_pattern_analysis::{analyze_match, MatchArm};
|
||||
|
||||
use crate::errors::*;
|
||||
|
||||
use rustc_arena::TypedArena;
|
||||
use rustc_arena::{DroplessArena, TypedArena};
|
||||
use rustc_ast::Mutability;
|
||||
use rustc_data_structures::fx::FxIndexSet;
|
||||
use rustc_data_structures::stack::ensure_sufficient_stack;
|
||||
@ -31,6 +31,7 @@ pub(crate) fn check_match(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), Err
|
||||
let (thir, expr) = tcx.thir_body(def_id)?;
|
||||
let thir = thir.borrow();
|
||||
let pattern_arena = TypedArena::default();
|
||||
let dropless_arena = DroplessArena::default();
|
||||
let mut visitor = MatchVisitor {
|
||||
tcx,
|
||||
thir: &*thir,
|
||||
@ -38,6 +39,7 @@ pub(crate) fn check_match(tcx: TyCtxt<'_>, def_id: LocalDefId) -> Result<(), Err
|
||||
lint_level: tcx.local_def_id_to_hir_id(def_id),
|
||||
let_source: LetSource::None,
|
||||
pattern_arena: &pattern_arena,
|
||||
dropless_arena: &dropless_arena,
|
||||
error: Ok(()),
|
||||
};
|
||||
visitor.visit_expr(&thir[expr]);
|
||||
@ -82,6 +84,7 @@ struct MatchVisitor<'thir, 'p, 'tcx> {
|
||||
lint_level: HirId,
|
||||
let_source: LetSource,
|
||||
pattern_arena: &'p TypedArena<DeconstructedPat<'p, 'tcx>>,
|
||||
dropless_arena: &'p DroplessArena,
|
||||
/// Tracks if we encountered an error while checking this body. That the first function to
|
||||
/// report it stores it here. Some functions return `Result` to allow callers to short-circuit
|
||||
/// on error, but callers don't need to store it here again.
|
||||
@ -382,6 +385,7 @@ impl<'thir, 'p, 'tcx> MatchVisitor<'thir, 'p, 'tcx> {
|
||||
param_env: self.param_env,
|
||||
module: self.tcx.parent_module(self.lint_level).to_def_id(),
|
||||
pattern_arena: self.pattern_arena,
|
||||
dropless_arena: self.dropless_arena,
|
||||
match_lint_level: self.lint_level,
|
||||
whole_match_span,
|
||||
scrut_span,
|
||||
@ -425,7 +429,8 @@ impl<'thir, 'p, 'tcx> MatchVisitor<'thir, 'p, 'tcx> {
|
||||
let arm = &self.thir.arms[arm];
|
||||
let got_error = self.with_lint_level(arm.lint_level, |this| {
|
||||
let Ok(pat) = this.lower_pattern(&cx, &arm.pattern) else { return true };
|
||||
let arm = MatchArm { pat, hir_id: this.lint_level, has_guard: arm.guard.is_some() };
|
||||
let arm =
|
||||
MatchArm { pat, arm_data: this.lint_level, has_guard: arm.guard.is_some() };
|
||||
tarms.push(arm);
|
||||
false
|
||||
});
|
||||
@ -548,7 +553,7 @@ impl<'thir, 'p, 'tcx> MatchVisitor<'thir, 'p, 'tcx> {
|
||||
) -> Result<(MatchCheckCtxt<'p, 'tcx>, UsefulnessReport<'p, 'tcx>), ErrorGuaranteed> {
|
||||
let cx = self.new_cx(refutability, None, scrut, pat.span);
|
||||
let pat = self.lower_pattern(&cx, pat)?;
|
||||
let arms = [MatchArm { pat, hir_id: self.lint_level, has_guard: false }];
|
||||
let arms = [MatchArm { pat, arm_data: self.lint_level, has_guard: false }];
|
||||
let report = analyze_match(&cx, &arms, pat.ty());
|
||||
Ok((cx, report))
|
||||
}
|
||||
@ -847,34 +852,34 @@ fn report_arm_reachability<'p, 'tcx>(
|
||||
);
|
||||
};
|
||||
|
||||
use Usefulness::*;
|
||||
let mut catchall = None;
|
||||
for (arm, is_useful) in report.arm_usefulness.iter() {
|
||||
match is_useful {
|
||||
Redundant => report_unreachable_pattern(arm.pat.span(), arm.hir_id, catchall),
|
||||
Useful(redundant_spans) if redundant_spans.is_empty() => {}
|
||||
Usefulness::Redundant => {
|
||||
report_unreachable_pattern(*arm.pat.data(), arm.arm_data, catchall)
|
||||
}
|
||||
Usefulness::Useful(redundant_subpats) if redundant_subpats.is_empty() => {}
|
||||
// The arm is reachable, but contains redundant subpatterns (from or-patterns).
|
||||
Useful(redundant_spans) => {
|
||||
let mut redundant_spans = redundant_spans.clone();
|
||||
Usefulness::Useful(redundant_subpats) => {
|
||||
let mut redundant_subpats = redundant_subpats.clone();
|
||||
// Emit lints in the order in which they occur in the file.
|
||||
redundant_spans.sort_unstable();
|
||||
for span in redundant_spans {
|
||||
report_unreachable_pattern(span, arm.hir_id, None);
|
||||
redundant_subpats.sort_unstable_by_key(|pat| pat.data());
|
||||
for pat in redundant_subpats {
|
||||
report_unreachable_pattern(*pat.data(), arm.arm_data, None);
|
||||
}
|
||||
}
|
||||
}
|
||||
if !arm.has_guard && catchall.is_none() && pat_is_catchall(arm.pat) {
|
||||
catchall = Some(arm.pat.span());
|
||||
catchall = Some(*arm.pat.data());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Checks for common cases of "catchall" patterns that may not be intended as such.
|
||||
fn pat_is_catchall(pat: &DeconstructedPat<'_, '_>) -> bool {
|
||||
use Constructor::*;
|
||||
match pat.ctor() {
|
||||
Wildcard => true,
|
||||
Single => pat.iter_fields().all(|pat| pat_is_catchall(pat)),
|
||||
Constructor::Wildcard => true,
|
||||
Constructor::Struct | Constructor::Ref => pat.iter_fields().all(|pat| pat_is_catchall(pat)),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
@ -885,7 +890,7 @@ fn report_non_exhaustive_match<'p, 'tcx>(
|
||||
thir: &Thir<'tcx>,
|
||||
scrut_ty: Ty<'tcx>,
|
||||
sp: Span,
|
||||
witnesses: Vec<WitnessPat<'tcx>>,
|
||||
witnesses: Vec<WitnessPat<'p, 'tcx>>,
|
||||
arms: &[ArmId],
|
||||
expr_span: Span,
|
||||
) -> ErrorGuaranteed {
|
||||
@ -1082,10 +1087,10 @@ fn report_non_exhaustive_match<'p, 'tcx>(
|
||||
|
||||
fn joined_uncovered_patterns<'p, 'tcx>(
|
||||
cx: &MatchCheckCtxt<'p, 'tcx>,
|
||||
witnesses: &[WitnessPat<'tcx>],
|
||||
witnesses: &[WitnessPat<'p, 'tcx>],
|
||||
) -> String {
|
||||
const LIMIT: usize = 3;
|
||||
let pat_to_str = |pat: &WitnessPat<'tcx>| cx.hoist_witness_pat(pat).to_string();
|
||||
let pat_to_str = |pat: &WitnessPat<'p, 'tcx>| cx.hoist_witness_pat(pat).to_string();
|
||||
match witnesses {
|
||||
[] => bug!(),
|
||||
[witness] => format!("`{}`", cx.hoist_witness_pat(witness)),
|
||||
@ -1103,7 +1108,7 @@ fn joined_uncovered_patterns<'p, 'tcx>(
|
||||
|
||||
fn collect_non_exhaustive_tys<'tcx>(
|
||||
cx: &MatchCheckCtxt<'_, 'tcx>,
|
||||
pat: &WitnessPat<'tcx>,
|
||||
pat: &WitnessPat<'_, 'tcx>,
|
||||
non_exhaustive_tys: &mut FxIndexSet<Ty<'tcx>>,
|
||||
) {
|
||||
if matches!(pat.ctor(), Constructor::NonExhaustive) {
|
||||
@ -1122,7 +1127,7 @@ fn collect_non_exhaustive_tys<'tcx>(
|
||||
fn report_adt_defined_here<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
ty: Ty<'tcx>,
|
||||
witnesses: &[WitnessPat<'tcx>],
|
||||
witnesses: &[WitnessPat<'_, 'tcx>],
|
||||
point_at_non_local_ty: bool,
|
||||
) -> Option<AdtDefinedHere<'tcx>> {
|
||||
let ty = ty.peel_refs();
|
||||
@ -1144,15 +1149,14 @@ fn report_adt_defined_here<'tcx>(
|
||||
Some(AdtDefinedHere { adt_def_span, ty, variants })
|
||||
}
|
||||
|
||||
fn maybe_point_at_variant<'a, 'tcx: 'a>(
|
||||
fn maybe_point_at_variant<'a, 'p: 'a, 'tcx: 'p>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
def: AdtDef<'tcx>,
|
||||
patterns: impl Iterator<Item = &'a WitnessPat<'tcx>>,
|
||||
patterns: impl Iterator<Item = &'a WitnessPat<'p, 'tcx>>,
|
||||
) -> Vec<Span> {
|
||||
use Constructor::*;
|
||||
let mut covered = vec![];
|
||||
for pattern in patterns {
|
||||
if let Variant(variant_index) = pattern.ctor() {
|
||||
if let Constructor::Variant(variant_index) = pattern.ctor() {
|
||||
if let ty::Adt(this_def, _) = pattern.ty().kind()
|
||||
&& this_def.did() != def.did()
|
||||
{
|
||||
|
@ -68,32 +68,21 @@ impl<'tcx> MirPass<'tcx> for InstrumentCoverage {
|
||||
struct Instrumentor<'a, 'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
mir_body: &'a mut mir::Body<'tcx>,
|
||||
fn_sig_span: Span,
|
||||
body_span: Span,
|
||||
function_source_hash: u64,
|
||||
hir_info: ExtractedHirInfo,
|
||||
basic_coverage_blocks: CoverageGraph,
|
||||
coverage_counters: CoverageCounters,
|
||||
}
|
||||
|
||||
impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
|
||||
fn new(tcx: TyCtxt<'tcx>, mir_body: &'a mut mir::Body<'tcx>) -> Self {
|
||||
let hir_info @ ExtractedHirInfo { function_source_hash, fn_sig_span, body_span } =
|
||||
extract_hir_info(tcx, mir_body.source.def_id().expect_local());
|
||||
let hir_info = extract_hir_info(tcx, mir_body.source.def_id().expect_local());
|
||||
|
||||
debug!(?hir_info, "instrumenting {:?}", mir_body.source.def_id());
|
||||
|
||||
let basic_coverage_blocks = CoverageGraph::from_mir(mir_body);
|
||||
let coverage_counters = CoverageCounters::new(&basic_coverage_blocks);
|
||||
|
||||
Self {
|
||||
tcx,
|
||||
mir_body,
|
||||
fn_sig_span,
|
||||
body_span,
|
||||
function_source_hash,
|
||||
basic_coverage_blocks,
|
||||
coverage_counters,
|
||||
}
|
||||
Self { tcx, mir_body, hir_info, basic_coverage_blocks, coverage_counters }
|
||||
}
|
||||
|
||||
fn inject_counters(&'a mut self) {
|
||||
@ -101,8 +90,7 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
|
||||
// Compute coverage spans from the `CoverageGraph`.
|
||||
let Some(coverage_spans) = CoverageSpans::generate_coverage_spans(
|
||||
self.mir_body,
|
||||
self.fn_sig_span,
|
||||
self.body_span,
|
||||
&self.hir_info,
|
||||
&self.basic_coverage_blocks,
|
||||
) else {
|
||||
// No relevant spans were found in MIR, so skip instrumenting this function.
|
||||
@ -121,7 +109,7 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
|
||||
let mappings = self.create_mappings_and_inject_coverage_statements(&coverage_spans);
|
||||
|
||||
self.mir_body.function_coverage_info = Some(Box::new(FunctionCoverageInfo {
|
||||
function_source_hash: self.function_source_hash,
|
||||
function_source_hash: self.hir_info.function_source_hash,
|
||||
num_counters: self.coverage_counters.num_counters(),
|
||||
expressions: self.coverage_counters.take_expressions(),
|
||||
mappings,
|
||||
@ -136,7 +124,7 @@ impl<'a, 'tcx> Instrumentor<'a, 'tcx> {
|
||||
coverage_spans: &CoverageSpans,
|
||||
) -> Vec<Mapping> {
|
||||
let source_map = self.tcx.sess.source_map();
|
||||
let body_span = self.body_span;
|
||||
let body_span = self.hir_info.body_span;
|
||||
|
||||
let source_file = source_map.lookup_source_file(body_span.lo());
|
||||
use rustc_session::RemapFileNameExt;
|
||||
@ -311,6 +299,7 @@ fn is_eligible_for_coverage(tcx: TyCtxt<'_>, def_id: LocalDefId) -> bool {
|
||||
#[derive(Debug)]
|
||||
struct ExtractedHirInfo {
|
||||
function_source_hash: u64,
|
||||
is_async_fn: bool,
|
||||
fn_sig_span: Span,
|
||||
body_span: Span,
|
||||
}
|
||||
@ -324,6 +313,7 @@ fn extract_hir_info<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> ExtractedHir
|
||||
hir::map::associated_body(hir_node).expect("HIR node is a function with body");
|
||||
let hir_body = tcx.hir().body(fn_body_id);
|
||||
|
||||
let is_async_fn = hir_node.fn_sig().is_some_and(|fn_sig| fn_sig.header.is_async());
|
||||
let body_span = get_body_span(tcx, hir_body, def_id);
|
||||
|
||||
// The actual signature span is only used if it has the same context and
|
||||
@ -345,7 +335,7 @@ fn extract_hir_info<'tcx>(tcx: TyCtxt<'tcx>, def_id: LocalDefId) -> ExtractedHir
|
||||
|
||||
let function_source_hash = hash_mir_source(tcx, hir_body);
|
||||
|
||||
ExtractedHirInfo { function_source_hash, fn_sig_span, body_span }
|
||||
ExtractedHirInfo { function_source_hash, is_async_fn, fn_sig_span, body_span }
|
||||
}
|
||||
|
||||
fn get_body_span<'tcx>(
|
||||
|
@ -6,6 +6,7 @@ use rustc_middle::mir;
|
||||
use rustc_span::{BytePos, ExpnKind, MacroKind, Span, Symbol, DUMMY_SP};
|
||||
|
||||
use super::graph::{BasicCoverageBlock, CoverageGraph, START_BCB};
|
||||
use crate::coverage::ExtractedHirInfo;
|
||||
|
||||
mod from_mir;
|
||||
|
||||
@ -21,14 +22,12 @@ impl CoverageSpans {
|
||||
/// Returns `None` if no coverage-relevant spans could be extracted.
|
||||
pub(super) fn generate_coverage_spans(
|
||||
mir_body: &mir::Body<'_>,
|
||||
fn_sig_span: Span,
|
||||
body_span: Span,
|
||||
hir_info: &ExtractedHirInfo,
|
||||
basic_coverage_blocks: &CoverageGraph,
|
||||
) -> Option<Self> {
|
||||
let coverage_spans = CoverageSpansGenerator::generate_coverage_spans(
|
||||
mir_body,
|
||||
fn_sig_span,
|
||||
body_span,
|
||||
hir_info,
|
||||
basic_coverage_blocks,
|
||||
);
|
||||
|
||||
@ -230,19 +229,17 @@ impl<'a> CoverageSpansGenerator<'a> {
|
||||
/// to be).
|
||||
pub(super) fn generate_coverage_spans(
|
||||
mir_body: &mir::Body<'_>,
|
||||
fn_sig_span: Span, // Ensured to be same SourceFile and SyntaxContext as `body_span`
|
||||
body_span: Span,
|
||||
hir_info: &ExtractedHirInfo,
|
||||
basic_coverage_blocks: &'a CoverageGraph,
|
||||
) -> Vec<CoverageSpan> {
|
||||
let sorted_spans = from_mir::mir_to_initial_sorted_coverage_spans(
|
||||
mir_body,
|
||||
fn_sig_span,
|
||||
body_span,
|
||||
hir_info,
|
||||
basic_coverage_blocks,
|
||||
);
|
||||
|
||||
let coverage_spans = Self {
|
||||
body_span,
|
||||
body_span: hir_info.body_span,
|
||||
basic_coverage_blocks,
|
||||
sorted_spans_iter: sorted_spans.into_iter(),
|
||||
some_curr: None,
|
||||
|
@ -7,13 +7,22 @@ use rustc_span::Span;
|
||||
|
||||
use crate::coverage::graph::{BasicCoverageBlock, BasicCoverageBlockData, CoverageGraph};
|
||||
use crate::coverage::spans::CoverageSpan;
|
||||
use crate::coverage::ExtractedHirInfo;
|
||||
|
||||
pub(super) fn mir_to_initial_sorted_coverage_spans(
|
||||
mir_body: &mir::Body<'_>,
|
||||
fn_sig_span: Span,
|
||||
body_span: Span,
|
||||
hir_info: &ExtractedHirInfo,
|
||||
basic_coverage_blocks: &CoverageGraph,
|
||||
) -> Vec<CoverageSpan> {
|
||||
let &ExtractedHirInfo { is_async_fn, fn_sig_span, body_span, .. } = hir_info;
|
||||
if is_async_fn {
|
||||
// An async function desugars into a function that returns a future,
|
||||
// with the user code wrapped in a closure. Any spans in the desugared
|
||||
// outer function will be unhelpful, so just produce a single span
|
||||
// associating the function signature with its entry BCB.
|
||||
return vec![CoverageSpan::for_fn_sig(fn_sig_span)];
|
||||
}
|
||||
|
||||
let mut initial_spans = Vec::with_capacity(mir_body.basic_blocks.len() * 2);
|
||||
for (bcb, bcb_data) in basic_coverage_blocks.iter_enumerated() {
|
||||
initial_spans.extend(bcb_to_initial_coverage_spans(mir_body, body_span, bcb, bcb_data));
|
||||
@ -44,16 +53,6 @@ pub(super) fn mir_to_initial_sorted_coverage_spans(
|
||||
.then_with(|| Ord::cmp(&a.is_closure, &b.is_closure).reverse())
|
||||
});
|
||||
|
||||
// The desugaring of an async function includes a closure containing the
|
||||
// original function body, and a terminator that returns the `impl Future`.
|
||||
// That terminator will cause a confusing coverage count for the function's
|
||||
// closing brace, so discard everything after the body closure span.
|
||||
if let Some(body_closure_index) =
|
||||
initial_spans.iter().rposition(|covspan| covspan.is_closure && covspan.span == body_span)
|
||||
{
|
||||
initial_spans.truncate(body_closure_index + 1);
|
||||
}
|
||||
|
||||
initial_spans
|
||||
}
|
||||
|
||||
|
@ -10,13 +10,13 @@ use crate::errors::{
|
||||
ConstGenericWithoutBracesSugg, DocCommentDoesNotDocumentAnything, DocCommentOnParamType,
|
||||
DoubleColonInBound, ExpectedIdentifier, ExpectedSemi, ExpectedSemiSugg,
|
||||
GenericParamsWithoutAngleBrackets, GenericParamsWithoutAngleBracketsSugg,
|
||||
HelpIdentifierStartsWithNumber, InInTypo, IncorrectAwait, IncorrectSemicolon,
|
||||
IncorrectUseOfAwait, PatternMethodParamWithoutBody, QuestionMarkInType, QuestionMarkInTypeSugg,
|
||||
SelfParamNotFirst, StructLiteralBodyWithoutPath, StructLiteralBodyWithoutPathSugg,
|
||||
StructLiteralNeedingParens, StructLiteralNeedingParensSugg, SuggAddMissingLetStmt,
|
||||
SuggEscapeIdentifier, SuggRemoveComma, TernaryOperator, UnexpectedConstInGenericParam,
|
||||
UnexpectedConstParamDeclaration, UnexpectedConstParamDeclarationSugg, UnmatchedAngleBrackets,
|
||||
UseEqInstead, WrapType,
|
||||
HelpIdentifierStartsWithNumber, HelpUseLatestEdition, InInTypo, IncorrectAwait,
|
||||
IncorrectSemicolon, IncorrectUseOfAwait, PatternMethodParamWithoutBody, QuestionMarkInType,
|
||||
QuestionMarkInTypeSugg, SelfParamNotFirst, StructLiteralBodyWithoutPath,
|
||||
StructLiteralBodyWithoutPathSugg, StructLiteralNeedingParens, StructLiteralNeedingParensSugg,
|
||||
SuggAddMissingLetStmt, SuggEscapeIdentifier, SuggRemoveComma, TernaryOperator,
|
||||
UnexpectedConstInGenericParam, UnexpectedConstParamDeclaration,
|
||||
UnexpectedConstParamDeclarationSugg, UnmatchedAngleBrackets, UseEqInstead, WrapType,
|
||||
};
|
||||
use crate::fluent_generated as fluent;
|
||||
use crate::parser;
|
||||
@ -640,6 +640,28 @@ impl<'a> Parser<'a> {
|
||||
}
|
||||
}
|
||||
|
||||
// Try to detect an intended c-string literal while using a pre-2021 edition. The heuristic
|
||||
// here is to identify a cooked, uninterpolated `c` id immediately followed by a string, or
|
||||
// a cooked, uninterpolated `cr` id immediately followed by a string or a `#`, in an edition
|
||||
// where c-string literals are not allowed. There is the very slight possibility of a false
|
||||
// positive for a `cr#` that wasn't intended to start a c-string literal, but identifying
|
||||
// that in the parser requires unbounded lookahead, so we only add a hint to the existing
|
||||
// error rather than replacing it entirely.
|
||||
if ((self.prev_token.kind == TokenKind::Ident(sym::c, false)
|
||||
&& matches!(&self.token.kind, TokenKind::Literal(token::Lit { kind: token::Str, .. })))
|
||||
|| (self.prev_token.kind == TokenKind::Ident(sym::cr, false)
|
||||
&& matches!(
|
||||
&self.token.kind,
|
||||
TokenKind::Literal(token::Lit { kind: token::Str, .. }) | token::Pound
|
||||
)))
|
||||
&& self.prev_token.span.hi() == self.token.span.lo()
|
||||
&& !self.token.span.at_least_rust_2021()
|
||||
{
|
||||
err.note("you may be trying to write a c-string literal");
|
||||
err.note("c-string literals require Rust 2021 or later");
|
||||
HelpUseLatestEdition::new().add_to_diagnostic(&mut err);
|
||||
}
|
||||
|
||||
// `pub` may be used for an item or `pub(crate)`
|
||||
if self.prev_token.is_ident_named(sym::public)
|
||||
&& (self.token.can_begin_item()
|
||||
|
@ -1484,7 +1484,7 @@ impl<'a> Parser<'a> {
|
||||
(thin_vec![], true)
|
||||
}
|
||||
};
|
||||
VariantData::Struct(fields, recovered)
|
||||
VariantData::Struct { fields, recovered }
|
||||
} else if this.check(&token::OpenDelim(Delimiter::Parenthesis)) {
|
||||
let body = match this.parse_tuple_struct_body() {
|
||||
Ok(body) => body,
|
||||
@ -1569,7 +1569,7 @@ impl<'a> Parser<'a> {
|
||||
class_name.span,
|
||||
generics.where_clause.has_where_token,
|
||||
)?;
|
||||
VariantData::Struct(fields, recovered)
|
||||
VariantData::Struct { fields, recovered }
|
||||
}
|
||||
// No `where` so: `struct Foo<T>;`
|
||||
} else if self.eat(&token::Semi) {
|
||||
@ -1581,7 +1581,7 @@ impl<'a> Parser<'a> {
|
||||
class_name.span,
|
||||
generics.where_clause.has_where_token,
|
||||
)?;
|
||||
VariantData::Struct(fields, recovered)
|
||||
VariantData::Struct { fields, recovered }
|
||||
// Tuple-style struct definition with optional where-clause.
|
||||
} else if self.token == token::OpenDelim(Delimiter::Parenthesis) {
|
||||
let body = VariantData::Tuple(self.parse_tuple_struct_body()?, DUMMY_NODE_ID);
|
||||
@ -1610,14 +1610,14 @@ impl<'a> Parser<'a> {
|
||||
class_name.span,
|
||||
generics.where_clause.has_where_token,
|
||||
)?;
|
||||
VariantData::Struct(fields, recovered)
|
||||
VariantData::Struct { fields, recovered }
|
||||
} else if self.token == token::OpenDelim(Delimiter::Brace) {
|
||||
let (fields, recovered) = self.parse_record_struct_body(
|
||||
"union",
|
||||
class_name.span,
|
||||
generics.where_clause.has_where_token,
|
||||
)?;
|
||||
VariantData::Struct(fields, recovered)
|
||||
VariantData::Struct { fields, recovered }
|
||||
} else {
|
||||
let token_str = super::token_descr(&self.token);
|
||||
let msg = format!("expected `where` or `{{` after union name, found {token_str}");
|
||||
|
@ -83,9 +83,6 @@ fn all_diagnostic_items(tcx: TyCtxt<'_>, (): ()) -> DiagnosticItems {
|
||||
|
||||
// Collect diagnostic items in other crates.
|
||||
for &cnum in tcx.crates(()).iter().chain(std::iter::once(&LOCAL_CRATE)) {
|
||||
// We are collecting many DiagnosticItems hash maps into one
|
||||
// DiagnosticItems hash map. The iteration order does not matter.
|
||||
#[allow(rustc::potential_query_instability)]
|
||||
for (&name, &def_id) in &tcx.diagnostic_items(cnum).name_to_id {
|
||||
collect_item(tcx, &mut items, name, def_id);
|
||||
}
|
||||
|
@ -6,17 +6,40 @@ edition = "2021"
|
||||
[dependencies]
|
||||
# tidy-alphabetical-start
|
||||
rustc_apfloat = "0.2.0"
|
||||
rustc_arena = { path = "../rustc_arena" }
|
||||
rustc_data_structures = { path = "../rustc_data_structures" }
|
||||
rustc_errors = { path = "../rustc_errors" }
|
||||
rustc_fluent_macro = { path = "../rustc_fluent_macro" }
|
||||
rustc_hir = { path = "../rustc_hir" }
|
||||
rustc_index = { path = "../rustc_index" }
|
||||
rustc_macros = { path = "../rustc_macros" }
|
||||
rustc_middle = { path = "../rustc_middle" }
|
||||
rustc_session = { path = "../rustc_session" }
|
||||
rustc_span = { path = "../rustc_span" }
|
||||
rustc_target = { path = "../rustc_target" }
|
||||
smallvec = { version = "1.8.1", features = ["union", "may_dangle"] }
|
||||
rustc_arena = { path = "../rustc_arena", optional = true }
|
||||
rustc_data_structures = { path = "../rustc_data_structures", optional = true }
|
||||
rustc_errors = { path = "../rustc_errors", optional = true }
|
||||
rustc_fluent_macro = { path = "../rustc_fluent_macro", optional = true }
|
||||
rustc_hir = { path = "../rustc_hir", optional = true }
|
||||
rustc_index = { path = "../rustc_index", default-features = false }
|
||||
rustc_macros = { path = "../rustc_macros", optional = true }
|
||||
rustc_middle = { path = "../rustc_middle", optional = true }
|
||||
rustc_session = { path = "../rustc_session", optional = true }
|
||||
rustc_span = { path = "../rustc_span", optional = true }
|
||||
rustc_target = { path = "../rustc_target", optional = true }
|
||||
smallvec = { version = "1.8.1", features = ["union"] }
|
||||
tracing = "0.1"
|
||||
typed-arena = { version = "2.0.2", optional = true }
|
||||
# tidy-alphabetical-end
|
||||
|
||||
[features]
|
||||
default = ["rustc"]
|
||||
# It's not possible to only enable the `typed_arena` dependency when the `rustc` feature is off, so
|
||||
# we use another feature instead. The crate won't compile if one of these isn't enabled.
|
||||
rustc = [
|
||||
"dep:rustc_arena",
|
||||
"dep:rustc_data_structures",
|
||||
"dep:rustc_errors",
|
||||
"dep:rustc_fluent_macro",
|
||||
"dep:rustc_hir",
|
||||
"dep:rustc_macros",
|
||||
"dep:rustc_middle",
|
||||
"dep:rustc_session",
|
||||
"dep:rustc_span",
|
||||
"dep:rustc_target",
|
||||
"smallvec/may_dangle",
|
||||
"rustc_index/nightly",
|
||||
]
|
||||
stable = [
|
||||
"dep:typed-arena",
|
||||
]
|
||||
|
@ -40,7 +40,7 @@
|
||||
//! - That have no non-trivial intersection with any of the constructors in the column (i.e. they're
|
||||
//! each either disjoint with or covered by any given column constructor).
|
||||
//!
|
||||
//! We compute this in two steps: first [`crate::cx::MatchCheckCtxt::ctors_for_ty`] determines the
|
||||
//! We compute this in two steps: first [`TypeCx::ctors_for_ty`] determines the
|
||||
//! set of all possible constructors for the type. Then [`ConstructorSet::split`] looks at the
|
||||
//! column of constructors and splits the set into groups accordingly. The precise invariants of
|
||||
//! [`ConstructorSet::split`] is described in [`SplitConstructorSet`].
|
||||
@ -136,7 +136,7 @@
|
||||
//! the algorithm can't distinguish them from a nonempty constructor. The only known case where this
|
||||
//! could happen is the `[..]` pattern on `[!; N]` with `N > 0` so we must take care to not emit it.
|
||||
//!
|
||||
//! This is all handled by [`crate::cx::MatchCheckCtxt::ctors_for_ty`] and
|
||||
//! This is all handled by [`TypeCx::ctors_for_ty`] and
|
||||
//! [`ConstructorSet::split`]. The invariants of [`SplitConstructorSet`] are also of interest.
|
||||
//!
|
||||
//!
|
||||
@ -155,17 +155,15 @@ use std::iter::once;
|
||||
use smallvec::SmallVec;
|
||||
|
||||
use rustc_apfloat::ieee::{DoubleS, IeeeFloat, SingleS};
|
||||
use rustc_data_structures::fx::FxHashSet;
|
||||
use rustc_hir::RangeEnd;
|
||||
use rustc_index::bit_set::{BitSet, GrowableBitSet};
|
||||
use rustc_index::IndexVec;
|
||||
use rustc_middle::mir::Const;
|
||||
use rustc_target::abi::VariantIdx;
|
||||
|
||||
use self::Constructor::*;
|
||||
use self::MaybeInfiniteInt::*;
|
||||
use self::SliceKind::*;
|
||||
|
||||
use crate::usefulness::PatCtxt;
|
||||
use crate::usefulness::PlaceCtxt;
|
||||
use crate::TypeCx;
|
||||
|
||||
/// Whether we have seen a constructor in the column or not.
|
||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
|
||||
@ -174,6 +172,21 @@ enum Presence {
|
||||
Seen,
|
||||
}
|
||||
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
pub enum RangeEnd {
|
||||
Included,
|
||||
Excluded,
|
||||
}
|
||||
|
||||
impl fmt::Display for RangeEnd {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.write_str(match self {
|
||||
RangeEnd::Included => "..=",
|
||||
RangeEnd::Excluded => "..",
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/// A possibly infinite integer. Values are encoded such that the ordering on `u128` matches the
|
||||
/// natural order on the original type. For example, `-128i8` is encoded as `0` and `127i8` as
|
||||
/// `255`. See `signed_bias` for details.
|
||||
@ -221,7 +234,7 @@ impl MaybeInfiniteInt {
|
||||
match self {
|
||||
Finite(n) => match n.checked_sub(1) {
|
||||
Some(m) => Finite(m),
|
||||
None => bug!(),
|
||||
None => panic!("Called `MaybeInfiniteInt::minus_one` on 0"),
|
||||
},
|
||||
JustAfterMax => Finite(u128::MAX),
|
||||
x => x,
|
||||
@ -234,7 +247,7 @@ impl MaybeInfiniteInt {
|
||||
Some(m) => Finite(m),
|
||||
None => JustAfterMax,
|
||||
},
|
||||
JustAfterMax => bug!(),
|
||||
JustAfterMax => panic!("Called `MaybeInfiniteInt::plus_one` on u128::MAX+1"),
|
||||
x => x,
|
||||
}
|
||||
}
|
||||
@ -253,7 +266,7 @@ pub struct IntRange {
|
||||
|
||||
impl IntRange {
|
||||
/// Best effort; will not know that e.g. `255u8..` is a singleton.
|
||||
pub(crate) fn is_singleton(&self) -> bool {
|
||||
pub fn is_singleton(&self) -> bool {
|
||||
// Since `lo` and `hi` can't be the same `Infinity` and `plus_one` never changes from finite
|
||||
// to infinite, this correctly only detects ranges that contain exacly one `Finite(x)`.
|
||||
self.lo.plus_one() == self.hi
|
||||
@ -271,7 +284,7 @@ impl IntRange {
|
||||
}
|
||||
if lo >= hi {
|
||||
// This should have been caught earlier by E0030.
|
||||
bug!("malformed range pattern: {lo:?}..{hi:?}");
|
||||
panic!("malformed range pattern: {lo:?}..{hi:?}");
|
||||
}
|
||||
IntRange { lo, hi }
|
||||
}
|
||||
@ -432,7 +445,7 @@ impl Slice {
|
||||
let kind = match (array_len, kind) {
|
||||
// If the middle `..` has length 0, we effectively have a fixed-length pattern.
|
||||
(Some(len), VarLen(prefix, suffix)) if prefix + suffix == len => FixedLen(len),
|
||||
(Some(len), VarLen(prefix, suffix)) if prefix + suffix > len => bug!(
|
||||
(Some(len), VarLen(prefix, suffix)) if prefix + suffix > len => panic!(
|
||||
"Slice pattern of length {} longer than its array length {len}",
|
||||
prefix + suffix
|
||||
),
|
||||
@ -532,7 +545,7 @@ impl Slice {
|
||||
// therefore `Presence::Seen` in the column.
|
||||
let mut min_var_len = usize::MAX;
|
||||
// Tracks the fixed-length slices we've seen, to mark them as `Presence::Seen`.
|
||||
let mut seen_fixed_lens = FxHashSet::default();
|
||||
let mut seen_fixed_lens = GrowableBitSet::new_empty();
|
||||
match &mut max_slice {
|
||||
VarLen(max_prefix_len, max_suffix_len) => {
|
||||
// A length larger than any fixed-length slice encountered.
|
||||
@ -600,7 +613,7 @@ impl Slice {
|
||||
|
||||
smaller_lengths.map(FixedLen).chain(once(max_slice)).map(move |kind| {
|
||||
let arity = kind.arity();
|
||||
let seen = if min_var_len <= arity || seen_fixed_lens.contains(&arity) {
|
||||
let seen = if min_var_len <= arity || seen_fixed_lens.contains(arity) {
|
||||
Presence::Seen
|
||||
} else {
|
||||
Presence::Unseen
|
||||
@ -630,12 +643,17 @@ impl OpaqueId {
|
||||
/// constructor. `Constructor::apply` reconstructs the pattern from a pair of `Constructor` and
|
||||
/// `Fields`.
|
||||
#[derive(Clone, Debug, PartialEq)]
|
||||
pub enum Constructor<'tcx> {
|
||||
/// The constructor for patterns that have a single constructor, like tuples, struct patterns,
|
||||
/// and references. Fixed-length arrays are treated separately with `Slice`.
|
||||
Single,
|
||||
pub enum Constructor<Cx: TypeCx> {
|
||||
/// Tuples and structs.
|
||||
Struct,
|
||||
/// Enum variants.
|
||||
Variant(VariantIdx),
|
||||
Variant(Cx::VariantIdx),
|
||||
/// References
|
||||
Ref,
|
||||
/// Array and slice patterns.
|
||||
Slice(Slice),
|
||||
/// Union field accesses.
|
||||
UnionField,
|
||||
/// Booleans
|
||||
Bool(bool),
|
||||
/// Ranges of integer literal values (`2`, `2..=5` or `2..5`).
|
||||
@ -644,9 +662,7 @@ pub enum Constructor<'tcx> {
|
||||
F32Range(IeeeFloat<SingleS>, IeeeFloat<SingleS>, RangeEnd),
|
||||
F64Range(IeeeFloat<DoubleS>, IeeeFloat<DoubleS>, RangeEnd),
|
||||
/// String literals. Strings are not quite the same as `&[u8]` so we treat them separately.
|
||||
Str(Const<'tcx>),
|
||||
/// Array and slice patterns.
|
||||
Slice(Slice),
|
||||
Str(Cx::StrLit),
|
||||
/// 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.
|
||||
@ -669,12 +685,12 @@ pub enum Constructor<'tcx> {
|
||||
Missing,
|
||||
}
|
||||
|
||||
impl<'tcx> Constructor<'tcx> {
|
||||
impl<Cx: TypeCx> Constructor<Cx> {
|
||||
pub(crate) fn is_non_exhaustive(&self) -> bool {
|
||||
matches!(self, NonExhaustive)
|
||||
}
|
||||
|
||||
pub(crate) fn as_variant(&self) -> Option<VariantIdx> {
|
||||
pub(crate) fn as_variant(&self) -> Option<Cx::VariantIdx> {
|
||||
match self {
|
||||
Variant(i) => Some(*i),
|
||||
_ => None,
|
||||
@ -701,8 +717,8 @@ impl<'tcx> Constructor<'tcx> {
|
||||
|
||||
/// The number of fields for this constructor. This must be kept in sync with
|
||||
/// `Fields::wildcards`.
|
||||
pub(crate) fn arity(&self, pcx: &PatCtxt<'_, '_, 'tcx>) -> usize {
|
||||
pcx.cx.ctor_arity(self, pcx.ty)
|
||||
pub(crate) fn arity(&self, pcx: &PlaceCtxt<'_, '_, Cx>) -> usize {
|
||||
pcx.ctor_arity(self)
|
||||
}
|
||||
|
||||
/// Returns whether `self` is covered by `other`, i.e. whether `self` is a subset of `other`.
|
||||
@ -710,20 +726,20 @@ impl<'tcx> Constructor<'tcx> {
|
||||
/// this checks for inclusion.
|
||||
// We inline because this has a single call site in `Matrix::specialize_constructor`.
|
||||
#[inline]
|
||||
pub(crate) fn is_covered_by<'p>(&self, pcx: &PatCtxt<'_, 'p, 'tcx>, other: &Self) -> bool {
|
||||
pub(crate) fn is_covered_by<'p>(&self, pcx: &PlaceCtxt<'_, 'p, Cx>, other: &Self) -> bool {
|
||||
match (self, other) {
|
||||
(Wildcard, _) => {
|
||||
span_bug!(
|
||||
pcx.cx.scrut_span,
|
||||
"Constructor splitting should not have returned `Wildcard`"
|
||||
)
|
||||
}
|
||||
(Wildcard, _) => pcx
|
||||
.mcx
|
||||
.tycx
|
||||
.bug(format_args!("Constructor splitting should not have returned `Wildcard`")),
|
||||
// Wildcards cover anything
|
||||
(_, Wildcard) => true,
|
||||
// Only a wildcard pattern can match these special constructors.
|
||||
(Missing { .. } | NonExhaustive | Hidden, _) => false,
|
||||
|
||||
(Single, Single) => true,
|
||||
(Struct, Struct) => true,
|
||||
(Ref, Ref) => true,
|
||||
(UnionField, UnionField) => true,
|
||||
(Variant(self_id), Variant(other_id)) => self_id == other_id,
|
||||
(Bool(self_b), Bool(other_b)) => self_b == other_b,
|
||||
|
||||
@ -756,12 +772,9 @@ impl<'tcx> Constructor<'tcx> {
|
||||
(Opaque(self_id), Opaque(other_id)) => self_id == other_id,
|
||||
(Opaque(..), _) | (_, Opaque(..)) => false,
|
||||
|
||||
_ => span_bug!(
|
||||
pcx.cx.scrut_span,
|
||||
"trying to compare incompatible constructors {:?} and {:?}",
|
||||
self,
|
||||
other
|
||||
),
|
||||
_ => pcx.mcx.tycx.bug(format_args!(
|
||||
"trying to compare incompatible constructors {self:?} and {other:?}"
|
||||
)),
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -785,13 +798,16 @@ pub enum VariantVisibility {
|
||||
/// In terms of division of responsibility, [`ConstructorSet::split`] handles all of the
|
||||
/// `exhaustive_patterns` feature.
|
||||
#[derive(Debug)]
|
||||
pub enum ConstructorSet {
|
||||
/// The type has a single constructor, e.g. `&T` or a struct. `empty` tracks whether the
|
||||
/// constructor is empty.
|
||||
Single { empty: bool },
|
||||
pub enum ConstructorSet<Cx: TypeCx> {
|
||||
/// The type is a tuple or struct. `empty` tracks whether the type is empty.
|
||||
Struct { empty: bool },
|
||||
/// This type has the following list of constructors. If `variants` is empty and
|
||||
/// `non_exhaustive` is false, don't use this; use `NoConstructors` instead.
|
||||
Variants { variants: IndexVec<VariantIdx, VariantVisibility>, non_exhaustive: bool },
|
||||
Variants { variants: IndexVec<Cx::VariantIdx, VariantVisibility>, non_exhaustive: bool },
|
||||
/// The type is `&T`.
|
||||
Ref,
|
||||
/// The type is a union.
|
||||
Union,
|
||||
/// Booleans.
|
||||
Bool,
|
||||
/// The type is spanned by integer values. The range or ranges give the set of allowed values.
|
||||
@ -830,25 +846,25 @@ pub enum ConstructorSet {
|
||||
/// of the `ConstructorSet` for the type, yet if we forgot to include them in `present` we would be
|
||||
/// ignoring any row with `Opaque`s in the algorithm. Hence the importance of point 4.
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct SplitConstructorSet<'tcx> {
|
||||
pub(crate) present: SmallVec<[Constructor<'tcx>; 1]>,
|
||||
pub(crate) missing: Vec<Constructor<'tcx>>,
|
||||
pub(crate) missing_empty: Vec<Constructor<'tcx>>,
|
||||
pub(crate) struct SplitConstructorSet<Cx: TypeCx> {
|
||||
pub(crate) present: SmallVec<[Constructor<Cx>; 1]>,
|
||||
pub(crate) missing: Vec<Constructor<Cx>>,
|
||||
pub(crate) missing_empty: Vec<Constructor<Cx>>,
|
||||
}
|
||||
|
||||
impl ConstructorSet {
|
||||
impl<Cx: TypeCx> ConstructorSet<Cx> {
|
||||
/// This analyzes a column of constructors to 1/ determine which constructors of the type (if
|
||||
/// any) are missing; 2/ split constructors to handle non-trivial intersections e.g. on ranges
|
||||
/// or slices. This can get subtle; see [`SplitConstructorSet`] for details of this operation
|
||||
/// and its invariants.
|
||||
#[instrument(level = "debug", skip(self, pcx, ctors), ret)]
|
||||
pub(crate) fn split<'a, 'tcx>(
|
||||
pub(crate) fn split<'a>(
|
||||
&self,
|
||||
pcx: &PatCtxt<'_, '_, 'tcx>,
|
||||
ctors: impl Iterator<Item = &'a Constructor<'tcx>> + Clone,
|
||||
) -> SplitConstructorSet<'tcx>
|
||||
pcx: &PlaceCtxt<'_, '_, Cx>,
|
||||
ctors: impl Iterator<Item = &'a Constructor<Cx>> + Clone,
|
||||
) -> SplitConstructorSet<Cx>
|
||||
where
|
||||
'tcx: 'a,
|
||||
Cx: 'a,
|
||||
{
|
||||
let mut present: SmallVec<[_; 1]> = SmallVec::new();
|
||||
// Empty constructors found missing.
|
||||
@ -866,22 +882,39 @@ impl ConstructorSet {
|
||||
}
|
||||
|
||||
match self {
|
||||
ConstructorSet::Single { empty } => {
|
||||
ConstructorSet::Struct { empty } => {
|
||||
if !seen.is_empty() {
|
||||
present.push(Single);
|
||||
present.push(Struct);
|
||||
} else if *empty {
|
||||
missing_empty.push(Single);
|
||||
missing_empty.push(Struct);
|
||||
} else {
|
||||
missing.push(Single);
|
||||
missing.push(Struct);
|
||||
}
|
||||
}
|
||||
ConstructorSet::Ref => {
|
||||
if !seen.is_empty() {
|
||||
present.push(Ref);
|
||||
} else {
|
||||
missing.push(Ref);
|
||||
}
|
||||
}
|
||||
ConstructorSet::Union => {
|
||||
if !seen.is_empty() {
|
||||
present.push(UnionField);
|
||||
} else {
|
||||
missing.push(UnionField);
|
||||
}
|
||||
}
|
||||
ConstructorSet::Variants { variants, non_exhaustive } => {
|
||||
let seen_set: FxHashSet<_> = seen.iter().map(|c| c.as_variant().unwrap()).collect();
|
||||
let mut seen_set: BitSet<_> = BitSet::new_empty(variants.len());
|
||||
for idx in seen.iter().map(|c| c.as_variant().unwrap()) {
|
||||
seen_set.insert(idx);
|
||||
}
|
||||
let mut skipped_a_hidden_variant = false;
|
||||
|
||||
for (idx, visibility) in variants.iter_enumerated() {
|
||||
let ctor = Variant(idx);
|
||||
if seen_set.contains(&idx) {
|
||||
if seen_set.contains(idx) {
|
||||
present.push(ctor);
|
||||
} else {
|
||||
// We only put visible variants directly into `missing`.
|
||||
@ -975,8 +1008,8 @@ impl ConstructorSet {
|
||||
// We have now grouped all the constructors into 3 buckets: present, missing, missing_empty.
|
||||
// In the absence of the `exhaustive_patterns` feature however, we don't count nested empty
|
||||
// types as empty. Only non-nested `!` or `enum Foo {}` are considered empty.
|
||||
if !pcx.cx.tcx.features().exhaustive_patterns
|
||||
&& !(pcx.is_top_level && matches!(self, Self::NoConstructors))
|
||||
if !pcx.mcx.tycx.is_exhaustive_patterns_feature_on()
|
||||
&& !(pcx.is_scrutinee && matches!(self, Self::NoConstructors))
|
||||
{
|
||||
// Treat all missing constructors as nonempty.
|
||||
// This clears `missing_empty`.
|
||||
|
@ -1,11 +1,11 @@
|
||||
use crate::{cx::MatchCheckCtxt, pat::WitnessPat};
|
||||
|
||||
use rustc_errors::{AddToDiagnostic, Diagnostic, SubdiagnosticMessage};
|
||||
use rustc_macros::{LintDiagnostic, Subdiagnostic};
|
||||
use rustc_middle::thir::Pat;
|
||||
use rustc_middle::ty::Ty;
|
||||
use rustc_span::Span;
|
||||
|
||||
use crate::rustc::{RustcMatchCheckCtxt, WitnessPat};
|
||||
|
||||
#[derive(Subdiagnostic)]
|
||||
#[label(pattern_analysis_uncovered)]
|
||||
pub struct Uncovered<'tcx> {
|
||||
@ -21,8 +21,8 @@ pub struct Uncovered<'tcx> {
|
||||
impl<'tcx> Uncovered<'tcx> {
|
||||
pub fn new<'p>(
|
||||
span: Span,
|
||||
cx: &MatchCheckCtxt<'p, 'tcx>,
|
||||
witnesses: Vec<WitnessPat<'tcx>>,
|
||||
cx: &RustcMatchCheckCtxt<'p, 'tcx>,
|
||||
witnesses: Vec<WitnessPat<'p, 'tcx>>,
|
||||
) -> Self {
|
||||
let witness_1 = cx.hoist_witness_pat(witnesses.get(0).unwrap());
|
||||
Self {
|
||||
|
@ -1,54 +1,133 @@
|
||||
//! Analysis of patterns, notably match exhaustiveness checking.
|
||||
|
||||
pub mod constructor;
|
||||
pub mod cx;
|
||||
#[cfg(feature = "rustc")]
|
||||
pub mod errors;
|
||||
#[cfg(feature = "rustc")]
|
||||
pub(crate) mod lints;
|
||||
pub mod pat;
|
||||
#[cfg(feature = "rustc")]
|
||||
pub mod rustc;
|
||||
pub mod usefulness;
|
||||
|
||||
#[macro_use]
|
||||
extern crate tracing;
|
||||
#[cfg(feature = "rustc")]
|
||||
#[macro_use]
|
||||
extern crate rustc_middle;
|
||||
|
||||
#[cfg(feature = "rustc")]
|
||||
rustc_fluent_macro::fluent_messages! { "../messages.ftl" }
|
||||
|
||||
use lints::PatternColumn;
|
||||
use rustc_hir::HirId;
|
||||
use rustc_middle::ty::Ty;
|
||||
use usefulness::{compute_match_usefulness, UsefulnessReport};
|
||||
use std::fmt;
|
||||
|
||||
use crate::cx::MatchCheckCtxt;
|
||||
use crate::lints::{lint_nonexhaustive_missing_variants, lint_overlapping_range_endpoints};
|
||||
use rustc_index::Idx;
|
||||
#[cfg(feature = "rustc")]
|
||||
use rustc_middle::ty::Ty;
|
||||
|
||||
use crate::constructor::{Constructor, ConstructorSet};
|
||||
#[cfg(feature = "rustc")]
|
||||
use crate::lints::{
|
||||
lint_nonexhaustive_missing_variants, lint_overlapping_range_endpoints, PatternColumn,
|
||||
};
|
||||
use crate::pat::DeconstructedPat;
|
||||
#[cfg(feature = "rustc")]
|
||||
use crate::rustc::RustcMatchCheckCtxt;
|
||||
#[cfg(feature = "rustc")]
|
||||
use crate::usefulness::{compute_match_usefulness, ValidityConstraint};
|
||||
|
||||
// It's not possible to only enable the `typed_arena` dependency when the `rustc` feature is off, so
|
||||
// we use another feature instead. The crate won't compile if one of these isn't enabled.
|
||||
#[cfg(feature = "rustc")]
|
||||
pub(crate) use rustc_arena::TypedArena;
|
||||
#[cfg(feature = "stable")]
|
||||
pub(crate) use typed_arena::Arena as TypedArena;
|
||||
|
||||
pub trait Captures<'a> {}
|
||||
impl<'a, T: ?Sized> Captures<'a> for T {}
|
||||
|
||||
/// Context that provides type information about constructors.
|
||||
///
|
||||
/// Most of the crate is parameterized on a type that implements this trait.
|
||||
pub trait TypeCx: Sized + Clone + fmt::Debug {
|
||||
/// The type of a pattern.
|
||||
type Ty: Copy + Clone + fmt::Debug; // FIXME: remove Copy
|
||||
/// The index of an enum variant.
|
||||
type VariantIdx: Clone + Idx;
|
||||
/// A string literal
|
||||
type StrLit: Clone + PartialEq + fmt::Debug;
|
||||
/// Extra data to store in a match arm.
|
||||
type ArmData: Copy + Clone + fmt::Debug;
|
||||
/// Extra data to store in a pattern. `Default` needed when we create fictitious wildcard
|
||||
/// patterns during analysis.
|
||||
type PatData: Clone + Default;
|
||||
|
||||
fn is_opaque_ty(ty: Self::Ty) -> bool;
|
||||
fn is_exhaustive_patterns_feature_on(&self) -> bool;
|
||||
|
||||
/// The number of fields for this constructor.
|
||||
fn ctor_arity(&self, ctor: &Constructor<Self>, ty: Self::Ty) -> usize;
|
||||
|
||||
/// The types of the fields for this constructor. The result must have a length of
|
||||
/// `ctor_arity()`.
|
||||
fn ctor_sub_tys(&self, ctor: &Constructor<Self>, ty: Self::Ty) -> &[Self::Ty];
|
||||
|
||||
/// The set of all the constructors for `ty`.
|
||||
///
|
||||
/// This must follow the invariants of `ConstructorSet`
|
||||
fn ctors_for_ty(&self, ty: Self::Ty) -> ConstructorSet<Self>;
|
||||
|
||||
/// Best-effort `Debug` implementation.
|
||||
fn debug_pat(f: &mut fmt::Formatter<'_>, pat: &DeconstructedPat<'_, Self>) -> fmt::Result;
|
||||
|
||||
/// Raise a bug.
|
||||
fn bug(&self, fmt: fmt::Arguments<'_>) -> !;
|
||||
}
|
||||
|
||||
/// Context that provides information global to a match.
|
||||
#[derive(Clone)]
|
||||
pub struct MatchCtxt<'a, 'p, Cx: TypeCx> {
|
||||
/// The context for type information.
|
||||
pub tycx: &'a Cx,
|
||||
/// An arena to store the wildcards we produce during analysis.
|
||||
pub wildcard_arena: &'a TypedArena<DeconstructedPat<'p, Cx>>,
|
||||
}
|
||||
|
||||
impl<'a, 'p, Cx: TypeCx> Copy for MatchCtxt<'a, 'p, Cx> {}
|
||||
|
||||
/// The arm of a match expression.
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
pub struct MatchArm<'p, 'tcx> {
|
||||
/// The pattern must have been lowered through `check_match::MatchVisitor::lower_pattern`.
|
||||
pub pat: &'p DeconstructedPat<'p, 'tcx>,
|
||||
pub hir_id: HirId,
|
||||
#[derive(Clone, Debug)]
|
||||
pub struct MatchArm<'p, Cx: TypeCx> {
|
||||
pub pat: &'p DeconstructedPat<'p, Cx>,
|
||||
pub has_guard: bool,
|
||||
pub arm_data: Cx::ArmData,
|
||||
}
|
||||
|
||||
impl<'p, Cx: TypeCx> Copy for MatchArm<'p, Cx> {}
|
||||
|
||||
/// The entrypoint for this crate. Computes whether a match is exhaustive and which of its arms are
|
||||
/// useful, and runs some lints.
|
||||
#[cfg(feature = "rustc")]
|
||||
pub fn analyze_match<'p, 'tcx>(
|
||||
cx: &MatchCheckCtxt<'p, 'tcx>,
|
||||
arms: &[MatchArm<'p, 'tcx>],
|
||||
tycx: &RustcMatchCheckCtxt<'p, 'tcx>,
|
||||
arms: &[rustc::MatchArm<'p, 'tcx>],
|
||||
scrut_ty: Ty<'tcx>,
|
||||
) -> UsefulnessReport<'p, 'tcx> {
|
||||
let pat_column = PatternColumn::new(arms);
|
||||
) -> rustc::UsefulnessReport<'p, 'tcx> {
|
||||
// Arena to store the extra wildcards we construct during analysis.
|
||||
let wildcard_arena = tycx.pattern_arena;
|
||||
let scrut_validity = ValidityConstraint::from_bool(tycx.known_valid_scrutinee);
|
||||
let cx = MatchCtxt { tycx, wildcard_arena };
|
||||
|
||||
let report = compute_match_usefulness(cx, arms, scrut_ty);
|
||||
let report = compute_match_usefulness(cx, arms, scrut_ty, scrut_validity);
|
||||
|
||||
let pat_column = PatternColumn::new(arms);
|
||||
|
||||
// Lint on ranges that overlap on their endpoints, which is likely a mistake.
|
||||
lint_overlapping_range_endpoints(cx, &pat_column);
|
||||
|
||||
// Run the non_exhaustive_omitted_patterns lint. Only run on refutable patterns to avoid hitting
|
||||
// `if let`s. Only run if the match is exhaustive otherwise the error is redundant.
|
||||
if cx.refutable && report.non_exhaustiveness_witnesses.is_empty() {
|
||||
if tycx.refutable && report.non_exhaustiveness_witnesses.is_empty() {
|
||||
lint_nonexhaustive_missing_variants(cx, arms, &pat_column, scrut_ty)
|
||||
}
|
||||
|
||||
|
@ -6,15 +6,16 @@ use rustc_session::lint;
|
||||
use rustc_session::lint::builtin::NON_EXHAUSTIVE_OMITTED_PATTERNS;
|
||||
use rustc_span::Span;
|
||||
|
||||
use crate::constructor::{Constructor, IntRange, MaybeInfiniteInt, SplitConstructorSet};
|
||||
use crate::cx::MatchCheckCtxt;
|
||||
use crate::constructor::{IntRange, MaybeInfiniteInt};
|
||||
use crate::errors::{
|
||||
NonExhaustiveOmittedPattern, NonExhaustiveOmittedPatternLintOnArm, Overlap,
|
||||
OverlappingRangeEndpoints, Uncovered,
|
||||
};
|
||||
use crate::pat::{DeconstructedPat, WitnessPat};
|
||||
use crate::usefulness::PatCtxt;
|
||||
use crate::MatchArm;
|
||||
use crate::rustc::{
|
||||
Constructor, DeconstructedPat, MatchArm, MatchCtxt, PlaceCtxt, RustcMatchCheckCtxt,
|
||||
SplitConstructorSet, WitnessPat,
|
||||
};
|
||||
use crate::TypeCx;
|
||||
|
||||
/// A column of patterns in the matrix, where a column is the intuitive notion of "subpatterns that
|
||||
/// inspect the same subvalue/place".
|
||||
@ -27,11 +28,11 @@ use crate::MatchArm;
|
||||
///
|
||||
/// This is not used in the main algorithm; only in lints.
|
||||
#[derive(Debug)]
|
||||
pub(crate) struct PatternColumn<'p, 'tcx> {
|
||||
patterns: Vec<&'p DeconstructedPat<'p, 'tcx>>,
|
||||
pub(crate) struct PatternColumn<'a, 'p, 'tcx> {
|
||||
patterns: Vec<&'a DeconstructedPat<'p, 'tcx>>,
|
||||
}
|
||||
|
||||
impl<'p, 'tcx> PatternColumn<'p, 'tcx> {
|
||||
impl<'a, 'p, 'tcx> PatternColumn<'a, 'p, 'tcx> {
|
||||
pub(crate) fn new(arms: &[MatchArm<'p, 'tcx>]) -> Self {
|
||||
let mut patterns = Vec::with_capacity(arms.len());
|
||||
for arm in arms {
|
||||
@ -53,12 +54,11 @@ impl<'p, 'tcx> PatternColumn<'p, 'tcx> {
|
||||
}
|
||||
// If the type is opaque and it is revealed anywhere in the column, we take the revealed
|
||||
// version. Otherwise we could encounter constructors for the revealed type and crash.
|
||||
let is_opaque = |ty: Ty<'tcx>| matches!(ty.kind(), ty::Alias(ty::Opaque, ..));
|
||||
let first_ty = self.patterns[0].ty();
|
||||
if is_opaque(first_ty) {
|
||||
if RustcMatchCheckCtxt::is_opaque_ty(first_ty) {
|
||||
for pat in &self.patterns {
|
||||
let ty = pat.ty();
|
||||
if !is_opaque(ty) {
|
||||
if !RustcMatchCheckCtxt::is_opaque_ty(ty) {
|
||||
return Some(ty);
|
||||
}
|
||||
}
|
||||
@ -67,12 +67,12 @@ impl<'p, 'tcx> PatternColumn<'p, 'tcx> {
|
||||
}
|
||||
|
||||
/// Do constructor splitting on the constructors of the column.
|
||||
fn analyze_ctors(&self, pcx: &PatCtxt<'_, 'p, 'tcx>) -> SplitConstructorSet<'tcx> {
|
||||
fn analyze_ctors(&self, pcx: &PlaceCtxt<'_, 'p, 'tcx>) -> SplitConstructorSet<'p, 'tcx> {
|
||||
let column_ctors = self.patterns.iter().map(|p| p.ctor());
|
||||
pcx.cx.ctors_for_ty(pcx.ty).split(pcx, column_ctors)
|
||||
pcx.ctors_for_ty().split(pcx, column_ctors)
|
||||
}
|
||||
|
||||
fn iter<'a>(&'a self) -> impl Iterator<Item = &'p DeconstructedPat<'p, 'tcx>> + Captures<'a> {
|
||||
fn iter<'b>(&'b self) -> impl Iterator<Item = &'a DeconstructedPat<'p, 'tcx>> + Captures<'b> {
|
||||
self.patterns.iter().copied()
|
||||
}
|
||||
|
||||
@ -81,7 +81,11 @@ impl<'p, 'tcx> PatternColumn<'p, 'tcx> {
|
||||
/// This returns one column per field of the constructor. They usually all have the same length
|
||||
/// (the number of patterns in `self` that matched `ctor`), except that we expand or-patterns
|
||||
/// which may change the lengths.
|
||||
fn specialize(&self, pcx: &PatCtxt<'_, 'p, 'tcx>, ctor: &Constructor<'tcx>) -> Vec<Self> {
|
||||
fn specialize(
|
||||
&self,
|
||||
pcx: &PlaceCtxt<'a, 'p, 'tcx>,
|
||||
ctor: &Constructor<'p, 'tcx>,
|
||||
) -> Vec<PatternColumn<'a, 'p, 'tcx>> {
|
||||
let arity = ctor.arity(pcx);
|
||||
if arity == 0 {
|
||||
return Vec::new();
|
||||
@ -117,14 +121,14 @@ impl<'p, 'tcx> PatternColumn<'p, 'tcx> {
|
||||
/// Traverse the patterns to collect any variants of a non_exhaustive enum that fail to be mentioned
|
||||
/// in a given column.
|
||||
#[instrument(level = "debug", skip(cx), ret)]
|
||||
fn collect_nonexhaustive_missing_variants<'p, 'tcx>(
|
||||
cx: &MatchCheckCtxt<'p, 'tcx>,
|
||||
column: &PatternColumn<'p, 'tcx>,
|
||||
) -> Vec<WitnessPat<'tcx>> {
|
||||
fn collect_nonexhaustive_missing_variants<'a, 'p, 'tcx>(
|
||||
cx: MatchCtxt<'a, 'p, 'tcx>,
|
||||
column: &PatternColumn<'a, 'p, 'tcx>,
|
||||
) -> Vec<WitnessPat<'p, 'tcx>> {
|
||||
let Some(ty) = column.head_ty() else {
|
||||
return Vec::new();
|
||||
};
|
||||
let pcx = &PatCtxt::new_dummy(cx, ty);
|
||||
let pcx = &PlaceCtxt::new_dummy(cx, ty);
|
||||
|
||||
let set = column.analyze_ctors(pcx);
|
||||
if set.present.is_empty() {
|
||||
@ -135,7 +139,7 @@ fn collect_nonexhaustive_missing_variants<'p, 'tcx>(
|
||||
}
|
||||
|
||||
let mut witnesses = Vec::new();
|
||||
if cx.is_foreign_non_exhaustive_enum(ty) {
|
||||
if cx.tycx.is_foreign_non_exhaustive_enum(ty) {
|
||||
witnesses.extend(
|
||||
set.missing
|
||||
.into_iter()
|
||||
@ -164,14 +168,15 @@ fn collect_nonexhaustive_missing_variants<'p, 'tcx>(
|
||||
witnesses
|
||||
}
|
||||
|
||||
pub(crate) fn lint_nonexhaustive_missing_variants<'p, 'tcx>(
|
||||
cx: &MatchCheckCtxt<'p, 'tcx>,
|
||||
pub(crate) fn lint_nonexhaustive_missing_variants<'a, 'p, 'tcx>(
|
||||
cx: MatchCtxt<'a, 'p, 'tcx>,
|
||||
arms: &[MatchArm<'p, 'tcx>],
|
||||
pat_column: &PatternColumn<'p, 'tcx>,
|
||||
pat_column: &PatternColumn<'a, 'p, 'tcx>,
|
||||
scrut_ty: Ty<'tcx>,
|
||||
) {
|
||||
let rcx: &RustcMatchCheckCtxt<'_, '_> = cx.tycx;
|
||||
if !matches!(
|
||||
cx.tcx.lint_level_at_node(NON_EXHAUSTIVE_OMITTED_PATTERNS, cx.match_lint_level).0,
|
||||
rcx.tcx.lint_level_at_node(NON_EXHAUSTIVE_OMITTED_PATTERNS, rcx.match_lint_level).0,
|
||||
rustc_session::lint::Level::Allow
|
||||
) {
|
||||
let witnesses = collect_nonexhaustive_missing_variants(cx, pat_column);
|
||||
@ -180,13 +185,13 @@ pub(crate) fn lint_nonexhaustive_missing_variants<'p, 'tcx>(
|
||||
// is not exhaustive enough.
|
||||
//
|
||||
// NB: The partner lint for structs lives in `compiler/rustc_hir_analysis/src/check/pat.rs`.
|
||||
cx.tcx.emit_spanned_lint(
|
||||
rcx.tcx.emit_spanned_lint(
|
||||
NON_EXHAUSTIVE_OMITTED_PATTERNS,
|
||||
cx.match_lint_level,
|
||||
cx.scrut_span,
|
||||
rcx.match_lint_level,
|
||||
rcx.scrut_span,
|
||||
NonExhaustiveOmittedPattern {
|
||||
scrut_ty,
|
||||
uncovered: Uncovered::new(cx.scrut_span, cx, witnesses),
|
||||
uncovered: Uncovered::new(rcx.scrut_span, rcx, witnesses),
|
||||
},
|
||||
);
|
||||
}
|
||||
@ -196,17 +201,17 @@ pub(crate) fn lint_nonexhaustive_missing_variants<'p, 'tcx>(
|
||||
// usage of the lint.
|
||||
for arm in arms {
|
||||
let (lint_level, lint_level_source) =
|
||||
cx.tcx.lint_level_at_node(NON_EXHAUSTIVE_OMITTED_PATTERNS, arm.hir_id);
|
||||
rcx.tcx.lint_level_at_node(NON_EXHAUSTIVE_OMITTED_PATTERNS, arm.arm_data);
|
||||
if !matches!(lint_level, rustc_session::lint::Level::Allow) {
|
||||
let decorator = NonExhaustiveOmittedPatternLintOnArm {
|
||||
lint_span: lint_level_source.span(),
|
||||
suggest_lint_on_match: cx.whole_match_span.map(|span| span.shrink_to_lo()),
|
||||
suggest_lint_on_match: rcx.whole_match_span.map(|span| span.shrink_to_lo()),
|
||||
lint_level: lint_level.as_str(),
|
||||
lint_name: "non_exhaustive_omitted_patterns",
|
||||
};
|
||||
|
||||
use rustc_errors::DecorateLint;
|
||||
let mut err = cx.tcx.sess.struct_span_warn(arm.pat.span(), "");
|
||||
let mut err = rcx.tcx.sess.struct_span_warn(*arm.pat.data(), "");
|
||||
err.set_primary_message(decorator.msg());
|
||||
decorator.decorate_lint(&mut err);
|
||||
err.emit();
|
||||
@ -217,28 +222,29 @@ pub(crate) fn lint_nonexhaustive_missing_variants<'p, 'tcx>(
|
||||
|
||||
/// Traverse the patterns to warn the user about ranges that overlap on their endpoints.
|
||||
#[instrument(level = "debug", skip(cx))]
|
||||
pub(crate) fn lint_overlapping_range_endpoints<'p, 'tcx>(
|
||||
cx: &MatchCheckCtxt<'p, 'tcx>,
|
||||
column: &PatternColumn<'p, 'tcx>,
|
||||
pub(crate) fn lint_overlapping_range_endpoints<'a, 'p, 'tcx>(
|
||||
cx: MatchCtxt<'a, 'p, 'tcx>,
|
||||
column: &PatternColumn<'a, 'p, 'tcx>,
|
||||
) {
|
||||
let Some(ty) = column.head_ty() else {
|
||||
return;
|
||||
};
|
||||
let pcx = &PatCtxt::new_dummy(cx, ty);
|
||||
let pcx = &PlaceCtxt::new_dummy(cx, ty);
|
||||
let rcx: &RustcMatchCheckCtxt<'_, '_> = cx.tycx;
|
||||
|
||||
let set = column.analyze_ctors(pcx);
|
||||
|
||||
if matches!(ty.kind(), ty::Char | ty::Int(_) | ty::Uint(_)) {
|
||||
let emit_lint = |overlap: &IntRange, this_span: Span, overlapped_spans: &[Span]| {
|
||||
let overlap_as_pat = cx.hoist_pat_range(overlap, ty);
|
||||
let overlap_as_pat = rcx.hoist_pat_range(overlap, ty);
|
||||
let overlaps: Vec<_> = overlapped_spans
|
||||
.iter()
|
||||
.copied()
|
||||
.map(|span| Overlap { range: overlap_as_pat.clone(), span })
|
||||
.collect();
|
||||
cx.tcx.emit_spanned_lint(
|
||||
rcx.tcx.emit_spanned_lint(
|
||||
lint::builtin::OVERLAPPING_RANGE_ENDPOINTS,
|
||||
cx.match_lint_level,
|
||||
rcx.match_lint_level,
|
||||
this_span,
|
||||
OverlappingRangeEndpoints { overlap: overlaps, range: this_span },
|
||||
);
|
||||
@ -255,7 +261,7 @@ pub(crate) fn lint_overlapping_range_endpoints<'p, 'tcx>(
|
||||
let mut suffixes: SmallVec<[_; 1]> = Default::default();
|
||||
// Iterate on patterns that contained `overlap`.
|
||||
for pat in column.iter() {
|
||||
let this_span = pat.span();
|
||||
let this_span = *pat.data();
|
||||
let Constructor::IntRange(this_range) = pat.ctor() else { continue };
|
||||
if this_range.is_singleton() {
|
||||
// Don't lint when one of the ranges is a singleton.
|
||||
|
@ -5,16 +5,11 @@ use std::fmt;
|
||||
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
|
||||
use rustc_data_structures::captures::Captures;
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_span::{Span, DUMMY_SP};
|
||||
use crate::constructor::{Constructor, Slice, SliceKind};
|
||||
use crate::usefulness::PlaceCtxt;
|
||||
use crate::{Captures, TypeCx};
|
||||
|
||||
use self::Constructor::*;
|
||||
use self::SliceKind::*;
|
||||
|
||||
use crate::constructor::{Constructor, SliceKind};
|
||||
use crate::cx::MatchCheckCtxt;
|
||||
use crate::usefulness::PatCtxt;
|
||||
|
||||
/// Values and patterns can be represented as a constructor applied to some fields. This represents
|
||||
/// a pattern in this form.
|
||||
@ -27,34 +22,34 @@ use crate::usefulness::PatCtxt;
|
||||
/// This happens if a private or `non_exhaustive` field is uninhabited, because the code mustn't
|
||||
/// observe that it is uninhabited. In that case that field is not included in `fields`. Care must
|
||||
/// be taken when converting to/from `thir::Pat`.
|
||||
pub struct DeconstructedPat<'p, 'tcx> {
|
||||
ctor: Constructor<'tcx>,
|
||||
fields: &'p [DeconstructedPat<'p, 'tcx>],
|
||||
ty: Ty<'tcx>,
|
||||
span: Span,
|
||||
pub struct DeconstructedPat<'p, Cx: TypeCx> {
|
||||
ctor: Constructor<Cx>,
|
||||
fields: &'p [DeconstructedPat<'p, Cx>],
|
||||
ty: Cx::Ty,
|
||||
data: Cx::PatData,
|
||||
/// Whether removing this arm would change the behavior of the match expression.
|
||||
useful: Cell<bool>,
|
||||
}
|
||||
|
||||
impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> {
|
||||
pub fn wildcard(ty: Ty<'tcx>, span: Span) -> Self {
|
||||
Self::new(Wildcard, &[], ty, span)
|
||||
impl<'p, Cx: TypeCx> DeconstructedPat<'p, Cx> {
|
||||
pub fn wildcard(ty: Cx::Ty, data: Cx::PatData) -> Self {
|
||||
Self::new(Wildcard, &[], ty, data)
|
||||
}
|
||||
|
||||
pub fn new(
|
||||
ctor: Constructor<'tcx>,
|
||||
fields: &'p [DeconstructedPat<'p, 'tcx>],
|
||||
ty: Ty<'tcx>,
|
||||
span: Span,
|
||||
ctor: Constructor<Cx>,
|
||||
fields: &'p [DeconstructedPat<'p, Cx>],
|
||||
ty: Cx::Ty,
|
||||
data: Cx::PatData,
|
||||
) -> Self {
|
||||
DeconstructedPat { ctor, fields, ty, span, useful: Cell::new(false) }
|
||||
DeconstructedPat { ctor, fields, ty, data, useful: Cell::new(false) }
|
||||
}
|
||||
|
||||
pub(crate) fn is_or_pat(&self) -> bool {
|
||||
matches!(self.ctor, Or)
|
||||
}
|
||||
/// Expand this (possibly-nested) or-pattern into its alternatives.
|
||||
pub(crate) fn flatten_or_pat(&'p self) -> SmallVec<[&'p Self; 1]> {
|
||||
pub(crate) fn flatten_or_pat(&self) -> SmallVec<[&Self; 1]> {
|
||||
if self.is_or_pat() {
|
||||
self.iter_fields().flat_map(|p| p.flatten_or_pat()).collect()
|
||||
} else {
|
||||
@ -62,66 +57,64 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn ctor(&self) -> &Constructor<'tcx> {
|
||||
pub fn ctor(&self) -> &Constructor<Cx> {
|
||||
&self.ctor
|
||||
}
|
||||
pub fn ty(&self) -> Ty<'tcx> {
|
||||
pub fn ty(&self) -> Cx::Ty {
|
||||
self.ty
|
||||
}
|
||||
pub fn span(&self) -> Span {
|
||||
self.span
|
||||
pub fn data(&self) -> &Cx::PatData {
|
||||
&self.data
|
||||
}
|
||||
|
||||
pub fn iter_fields<'a>(
|
||||
&'a self,
|
||||
) -> impl Iterator<Item = &'p DeconstructedPat<'p, 'tcx>> + Captures<'a> {
|
||||
) -> impl Iterator<Item = &'p DeconstructedPat<'p, Cx>> + Captures<'a> {
|
||||
self.fields.iter()
|
||||
}
|
||||
|
||||
/// Specialize this pattern with a constructor.
|
||||
/// `other_ctor` can be different from `self.ctor`, but must be covered by it.
|
||||
pub(crate) fn specialize<'a>(
|
||||
&'a self,
|
||||
pcx: &PatCtxt<'_, 'p, 'tcx>,
|
||||
other_ctor: &Constructor<'tcx>,
|
||||
) -> SmallVec<[&'p DeconstructedPat<'p, 'tcx>; 2]> {
|
||||
&self,
|
||||
pcx: &PlaceCtxt<'a, 'p, Cx>,
|
||||
other_ctor: &Constructor<Cx>,
|
||||
) -> SmallVec<[&'a DeconstructedPat<'p, Cx>; 2]> {
|
||||
let wildcard_sub_tys = || {
|
||||
let tys = pcx.ctor_sub_tys(other_ctor);
|
||||
tys.iter()
|
||||
.map(|ty| DeconstructedPat::wildcard(*ty, Cx::PatData::default()))
|
||||
.map(|pat| pcx.mcx.wildcard_arena.alloc(pat) as &_)
|
||||
.collect()
|
||||
};
|
||||
match (&self.ctor, other_ctor) {
|
||||
(Wildcard, _) => {
|
||||
// We return a wildcard for each field of `other_ctor`.
|
||||
pcx.cx.ctor_wildcard_fields(other_ctor, pcx.ty).iter().collect()
|
||||
}
|
||||
(Slice(self_slice), Slice(other_slice))
|
||||
if self_slice.arity() != other_slice.arity() =>
|
||||
{
|
||||
// The only tricky case: two slices of different arity. Since `self_slice` covers
|
||||
// `other_slice`, `self_slice` must be `VarLen`, i.e. of the form
|
||||
// `[prefix, .., suffix]`. Moreover `other_slice` is guaranteed to have a larger
|
||||
// arity. So we fill the middle part with enough wildcards to reach the length of
|
||||
// the new, larger slice.
|
||||
match self_slice.kind {
|
||||
FixedLen(_) => bug!("{:?} doesn't cover {:?}", self_slice, other_slice),
|
||||
VarLen(prefix, suffix) => {
|
||||
let (ty::Slice(inner_ty) | ty::Array(inner_ty, _)) = *self.ty.kind() else {
|
||||
bug!("bad slice pattern {:?} {:?}", self.ctor, self.ty);
|
||||
};
|
||||
let prefix = &self.fields[..prefix];
|
||||
let suffix = &self.fields[self_slice.arity() - suffix..];
|
||||
let wildcard: &_ = pcx
|
||||
.cx
|
||||
.pattern_arena
|
||||
.alloc(DeconstructedPat::wildcard(inner_ty, DUMMY_SP));
|
||||
let extra_wildcards = other_slice.arity() - self_slice.arity();
|
||||
let extra_wildcards = (0..extra_wildcards).map(|_| wildcard);
|
||||
prefix.iter().chain(extra_wildcards).chain(suffix).collect()
|
||||
}
|
||||
// Return a wildcard for each field of `other_ctor`.
|
||||
(Wildcard, _) => wildcard_sub_tys(),
|
||||
// The only non-trivial case: two slices of different arity. `other_slice` is
|
||||
// guaranteed to have a larger arity, so we fill the middle part with enough
|
||||
// wildcards to reach the length of the new, larger slice.
|
||||
(
|
||||
&Slice(self_slice @ Slice { kind: SliceKind::VarLen(prefix, suffix), .. }),
|
||||
&Slice(other_slice),
|
||||
) if self_slice.arity() != other_slice.arity() => {
|
||||
// Start with a slice of wildcards of the appropriate length.
|
||||
let mut fields: SmallVec<[_; 2]> = wildcard_sub_tys();
|
||||
// Fill in the fields from both ends.
|
||||
let new_arity = fields.len();
|
||||
for i in 0..prefix {
|
||||
fields[i] = &self.fields[i];
|
||||
}
|
||||
for i in 0..suffix {
|
||||
fields[new_arity - 1 - i] = &self.fields[self.fields.len() - 1 - i];
|
||||
}
|
||||
fields
|
||||
}
|
||||
_ => self.fields.iter().collect(),
|
||||
}
|
||||
}
|
||||
|
||||
/// We keep track for each pattern if it was ever useful during the analysis. This is used
|
||||
/// with `redundant_spans` to report redundant subpatterns arising from or patterns.
|
||||
/// We keep track for each pattern if it was ever useful during the analysis. This is used with
|
||||
/// `redundant_subpatterns` to report redundant subpatterns arising from or patterns.
|
||||
pub(crate) fn set_useful(&self) {
|
||||
self.useful.set(true)
|
||||
}
|
||||
@ -139,19 +132,19 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Report the spans of subpatterns that were not useful, if any.
|
||||
pub(crate) fn redundant_spans(&self) -> Vec<Span> {
|
||||
let mut spans = Vec::new();
|
||||
self.collect_redundant_spans(&mut spans);
|
||||
spans
|
||||
/// Report the subpatterns that were not useful, if any.
|
||||
pub(crate) fn redundant_subpatterns(&self) -> Vec<&Self> {
|
||||
let mut subpats = Vec::new();
|
||||
self.collect_redundant_subpatterns(&mut subpats);
|
||||
subpats
|
||||
}
|
||||
fn collect_redundant_spans(&self, spans: &mut Vec<Span>) {
|
||||
fn collect_redundant_subpatterns<'a>(&'a self, subpats: &mut Vec<&'a Self>) {
|
||||
// We don't look at subpatterns if we already reported the whole pattern as redundant.
|
||||
if !self.is_useful() {
|
||||
spans.push(self.span);
|
||||
subpats.push(self);
|
||||
} else {
|
||||
for p in self.iter_fields() {
|
||||
p.collect_redundant_spans(spans);
|
||||
p.collect_redundant_subpatterns(subpats);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -159,47 +152,46 @@ impl<'p, 'tcx> DeconstructedPat<'p, 'tcx> {
|
||||
|
||||
/// This is mostly copied from the `Pat` impl. This is best effort and not good enough for a
|
||||
/// `Display` impl.
|
||||
impl<'p, 'tcx> fmt::Debug for DeconstructedPat<'p, 'tcx> {
|
||||
impl<'p, Cx: TypeCx> fmt::Debug for DeconstructedPat<'p, Cx> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
MatchCheckCtxt::debug_pat(f, self)
|
||||
Cx::debug_pat(f, self)
|
||||
}
|
||||
}
|
||||
|
||||
/// Same idea as `DeconstructedPat`, except this is a fictitious pattern built up for diagnostics
|
||||
/// purposes. As such they don't use interning and can be cloned.
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct WitnessPat<'tcx> {
|
||||
ctor: Constructor<'tcx>,
|
||||
pub(crate) fields: Vec<WitnessPat<'tcx>>,
|
||||
ty: Ty<'tcx>,
|
||||
pub struct WitnessPat<Cx: TypeCx> {
|
||||
ctor: Constructor<Cx>,
|
||||
pub(crate) fields: Vec<WitnessPat<Cx>>,
|
||||
ty: Cx::Ty,
|
||||
}
|
||||
|
||||
impl<'tcx> WitnessPat<'tcx> {
|
||||
pub(crate) fn new(ctor: Constructor<'tcx>, fields: Vec<Self>, ty: Ty<'tcx>) -> Self {
|
||||
impl<Cx: TypeCx> WitnessPat<Cx> {
|
||||
pub(crate) fn new(ctor: Constructor<Cx>, fields: Vec<Self>, ty: Cx::Ty) -> Self {
|
||||
Self { ctor, fields, ty }
|
||||
}
|
||||
pub(crate) fn wildcard(ty: Ty<'tcx>) -> Self {
|
||||
pub(crate) fn wildcard(ty: Cx::Ty) -> Self {
|
||||
Self::new(Wildcard, Vec::new(), ty)
|
||||
}
|
||||
|
||||
/// Construct a pattern that matches everything that starts with this constructor.
|
||||
/// For example, if `ctor` is a `Constructor::Variant` for `Option::Some`, we get the pattern
|
||||
/// `Some(_)`.
|
||||
pub(crate) fn wild_from_ctor(pcx: &PatCtxt<'_, '_, 'tcx>, ctor: Constructor<'tcx>) -> Self {
|
||||
let field_tys =
|
||||
pcx.cx.ctor_wildcard_fields(&ctor, pcx.ty).iter().map(|deco_pat| deco_pat.ty());
|
||||
let fields = field_tys.map(|ty| Self::wildcard(ty)).collect();
|
||||
pub(crate) fn wild_from_ctor(pcx: &PlaceCtxt<'_, '_, Cx>, ctor: Constructor<Cx>) -> Self {
|
||||
let field_tys = pcx.ctor_sub_tys(&ctor);
|
||||
let fields = field_tys.iter().map(|ty| Self::wildcard(*ty)).collect();
|
||||
Self::new(ctor, fields, pcx.ty)
|
||||
}
|
||||
|
||||
pub fn ctor(&self) -> &Constructor<'tcx> {
|
||||
pub fn ctor(&self) -> &Constructor<Cx> {
|
||||
&self.ctor
|
||||
}
|
||||
pub fn ty(&self) -> Ty<'tcx> {
|
||||
pub fn ty(&self) -> Cx::Ty {
|
||||
self.ty
|
||||
}
|
||||
|
||||
pub fn iter_fields<'a>(&'a self) -> impl Iterator<Item = &'a WitnessPat<'tcx>> {
|
||||
pub fn iter_fields<'a>(&'a self) -> impl Iterator<Item = &'a WitnessPat<Cx>> {
|
||||
self.fields.iter()
|
||||
}
|
||||
}
|
||||
|
@ -1,15 +1,15 @@
|
||||
use std::fmt;
|
||||
use std::iter::once;
|
||||
|
||||
use rustc_arena::TypedArena;
|
||||
use rustc_arena::{DroplessArena, TypedArena};
|
||||
use rustc_data_structures::captures::Captures;
|
||||
use rustc_hir::def_id::DefId;
|
||||
use rustc_hir::{HirId, RangeEnd};
|
||||
use rustc_hir::HirId;
|
||||
use rustc_index::Idx;
|
||||
use rustc_index::IndexVec;
|
||||
use rustc_middle::middle::stability::EvalResult;
|
||||
use rustc_middle::mir;
|
||||
use rustc_middle::mir::interpret::Scalar;
|
||||
use rustc_middle::mir::{self, Const};
|
||||
use rustc_middle::thir::{FieldPat, Pat, PatKind, PatRange, PatRangeBoundary};
|
||||
use rustc_middle::ty::layout::IntegerExt;
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt, VariantDef};
|
||||
@ -18,14 +18,31 @@ use rustc_target::abi::{FieldIdx, Integer, VariantIdx, FIRST_VARIANT};
|
||||
use smallvec::SmallVec;
|
||||
|
||||
use crate::constructor::{
|
||||
Constructor, ConstructorSet, IntRange, MaybeInfiniteInt, OpaqueId, Slice, SliceKind,
|
||||
VariantVisibility,
|
||||
IntRange, MaybeInfiniteInt, OpaqueId, RangeEnd, Slice, SliceKind, VariantVisibility,
|
||||
};
|
||||
use crate::pat::{DeconstructedPat, WitnessPat};
|
||||
use crate::TypeCx;
|
||||
|
||||
use Constructor::*;
|
||||
use crate::constructor::Constructor::*;
|
||||
|
||||
pub struct MatchCheckCtxt<'p, 'tcx> {
|
||||
// Re-export rustc-specific versions of all these types.
|
||||
pub type Constructor<'p, 'tcx> = crate::constructor::Constructor<RustcMatchCheckCtxt<'p, 'tcx>>;
|
||||
pub type ConstructorSet<'p, 'tcx> =
|
||||
crate::constructor::ConstructorSet<RustcMatchCheckCtxt<'p, 'tcx>>;
|
||||
pub type DeconstructedPat<'p, 'tcx> =
|
||||
crate::pat::DeconstructedPat<'p, RustcMatchCheckCtxt<'p, 'tcx>>;
|
||||
pub type MatchArm<'p, 'tcx> = crate::MatchArm<'p, RustcMatchCheckCtxt<'p, 'tcx>>;
|
||||
pub type MatchCtxt<'a, 'p, 'tcx> = crate::MatchCtxt<'a, 'p, RustcMatchCheckCtxt<'p, 'tcx>>;
|
||||
pub(crate) type PlaceCtxt<'a, 'p, 'tcx> =
|
||||
crate::usefulness::PlaceCtxt<'a, 'p, RustcMatchCheckCtxt<'p, 'tcx>>;
|
||||
pub(crate) type SplitConstructorSet<'p, 'tcx> =
|
||||
crate::constructor::SplitConstructorSet<RustcMatchCheckCtxt<'p, 'tcx>>;
|
||||
pub type Usefulness<'p, 'tcx> = crate::usefulness::Usefulness<'p, RustcMatchCheckCtxt<'p, 'tcx>>;
|
||||
pub type UsefulnessReport<'p, 'tcx> =
|
||||
crate::usefulness::UsefulnessReport<'p, RustcMatchCheckCtxt<'p, 'tcx>>;
|
||||
pub type WitnessPat<'p, 'tcx> = crate::pat::WitnessPat<RustcMatchCheckCtxt<'p, 'tcx>>;
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct RustcMatchCheckCtxt<'p, 'tcx> {
|
||||
pub tcx: TyCtxt<'tcx>,
|
||||
/// The module in which the match occurs. This is necessary for
|
||||
/// checking inhabited-ness of types because whether a type is (visibly)
|
||||
@ -35,6 +52,7 @@ pub struct MatchCheckCtxt<'p, 'tcx> {
|
||||
pub module: DefId,
|
||||
pub param_env: ty::ParamEnv<'tcx>,
|
||||
pub pattern_arena: &'p TypedArena<DeconstructedPat<'p, 'tcx>>,
|
||||
pub dropless_arena: &'p DroplessArena,
|
||||
/// Lint level at the match.
|
||||
pub match_lint_level: HirId,
|
||||
/// The span of the whole match, if applicable.
|
||||
@ -48,8 +66,14 @@ pub struct MatchCheckCtxt<'p, 'tcx> {
|
||||
pub known_valid_scrutinee: bool,
|
||||
}
|
||||
|
||||
impl<'p, 'tcx> MatchCheckCtxt<'p, 'tcx> {
|
||||
pub(super) fn is_uninhabited(&self, ty: Ty<'tcx>) -> bool {
|
||||
impl<'p, 'tcx> fmt::Debug for RustcMatchCheckCtxt<'p, 'tcx> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("RustcMatchCheckCtxt").finish()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'p, 'tcx> RustcMatchCheckCtxt<'p, 'tcx> {
|
||||
pub(crate) fn is_uninhabited(&self, ty: Ty<'tcx>) -> bool {
|
||||
!ty.is_inhabited_from(self.tcx, self.module, self.param_env)
|
||||
}
|
||||
|
||||
@ -63,12 +87,18 @@ impl<'p, 'tcx> MatchCheckCtxt<'p, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn alloc_wildcard_slice(
|
||||
&self,
|
||||
tys: impl IntoIterator<Item = Ty<'tcx>>,
|
||||
) -> &'p [DeconstructedPat<'p, 'tcx>] {
|
||||
self.pattern_arena
|
||||
.alloc_from_iter(tys.into_iter().map(|ty| DeconstructedPat::wildcard(ty, DUMMY_SP)))
|
||||
/// Whether the range denotes the fictitious values before `isize::MIN` or after
|
||||
/// `usize::MAX`/`isize::MAX` (see doc of [`IntRange::split`] for why these exist).
|
||||
pub fn is_range_beyond_boundaries(&self, range: &IntRange, ty: Ty<'tcx>) -> bool {
|
||||
ty.is_ptr_sized_integral() && {
|
||||
// The two invalid ranges are `NegInfinity..isize::MIN` (represented as
|
||||
// `NegInfinity..0`), and `{u,i}size::MAX+1..PosInfinity`. `hoist_pat_range_bdy`
|
||||
// converts `MAX+1` to `PosInfinity`, and we couldn't have `PosInfinity` in `range.lo`
|
||||
// otherwise.
|
||||
let lo = self.hoist_pat_range_bdy(range.lo, ty);
|
||||
matches!(lo, PatRangeBoundary::PosInfinity)
|
||||
|| matches!(range.hi, MaybeInfiniteInt::Finite(0))
|
||||
}
|
||||
}
|
||||
|
||||
// In the cases of either a `#[non_exhaustive]` field list or a non-public field, we hide
|
||||
@ -100,12 +130,12 @@ impl<'p, 'tcx> MatchCheckCtxt<'p, 'tcx> {
|
||||
}
|
||||
|
||||
pub(crate) fn variant_index_for_adt(
|
||||
ctor: &Constructor<'tcx>,
|
||||
ctor: &Constructor<'p, 'tcx>,
|
||||
adt: ty::AdtDef<'tcx>,
|
||||
) -> VariantIdx {
|
||||
match *ctor {
|
||||
Variant(idx) => idx,
|
||||
Single => {
|
||||
Struct | UnionField => {
|
||||
assert!(!adt.is_enum());
|
||||
FIRST_VARIANT
|
||||
}
|
||||
@ -113,37 +143,36 @@ impl<'p, 'tcx> MatchCheckCtxt<'p, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Creates a new list of wildcard fields for a given constructor. The result must have a length
|
||||
/// of `ctor.arity()`.
|
||||
/// Returns the types of the fields for a given constructor. The result must have a length of
|
||||
/// `ctor.arity()`.
|
||||
#[instrument(level = "trace", skip(self))]
|
||||
pub(crate) fn ctor_wildcard_fields(
|
||||
&self,
|
||||
ctor: &Constructor<'tcx>,
|
||||
ty: Ty<'tcx>,
|
||||
) -> &'p [DeconstructedPat<'p, 'tcx>] {
|
||||
pub(crate) fn ctor_sub_tys(&self, ctor: &Constructor<'p, 'tcx>, ty: Ty<'tcx>) -> &[Ty<'tcx>] {
|
||||
let cx = self;
|
||||
match ctor {
|
||||
Single | Variant(_) => match ty.kind() {
|
||||
ty::Tuple(fs) => cx.alloc_wildcard_slice(fs.iter()),
|
||||
ty::Ref(_, rty, _) => cx.alloc_wildcard_slice(once(*rty)),
|
||||
Struct | Variant(_) | UnionField => match ty.kind() {
|
||||
ty::Tuple(fs) => cx.dropless_arena.alloc_from_iter(fs.iter()),
|
||||
ty::Adt(adt, args) => {
|
||||
if adt.is_box() {
|
||||
// The only legal patterns of type `Box` (outside `std`) are `_` and box
|
||||
// patterns. If we're here we can assume this is a box pattern.
|
||||
cx.alloc_wildcard_slice(once(args.type_at(0)))
|
||||
cx.dropless_arena.alloc_from_iter(once(args.type_at(0)))
|
||||
} else {
|
||||
let variant =
|
||||
&adt.variant(MatchCheckCtxt::variant_index_for_adt(&ctor, *adt));
|
||||
&adt.variant(RustcMatchCheckCtxt::variant_index_for_adt(&ctor, *adt));
|
||||
let tys = cx.list_variant_nonhidden_fields(ty, variant).map(|(_, ty)| ty);
|
||||
cx.alloc_wildcard_slice(tys)
|
||||
cx.dropless_arena.alloc_from_iter(tys)
|
||||
}
|
||||
}
|
||||
_ => bug!("Unexpected type for `Single` constructor: {:?}", ty),
|
||||
_ => bug!("Unexpected type for constructor `{ctor:?}`: {ty:?}"),
|
||||
},
|
||||
Ref => match ty.kind() {
|
||||
ty::Ref(_, rty, _) => cx.dropless_arena.alloc_from_iter(once(*rty)),
|
||||
_ => bug!("Unexpected type for `Ref` constructor: {ty:?}"),
|
||||
},
|
||||
Slice(slice) => match *ty.kind() {
|
||||
ty::Slice(ty) | ty::Array(ty, _) => {
|
||||
let arity = slice.arity();
|
||||
cx.alloc_wildcard_slice((0..arity).map(|_| ty))
|
||||
cx.dropless_arena.alloc_from_iter((0..arity).map(|_| ty))
|
||||
}
|
||||
_ => bug!("bad slice pattern {:?} {:?}", ctor, ty),
|
||||
},
|
||||
@ -163,13 +192,11 @@ impl<'p, 'tcx> MatchCheckCtxt<'p, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
/// The number of fields for this constructor. This must be kept in sync with
|
||||
/// `Fields::wildcards`.
|
||||
pub(crate) fn ctor_arity(&self, ctor: &Constructor<'tcx>, ty: Ty<'tcx>) -> usize {
|
||||
/// The number of fields for this constructor.
|
||||
pub(crate) fn ctor_arity(&self, ctor: &Constructor<'p, 'tcx>, ty: Ty<'tcx>) -> usize {
|
||||
match ctor {
|
||||
Single | Variant(_) => match ty.kind() {
|
||||
Struct | Variant(_) | UnionField => match ty.kind() {
|
||||
ty::Tuple(fs) => fs.len(),
|
||||
ty::Ref(..) => 1,
|
||||
ty::Adt(adt, ..) => {
|
||||
if adt.is_box() {
|
||||
// The only legal patterns of type `Box` (outside `std`) are `_` and box
|
||||
@ -177,12 +204,13 @@ impl<'p, 'tcx> MatchCheckCtxt<'p, 'tcx> {
|
||||
1
|
||||
} else {
|
||||
let variant =
|
||||
&adt.variant(MatchCheckCtxt::variant_index_for_adt(&ctor, *adt));
|
||||
&adt.variant(RustcMatchCheckCtxt::variant_index_for_adt(&ctor, *adt));
|
||||
self.list_variant_nonhidden_fields(ty, variant).count()
|
||||
}
|
||||
}
|
||||
_ => bug!("Unexpected type for `Single` constructor: {:?}", ty),
|
||||
_ => bug!("Unexpected type for constructor `{ctor:?}`: {ty:?}"),
|
||||
},
|
||||
Ref => 1,
|
||||
Slice(slice) => slice.arity(),
|
||||
Bool(..)
|
||||
| IntRange(..)
|
||||
@ -202,7 +230,7 @@ impl<'p, 'tcx> MatchCheckCtxt<'p, 'tcx> {
|
||||
///
|
||||
/// See [`crate::constructor`] for considerations of emptiness.
|
||||
#[instrument(level = "debug", skip(self), ret)]
|
||||
pub fn ctors_for_ty(&self, ty: Ty<'tcx>) -> ConstructorSet {
|
||||
pub fn ctors_for_ty(&self, ty: Ty<'tcx>) -> ConstructorSet<'p, 'tcx> {
|
||||
let cx = self;
|
||||
let make_uint_range = |start, end| {
|
||||
IntRange::from_range(
|
||||
@ -298,9 +326,9 @@ impl<'p, 'tcx> MatchCheckCtxt<'p, 'tcx> {
|
||||
ConstructorSet::Variants { variants, non_exhaustive: is_declared_nonexhaustive }
|
||||
}
|
||||
}
|
||||
ty::Adt(..) | ty::Tuple(..) | ty::Ref(..) => {
|
||||
ConstructorSet::Single { empty: cx.is_uninhabited(ty) }
|
||||
}
|
||||
ty::Adt(def, _) if def.is_union() => ConstructorSet::Union,
|
||||
ty::Adt(..) | ty::Tuple(..) => ConstructorSet::Struct { empty: cx.is_uninhabited(ty) },
|
||||
ty::Ref(..) => ConstructorSet::Ref,
|
||||
ty::Never => ConstructorSet::NoConstructors,
|
||||
// This type is one for which we cannot list constructors, like `str` or `f64`.
|
||||
// FIXME(Nadrieril): which of these are actually allowed?
|
||||
@ -359,13 +387,18 @@ impl<'p, 'tcx> MatchCheckCtxt<'p, 'tcx> {
|
||||
fields = &[];
|
||||
}
|
||||
PatKind::Deref { subpattern } => {
|
||||
ctor = Single;
|
||||
fields = singleton(self.lower_pat(subpattern));
|
||||
ctor = match pat.ty.kind() {
|
||||
// This is a box pattern.
|
||||
ty::Adt(adt, ..) if adt.is_box() => Struct,
|
||||
ty::Ref(..) => Ref,
|
||||
_ => bug!("pattern has unexpected type: pat: {:?}, ty: {:?}", pat, pat.ty),
|
||||
};
|
||||
}
|
||||
PatKind::Leaf { subpatterns } | PatKind::Variant { subpatterns, .. } => {
|
||||
match pat.ty.kind() {
|
||||
ty::Tuple(fs) => {
|
||||
ctor = Single;
|
||||
ctor = Struct;
|
||||
let mut wilds: SmallVec<[_; 2]> =
|
||||
fs.iter().map(|ty| DeconstructedPat::wildcard(ty, pat.span)).collect();
|
||||
for pat in subpatterns {
|
||||
@ -380,7 +413,7 @@ impl<'p, 'tcx> MatchCheckCtxt<'p, 'tcx> {
|
||||
// _)` or a box pattern. As a hack to avoid an ICE with the former, we
|
||||
// ignore other fields than the first one. This will trigger an error later
|
||||
// anyway.
|
||||
// See https://github.com/rust-lang/rust/issues/82772 ,
|
||||
// See https://github.com/rust-lang/rust/issues/82772,
|
||||
// explanation: https://github.com/rust-lang/rust/pull/82789#issuecomment-796921977
|
||||
// The problem is that we can't know from the type whether we'll match
|
||||
// normally or through box-patterns. We'll have to figure out a proper
|
||||
@ -392,17 +425,18 @@ impl<'p, 'tcx> MatchCheckCtxt<'p, 'tcx> {
|
||||
} else {
|
||||
DeconstructedPat::wildcard(args.type_at(0), pat.span)
|
||||
};
|
||||
ctor = Single;
|
||||
ctor = Struct;
|
||||
fields = singleton(pat);
|
||||
}
|
||||
ty::Adt(adt, _) => {
|
||||
ctor = match pat.kind {
|
||||
PatKind::Leaf { .. } => Single,
|
||||
PatKind::Leaf { .. } if adt.is_union() => UnionField,
|
||||
PatKind::Leaf { .. } => Struct,
|
||||
PatKind::Variant { variant_index, .. } => Variant(variant_index),
|
||||
_ => bug!(),
|
||||
};
|
||||
let variant =
|
||||
&adt.variant(MatchCheckCtxt::variant_index_for_adt(&ctor, *adt));
|
||||
&adt.variant(RustcMatchCheckCtxt::variant_index_for_adt(&ctor, *adt));
|
||||
// For each field in the variant, we store the relevant index into `self.fields` if any.
|
||||
let mut field_id_to_id: Vec<Option<usize>> =
|
||||
(0..variant.fields.len()).map(|_| None).collect();
|
||||
@ -477,11 +511,11 @@ impl<'p, 'tcx> MatchCheckCtxt<'p, 'tcx> {
|
||||
// with other `Deref` patterns. This could have been done in `const_to_pat`,
|
||||
// but that causes issues with the rest of the matching code.
|
||||
// So here, the constructor for a `"foo"` pattern is `&` (represented by
|
||||
// `Single`), and has one field. That field has constructor `Str(value)` and no
|
||||
// fields.
|
||||
// `Ref`), and has one field. That field has constructor `Str(value)` and no
|
||||
// subfields.
|
||||
// Note: `t` is `str`, not `&str`.
|
||||
let subpattern = DeconstructedPat::new(Str(*value), &[], *t, pat.span);
|
||||
ctor = Single;
|
||||
ctor = Ref;
|
||||
fields = singleton(subpattern)
|
||||
}
|
||||
// All constants that can be structurally matched have already been expanded
|
||||
@ -495,12 +529,16 @@ impl<'p, 'tcx> MatchCheckCtxt<'p, 'tcx> {
|
||||
}
|
||||
PatKind::Range(patrange) => {
|
||||
let PatRange { lo, hi, end, .. } = patrange.as_ref();
|
||||
let end = match end {
|
||||
rustc_hir::RangeEnd::Included => RangeEnd::Included,
|
||||
rustc_hir::RangeEnd::Excluded => RangeEnd::Excluded,
|
||||
};
|
||||
let ty = pat.ty;
|
||||
ctor = match ty.kind() {
|
||||
ty::Char | ty::Int(_) | ty::Uint(_) => {
|
||||
let lo = cx.lower_pat_range_bdy(*lo, ty);
|
||||
let hi = cx.lower_pat_range_bdy(*hi, ty);
|
||||
IntRange(IntRange::from_range(lo, hi, *end))
|
||||
IntRange(IntRange::from_range(lo, hi, end))
|
||||
}
|
||||
ty::Float(fty) => {
|
||||
use rustc_apfloat::Float;
|
||||
@ -511,13 +549,13 @@ impl<'p, 'tcx> MatchCheckCtxt<'p, 'tcx> {
|
||||
use rustc_apfloat::ieee::Single;
|
||||
let lo = lo.map(Single::from_bits).unwrap_or(-Single::INFINITY);
|
||||
let hi = hi.map(Single::from_bits).unwrap_or(Single::INFINITY);
|
||||
F32Range(lo, hi, *end)
|
||||
F32Range(lo, hi, end)
|
||||
}
|
||||
ty::FloatTy::F64 => {
|
||||
use rustc_apfloat::ieee::Double;
|
||||
let lo = lo.map(Double::from_bits).unwrap_or(-Double::INFINITY);
|
||||
let hi = hi.map(Double::from_bits).unwrap_or(Double::INFINITY);
|
||||
F64Range(lo, hi, *end)
|
||||
F64Range(lo, hi, end)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -597,20 +635,6 @@ impl<'p, 'tcx> MatchCheckCtxt<'p, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
/// Whether the range denotes the fictitious values before `isize::MIN` or after
|
||||
/// `usize::MAX`/`isize::MAX` (see doc of [`IntRange::split`] for why these exist).
|
||||
pub fn is_range_beyond_boundaries(&self, range: &IntRange, ty: Ty<'tcx>) -> bool {
|
||||
ty.is_ptr_sized_integral() && {
|
||||
// The two invalid ranges are `NegInfinity..isize::MIN` (represented as
|
||||
// `NegInfinity..0`), and `{u,i}size::MAX+1..PosInfinity`. `hoist_pat_range_bdy`
|
||||
// converts `MAX+1` to `PosInfinity`, and we couldn't have `PosInfinity` in `range.lo`
|
||||
// otherwise.
|
||||
let lo = self.hoist_pat_range_bdy(range.lo, ty);
|
||||
matches!(lo, PatRangeBoundary::PosInfinity)
|
||||
|| matches!(range.hi, MaybeInfiniteInt::Finite(0))
|
||||
}
|
||||
}
|
||||
|
||||
/// Convert back to a `thir::Pat` for diagnostic purposes.
|
||||
pub(crate) fn hoist_pat_range(&self, range: &IntRange, ty: Ty<'tcx>) -> Pat<'tcx> {
|
||||
use MaybeInfiniteInt::*;
|
||||
@ -623,7 +647,7 @@ impl<'p, 'tcx> MatchCheckCtxt<'p, 'tcx> {
|
||||
PatKind::Constant { value }
|
||||
} else {
|
||||
// We convert to an inclusive range for diagnostics.
|
||||
let mut end = RangeEnd::Included;
|
||||
let mut end = rustc_hir::RangeEnd::Included;
|
||||
let mut lo = cx.hoist_pat_range_bdy(range.lo, ty);
|
||||
if matches!(lo, PatRangeBoundary::PosInfinity) {
|
||||
// The only reason to get `PosInfinity` here is the special case where
|
||||
@ -637,7 +661,7 @@ impl<'p, 'tcx> MatchCheckCtxt<'p, 'tcx> {
|
||||
}
|
||||
let hi = if matches!(range.hi, Finite(0)) {
|
||||
// The range encodes `..ty::MIN`, so we can't convert it to an inclusive range.
|
||||
end = RangeEnd::Excluded;
|
||||
end = rustc_hir::RangeEnd::Excluded;
|
||||
range.hi
|
||||
} else {
|
||||
range.hi.minus_one()
|
||||
@ -650,14 +674,14 @@ impl<'p, 'tcx> MatchCheckCtxt<'p, 'tcx> {
|
||||
}
|
||||
/// Convert back to a `thir::Pat` for diagnostic purposes. This panics for patterns that don't
|
||||
/// appear in diagnostics, like float ranges.
|
||||
pub fn hoist_witness_pat(&self, pat: &WitnessPat<'tcx>) -> Pat<'tcx> {
|
||||
pub fn hoist_witness_pat(&self, pat: &WitnessPat<'p, 'tcx>) -> Pat<'tcx> {
|
||||
let cx = self;
|
||||
let is_wildcard = |pat: &Pat<'_>| matches!(pat.kind, PatKind::Wild);
|
||||
let mut subpatterns = pat.iter_fields().map(|p| Box::new(cx.hoist_witness_pat(p)));
|
||||
let kind = match pat.ctor() {
|
||||
Bool(b) => PatKind::Constant { value: mir::Const::from_bool(cx.tcx, *b) },
|
||||
IntRange(range) => return self.hoist_pat_range(range, pat.ty()),
|
||||
Single | Variant(_) => match pat.ty().kind() {
|
||||
Struct | Variant(_) | UnionField => match pat.ty().kind() {
|
||||
ty::Tuple(..) => PatKind::Leaf {
|
||||
subpatterns: subpatterns
|
||||
.enumerate()
|
||||
@ -672,7 +696,7 @@ impl<'p, 'tcx> MatchCheckCtxt<'p, 'tcx> {
|
||||
}
|
||||
ty::Adt(adt_def, args) => {
|
||||
let variant_index =
|
||||
MatchCheckCtxt::variant_index_for_adt(&pat.ctor(), *adt_def);
|
||||
RustcMatchCheckCtxt::variant_index_for_adt(&pat.ctor(), *adt_def);
|
||||
let variant = &adt_def.variant(variant_index);
|
||||
let subpatterns = cx
|
||||
.list_variant_nonhidden_fields(pat.ty(), variant)
|
||||
@ -686,13 +710,13 @@ impl<'p, 'tcx> MatchCheckCtxt<'p, 'tcx> {
|
||||
PatKind::Leaf { subpatterns }
|
||||
}
|
||||
}
|
||||
// Note: given the expansion of `&str` patterns done in `expand_pattern`, we should
|
||||
// be careful to reconstruct the correct constant pattern here. However a string
|
||||
// literal pattern will never be reported as a non-exhaustiveness witness, so we
|
||||
// ignore this issue.
|
||||
ty::Ref(..) => PatKind::Deref { subpattern: subpatterns.next().unwrap() },
|
||||
_ => bug!("unexpected ctor for type {:?} {:?}", pat.ctor(), pat.ty()),
|
||||
},
|
||||
// Note: given the expansion of `&str` patterns done in `expand_pattern`, we should
|
||||
// be careful to reconstruct the correct constant pattern here. However a string
|
||||
// literal pattern will never be reported as a non-exhaustiveness witness, so we
|
||||
// ignore this issue.
|
||||
Ref => PatKind::Deref { subpattern: subpatterns.next().unwrap() },
|
||||
Slice(slice) => {
|
||||
match slice.kind {
|
||||
SliceKind::FixedLen(_) => PatKind::Slice {
|
||||
@ -744,7 +768,7 @@ impl<'p, 'tcx> MatchCheckCtxt<'p, 'tcx> {
|
||||
/// Best-effort `Debug` implementation.
|
||||
pub(crate) fn debug_pat(
|
||||
f: &mut fmt::Formatter<'_>,
|
||||
pat: &DeconstructedPat<'p, 'tcx>,
|
||||
pat: &crate::pat::DeconstructedPat<'_, Self>,
|
||||
) -> fmt::Result {
|
||||
let mut first = true;
|
||||
let mut start_or_continue = |s| {
|
||||
@ -758,7 +782,7 @@ impl<'p, 'tcx> MatchCheckCtxt<'p, 'tcx> {
|
||||
let mut start_or_comma = || start_or_continue(", ");
|
||||
|
||||
match pat.ctor() {
|
||||
Single | Variant(_) => match pat.ty().kind() {
|
||||
Struct | Variant(_) | UnionField => match pat.ty().kind() {
|
||||
ty::Adt(def, _) if def.is_box() => {
|
||||
// Without `box_patterns`, the only legal pattern of type `Box` is `_` (outside
|
||||
// of `std`). So this branch is only reachable when the feature is enabled and
|
||||
@ -767,13 +791,14 @@ impl<'p, 'tcx> MatchCheckCtxt<'p, 'tcx> {
|
||||
write!(f, "box {subpattern:?}")
|
||||
}
|
||||
ty::Adt(..) | ty::Tuple(..) => {
|
||||
let variant = match pat.ty().kind() {
|
||||
ty::Adt(adt, _) => Some(
|
||||
adt.variant(MatchCheckCtxt::variant_index_for_adt(pat.ctor(), *adt)),
|
||||
),
|
||||
ty::Tuple(_) => None,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
let variant =
|
||||
match pat.ty().kind() {
|
||||
ty::Adt(adt, _) => Some(adt.variant(
|
||||
RustcMatchCheckCtxt::variant_index_for_adt(pat.ctor(), *adt),
|
||||
)),
|
||||
ty::Tuple(_) => None,
|
||||
_ => unreachable!(),
|
||||
};
|
||||
|
||||
if let Some(variant) = variant {
|
||||
write!(f, "{}", variant.name)?;
|
||||
@ -789,15 +814,15 @@ impl<'p, 'tcx> MatchCheckCtxt<'p, 'tcx> {
|
||||
}
|
||||
write!(f, ")")
|
||||
}
|
||||
// Note: given the expansion of `&str` patterns done in `expand_pattern`, we should
|
||||
// be careful to detect strings here. However a string literal pattern will never
|
||||
// be reported as a non-exhaustiveness witness, so we can ignore this issue.
|
||||
ty::Ref(_, _, mutbl) => {
|
||||
let subpattern = pat.iter_fields().next().unwrap();
|
||||
write!(f, "&{}{:?}", mutbl.prefix_str(), subpattern)
|
||||
}
|
||||
_ => write!(f, "_"),
|
||||
},
|
||||
// Note: given the expansion of `&str` patterns done in `expand_pattern`, we should
|
||||
// be careful to detect strings here. However a string literal pattern will never
|
||||
// be reported as a non-exhaustiveness witness, so we can ignore this issue.
|
||||
Ref => {
|
||||
let subpattern = pat.iter_fields().next().unwrap();
|
||||
write!(f, "&{:?}", subpattern)
|
||||
}
|
||||
Slice(slice) => {
|
||||
let mut subpatterns = pat.iter_fields();
|
||||
write!(f, "[")?;
|
||||
@ -838,6 +863,45 @@ impl<'p, 'tcx> MatchCheckCtxt<'p, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'p, 'tcx> TypeCx for RustcMatchCheckCtxt<'p, 'tcx> {
|
||||
type Ty = Ty<'tcx>;
|
||||
type VariantIdx = VariantIdx;
|
||||
type StrLit = Const<'tcx>;
|
||||
type ArmData = HirId;
|
||||
type PatData = Span;
|
||||
|
||||
fn is_exhaustive_patterns_feature_on(&self) -> bool {
|
||||
self.tcx.features().exhaustive_patterns
|
||||
}
|
||||
fn is_opaque_ty(ty: Self::Ty) -> bool {
|
||||
matches!(ty.kind(), ty::Alias(ty::Opaque, ..))
|
||||
}
|
||||
|
||||
fn ctor_arity(&self, ctor: &crate::constructor::Constructor<Self>, ty: Self::Ty) -> usize {
|
||||
self.ctor_arity(ctor, ty)
|
||||
}
|
||||
fn ctor_sub_tys(
|
||||
&self,
|
||||
ctor: &crate::constructor::Constructor<Self>,
|
||||
ty: Self::Ty,
|
||||
) -> &[Self::Ty] {
|
||||
self.ctor_sub_tys(ctor, ty)
|
||||
}
|
||||
fn ctors_for_ty(&self, ty: Self::Ty) -> crate::constructor::ConstructorSet<Self> {
|
||||
self.ctors_for_ty(ty)
|
||||
}
|
||||
|
||||
fn debug_pat(
|
||||
f: &mut fmt::Formatter<'_>,
|
||||
pat: &crate::pat::DeconstructedPat<'_, Self>,
|
||||
) -> fmt::Result {
|
||||
Self::debug_pat(f, pat)
|
||||
}
|
||||
fn bug(&self, fmt: fmt::Arguments<'_>) -> ! {
|
||||
span_bug!(self.scrut_span, "{}", fmt)
|
||||
}
|
||||
}
|
||||
|
||||
/// Recursively expand this pattern into its subpatterns. Only useful for or-patterns.
|
||||
fn expand_or_pat<'p, 'tcx>(pat: &'p Pat<'tcx>) -> Vec<&'p Pat<'tcx>> {
|
||||
fn expand<'p, 'tcx>(pat: &'p Pat<'tcx>, vec: &mut Vec<&'p Pat<'tcx>>) {
|
@ -242,7 +242,7 @@
|
||||
//! Therefore `usefulness(tp_1, tp_2, tq)` returns the single witness-tuple `[Variant2(Some(true), 0)]`.
|
||||
//!
|
||||
//!
|
||||
//! Computing the set of constructors for a type is done in [`MatchCheckCtxt::ctors_for_ty`]. See
|
||||
//! Computing the set of constructors for a type is done in [`TypeCx::ctors_for_ty`]. See
|
||||
//! the following sections for more accurate versions of the algorithm and corresponding links.
|
||||
//!
|
||||
//!
|
||||
@ -555,37 +555,52 @@
|
||||
use smallvec::{smallvec, SmallVec};
|
||||
use std::fmt;
|
||||
|
||||
use rustc_data_structures::{captures::Captures, stack::ensure_sufficient_stack};
|
||||
use rustc_middle::ty::{self, Ty};
|
||||
use rustc_span::{Span, DUMMY_SP};
|
||||
|
||||
use crate::constructor::{Constructor, ConstructorSet};
|
||||
use crate::cx::MatchCheckCtxt;
|
||||
use crate::pat::{DeconstructedPat, WitnessPat};
|
||||
use crate::MatchArm;
|
||||
use crate::{Captures, MatchArm, MatchCtxt, TypeCx, TypedArena};
|
||||
|
||||
use self::ValidityConstraint::*;
|
||||
|
||||
#[derive(Copy, Clone)]
|
||||
pub(crate) struct PatCtxt<'a, 'p, 'tcx> {
|
||||
pub(crate) cx: &'a MatchCheckCtxt<'p, 'tcx>,
|
||||
/// Type of the current column under investigation.
|
||||
pub(crate) ty: Ty<'tcx>,
|
||||
/// Whether the current pattern is the whole pattern as found in a match arm, or if it's a
|
||||
/// subpattern.
|
||||
pub(crate) is_top_level: bool,
|
||||
#[cfg(feature = "rustc")]
|
||||
use rustc_data_structures::stack::ensure_sufficient_stack;
|
||||
#[cfg(not(feature = "rustc"))]
|
||||
pub fn ensure_sufficient_stack<R>(f: impl FnOnce() -> R) -> R {
|
||||
f()
|
||||
}
|
||||
|
||||
impl<'a, 'p, 'tcx> PatCtxt<'a, 'p, 'tcx> {
|
||||
/// A `PatCtxt` when code other than `is_useful` needs one.
|
||||
pub(crate) fn new_dummy(cx: &'a MatchCheckCtxt<'p, 'tcx>, ty: Ty<'tcx>) -> Self {
|
||||
PatCtxt { cx, ty, is_top_level: false }
|
||||
/// Context that provides information local to a place under investigation.
|
||||
#[derive(Clone)]
|
||||
pub(crate) struct PlaceCtxt<'a, 'p, Cx: TypeCx> {
|
||||
pub(crate) mcx: MatchCtxt<'a, 'p, Cx>,
|
||||
/// Type of the place under investigation.
|
||||
pub(crate) ty: Cx::Ty,
|
||||
/// Whether the place is the original scrutinee place, as opposed to a subplace of it.
|
||||
pub(crate) is_scrutinee: bool,
|
||||
}
|
||||
|
||||
impl<'a, 'p, Cx: TypeCx> PlaceCtxt<'a, 'p, Cx> {
|
||||
/// A `PlaceCtxt` when code other than `is_useful` needs one.
|
||||
#[cfg_attr(not(feature = "rustc"), allow(dead_code))]
|
||||
pub(crate) fn new_dummy(mcx: MatchCtxt<'a, 'p, Cx>, ty: Cx::Ty) -> Self {
|
||||
PlaceCtxt { mcx, ty, is_scrutinee: false }
|
||||
}
|
||||
|
||||
pub(crate) fn ctor_arity(&self, ctor: &Constructor<Cx>) -> usize {
|
||||
self.mcx.tycx.ctor_arity(ctor, self.ty)
|
||||
}
|
||||
pub(crate) fn ctor_sub_tys(&self, ctor: &Constructor<Cx>) -> &[Cx::Ty] {
|
||||
self.mcx.tycx.ctor_sub_tys(ctor, self.ty)
|
||||
}
|
||||
pub(crate) fn ctors_for_ty(&self) -> ConstructorSet<Cx> {
|
||||
self.mcx.tycx.ctors_for_ty(self.ty)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'a, 'p, 'tcx> fmt::Debug for PatCtxt<'a, 'p, 'tcx> {
|
||||
impl<'a, 'p, Cx: TypeCx> Copy for PlaceCtxt<'a, 'p, Cx> {}
|
||||
|
||||
impl<'a, 'p, Cx: TypeCx> fmt::Debug for PlaceCtxt<'a, 'p, Cx> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
f.debug_struct("PatCtxt").field("ty", &self.ty).finish()
|
||||
f.debug_struct("PlaceCtxt").field("ty", &self.ty).finish()
|
||||
}
|
||||
}
|
||||
|
||||
@ -595,7 +610,7 @@ impl<'a, 'p, 'tcx> fmt::Debug for PatCtxt<'a, 'p, 'tcx> {
|
||||
/// - in the matrix, track whether a given place (aka column) is known to contain a valid value or
|
||||
/// not.
|
||||
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
|
||||
enum ValidityConstraint {
|
||||
pub enum ValidityConstraint {
|
||||
ValidOnly,
|
||||
MaybeInvalid,
|
||||
/// Option for backwards compatibility: the place is not known to be valid but we allow omitting
|
||||
@ -604,7 +619,7 @@ enum ValidityConstraint {
|
||||
}
|
||||
|
||||
impl ValidityConstraint {
|
||||
fn from_bool(is_valid_only: bool) -> Self {
|
||||
pub fn from_bool(is_valid_only: bool) -> Self {
|
||||
if is_valid_only { ValidOnly } else { MaybeInvalid }
|
||||
}
|
||||
|
||||
@ -629,12 +644,9 @@ impl ValidityConstraint {
|
||||
///
|
||||
/// Pending further opsem decisions, the current behavior is: validity is preserved, except
|
||||
/// inside `&` and union fields where validity is reset to `MaybeInvalid`.
|
||||
fn specialize<'tcx>(self, pcx: &PatCtxt<'_, '_, 'tcx>, ctor: &Constructor<'tcx>) -> Self {
|
||||
fn specialize<Cx: TypeCx>(self, ctor: &Constructor<Cx>) -> Self {
|
||||
// We preserve validity except when we go inside a reference or a union field.
|
||||
if matches!(ctor, Constructor::Single)
|
||||
&& (matches!(pcx.ty.kind(), ty::Ref(..))
|
||||
|| matches!(pcx.ty.kind(), ty::Adt(def, ..) if def.is_union()))
|
||||
{
|
||||
if matches!(ctor, Constructor::Ref | Constructor::UnionField) {
|
||||
// Validity of `x: &T` does not imply validity of `*x: T`.
|
||||
MaybeInvalid
|
||||
} else {
|
||||
@ -654,14 +666,18 @@ impl fmt::Display for ValidityConstraint {
|
||||
}
|
||||
|
||||
/// Represents a pattern-tuple under investigation.
|
||||
// The three lifetimes are:
|
||||
// - 'a allocated by us
|
||||
// - 'p coming from the input
|
||||
// - Cx global compilation context
|
||||
#[derive(Clone)]
|
||||
struct PatStack<'p, 'tcx> {
|
||||
struct PatStack<'a, 'p, Cx: TypeCx> {
|
||||
// Rows of len 1 are very common, which is why `SmallVec[_; 2]` works well.
|
||||
pats: SmallVec<[&'p DeconstructedPat<'p, 'tcx>; 2]>,
|
||||
pats: SmallVec<[&'a DeconstructedPat<'p, Cx>; 2]>,
|
||||
}
|
||||
|
||||
impl<'p, 'tcx> PatStack<'p, 'tcx> {
|
||||
fn from_pattern(pat: &'p DeconstructedPat<'p, 'tcx>) -> Self {
|
||||
impl<'a, 'p, Cx: TypeCx> PatStack<'a, 'p, Cx> {
|
||||
fn from_pattern(pat: &'a DeconstructedPat<'p, Cx>) -> Self {
|
||||
PatStack { pats: smallvec![pat] }
|
||||
}
|
||||
|
||||
@ -673,17 +689,17 @@ impl<'p, 'tcx> PatStack<'p, 'tcx> {
|
||||
self.pats.len()
|
||||
}
|
||||
|
||||
fn head(&self) -> &'p DeconstructedPat<'p, 'tcx> {
|
||||
fn head(&self) -> &'a DeconstructedPat<'p, Cx> {
|
||||
self.pats[0]
|
||||
}
|
||||
|
||||
fn iter(&self) -> impl Iterator<Item = &DeconstructedPat<'p, 'tcx>> {
|
||||
fn iter<'b>(&'b self) -> impl Iterator<Item = &'a DeconstructedPat<'p, Cx>> + Captures<'b> {
|
||||
self.pats.iter().copied()
|
||||
}
|
||||
|
||||
// Recursively expand the first or-pattern into its subpatterns. Only useful if the pattern is
|
||||
// an or-pattern. Panics if `self` is empty.
|
||||
fn expand_or_pat<'a>(&'a self) -> impl Iterator<Item = PatStack<'p, 'tcx>> + Captures<'a> {
|
||||
fn expand_or_pat<'b>(&'b self) -> impl Iterator<Item = PatStack<'a, 'p, Cx>> + Captures<'b> {
|
||||
self.head().flatten_or_pat().into_iter().map(move |pat| {
|
||||
let mut new = self.clone();
|
||||
new.pats[0] = pat;
|
||||
@ -695,9 +711,9 @@ impl<'p, 'tcx> PatStack<'p, 'tcx> {
|
||||
/// Only call if `ctor.is_covered_by(self.head().ctor())` is true.
|
||||
fn pop_head_constructor(
|
||||
&self,
|
||||
pcx: &PatCtxt<'_, 'p, 'tcx>,
|
||||
ctor: &Constructor<'tcx>,
|
||||
) -> PatStack<'p, 'tcx> {
|
||||
pcx: &PlaceCtxt<'a, 'p, Cx>,
|
||||
ctor: &Constructor<Cx>,
|
||||
) -> PatStack<'a, 'p, Cx> {
|
||||
// We pop the head pattern and push the new fields extracted from the arguments of
|
||||
// `self.head()`.
|
||||
let mut new_pats = self.head().specialize(pcx, ctor);
|
||||
@ -706,7 +722,7 @@ impl<'p, 'tcx> PatStack<'p, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'p, 'tcx> fmt::Debug for PatStack<'p, 'tcx> {
|
||||
impl<'a, 'p, Cx: TypeCx> fmt::Debug for PatStack<'a, 'p, Cx> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
// We pretty-print similarly to the `Debug` impl of `Matrix`.
|
||||
write!(f, "+")?;
|
||||
@ -719,9 +735,9 @@ impl<'p, 'tcx> fmt::Debug for PatStack<'p, 'tcx> {
|
||||
|
||||
/// A row of the matrix.
|
||||
#[derive(Clone)]
|
||||
struct MatrixRow<'p, 'tcx> {
|
||||
struct MatrixRow<'a, 'p, Cx: TypeCx> {
|
||||
// The patterns in the row.
|
||||
pats: PatStack<'p, 'tcx>,
|
||||
pats: PatStack<'a, 'p, Cx>,
|
||||
/// Whether the original arm had a guard. This is inherited when specializing.
|
||||
is_under_guard: bool,
|
||||
/// When we specialize, we remember which row of the original matrix produced a given row of the
|
||||
@ -734,7 +750,7 @@ struct MatrixRow<'p, 'tcx> {
|
||||
useful: bool,
|
||||
}
|
||||
|
||||
impl<'p, 'tcx> MatrixRow<'p, 'tcx> {
|
||||
impl<'a, 'p, Cx: TypeCx> MatrixRow<'a, 'p, Cx> {
|
||||
fn is_empty(&self) -> bool {
|
||||
self.pats.is_empty()
|
||||
}
|
||||
@ -743,17 +759,17 @@ impl<'p, 'tcx> MatrixRow<'p, 'tcx> {
|
||||
self.pats.len()
|
||||
}
|
||||
|
||||
fn head(&self) -> &'p DeconstructedPat<'p, 'tcx> {
|
||||
fn head(&self) -> &'a DeconstructedPat<'p, Cx> {
|
||||
self.pats.head()
|
||||
}
|
||||
|
||||
fn iter(&self) -> impl Iterator<Item = &DeconstructedPat<'p, 'tcx>> {
|
||||
fn iter<'b>(&'b self) -> impl Iterator<Item = &'a DeconstructedPat<'p, Cx>> + Captures<'b> {
|
||||
self.pats.iter()
|
||||
}
|
||||
|
||||
// Recursively expand the first or-pattern into its subpatterns. Only useful if the pattern is
|
||||
// an or-pattern. Panics if `self` is empty.
|
||||
fn expand_or_pat<'a>(&'a self) -> impl Iterator<Item = MatrixRow<'p, 'tcx>> + Captures<'a> {
|
||||
fn expand_or_pat<'b>(&'b self) -> impl Iterator<Item = MatrixRow<'a, 'p, Cx>> + Captures<'b> {
|
||||
self.pats.expand_or_pat().map(|patstack| MatrixRow {
|
||||
pats: patstack,
|
||||
parent_row: self.parent_row,
|
||||
@ -766,10 +782,10 @@ impl<'p, 'tcx> MatrixRow<'p, 'tcx> {
|
||||
/// Only call if `ctor.is_covered_by(self.head().ctor())` is true.
|
||||
fn pop_head_constructor(
|
||||
&self,
|
||||
pcx: &PatCtxt<'_, 'p, 'tcx>,
|
||||
ctor: &Constructor<'tcx>,
|
||||
pcx: &PlaceCtxt<'a, 'p, Cx>,
|
||||
ctor: &Constructor<Cx>,
|
||||
parent_row: usize,
|
||||
) -> MatrixRow<'p, 'tcx> {
|
||||
) -> MatrixRow<'a, 'p, Cx> {
|
||||
MatrixRow {
|
||||
pats: self.pats.pop_head_constructor(pcx, ctor),
|
||||
parent_row,
|
||||
@ -779,7 +795,7 @@ impl<'p, 'tcx> MatrixRow<'p, 'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'p, 'tcx> fmt::Debug for MatrixRow<'p, 'tcx> {
|
||||
impl<'a, 'p, Cx: TypeCx> fmt::Debug for MatrixRow<'a, 'p, Cx> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
self.pats.fmt(f)
|
||||
}
|
||||
@ -796,22 +812,22 @@ impl<'p, 'tcx> fmt::Debug for MatrixRow<'p, 'tcx> {
|
||||
/// specializing `(,)` and `Some` on a pattern of type `(Option<u32>, bool)`, the first column of
|
||||
/// the matrix will correspond to `scrutinee.0.Some.0` and the second column to `scrutinee.1`.
|
||||
#[derive(Clone)]
|
||||
struct Matrix<'p, 'tcx> {
|
||||
struct Matrix<'a, 'p, Cx: TypeCx> {
|
||||
/// Vector of rows. The rows must form a rectangular 2D array. Moreover, all the patterns of
|
||||
/// each column must have the same type. Each column corresponds to a place within the
|
||||
/// scrutinee.
|
||||
rows: Vec<MatrixRow<'p, 'tcx>>,
|
||||
rows: Vec<MatrixRow<'a, 'p, Cx>>,
|
||||
/// Stores an extra fictitious row full of wildcards. Mostly used to keep track of the type of
|
||||
/// each column. This must obey the same invariants as the real rows.
|
||||
wildcard_row: PatStack<'p, 'tcx>,
|
||||
wildcard_row: PatStack<'a, 'p, Cx>,
|
||||
/// Track for each column/place whether it contains a known valid value.
|
||||
place_validity: SmallVec<[ValidityConstraint; 2]>,
|
||||
}
|
||||
|
||||
impl<'p, 'tcx> Matrix<'p, 'tcx> {
|
||||
impl<'a, 'p, Cx: TypeCx> Matrix<'a, 'p, Cx> {
|
||||
/// Pushes a new row to the matrix. If the row starts with an or-pattern, this recursively
|
||||
/// expands it. Internal method, prefer [`Matrix::new`].
|
||||
fn expand_and_push(&mut self, row: MatrixRow<'p, 'tcx>) {
|
||||
fn expand_and_push(&mut self, row: MatrixRow<'a, 'p, Cx>) {
|
||||
if !row.is_empty() && row.head().is_or_pat() {
|
||||
// Expand nested or-patterns.
|
||||
for new_row in row.expand_or_pat() {
|
||||
@ -823,16 +839,14 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> {
|
||||
}
|
||||
|
||||
/// Build a new matrix from an iterator of `MatchArm`s.
|
||||
fn new<'a>(
|
||||
cx: &MatchCheckCtxt<'p, 'tcx>,
|
||||
arms: &[MatchArm<'p, 'tcx>],
|
||||
scrut_ty: Ty<'tcx>,
|
||||
fn new(
|
||||
wildcard_arena: &'a TypedArena<DeconstructedPat<'p, Cx>>,
|
||||
arms: &'a [MatchArm<'p, Cx>],
|
||||
scrut_ty: Cx::Ty,
|
||||
scrut_validity: ValidityConstraint,
|
||||
) -> Self
|
||||
where
|
||||
'p: 'a,
|
||||
{
|
||||
let wild_pattern = cx.pattern_arena.alloc(DeconstructedPat::wildcard(scrut_ty, DUMMY_SP));
|
||||
) -> Self {
|
||||
let wild_pattern =
|
||||
wildcard_arena.alloc(DeconstructedPat::wildcard(scrut_ty, Default::default()));
|
||||
let wildcard_row = PatStack::from_pattern(wild_pattern);
|
||||
let mut matrix = Matrix {
|
||||
rows: Vec::with_capacity(arms.len()),
|
||||
@ -851,7 +865,7 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> {
|
||||
matrix
|
||||
}
|
||||
|
||||
fn head_ty(&self) -> Option<Ty<'tcx>> {
|
||||
fn head_ty(&self) -> Option<Cx::Ty> {
|
||||
if self.column_count() == 0 {
|
||||
return None;
|
||||
}
|
||||
@ -859,11 +873,10 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> {
|
||||
let mut ty = self.wildcard_row.head().ty();
|
||||
// If the type is opaque and it is revealed anywhere in the column, we take the revealed
|
||||
// version. Otherwise we could encounter constructors for the revealed type and crash.
|
||||
let is_opaque = |ty: Ty<'tcx>| matches!(ty.kind(), ty::Alias(ty::Opaque, ..));
|
||||
if is_opaque(ty) {
|
||||
if Cx::is_opaque_ty(ty) {
|
||||
for pat in self.heads() {
|
||||
let pat_ty = pat.ty();
|
||||
if !is_opaque(pat_ty) {
|
||||
if !Cx::is_opaque_ty(pat_ty) {
|
||||
ty = pat_ty;
|
||||
break;
|
||||
}
|
||||
@ -875,34 +888,34 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> {
|
||||
self.wildcard_row.len()
|
||||
}
|
||||
|
||||
fn rows<'a>(
|
||||
&'a self,
|
||||
) -> impl Iterator<Item = &'a MatrixRow<'p, 'tcx>> + Clone + DoubleEndedIterator + ExactSizeIterator
|
||||
fn rows<'b>(
|
||||
&'b self,
|
||||
) -> impl Iterator<Item = &'b MatrixRow<'a, 'p, Cx>> + Clone + DoubleEndedIterator + ExactSizeIterator
|
||||
{
|
||||
self.rows.iter()
|
||||
}
|
||||
fn rows_mut<'a>(
|
||||
&'a mut self,
|
||||
) -> impl Iterator<Item = &'a mut MatrixRow<'p, 'tcx>> + DoubleEndedIterator + ExactSizeIterator
|
||||
fn rows_mut<'b>(
|
||||
&'b mut self,
|
||||
) -> impl Iterator<Item = &'b mut MatrixRow<'a, 'p, Cx>> + DoubleEndedIterator + ExactSizeIterator
|
||||
{
|
||||
self.rows.iter_mut()
|
||||
}
|
||||
|
||||
/// Iterate over the first pattern of each row.
|
||||
fn heads<'a>(
|
||||
&'a self,
|
||||
) -> impl Iterator<Item = &'p DeconstructedPat<'p, 'tcx>> + Clone + Captures<'a> {
|
||||
fn heads<'b>(
|
||||
&'b self,
|
||||
) -> impl Iterator<Item = &'b DeconstructedPat<'p, Cx>> + Clone + Captures<'a> {
|
||||
self.rows().map(|r| r.head())
|
||||
}
|
||||
|
||||
/// This computes `specialize(ctor, self)`. See top of the file for explanations.
|
||||
fn specialize_constructor(
|
||||
&self,
|
||||
pcx: &PatCtxt<'_, 'p, 'tcx>,
|
||||
ctor: &Constructor<'tcx>,
|
||||
) -> Matrix<'p, 'tcx> {
|
||||
pcx: &PlaceCtxt<'a, 'p, Cx>,
|
||||
ctor: &Constructor<Cx>,
|
||||
) -> Matrix<'a, 'p, Cx> {
|
||||
let wildcard_row = self.wildcard_row.pop_head_constructor(pcx, ctor);
|
||||
let new_validity = self.place_validity[0].specialize(pcx, ctor);
|
||||
let new_validity = self.place_validity[0].specialize(ctor);
|
||||
let new_place_validity = std::iter::repeat(new_validity)
|
||||
.take(ctor.arity(pcx))
|
||||
.chain(self.place_validity[1..].iter().copied())
|
||||
@ -929,7 +942,7 @@ impl<'p, 'tcx> Matrix<'p, 'tcx> {
|
||||
/// + _ + [_, _, tail @ ..] +
|
||||
/// | ✓ | ? | // column validity
|
||||
/// ```
|
||||
impl<'p, 'tcx> fmt::Debug for Matrix<'p, 'tcx> {
|
||||
impl<'a, 'p, Cx: TypeCx> fmt::Debug for Matrix<'a, 'p, Cx> {
|
||||
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
|
||||
write!(f, "\n")?;
|
||||
|
||||
@ -1020,17 +1033,17 @@ impl<'p, 'tcx> fmt::Debug for Matrix<'p, 'tcx> {
|
||||
///
|
||||
/// See the top of the file for more detailed explanations and examples.
|
||||
#[derive(Debug, Clone)]
|
||||
struct WitnessStack<'tcx>(Vec<WitnessPat<'tcx>>);
|
||||
struct WitnessStack<Cx: TypeCx>(Vec<WitnessPat<Cx>>);
|
||||
|
||||
impl<'tcx> WitnessStack<'tcx> {
|
||||
impl<Cx: TypeCx> WitnessStack<Cx> {
|
||||
/// Asserts that the witness contains a single pattern, and returns it.
|
||||
fn single_pattern(self) -> WitnessPat<'tcx> {
|
||||
fn single_pattern(self) -> WitnessPat<Cx> {
|
||||
assert_eq!(self.0.len(), 1);
|
||||
self.0.into_iter().next().unwrap()
|
||||
}
|
||||
|
||||
/// Reverses specialization by the `Missing` constructor by pushing a whole new pattern.
|
||||
fn push_pattern(&mut self, pat: WitnessPat<'tcx>) {
|
||||
fn push_pattern(&mut self, pat: WitnessPat<Cx>) {
|
||||
self.0.push(pat);
|
||||
}
|
||||
|
||||
@ -1048,7 +1061,7 @@ impl<'tcx> WitnessStack<'tcx> {
|
||||
/// pats: [(false, "foo"), _, true]
|
||||
/// result: [Enum::Variant { a: (false, "foo"), b: _ }, true]
|
||||
/// ```
|
||||
fn apply_constructor(&mut self, pcx: &PatCtxt<'_, '_, 'tcx>, ctor: &Constructor<'tcx>) {
|
||||
fn apply_constructor(&mut self, pcx: &PlaceCtxt<'_, '_, Cx>, ctor: &Constructor<Cx>) {
|
||||
let len = self.0.len();
|
||||
let arity = ctor.arity(pcx);
|
||||
let fields = self.0.drain((len - arity)..).rev().collect();
|
||||
@ -1067,9 +1080,9 @@ impl<'tcx> WitnessStack<'tcx> {
|
||||
/// Just as the `Matrix` starts with a single column, by the end of the algorithm, this has a single
|
||||
/// column, which contains the patterns that are missing for the match to be exhaustive.
|
||||
#[derive(Debug, Clone)]
|
||||
struct WitnessMatrix<'tcx>(Vec<WitnessStack<'tcx>>);
|
||||
struct WitnessMatrix<Cx: TypeCx>(Vec<WitnessStack<Cx>>);
|
||||
|
||||
impl<'tcx> WitnessMatrix<'tcx> {
|
||||
impl<Cx: TypeCx> WitnessMatrix<Cx> {
|
||||
/// New matrix with no witnesses.
|
||||
fn empty() -> Self {
|
||||
WitnessMatrix(vec![])
|
||||
@ -1084,12 +1097,12 @@ impl<'tcx> WitnessMatrix<'tcx> {
|
||||
self.0.is_empty()
|
||||
}
|
||||
/// Asserts that there is a single column and returns the patterns in it.
|
||||
fn single_column(self) -> Vec<WitnessPat<'tcx>> {
|
||||
fn single_column(self) -> Vec<WitnessPat<Cx>> {
|
||||
self.0.into_iter().map(|w| w.single_pattern()).collect()
|
||||
}
|
||||
|
||||
/// Reverses specialization by the `Missing` constructor by pushing a whole new pattern.
|
||||
fn push_pattern(&mut self, pat: WitnessPat<'tcx>) {
|
||||
fn push_pattern(&mut self, pat: WitnessPat<Cx>) {
|
||||
for witness in self.0.iter_mut() {
|
||||
witness.push_pattern(pat.clone())
|
||||
}
|
||||
@ -1098,9 +1111,9 @@ impl<'tcx> WitnessMatrix<'tcx> {
|
||||
/// Reverses specialization by `ctor`. See the section on `unspecialize` at the top of the file.
|
||||
fn apply_constructor(
|
||||
&mut self,
|
||||
pcx: &PatCtxt<'_, '_, 'tcx>,
|
||||
missing_ctors: &[Constructor<'tcx>],
|
||||
ctor: &Constructor<'tcx>,
|
||||
pcx: &PlaceCtxt<'_, '_, Cx>,
|
||||
missing_ctors: &[Constructor<Cx>],
|
||||
ctor: &Constructor<Cx>,
|
||||
report_individual_missing_ctors: bool,
|
||||
) {
|
||||
if self.is_empty() {
|
||||
@ -1160,12 +1173,12 @@ impl<'tcx> WitnessMatrix<'tcx> {
|
||||
/// - unspecialization, where we lift the results from the previous step into results for this step
|
||||
/// (using `apply_constructor` and by updating `row.useful` for each parent row).
|
||||
/// This is all explained at the top of the file.
|
||||
#[instrument(level = "debug", skip(cx, is_top_level), ret)]
|
||||
fn compute_exhaustiveness_and_usefulness<'p, 'tcx>(
|
||||
cx: &MatchCheckCtxt<'p, 'tcx>,
|
||||
matrix: &mut Matrix<'p, 'tcx>,
|
||||
#[instrument(level = "debug", skip(mcx, is_top_level), ret)]
|
||||
fn compute_exhaustiveness_and_usefulness<'a, 'p, Cx: TypeCx>(
|
||||
mcx: MatchCtxt<'a, 'p, Cx>,
|
||||
matrix: &mut Matrix<'a, 'p, Cx>,
|
||||
is_top_level: bool,
|
||||
) -> WitnessMatrix<'tcx> {
|
||||
) -> WitnessMatrix<Cx> {
|
||||
debug_assert!(matrix.rows().all(|r| r.len() == matrix.column_count()));
|
||||
|
||||
let Some(ty) = matrix.head_ty() else {
|
||||
@ -1185,7 +1198,7 @@ fn compute_exhaustiveness_and_usefulness<'p, 'tcx>(
|
||||
};
|
||||
|
||||
debug!("ty: {ty:?}");
|
||||
let pcx = &PatCtxt { cx, ty, is_top_level };
|
||||
let pcx = &PlaceCtxt { mcx, ty, is_scrutinee: is_top_level };
|
||||
|
||||
// Whether the place/column we are inspecting is known to contain valid data.
|
||||
let place_validity = matrix.place_validity[0];
|
||||
@ -1194,7 +1207,7 @@ fn compute_exhaustiveness_and_usefulness<'p, 'tcx>(
|
||||
|
||||
// Analyze the constructors present in this column.
|
||||
let ctors = matrix.heads().map(|p| p.ctor());
|
||||
let ctors_for_ty = &cx.ctors_for_ty(ty);
|
||||
let ctors_for_ty = pcx.ctors_for_ty();
|
||||
let is_integers = matches!(ctors_for_ty, ConstructorSet::Integers { .. }); // For diagnostics.
|
||||
let split_set = ctors_for_ty.split(pcx, ctors);
|
||||
let all_missing = split_set.present.is_empty();
|
||||
@ -1228,7 +1241,7 @@ fn compute_exhaustiveness_and_usefulness<'p, 'tcx>(
|
||||
// Dig into rows that match `ctor`.
|
||||
let mut spec_matrix = matrix.specialize_constructor(pcx, &ctor);
|
||||
let mut witnesses = ensure_sufficient_stack(|| {
|
||||
compute_exhaustiveness_and_usefulness(cx, &mut spec_matrix, false)
|
||||
compute_exhaustiveness_and_usefulness(mcx, &mut spec_matrix, false)
|
||||
});
|
||||
|
||||
let counts_for_exhaustiveness = match ctor {
|
||||
@ -1270,34 +1283,34 @@ fn compute_exhaustiveness_and_usefulness<'p, 'tcx>(
|
||||
|
||||
/// Indicates whether or not a given arm is useful.
|
||||
#[derive(Clone, Debug)]
|
||||
pub enum Usefulness {
|
||||
pub enum Usefulness<'p, Cx: TypeCx> {
|
||||
/// The arm is useful. This additionally carries a set of or-pattern branches that have been
|
||||
/// found to be redundant despite the overall arm being useful. Used only in the presence of
|
||||
/// or-patterns, otherwise it stays empty.
|
||||
Useful(Vec<Span>),
|
||||
Useful(Vec<&'p DeconstructedPat<'p, Cx>>),
|
||||
/// The arm is redundant and can be removed without changing the behavior of the match
|
||||
/// expression.
|
||||
Redundant,
|
||||
}
|
||||
|
||||
/// The output of checking a match for exhaustiveness and arm usefulness.
|
||||
pub struct UsefulnessReport<'p, 'tcx> {
|
||||
pub struct UsefulnessReport<'p, Cx: TypeCx> {
|
||||
/// For each arm of the input, whether that arm is useful after the arms above it.
|
||||
pub arm_usefulness: Vec<(MatchArm<'p, 'tcx>, Usefulness)>,
|
||||
pub arm_usefulness: Vec<(MatchArm<'p, Cx>, Usefulness<'p, Cx>)>,
|
||||
/// If the match is exhaustive, this is empty. If not, this contains witnesses for the lack of
|
||||
/// exhaustiveness.
|
||||
pub non_exhaustiveness_witnesses: Vec<WitnessPat<'tcx>>,
|
||||
pub non_exhaustiveness_witnesses: Vec<WitnessPat<Cx>>,
|
||||
}
|
||||
|
||||
/// Computes whether a match is exhaustive and which of its arms are useful.
|
||||
#[instrument(skip(cx, arms), level = "debug")]
|
||||
pub(crate) fn compute_match_usefulness<'p, 'tcx>(
|
||||
cx: &MatchCheckCtxt<'p, 'tcx>,
|
||||
arms: &[MatchArm<'p, 'tcx>],
|
||||
scrut_ty: Ty<'tcx>,
|
||||
) -> UsefulnessReport<'p, 'tcx> {
|
||||
let scrut_validity = ValidityConstraint::from_bool(cx.known_valid_scrutinee);
|
||||
let mut matrix = Matrix::new(cx, arms, scrut_ty, scrut_validity);
|
||||
pub fn compute_match_usefulness<'p, Cx: TypeCx>(
|
||||
cx: MatchCtxt<'_, 'p, Cx>,
|
||||
arms: &[MatchArm<'p, Cx>],
|
||||
scrut_ty: Cx::Ty,
|
||||
scrut_validity: ValidityConstraint,
|
||||
) -> UsefulnessReport<'p, Cx> {
|
||||
let mut matrix = Matrix::new(cx.wildcard_arena, arms, scrut_ty, scrut_validity);
|
||||
let non_exhaustiveness_witnesses = compute_exhaustiveness_and_usefulness(cx, &mut matrix, true);
|
||||
|
||||
let non_exhaustiveness_witnesses: Vec<_> = non_exhaustiveness_witnesses.single_column();
|
||||
@ -1308,7 +1321,7 @@ pub(crate) fn compute_match_usefulness<'p, 'tcx>(
|
||||
debug!(?arm);
|
||||
// We warn when a pattern is not useful.
|
||||
let usefulness = if arm.pat.is_useful() {
|
||||
Usefulness::Useful(arm.pat.redundant_spans())
|
||||
Usefulness::Useful(arm.pat.redundant_subpatterns())
|
||||
} else {
|
||||
Usefulness::Redundant
|
||||
};
|
||||
|
@ -1765,15 +1765,6 @@ impl<'tcx> PrivateItemsInPublicInterfacesChecker<'tcx, '_> {
|
||||
|
||||
pub fn provide(providers: &mut Providers) {
|
||||
*providers = Providers {
|
||||
visibility: |tcx, def_id| {
|
||||
// Unique types created for closures participate in type privacy checking.
|
||||
// They have visibilities inherited from the module they are defined in.
|
||||
// FIXME: Consider evaluating visibilities for closures eagerly, like for all
|
||||
// other nodes. However, unlike for others, for closures it may cause a perf
|
||||
// regression, because closure visibilities are not commonly queried.
|
||||
assert_eq!(tcx.def_kind(def_id), DefKind::Closure);
|
||||
ty::Visibility::Restricted(tcx.parent_module_from_def_id(def_id).to_def_id())
|
||||
},
|
||||
effective_visibilities,
|
||||
check_private_in_public,
|
||||
check_mod_privacy,
|
||||
|
@ -394,6 +394,7 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> {
|
||||
id: NodeId,
|
||||
parent_prefix: &[Segment],
|
||||
nested: bool,
|
||||
list_stem: bool,
|
||||
// The whole `use` item
|
||||
item: &Item,
|
||||
vis: ty::Visibility,
|
||||
@ -404,7 +405,9 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> {
|
||||
parent_prefix, use_tree, nested
|
||||
);
|
||||
|
||||
if nested {
|
||||
// Top level use tree reuses the item's id and list stems reuse their parent
|
||||
// use tree's ids, so in both cases their visibilities are already filled.
|
||||
if nested && !list_stem {
|
||||
self.r.feed_visibility(self.r.local_def_id(id), vis);
|
||||
}
|
||||
|
||||
@ -592,7 +595,7 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> {
|
||||
for &(ref tree, id) in items {
|
||||
self.build_reduced_graph_for_use_tree(
|
||||
// This particular use tree
|
||||
tree, id, &prefix, true, // The whole `use` item
|
||||
tree, id, &prefix, true, false, // The whole `use` item
|
||||
item, vis, root_span,
|
||||
);
|
||||
}
|
||||
@ -613,6 +616,7 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> {
|
||||
id,
|
||||
&prefix,
|
||||
true,
|
||||
true,
|
||||
// The whole `use` item
|
||||
item,
|
||||
ty::Visibility::Restricted(
|
||||
@ -648,6 +652,7 @@ impl<'a, 'b, 'tcx> BuildReducedGraphVisitor<'a, 'b, 'tcx> {
|
||||
item.id,
|
||||
&[],
|
||||
false,
|
||||
false,
|
||||
// The whole `use` item
|
||||
item,
|
||||
vis,
|
||||
|
@ -3078,17 +3078,25 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
|
||||
binding = self.r.resolution(module, key).try_borrow().ok().and_then(|r| r.binding);
|
||||
debug!(?binding);
|
||||
}
|
||||
|
||||
let feed_visibility = |this: &mut Self, def_id| {
|
||||
let vis = this.r.tcx.visibility(def_id).expect_local();
|
||||
this.r.feed_visibility(this.r.local_def_id(id), vis);
|
||||
};
|
||||
|
||||
let Some(binding) = binding else {
|
||||
// We could not find the method: report an error.
|
||||
let candidate = self.find_similarly_named_assoc_item(ident.name, kind);
|
||||
let path = &self.current_trait_ref.as_ref().unwrap().1.path;
|
||||
let path_names = path_names_to_string(path);
|
||||
self.report_error(span, err(ident, path_names, candidate));
|
||||
feed_visibility(self, module.def_id());
|
||||
return;
|
||||
};
|
||||
|
||||
let res = binding.res();
|
||||
let Res::Def(def_kind, id_in_trait) = res else { bug!() };
|
||||
feed_visibility(self, id_in_trait);
|
||||
|
||||
match seen_trait_items.entry(id_in_trait) {
|
||||
Entry::Occupied(entry) => {
|
||||
@ -3112,8 +3120,6 @@ impl<'a: 'ast, 'b, 'ast, 'tcx> LateResolutionVisitor<'a, 'b, 'ast, 'tcx> {
|
||||
| (DefKind::AssocFn, AssocItemKind::Fn(..))
|
||||
| (DefKind::AssocConst, AssocItemKind::Const(..)) => {
|
||||
self.r.record_partial_res(id, PartialRes::new(res));
|
||||
let vis = self.r.tcx.visibility(id_in_trait).expect_local();
|
||||
self.r.feed_visibility(self.r.local_def_id(id), vis);
|
||||
return;
|
||||
}
|
||||
_ => {}
|
||||
|
@ -1084,7 +1084,7 @@ pub struct Resolver<'a, 'tcx> {
|
||||
|
||||
next_node_id: NodeId,
|
||||
|
||||
node_id_to_def_id: FxHashMap<ast::NodeId, LocalDefId>,
|
||||
node_id_to_def_id: NodeMap<LocalDefId>,
|
||||
def_id_to_node_id: IndexVec<LocalDefId, ast::NodeId>,
|
||||
|
||||
/// Indices of unnamed struct or variant fields with unresolved attributes.
|
||||
@ -1225,10 +1225,7 @@ impl<'tcx> Resolver<'_, 'tcx> {
|
||||
);
|
||||
|
||||
// FIXME: remove `def_span` body, pass in the right spans here and call `tcx.at().create_def()`
|
||||
let def_id = self.tcx.untracked().definitions.write().create_def(parent, data);
|
||||
|
||||
let feed = self.tcx.feed_local_def_id(def_id);
|
||||
feed.def_kind(def_kind);
|
||||
let def_id = self.tcx.create_def(parent, name, def_kind);
|
||||
|
||||
// Create the definition.
|
||||
if expn_id != ExpnId::root() {
|
||||
@ -1296,7 +1293,7 @@ impl<'a, 'tcx> Resolver<'a, 'tcx> {
|
||||
|
||||
let mut def_id_to_node_id = IndexVec::default();
|
||||
assert_eq!(def_id_to_node_id.push(CRATE_NODE_ID), CRATE_DEF_ID);
|
||||
let mut node_id_to_def_id = FxHashMap::default();
|
||||
let mut node_id_to_def_id = NodeMap::default();
|
||||
node_id_to_def_id.insert(CRATE_NODE_ID, CRATE_DEF_ID);
|
||||
|
||||
let mut invocation_parents = FxHashMap::default();
|
||||
|
@ -7,6 +7,7 @@
|
||||
use crate::rustc_smir::Tables;
|
||||
use rustc_middle::ty::{self as rustc_ty, Ty as InternalTy};
|
||||
use rustc_span::Symbol;
|
||||
use stable_mir::abi::Layout;
|
||||
use stable_mir::mir::alloc::AllocId;
|
||||
use stable_mir::mir::mono::{Instance, MonoItem, StaticDef};
|
||||
use stable_mir::mir::{Mutability, Safety};
|
||||
@ -460,6 +461,14 @@ impl<'tcx> RustcInternal<'tcx> for Span {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> RustcInternal<'tcx> for Layout {
|
||||
type T = rustc_target::abi::Layout<'tcx>;
|
||||
|
||||
fn internal(&self, tables: &mut Tables<'tcx>) -> Self::T {
|
||||
tables.layouts[*self]
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx, T> RustcInternal<'tcx> for &T
|
||||
where
|
||||
T: RustcInternal<'tcx>,
|
||||
|
@ -12,6 +12,7 @@ use rustc_middle::ty::TyCtxt;
|
||||
use rustc_span::def_id::{CrateNum, DefId};
|
||||
use rustc_span::Span;
|
||||
use scoped_tls::scoped_thread_local;
|
||||
use stable_mir::abi::Layout;
|
||||
use stable_mir::ty::IndexedVal;
|
||||
use stable_mir::Error;
|
||||
use std::cell::Cell;
|
||||
@ -136,6 +137,10 @@ impl<'tcx> Tables<'tcx> {
|
||||
pub(crate) fn static_def(&mut self, did: DefId) -> stable_mir::mir::mono::StaticDef {
|
||||
stable_mir::mir::mono::StaticDef(self.create_def_id(did))
|
||||
}
|
||||
|
||||
pub(crate) fn layout_id(&mut self, layout: rustc_target::abi::Layout<'tcx>) -> Layout {
|
||||
self.layouts.create_or_fetch(layout)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn crate_num(item: &stable_mir::Crate) -> CrateNum {
|
||||
@ -180,6 +185,7 @@ where
|
||||
types: IndexMap::default(),
|
||||
instances: IndexMap::default(),
|
||||
constants: IndexMap::default(),
|
||||
layouts: IndexMap::default(),
|
||||
}));
|
||||
stable_mir::compiler_interface::run(&tables, || init(&tables, f))
|
||||
}
|
||||
|
@ -3,12 +3,19 @@
|
||||
//! This trait is currently the main interface between the Rust compiler,
|
||||
//! and the `stable_mir` crate.
|
||||
|
||||
#![allow(rustc::usage_of_qualified_ty)]
|
||||
|
||||
use rustc_abi::HasDataLayout;
|
||||
use rustc_middle::ty;
|
||||
use rustc_middle::ty::layout::{
|
||||
FnAbiOf, FnAbiOfHelpers, HasParamEnv, HasTyCtxt, LayoutOf, LayoutOfHelpers,
|
||||
};
|
||||
use rustc_middle::ty::print::{with_forced_trimmed_paths, with_no_trimmed_paths};
|
||||
use rustc_middle::ty::{
|
||||
GenericPredicates, Instance, ParamEnv, ScalarInt, TypeVisitableExt, ValTree,
|
||||
GenericPredicates, Instance, List, ParamEnv, ScalarInt, TyCtxt, TypeVisitableExt, ValTree,
|
||||
};
|
||||
use rustc_span::def_id::LOCAL_CRATE;
|
||||
use stable_mir::abi::{FnAbi, Layout, LayoutShape};
|
||||
use stable_mir::compiler_interface::Context;
|
||||
use stable_mir::mir::alloc::GlobalAlloc;
|
||||
use stable_mir::mir::mono::{InstanceDef, StaticDef};
|
||||
@ -280,7 +287,6 @@ impl<'tcx> Context for TablesWrapper<'tcx> {
|
||||
tables.tcx.mk_ty_from_kind(internal_kind).stable(&mut *tables)
|
||||
}
|
||||
|
||||
#[allow(rustc::usage_of_qualified_ty)]
|
||||
fn new_box_ty(&self, ty: stable_mir::ty::Ty) -> stable_mir::ty::Ty {
|
||||
let mut tables = self.0.borrow_mut();
|
||||
let inner = ty.internal(&mut *tables);
|
||||
@ -335,6 +341,18 @@ impl<'tcx> Context for TablesWrapper<'tcx> {
|
||||
instance.ty(tables.tcx, ParamEnv::reveal_all()).stable(&mut *tables)
|
||||
}
|
||||
|
||||
fn instance_args(&self, def: InstanceDef) -> GenericArgs {
|
||||
let mut tables = self.0.borrow_mut();
|
||||
let instance = tables.instances[def];
|
||||
instance.args.stable(&mut *tables)
|
||||
}
|
||||
|
||||
fn instance_abi(&self, def: InstanceDef) -> Result<FnAbi, Error> {
|
||||
let mut tables = self.0.borrow_mut();
|
||||
let instance = tables.instances[def];
|
||||
Ok(tables.fn_abi_of_instance(instance, List::empty())?.stable(&mut *tables))
|
||||
}
|
||||
|
||||
fn instance_def_id(&self, def: InstanceDef) -> stable_mir::DefId {
|
||||
let mut tables = self.0.borrow_mut();
|
||||
let def_id = tables.instances[def].def_id();
|
||||
@ -473,6 +491,65 @@ impl<'tcx> Context for TablesWrapper<'tcx> {
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fn ty_layout(&self, ty: Ty) -> Result<Layout, Error> {
|
||||
let mut tables = self.0.borrow_mut();
|
||||
let ty = ty.internal(&mut *tables);
|
||||
let layout = tables.layout_of(ty)?.layout;
|
||||
Ok(layout.stable(&mut *tables))
|
||||
}
|
||||
|
||||
fn layout_shape(&self, id: Layout) -> LayoutShape {
|
||||
let mut tables = self.0.borrow_mut();
|
||||
id.internal(&mut *tables).0.stable(&mut *tables)
|
||||
}
|
||||
}
|
||||
|
||||
pub struct TablesWrapper<'tcx>(pub RefCell<Tables<'tcx>>);
|
||||
|
||||
/// Implement error handling for extracting function ABI information.
|
||||
impl<'tcx> FnAbiOfHelpers<'tcx> for Tables<'tcx> {
|
||||
type FnAbiOfResult = Result<&'tcx rustc_target::abi::call::FnAbi<'tcx, ty::Ty<'tcx>>, Error>;
|
||||
|
||||
#[inline]
|
||||
fn handle_fn_abi_err(
|
||||
&self,
|
||||
err: ty::layout::FnAbiError<'tcx>,
|
||||
_span: rustc_span::Span,
|
||||
fn_abi_request: ty::layout::FnAbiRequest<'tcx>,
|
||||
) -> Error {
|
||||
Error::new(format!("Failed to get ABI for `{fn_abi_request:?}`: {err:?}"))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> LayoutOfHelpers<'tcx> for Tables<'tcx> {
|
||||
type LayoutOfResult = Result<ty::layout::TyAndLayout<'tcx>, Error>;
|
||||
|
||||
#[inline]
|
||||
fn handle_layout_err(
|
||||
&self,
|
||||
err: ty::layout::LayoutError<'tcx>,
|
||||
_span: rustc_span::Span,
|
||||
ty: ty::Ty<'tcx>,
|
||||
) -> Error {
|
||||
Error::new(format!("Failed to get layout for `{ty}`: {err}"))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> HasParamEnv<'tcx> for Tables<'tcx> {
|
||||
fn param_env(&self) -> ty::ParamEnv<'tcx> {
|
||||
ty::ParamEnv::reveal_all()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> HasTyCtxt<'tcx> for Tables<'tcx> {
|
||||
fn tcx(&self) -> TyCtxt<'tcx> {
|
||||
self.tcx
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> HasDataLayout for Tables<'tcx> {
|
||||
fn data_layout(&self) -> &rustc_abi::TargetDataLayout {
|
||||
self.tcx.data_layout()
|
||||
}
|
||||
}
|
||||
|
242
compiler/rustc_smir/src/rustc_smir/convert/abi.rs
Normal file
242
compiler/rustc_smir/src/rustc_smir/convert/abi.rs
Normal file
@ -0,0 +1,242 @@
|
||||
//! Conversion of internal Rust compiler `rustc_target::abi` and `rustc_abi` items to stable ones.
|
||||
|
||||
#![allow(rustc::usage_of_qualified_ty)]
|
||||
|
||||
use crate::rustc_smir::{Stable, Tables};
|
||||
use rustc_middle::ty;
|
||||
use rustc_target::abi::call::Conv;
|
||||
use stable_mir::abi::{
|
||||
ArgAbi, CallConvention, FieldsShape, FnAbi, Layout, LayoutShape, PassMode, TagEncoding,
|
||||
TyAndLayout, ValueAbi, VariantsShape,
|
||||
};
|
||||
use stable_mir::ty::{Align, IndexedVal, Size, VariantIdx};
|
||||
use stable_mir::{opaque, Opaque};
|
||||
|
||||
impl<'tcx> Stable<'tcx> for rustc_target::abi::VariantIdx {
|
||||
type T = VariantIdx;
|
||||
fn stable(&self, _: &mut Tables<'tcx>) -> Self::T {
|
||||
VariantIdx::to_val(self.as_usize())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> Stable<'tcx> for rustc_abi::Endian {
|
||||
type T = stable_mir::target::Endian;
|
||||
|
||||
fn stable(&self, _tables: &mut Tables<'tcx>) -> Self::T {
|
||||
match self {
|
||||
rustc_abi::Endian::Little => stable_mir::target::Endian::Little,
|
||||
rustc_abi::Endian::Big => stable_mir::target::Endian::Big,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> Stable<'tcx> for rustc_target::abi::TyAndLayout<'tcx, ty::Ty<'tcx>> {
|
||||
type T = TyAndLayout;
|
||||
|
||||
fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T {
|
||||
TyAndLayout { ty: self.ty.stable(tables), layout: self.layout.stable(tables) }
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> Stable<'tcx> for rustc_target::abi::Layout<'tcx> {
|
||||
type T = Layout;
|
||||
|
||||
fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T {
|
||||
tables.layout_id(*self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> Stable<'tcx>
|
||||
for rustc_abi::LayoutS<rustc_target::abi::FieldIdx, rustc_target::abi::VariantIdx>
|
||||
{
|
||||
type T = LayoutShape;
|
||||
|
||||
fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T {
|
||||
LayoutShape {
|
||||
fields: self.fields.stable(tables),
|
||||
variants: self.variants.stable(tables),
|
||||
abi: self.abi.stable(tables),
|
||||
abi_align: self.align.abi.stable(tables),
|
||||
size: self.size.stable(tables),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> Stable<'tcx> for rustc_target::abi::call::FnAbi<'tcx, ty::Ty<'tcx>> {
|
||||
type T = FnAbi;
|
||||
|
||||
fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T {
|
||||
assert!(self.args.len() >= self.fixed_count as usize);
|
||||
assert!(!self.c_variadic || matches!(self.conv, Conv::C));
|
||||
FnAbi {
|
||||
args: self.args.as_ref().stable(tables),
|
||||
ret: self.ret.stable(tables),
|
||||
fixed_count: self.fixed_count,
|
||||
conv: self.conv.stable(tables),
|
||||
c_variadic: self.c_variadic,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> Stable<'tcx> for rustc_target::abi::call::ArgAbi<'tcx, ty::Ty<'tcx>> {
|
||||
type T = ArgAbi;
|
||||
|
||||
fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T {
|
||||
ArgAbi {
|
||||
ty: self.layout.ty.stable(tables),
|
||||
layout: self.layout.layout.stable(tables),
|
||||
mode: self.mode.stable(tables),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> Stable<'tcx> for rustc_target::abi::call::Conv {
|
||||
type T = CallConvention;
|
||||
|
||||
fn stable(&self, _tables: &mut Tables<'tcx>) -> Self::T {
|
||||
match self {
|
||||
Conv::C => CallConvention::C,
|
||||
Conv::Rust => CallConvention::Rust,
|
||||
Conv::Cold => CallConvention::Cold,
|
||||
Conv::PreserveMost => CallConvention::PreserveMost,
|
||||
Conv::PreserveAll => CallConvention::PreserveAll,
|
||||
Conv::ArmAapcs => CallConvention::ArmAapcs,
|
||||
Conv::CCmseNonSecureCall => CallConvention::CCmseNonSecureCall,
|
||||
Conv::Msp430Intr => CallConvention::Msp430Intr,
|
||||
Conv::PtxKernel => CallConvention::PtxKernel,
|
||||
Conv::X86Fastcall => CallConvention::X86Fastcall,
|
||||
Conv::X86Intr => CallConvention::X86Intr,
|
||||
Conv::X86Stdcall => CallConvention::X86Stdcall,
|
||||
Conv::X86ThisCall => CallConvention::X86ThisCall,
|
||||
Conv::X86VectorCall => CallConvention::X86VectorCall,
|
||||
Conv::X86_64SysV => CallConvention::X86_64SysV,
|
||||
Conv::X86_64Win64 => CallConvention::X86_64Win64,
|
||||
Conv::AmdGpuKernel => CallConvention::AmdGpuKernel,
|
||||
Conv::AvrInterrupt => CallConvention::AvrInterrupt,
|
||||
Conv::AvrNonBlockingInterrupt => CallConvention::AvrNonBlockingInterrupt,
|
||||
Conv::RiscvInterrupt { .. } => CallConvention::RiscvInterrupt,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> Stable<'tcx> for rustc_target::abi::call::PassMode {
|
||||
type T = PassMode;
|
||||
|
||||
fn stable(&self, _tables: &mut Tables<'tcx>) -> Self::T {
|
||||
match self {
|
||||
rustc_target::abi::call::PassMode::Ignore => PassMode::Ignore,
|
||||
rustc_target::abi::call::PassMode::Direct(attr) => PassMode::Direct(opaque(attr)),
|
||||
rustc_target::abi::call::PassMode::Pair(first, second) => {
|
||||
PassMode::Pair(opaque(first), opaque(second))
|
||||
}
|
||||
rustc_target::abi::call::PassMode::Cast { pad_i32, cast } => {
|
||||
PassMode::Cast { pad_i32: *pad_i32, cast: opaque(cast) }
|
||||
}
|
||||
rustc_target::abi::call::PassMode::Indirect { attrs, meta_attrs, on_stack } => {
|
||||
PassMode::Indirect {
|
||||
attrs: opaque(attrs),
|
||||
meta_attrs: opaque(meta_attrs),
|
||||
on_stack: *on_stack,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> Stable<'tcx> for rustc_abi::FieldsShape<rustc_target::abi::FieldIdx> {
|
||||
type T = FieldsShape;
|
||||
|
||||
fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T {
|
||||
match self {
|
||||
rustc_abi::FieldsShape::Primitive => FieldsShape::Primitive,
|
||||
rustc_abi::FieldsShape::Union(count) => FieldsShape::Union(*count),
|
||||
rustc_abi::FieldsShape::Array { stride, count } => {
|
||||
FieldsShape::Array { stride: stride.stable(tables), count: *count }
|
||||
}
|
||||
rustc_abi::FieldsShape::Arbitrary { offsets, .. } => {
|
||||
FieldsShape::Arbitrary { offsets: offsets.iter().as_slice().stable(tables) }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> Stable<'tcx>
|
||||
for rustc_abi::Variants<rustc_target::abi::FieldIdx, rustc_target::abi::VariantIdx>
|
||||
{
|
||||
type T = VariantsShape;
|
||||
|
||||
fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T {
|
||||
match self {
|
||||
rustc_abi::Variants::Single { index } => {
|
||||
VariantsShape::Single { index: index.stable(tables) }
|
||||
}
|
||||
rustc_abi::Variants::Multiple { tag, tag_encoding, tag_field, variants } => {
|
||||
VariantsShape::Multiple {
|
||||
tag: tag.stable(tables),
|
||||
tag_encoding: tag_encoding.stable(tables),
|
||||
tag_field: *tag_field,
|
||||
variants: variants.iter().as_slice().stable(tables),
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> Stable<'tcx> for rustc_abi::TagEncoding<rustc_target::abi::VariantIdx> {
|
||||
type T = TagEncoding;
|
||||
|
||||
fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T {
|
||||
match self {
|
||||
rustc_abi::TagEncoding::Direct => TagEncoding::Direct,
|
||||
rustc_abi::TagEncoding::Niche { untagged_variant, niche_variants, niche_start } => {
|
||||
TagEncoding::Niche {
|
||||
untagged_variant: untagged_variant.stable(tables),
|
||||
niche_variants: niche_variants.stable(tables),
|
||||
niche_start: *niche_start,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> Stable<'tcx> for rustc_abi::Abi {
|
||||
type T = ValueAbi;
|
||||
|
||||
fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T {
|
||||
match *self {
|
||||
rustc_abi::Abi::Uninhabited => ValueAbi::Uninhabited,
|
||||
rustc_abi::Abi::Scalar(scalar) => ValueAbi::Scalar(scalar.stable(tables)),
|
||||
rustc_abi::Abi::ScalarPair(first, second) => {
|
||||
ValueAbi::ScalarPair(first.stable(tables), second.stable(tables))
|
||||
}
|
||||
rustc_abi::Abi::Vector { element, count } => {
|
||||
ValueAbi::Vector { element: element.stable(tables), count }
|
||||
}
|
||||
rustc_abi::Abi::Aggregate { sized } => ValueAbi::Aggregate { sized },
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> Stable<'tcx> for rustc_abi::Size {
|
||||
type T = Size;
|
||||
|
||||
fn stable(&self, _tables: &mut Tables<'tcx>) -> Self::T {
|
||||
self.bytes_usize()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> Stable<'tcx> for rustc_abi::Align {
|
||||
type T = Align;
|
||||
|
||||
fn stable(&self, _tables: &mut Tables<'tcx>) -> Self::T {
|
||||
self.bytes()
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> Stable<'tcx> for rustc_abi::Scalar {
|
||||
type T = Opaque;
|
||||
|
||||
fn stable(&self, _tables: &mut Tables<'tcx>) -> Self::T {
|
||||
opaque(self)
|
||||
}
|
||||
}
|
@ -37,6 +37,7 @@ impl<'tcx> Stable<'tcx> for mir::Body<'tcx> {
|
||||
self.arg_count,
|
||||
self.var_debug_info.iter().map(|info| info.stable(tables)).collect(),
|
||||
self.spread_arg.stable(tables),
|
||||
self.span.stable(tables),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
@ -1,10 +1,10 @@
|
||||
//! Conversion of internal Rust compiler items to stable ones.
|
||||
|
||||
use rustc_target::abi::FieldIdx;
|
||||
use stable_mir::ty::{IndexedVal, VariantIdx};
|
||||
|
||||
use crate::rustc_smir::{Stable, Tables};
|
||||
|
||||
mod abi;
|
||||
mod error;
|
||||
mod mir;
|
||||
mod ty;
|
||||
@ -26,13 +26,6 @@ impl<'tcx> Stable<'tcx> for FieldIdx {
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> Stable<'tcx> for rustc_target::abi::VariantIdx {
|
||||
type T = VariantIdx;
|
||||
fn stable(&self, _: &mut Tables<'tcx>) -> Self::T {
|
||||
VariantIdx::to_val(self.as_usize())
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> Stable<'tcx> for rustc_hir::CoroutineSource {
|
||||
type T = stable_mir::mir::CoroutineSource;
|
||||
fn stable(&self, _: &mut Tables<'tcx>) -> Self::T {
|
||||
@ -79,14 +72,3 @@ impl<'tcx> Stable<'tcx> for rustc_span::Span {
|
||||
tables.create_span(*self)
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> Stable<'tcx> for rustc_abi::Endian {
|
||||
type T = stable_mir::target::Endian;
|
||||
|
||||
fn stable(&self, _tables: &mut Tables<'tcx>) -> Self::T {
|
||||
match self {
|
||||
rustc_abi::Endian::Little => stable_mir::target::Endian::Little,
|
||||
rustc_abi::Endian::Big => stable_mir::target::Endian::Big,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -12,9 +12,11 @@ use rustc_middle::mir;
|
||||
use rustc_middle::mir::interpret::AllocId;
|
||||
use rustc_middle::ty::{self, Instance, Ty, TyCtxt};
|
||||
use rustc_span::def_id::{CrateNum, DefId, LOCAL_CRATE};
|
||||
use stable_mir::abi::Layout;
|
||||
use stable_mir::mir::mono::InstanceDef;
|
||||
use stable_mir::ty::{ConstId, Span};
|
||||
use stable_mir::ItemKind;
|
||||
use stable_mir::{CtorKind, ItemKind};
|
||||
use std::ops::RangeInclusive;
|
||||
use tracing::debug;
|
||||
|
||||
use crate::rustc_internal::IndexMap;
|
||||
@ -32,6 +34,7 @@ pub struct Tables<'tcx> {
|
||||
pub(crate) types: IndexMap<Ty<'tcx>, stable_mir::ty::Ty>,
|
||||
pub(crate) instances: IndexMap<ty::Instance<'tcx>, InstanceDef>,
|
||||
pub(crate) constants: IndexMap<mir::Const<'tcx>, ConstId>,
|
||||
pub(crate) layouts: IndexMap<rustc_target::abi::Layout<'tcx>, Layout>,
|
||||
}
|
||||
|
||||
impl<'tcx> Tables<'tcx> {
|
||||
@ -85,7 +88,6 @@ pub(crate) fn new_item_kind(kind: DefKind) -> ItemKind {
|
||||
| DefKind::Field
|
||||
| DefKind::LifetimeParam
|
||||
| DefKind::Impl { .. }
|
||||
| DefKind::Ctor(_, _)
|
||||
| DefKind::GlobalAsm => {
|
||||
unreachable!("Not a valid item kind: {kind:?}");
|
||||
}
|
||||
@ -94,6 +96,8 @@ pub(crate) fn new_item_kind(kind: DefKind) -> ItemKind {
|
||||
ItemKind::Const
|
||||
}
|
||||
DefKind::Static(_) => ItemKind::Static,
|
||||
DefKind::Ctor(_, rustc_hir::def::CtorKind::Const) => ItemKind::Ctor(CtorKind::Const),
|
||||
DefKind::Ctor(_, rustc_hir::def::CtorKind::Fn) => ItemKind::Ctor(CtorKind::Fn),
|
||||
}
|
||||
}
|
||||
|
||||
@ -162,3 +166,13 @@ where
|
||||
(self.0.stable(tables), self.1.stable(tables))
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx, T> Stable<'tcx> for RangeInclusive<T>
|
||||
where
|
||||
T: Stable<'tcx>,
|
||||
{
|
||||
type T = RangeInclusive<T::T>;
|
||||
fn stable(&self, tables: &mut Tables<'tcx>) -> Self::T {
|
||||
RangeInclusive::new(self.start().stable(tables), self.end().stable(tables))
|
||||
}
|
||||
}
|
||||
|
@ -1578,6 +1578,7 @@ supported_targets! {
|
||||
("armv7k-apple-watchos", armv7k_apple_watchos),
|
||||
("arm64_32-apple-watchos", arm64_32_apple_watchos),
|
||||
("x86_64-apple-watchos-sim", x86_64_apple_watchos_sim),
|
||||
("aarch64-apple-watchos", aarch64_apple_watchos),
|
||||
("aarch64-apple-watchos-sim", aarch64_apple_watchos_sim),
|
||||
|
||||
("armebv7r-none-eabi", armebv7r_none_eabi),
|
||||
|
@ -0,0 +1,19 @@
|
||||
use crate::spec::base::apple::{opts, Arch};
|
||||
use crate::spec::{Target, TargetOptions};
|
||||
|
||||
pub fn target() -> Target {
|
||||
let base = opts("watchos", Arch::Arm64);
|
||||
Target {
|
||||
llvm_target: "aarch64-apple-watchos".into(),
|
||||
pointer_width: 64,
|
||||
data_layout: "e-m:o-i64:64-i128:128-n32:64-S128".into(),
|
||||
arch: "aarch64".into(),
|
||||
options: TargetOptions {
|
||||
features: "+v8a,+neon,+fp-armv8,+apple-a7".into(),
|
||||
max_atomic_width: Some(128),
|
||||
dynamic_linking: false,
|
||||
position_independent_executables: true,
|
||||
..base
|
||||
},
|
||||
}
|
||||
}
|
@ -7,23 +7,18 @@ pub fn target() -> Target {
|
||||
base.supported_sanitizers = SanitizerSet::ADDRESS | SanitizerSet::THREAD;
|
||||
|
||||
Target {
|
||||
// Clang automatically chooses a more specific target based on
|
||||
// IPHONEOS_DEPLOYMENT_TARGET.
|
||||
// This is required for the target to pick the right
|
||||
// MACH-O commands, so we do too.
|
||||
llvm_target: ios_llvm_target(arch).into(),
|
||||
pointer_width: 64,
|
||||
data_layout: "e-m:o-i64:64-i128:128-n32:64-S128".into(),
|
||||
arch: arch.target_arch(),
|
||||
options: TargetOptions {
|
||||
features: "+neon,+fp-armv8,+apple-a12,+v8.3a,+paca,+pacg".into(),
|
||||
features: "+neon,+fp-armv8,+apple-a12,+v8.3a,+pauth".into(),
|
||||
max_atomic_width: Some(128),
|
||||
forces_embed_bitcode: true,
|
||||
frame_pointer: FramePointer::NonLeaf,
|
||||
bitcode_llvm_cmdline: "-triple\0\
|
||||
arm64e-apple-ios14.1.0\0\
|
||||
-emit-obj\0\
|
||||
-disable-llvm-passes\0\
|
||||
-target-abi\0\
|
||||
darwinpcs\0\
|
||||
-Os\0"
|
||||
.into(),
|
||||
..base
|
||||
},
|
||||
}
|
||||
|
@ -11,7 +11,7 @@
|
||||
//! * bidirectional-normalizes-to: If `A` and `B` are both projections, and both
|
||||
//! may apply, then we can compute the "intersection" of both normalizes-to by
|
||||
//! performing them together. This is used specifically to resolve ambiguities.
|
||||
use super::EvalCtxt;
|
||||
use super::{EvalCtxt, GoalSource};
|
||||
use rustc_infer::infer::DefineOpaqueTypes;
|
||||
use rustc_infer::traits::query::NoSolution;
|
||||
use rustc_middle::traits::solve::{Certainty, Goal, QueryResult};
|
||||
@ -89,11 +89,10 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||
ty::TermKind::Const(_) => {
|
||||
if let Some(alias) = term.to_alias_ty(self.tcx()) {
|
||||
let term = self.next_term_infer_of_kind(term);
|
||||
self.add_goal(Goal::new(
|
||||
self.tcx(),
|
||||
param_env,
|
||||
ty::NormalizesTo { alias, term },
|
||||
));
|
||||
self.add_goal(
|
||||
GoalSource::Misc,
|
||||
Goal::new(self.tcx(), param_env, ty::NormalizesTo { alias, term }),
|
||||
);
|
||||
self.try_evaluate_added_goals()?;
|
||||
Ok(Some(self.resolve_vars_if_possible(term)))
|
||||
} else {
|
||||
@ -109,7 +108,10 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||
opaque: ty::AliasTy<'tcx>,
|
||||
term: ty::Term<'tcx>,
|
||||
) -> QueryResult<'tcx> {
|
||||
self.add_goal(Goal::new(self.tcx(), param_env, ty::NormalizesTo { alias: opaque, term }));
|
||||
self.add_goal(
|
||||
GoalSource::Misc,
|
||||
Goal::new(self.tcx(), param_env, ty::NormalizesTo { alias: opaque, term }),
|
||||
);
|
||||
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
|
||||
}
|
||||
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user