Auto merge of #3234 - RalfJung:rustup, r=RalfJung

Rustup
This commit is contained in:
bors 2023-12-21 16:46:51 +00:00
commit 23efae075a
216 changed files with 3971 additions and 1378 deletions

View File

@ -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"

View File

@ -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)
===========================

View File

@ -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),
}
}

View File

@ -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) => {

View File

@ -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 {

View File

@ -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);

View File

@ -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![];

View File

@ -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

View File

@ -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);

View File

@ -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>,
}

View File

@ -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 });
}
}
}

View File

@ -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);
}

View File

@ -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)

View File

@ -188,7 +188,7 @@ fn cs_clone(
}
let expr = match *vdata {
VariantData::Struct(..) => {
VariantData::Struct { .. } => {
let fields = all_fields
.iter()
.map(|field| {

View File

@ -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())
}

View File

@ -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() {

View File

@ -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);
}
}),

View File

@ -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]);

View File

@ -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

View File

@ -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]]

View File

@ -1,3 +1,3 @@
[toolchain]
channel = "nightly-2023-11-25"
channel = "nightly-2023-12-19"
components = ["rust-src", "rustc-dev", "llvm-tools"]

View File

@ -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

View File

@ -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;

View File

@ -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),
}
}

View File

@ -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;
}
}

View File

@ -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"),
}
}

View File

@ -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)
}

View 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

View 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
}

View File

@ -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 });

View File

@ -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(

View File

@ -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.

View File

@ -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,

View File

@ -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>>>;

View File

@ -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,

View File

@ -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 {

View File

@ -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,
}
}

View File

@ -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
}

View File

@ -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));

View File

@ -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(

View File

@ -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(..) => {

View File

@ -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();

View File

@ -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)

View File

@ -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> {

View File

@ -14,7 +14,6 @@
)]
#![cfg_attr(feature = "nightly", allow(internal_features))]
#[cfg(feature = "nightly")]
pub mod bit_set;
#[cfg(feature = "nightly")]
pub mod interval;

View File

@ -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"
}

View File

@ -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)
}

View File

@ -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 }
}),

View File

@ -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());

View File

@ -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

View File

@ -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)
})
}
}

View File

@ -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>);

View File

@ -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);
}

View File

@ -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"
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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

View File

@ -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);
}

View File

@ -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

View File

@ -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" }
}

View File

@ -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,

View File

@ -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

View File

@ -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")?,

View File

@ -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

View File

@ -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.

View File

@ -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>>,

View File

@ -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)),

View File

@ -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

View File

@ -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,

View File

@ -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>,

View File

@ -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()
{

View File

@ -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>(

View File

@ -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,

View File

@ -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
}

View File

@ -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()

View File

@ -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}");

View File

@ -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);
}

View File

@ -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",
]

View File

@ -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`.

View File

@ -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 {

View File

@ -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)
}

View File

@ -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.

View File

@ -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()
}
}

View File

@ -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>>) {

View File

@ -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
};

View File

@ -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,

View File

@ -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,

View File

@ -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;
}
_ => {}

View File

@ -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();

View File

@ -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>,

View File

@ -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))
}

View File

@ -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()
}
}

View 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)
}
}

View File

@ -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),
)
}
}

View File

@ -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,
}
}
}

View File

@ -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))
}
}

View File

@ -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),

View File

@ -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
},
}
}

View File

@ -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
},
}

View File

@ -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